[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Web / HttpCookie.cs
blobf758a643bb7e6a17c61b6357cb1475528d6ad5cc
1 //------------------------------------------------------------------------------
2 // <copyright file="HttpCookie.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 /*
8 * HttpCookie - collection + name + path
10 * Copyright (c) 1998 Microsoft Corporation
13 namespace System.Web {
14 using System.Text;
15 using System.Collections;
16 using System.Collections.Specialized;
17 using System.Globalization;
18 using System.Security.Permissions;
19 using System.Web.Configuration;
20 using System.Web.Management;
21 using Util;
24 /// <devdoc>
25 /// <para>
26 /// Provides a type-safe way
27 /// to access multiple HTTP cookies.
28 /// </para>
29 /// </devdoc>
30 public sealed class HttpCookie {
31 private String _name;
32 private String _path = "/";
33 private bool _secure;
34 private bool _httpOnly;
35 private String _domain;
36 private bool _expirationSet;
37 private DateTime _expires;
38 private String _stringValue;
39 private HttpValueCollection _multiValue;
40 private bool _changed;
41 private bool _added;
43 internal HttpCookie() {
44 _changed = true;
48 * Constructor - empty cookie with name
51 /// <devdoc>
52 /// <para>
53 /// Initializes a new instance of the <see cref='System.Web.HttpCookie'/>
54 /// class.
55 /// </para>
56 /// </devdoc>
57 public HttpCookie(String name) {
58 _name = name;
60 SetDefaultsFromConfig();
61 _changed = true;
65 * Constructor - cookie with name and value
68 /// <devdoc>
69 /// <para>
70 /// Initializes a new instance of the <see cref='System.Web.HttpCookie'/>
71 /// class.
72 /// </para>
73 /// </devdoc>
74 public HttpCookie(String name, String value) {
75 _name = name;
76 _stringValue = value;
78 SetDefaultsFromConfig();
79 _changed = true;
82 private void SetDefaultsFromConfig() {
83 HttpCookiesSection config = RuntimeConfig.GetConfig().HttpCookies;
84 _secure = config.RequireSSL;
85 _httpOnly = config.HttpOnlyCookies;
87 if (config.Domain != null && config.Domain.Length > 0)
88 _domain = config.Domain;
92 * Whether the cookie contents have changed
94 internal bool Changed {
95 get { return _changed; }
96 set { _changed = value; }
100 * Whether the cookie has been added
102 internal bool Added {
103 get { return _added; }
104 set { _added = value; }
107 // DevID 251951 Cookie is getting duplicated by ASP.NET when they are added via a native module
108 // This flag is used to remember that this cookie came from an IIS Set-Header flag,
109 // so we don't duplicate it and send it back to IIS
110 internal bool IsInResponseHeader {
111 get;
112 set;
116 * Cookie name
119 /// <devdoc>
120 /// <para>
121 /// Gets
122 /// or sets the name of cookie.
123 /// </para>
124 /// </devdoc>
125 public String Name {
126 get { return _name;}
127 set {
128 _name = value;
129 _changed = true;
134 * Cookie path
137 /// <devdoc>
138 /// <para>
139 /// Gets or sets the URL prefix to transmit with the
140 /// current cookie.
141 /// </para>
142 /// </devdoc>
143 public String Path {
144 get { return _path;}
145 set {
146 _path = value;
147 _changed = true;
152 * 'Secure' flag
155 /// <devdoc>
156 /// <para>
157 /// Indicates whether the cookie should be transmitted only over HTTPS.
158 /// </para>
159 /// </devdoc>
160 public bool Secure {
161 get { return _secure;}
162 set {
163 _secure = value;
164 _changed = true;
168 /// <summary>
169 /// Determines whether this cookie is allowed to participate in output caching.
170 /// </summary>
171 /// <remarks>
172 /// If a given HttpResponse contains one or more outbound cookies with Shareable = false (the default value),
173 /// output caching will be suppressed for that response. This prevents cookies that contain potentially
174 /// sensitive information, e.g. FormsAuth cookies, from being cached in the response and sent to multiple
175 /// clients. If a developer wants to allow a response containing cookies to be cached, he should configure
176 /// caching as normal for the response, e.g. via the OutputCache directive, MVC's [OutputCache] attribute,
177 /// etc., and he should make sure that all outbound cookies are marked Shareable = true.
178 /// </remarks>
179 public bool Shareable {
180 get;
181 set; // don't need to set _changed flag since Set-Cookie header isn't affected by value of Shareable
184 /// <devdoc>
185 /// <para>
186 /// Indicates whether the cookie should have HttpOnly attribute
187 /// </para>
188 /// </devdoc>
189 public bool HttpOnly {
190 get { return _httpOnly;}
191 set {
192 _httpOnly = value;
193 _changed = true;
198 * Cookie domain
201 /// <devdoc>
202 /// <para>
203 /// Restricts domain cookie is to be used with.
204 /// </para>
205 /// </devdoc>
206 public String Domain {
207 get { return _domain;}
208 set {
209 _domain = value;
210 _changed = true;
215 * Cookie expiration
218 /// <devdoc>
219 /// <para>
220 /// Expiration time for cookie (in minutes).
221 /// </para>
222 /// </devdoc>
223 public DateTime Expires {
224 get {
225 return(_expirationSet ? _expires : DateTime.MinValue);
228 set {
229 _expires = value;
230 _expirationSet = true;
231 _changed = true;
236 * Cookie value as string
239 /// <devdoc>
240 /// <para>
241 /// Gets
242 /// or
243 /// sets an individual cookie value.
244 /// </para>
245 /// </devdoc>
246 public String Value {
247 get {
248 if (_multiValue != null)
249 return _multiValue.ToString(false);
250 else
251 return _stringValue;
254 set {
255 if (_multiValue != null) {
256 // reset multivalue collection to contain
257 // single keyless value
258 _multiValue.Reset();
259 _multiValue.Add(null, value);
261 else {
262 // remember as string
263 _stringValue = value;
265 _changed = true;
270 * Checks is cookie has sub-keys
273 /// <devdoc>
274 /// <para>Gets a
275 /// value indicating whether the cookie has sub-keys.</para>
276 /// </devdoc>
277 public bool HasKeys {
278 get { return Values.HasKeys();}
281 private bool SupportsHttpOnly(HttpContext context) {
282 if (context != null && context.Request != null) {
283 HttpBrowserCapabilities browser = context.Request.Browser;
284 return (browser != null && (browser.Type != "IE5" || browser.Platform != "MacPPC"));
286 return false;
290 * Cookie values as multivalue collection
293 /// <devdoc>
294 /// <para>Gets individual key:value pairs within a single cookie object.</para>
295 /// </devdoc>
296 public NameValueCollection Values {
297 get {
298 if (_multiValue == null) {
299 // create collection on demand
300 _multiValue = new HttpValueCollection();
302 // convert existing string value into multivalue
303 if (_stringValue != null) {
304 if (_stringValue.IndexOf('&') >= 0 || _stringValue.IndexOf('=') >= 0)
305 _multiValue.FillFromString(_stringValue);
306 else
307 _multiValue.Add(null, _stringValue);
309 _stringValue = null;
313 _changed = true;
315 return _multiValue;
320 * Default indexed property -- lookup the multivalue collection
323 /// <devdoc>
324 /// <para>
325 /// Shortcut for HttpCookie$Values[key]. Required for ASP compatibility.
326 /// </para>
327 /// </devdoc>
328 public String this[String key]
330 get {
331 return Values[key];
334 set {
335 Values[key] = value;
336 _changed = true;
340 /// <summary>
341 /// Converts the specified string representation of an HTTP cookie to HttpCookie
342 /// </summary>
343 /// <param name="input"></param>
344 /// <param name="result"></param>
345 /// <returns></returns>
346 public static bool TryParse(string input, out HttpCookie result) {
347 result = null;
349 if (string.IsNullOrEmpty(input)) {
350 return false;
353 // The substring before the first ';' is cookie-pair, with format of cookiename[=key1=val2&key2=val2&...]
354 int dividerIndex = input.IndexOf(';');
355 string cookiePair = dividerIndex >= 0 ? input.Substring(0, dividerIndex) : input;
357 HttpCookie cookie = HttpRequest.CreateCookieFromString(cookiePair.Trim());
359 // If there was no cookie name being created, stop parsing and return
360 if (string.IsNullOrEmpty(cookie.Name)) {
361 return false;
365 // Parse the collections of cookie-av
366 // cookie-av = expires-av/max-age-av/domain-av/path-av/secure-av/httponly-av/extension-av
367 // https://tools.ietf.org/html/rfc6265
369 while (dividerIndex >= 0 && dividerIndex < input.Length - 1) {
370 int cookieAvStartIndex = dividerIndex + 1;
371 dividerIndex = input.IndexOf(';', cookieAvStartIndex);
372 string cookieAv = dividerIndex >= 0 ? input.Substring(cookieAvStartIndex, dividerIndex - cookieAvStartIndex).Trim() : input.Substring(cookieAvStartIndex).Trim();
374 int assignmentIndex = cookieAv.IndexOf('=');
375 string attributeName = assignmentIndex >= 0 ? cookieAv.Substring(0, assignmentIndex).Trim() : cookieAv;
376 string attributeValue = assignmentIndex >= 0 && assignmentIndex < cookieAv.Length - 1 ? cookieAv.Substring(assignmentIndex + 1).Trim() : null;
379 // Parse supported cookie-av Attribute
382 // Expires
383 if (StringUtil.EqualsIgnoreCase(attributeName, "Expires")) {
384 DateTime dt;
385 if (DateTime.TryParse(attributeValue, out dt)) {
386 cookie.Expires = dt;
390 // Domain
391 else if (attributeValue != null && StringUtil.EqualsIgnoreCase(attributeName, "Domain")) {
392 cookie.Domain = attributeValue;
395 // Path
396 else if (attributeValue != null && StringUtil.EqualsIgnoreCase(attributeName, "Path")) {
397 cookie.Path = attributeValue;
400 // Secure
401 else if (StringUtil.EqualsIgnoreCase(attributeName, "Secure")) {
402 cookie.Secure = true;
405 // HttpOnly
406 else if (StringUtil.EqualsIgnoreCase(attributeName, "HttpOnly")) {
407 cookie.HttpOnly = true;
411 result = cookie;
413 return true;
417 * Construct set-cookie header
419 internal HttpResponseHeader GetSetCookieHeader(HttpContext context) {
420 StringBuilder s = new StringBuilder();
422 // cookiename=
423 if (!String.IsNullOrEmpty(_name)) {
424 s.Append(_name);
425 s.Append('=');
428 // key=value&...
429 if (_multiValue != null)
430 s.Append(_multiValue.ToString(false));
431 else if (_stringValue != null)
432 s.Append(_stringValue);
434 // domain
435 if (!String.IsNullOrEmpty(_domain)) {
436 s.Append("; domain=");
437 s.Append(_domain);
440 // expiration
441 if (_expirationSet && _expires != DateTime.MinValue) {
442 s.Append("; expires=");
443 s.Append(HttpUtility.FormatHttpCookieDateTime(_expires));
446 // path
447 if (!String.IsNullOrEmpty(_path)) {
448 s.Append("; path=");
449 s.Append(_path);
452 // secure
453 if (_secure)
454 s.Append("; secure");
456 // httponly, Note: IE5 on the Mac doesn't support this
457 if (_httpOnly && SupportsHttpOnly(context)) {
458 s.Append("; HttpOnly");
461 // return as HttpResponseHeader
462 return new HttpResponseHeader(HttpWorkerRequest.HeaderSetCookie, s.ToString());
466 /////////////////////////////////////////////////////////////////////////////
467 /////////////////////////////////////////////////////////////////////////////
468 /////////////////////////////////////////////////////////////////////////////
470 public enum HttpCookieMode {
472 UseUri, // cookieless=true
474 UseCookies, // cookieless=false
476 AutoDetect, // cookieless=AutoDetect; Probe if device is cookied
478 UseDeviceProfile // cookieless=UseDeviceProfile; Base decision on caps