1 module hunt.http.codec.http.model.Cookie;
2 
3 import hunt.lang.exception;
4 
5 import std.array;
6 import std.conv;
7 import std.string;
8 
9 class Cookie {
10 
11 	//
12 	// The value of the cookie itself.
13 	//
14 
15 	private string name; // NAME= ... "$Name" style is reserved
16 	private string value; // value of NAME
17 
18 	//
19 	// Attributes encoded in the header's cookie fields.
20 	//
21 
22 	private string comment; // ;Comment=VALUE ... describes cookie's use
23 	// ;Discard ... implied by maxAge < 0
24 	private string domain; // ;Domain=VALUE ... domain that sees cookie
25 	private int maxAge = -1; // ;Max-Age=VALUE ... cookies auto-expire
26 	private string path; // ;Path=VALUE ... URLs that see the cookie
27 	private bool secure; // ;Secure ... e.g. use SSL
28 	private int _version = 0; // ;Version=1 ... means RFC 2109++ style
29 	private bool _isHttpOnly = false;
30 
31 	this() {
32 
33 	}
34 
35 	/**
36 	 * Constructs a cookie with the specified name and value.
37 	 *
38 	 * <p>
39 	 * The name must conform to RFC 2109. However, vendors may provide a
40 	 * configuration option that allows cookie names conforming to the original
41 	 * Netscape Cookie Specification to be accepted.
42 	 *
43 	 * <p>
44 	 * The name of a cookie cannot be changed once the cookie has been created.
45 	 *
46 	 * <p>
47 	 * The value can be anything the server chooses to send. Its value is
48 	 * probably of interest only to the server. The cookie's value can be
49 	 * changed after creation with the <code>setValue</code> method.
50 	 *
51 	 * <p>
52 	 * By default, cookies are created according to the Netscape cookie
53 	 * specification. The version can be changed with the
54 	 * <code>setVersion</code> method.
55 	 *
56 	 * @param name
57 	 *            the name of the cookie
58 	 *
59 	 * @param value
60 	 *            the value of the cookie
61 	 *
62 	 * @throws IllegalArgumentException
63 	 *             if the cookie name is null or empty or contains any illegal
64 	 *             characters (for example, a comma, space, or semicolon) or
65 	 *             matches a token reserved for use by the cookie protocol
66 	 *
67 	 * @see #setValue
68 	 * @see #setVersion
69 	 */
70 	this(string name, string value, int expires=-1, 
71 		string path = "/", string domain = null, 
72 		bool secure = false, bool httpOnly = true) {
73 		if (name.empty) {
74 			throw new IllegalArgumentException("the cookie name is empty");
75 		}
76 
77 		this.name = name;
78 		this.value = value;
79 		this.maxAge = expires;
80 		this.path = path;
81 		this.secure = secure;
82 		this.domain = domain;
83 		this._isHttpOnly = httpOnly;
84 	}
85 
86 	/**
87 	 * Specifies a comment that describes a cookie's purpose. The comment is
88 	 * useful if the browser presents the cookie to the user. Comments are not
89 	 * supported by Netscape Version 0 cookies.
90 	 *
91 	 * @param purpose
92 	 *            a <code>string</code> specifying the comment to display to the
93 	 *            user
94 	 *
95 	 * @see #getComment
96 	 */
97 	void setComment(string purpose) {
98 		comment = purpose;
99 	}
100 
101 	/**
102 	 * Returns the comment describing the purpose of this cookie, or
103 	 * <code>null</code> if the cookie has no comment.
104 	 *
105 	 * @return the comment of the cookie, or <code>null</code> if unspecified
106 	 *
107 	 * @see #setComment
108 	 */
109 	string getComment() {
110 		return comment;
111 	}
112 
113 	/**
114 	 *
115 	 * Specifies the domain within which this cookie should be presented.
116 	 *
117 	 * <p>
118 	 * The form of the domain name is specified by RFC 2109. A domain name
119 	 * begins with a dot (<code>.foo.com</code>) and means that the cookie is
120 	 * visible to servers in a specified Domain Name System (DNS) zone (for
121 	 * example, <code>www.foo.com</code>, but not <code>a.b.foo.com</code>). By
122 	 * default, cookies are only returned to the server that sent them.
123 	 *
124 	 * @param domain
125 	 *            the domain name within which this cookie is visible; form is
126 	 *            according to RFC 2109
127 	 *
128 	 * @see #getDomain
129 	 */
130 	void setDomain(string domain) {
131 		this.domain = domain.toLower(); // IE allegedly needs
132 															// this
133 	}
134 
135 	/**
136 	 * Gets the domain name of this Cookie.
137 	 *
138 	 * <p>
139 	 * Domain names are formatted according to RFC 2109.
140 	 *
141 	 * @return the domain name of this Cookie
142 	 *
143 	 * @see #setDomain
144 	 */
145 	string getDomain() {
146 		return domain;
147 	}
148 
149 	/**
150 	 * Sets the maximum age in seconds for this Cookie.
151 	 *
152 	 * <p>
153 	 * A positive value indicates that the cookie will expire after that many
154 	 * seconds have passed. Note that the value is the <i>maximum</i> age when
155 	 * the cookie will expire, not the cookie's current age.
156 	 *
157 	 * <p>
158 	 * A negative value means that the cookie is not stored persistently and
159 	 * will be deleted when the Web browser exits. A zero value causes the
160 	 * cookie to be deleted.
161 	 *
162 	 * @param expiry
163 	 *            an integer specifying the maximum age of the cookie in
164 	 *            seconds; if negative, means the cookie is not stored; if zero,
165 	 *            deletes the cookie
166 	 *
167 	 * @see #getMaxAge
168 	 */
169 	void setMaxAge(int expiry) {
170 		maxAge = expiry;
171 	}
172 
173 	/**
174 	 * Gets the maximum age in seconds of this Cookie.
175 	 *
176 	 * <p>
177 	 * By default, <code>-1</code> is returned, which indicates that the cookie
178 	 * will persist until browser shutdown.
179 	 *
180 	 * @return an integer specifying the maximum age of the cookie in seconds;
181 	 *         if negative, means the cookie persists until browser shutdown
182 	 *
183 	 * @see #setMaxAge
184 	 */
185 	int getMaxAge() {
186 		return maxAge;
187 	}
188 
189 	/**
190 	 * Specifies a path for the cookie to which the client should return the
191 	 * cookie.
192 	 *
193 	 * <p>
194 	 * The cookie is visible to all the pages in the directory you specify, and
195 	 * all the pages in that directory's subdirectories. A cookie's path, for
196 	 * example, <i>/catalog</i>, which makes the cookie visible to all
197 	 * directories on the server under <i>/catalog</i>.
198 	 *
199 	 * <p>
200 	 * Consult RFC 2109 (available on the Internet) for more information on
201 	 * setting path names for cookies.
202 	 *
203 	 *
204 	 * @param uri
205 	 *            a <code>string</code> specifying a path
206 	 *
207 	 * @see #getPath
208 	 */
209 	void setPath(string uri) {
210 		path = uri;
211 	}
212 
213 	/**
214 	 * Returns the path on the server to which the browser returns this cookie.
215 	 * The cookie is visible to all subpaths on the server.
216 	 *
217 	 * @return a <code>string</code> specifying a path , for example,
218 	 *         <i>/catalog</i>
219 	 *
220 	 * @see #setPath
221 	 */
222 	string getPath() {
223 		return path;
224 	}
225 
226 	/**
227 	 * Indicates to the browser whether the cookie should only be sent using a
228 	 * secure protocol, such as HTTPS or SSL.
229 	 *
230 	 * <p>
231 	 * The default value is <code>false</code>.
232 	 *
233 	 * @param flag
234 	 *            if <code>true</code>, sends the cookie from the browser to the
235 	 *            server only when using a secure protocol; if
236 	 *            <code>false</code>, sent on any protocol
237 	 *
238 	 * @see #getSecure
239 	 */
240 	void setSecure(bool flag) {
241 		secure = flag;
242 	}
243 
244 	/**
245 	 * Returns <code>true</code> if the browser is sending cookies only over a
246 	 * secure protocol, or <code>false</code> if the browser can send cookies
247 	 * using any protocol.
248 	 *
249 	 * @return <code>true</code> if the browser uses a secure protocol,
250 	 *         <code>false</code> otherwise
251 	 *
252 	 * @see #setSecure
253 	 */
254 	bool getSecure() {
255 		return secure;
256 	}
257 
258 	void setName(string name) {
259 		this.name = name;
260 	}
261 
262 	/**
263 	 * Returns the name of the cookie. The name cannot be changed after
264 	 * creation.
265 	 *
266 	 * @return the name of the cookie
267 	 */
268 	string getName() {
269 		return name;
270 	}
271 
272 	/**
273 	 * Assigns a new value to this Cookie.
274 	 * 
275 	 * <p>
276 	 * If you use a binary value, you may want to use BASE64 encoding.
277 	 *
278 	 * <p>
279 	 * With Version 0 cookies, values should not contain white space, brackets,
280 	 * parentheses, equals signs, commas, double quotes, slashes, question
281 	 * marks, at signs, colons, and semicolons. Empty values may not behave the
282 	 * same way on all browsers.
283 	 *
284 	 * @param newValue
285 	 *            the new value of the cookie
286 	 *
287 	 * @see #getValue
288 	 */
289 	void setValue(string newValue) {
290 		value = newValue;
291 	}
292 
293 	/**
294 	 * Gets the current value of this Cookie.
295 	 *
296 	 * @return the current value of this Cookie
297 	 *
298 	 * @see #setValue
299 	 */
300 	string getValue() {
301 		return value;
302 	}
303 
304 	/**
305 	 * Returns the version of the protocol this cookie complies with. Version 1
306 	 * complies with RFC 2109, and version 0 complies with the original cookie
307 	 * specification drafted by Netscape. Cookies provided by a browser use and
308 	 * identify the browser's cookie version.
309 	 * 
310 	 * @return 0 if the cookie complies with the original Netscape
311 	 *         specification; 1 if the cookie complies with RFC 2109
312 	 *
313 	 * @see #setVersion
314 	 */
315 	int getVersion() {
316 		return _version;
317 	}
318 
319 	/**
320 	 * Sets the version of the cookie protocol that this Cookie complies with.
321 	 *
322 	 * <p>
323 	 * Version 0 complies with the original Netscape cookie specification.
324 	 * Version 1 complies with RFC 2109.
325 	 *
326 	 * <p>
327 	 * Since RFC 2109 is still somewhat new, consider version 1 as experimental;
328 	 * do not use it yet on production sites.
329 	 *
330 	 * @param v
331 	 *            0 if the cookie should comply with the original Netscape
332 	 *            specification; 1 if the cookie should comply with RFC 2109
333 	 *
334 	 * @see #getVersion
335 	 */
336 	void setVersion(int v) {
337 		_version = v;
338 	}
339 
340 	/**
341 	 * Overrides the standard <code>java.lang.Object.clone</code> method to
342 	 * return a copy of this Cookie.
343 	 */
344 	// Object clone() {
345 	// 	try {
346 	// 		return super.clone();
347 	// 	} catch (NotSupportedException e) {
348 	// 		throw new RuntimeException(e.getMessage());
349 	// 	}
350 	// }
351 
352 	/**
353 	 * Marks or unmarks this Cookie as <i>HttpOnly</i>.
354 	 *
355 	 * <p>
356 	 * If <tt>isHttpOnly</tt> is set to <tt>true</tt>, this cookie is marked as
357 	 * <i>HttpOnly</i>, by adding the <tt>HttpOnly</tt> attribute to it.
358 	 *
359 	 * <p>
360 	 * <i>HttpOnly</i> cookies are not supposed to be exposed to client-side
361 	 * scripting code, and may therefore help mitigate certain kinds of
362 	 * cross-site scripting attacks.
363 	 *
364 	 * @param isHttpOnly
365 	 *            true if this cookie is to be marked as <i>HttpOnly</i>, false
366 	 *            otherwise
367 	 *
368 	 */
369 	void setHttpOnly(bool isHttpOnly) {
370 		this._isHttpOnly = isHttpOnly;
371 	}
372 
373 	/**
374 	 * Checks whether this Cookie has been marked as <i>HttpOnly</i>.
375 	 *
376 	 * @return true if this Cookie has been marked as <i>HttpOnly</i>, false
377 	 *         otherwise
378 	 *
379 	 */
380 	bool isHttpOnly() {
381 		return _isHttpOnly;
382 	}
383 
384 	override
385 	string toString() {
386 		return "Cookie [name=" ~ name ~ ", value=" ~ value ~ ", comment=" ~ comment ~ 
387 			", domain=" ~ domain ~ ", maxAge=" ~ maxAge.to!string ~ ", path=" ~ path ~ ", secure=" ~ 
388 			to!string(secure) ~  ", version=" ~ to!string(_version) ~ ", isHttpOnly=" ~ 
389 			to!string(_isHttpOnly) ~ "]";
390 	}
391 
392 }