1 //------------------------------------------------------------------------------
2 // <copyright file="HttpCookie.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 * HttpCookie - collection + name + path
10 * Copyright (c) 1998 Microsoft Corporation
13 namespace System
.Web
{
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
;
26 /// Provides a type-safe way
27 /// to access multiple HTTP cookies.
30 public sealed class HttpCookie
{
32 private String _path
= "/";
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
;
43 internal HttpCookie() {
48 * Constructor - empty cookie with name
53 /// Initializes a new instance of the <see cref='System.Web.HttpCookie'/>
57 public HttpCookie(String name
) {
60 SetDefaultsFromConfig();
65 * Constructor - cookie with name and value
70 /// Initializes a new instance of the <see cref='System.Web.HttpCookie'/>
74 public HttpCookie(String name
, String
value) {
78 SetDefaultsFromConfig();
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
{
122 /// or sets the name of cookie.
139 /// Gets or sets the URL prefix to transmit with the
157 /// Indicates whether the cookie should be transmitted only over HTTPS.
161 get { return _secure;}
169 /// Determines whether this cookie is allowed to participate in output caching.
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.
179 public bool Shareable
{
181 set; // don't need to set _changed flag since Set-Cookie header isn't affected by value of Shareable
186 /// Indicates whether the cookie should have HttpOnly attribute
189 public bool HttpOnly
{
190 get { return _httpOnly;}
203 /// Restricts domain cookie is to be used with.
206 public String Domain
{
207 get { return _domain;}
220 /// Expiration time for cookie (in minutes).
223 public DateTime Expires
{
225 return(_expirationSet
? _expires
: DateTime
.MinValue
);
230 _expirationSet
= true;
236 * Cookie value as string
243 /// sets an individual cookie value.
246 public String Value
{
248 if (_multiValue
!= null)
249 return _multiValue
.ToString(false);
255 if (_multiValue
!= null) {
256 // reset multivalue collection to contain
257 // single keyless value
259 _multiValue
.Add(null, value);
262 // remember as string
263 _stringValue
= value;
270 * Checks is cookie has sub-keys
275 /// value indicating whether the cookie has sub-keys.</para>
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"));
290 * Cookie values as multivalue collection
294 /// <para>Gets individual key:value pairs within a single cookie object.</para>
296 public NameValueCollection Values
{
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
);
307 _multiValue
.Add(null, _stringValue
);
320 * Default indexed property -- lookup the multivalue collection
325 /// Shortcut for HttpCookie$Values[key]. Required for ASP compatibility.
328 public String
this[String key
]
341 /// Converts the specified string representation of an HTTP cookie to HttpCookie
343 /// <param name="input"></param>
344 /// <param name="result"></param>
345 /// <returns></returns>
346 public static bool TryParse(string input
, out HttpCookie result
) {
349 if (string.IsNullOrEmpty(input
)) {
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
)) {
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
383 if (StringUtil
.EqualsIgnoreCase(attributeName
, "Expires")) {
385 if (DateTime
.TryParse(attributeValue
, out dt
)) {
391 else if (attributeValue
!= null && StringUtil
.EqualsIgnoreCase(attributeName
, "Domain")) {
392 cookie
.Domain
= attributeValue
;
396 else if (attributeValue
!= null && StringUtil
.EqualsIgnoreCase(attributeName
, "Path")) {
397 cookie
.Path
= attributeValue
;
401 else if (StringUtil
.EqualsIgnoreCase(attributeName
, "Secure")) {
402 cookie
.Secure
= true;
406 else if (StringUtil
.EqualsIgnoreCase(attributeName
, "HttpOnly")) {
407 cookie
.HttpOnly
= true;
417 * Construct set-cookie header
419 internal HttpResponseHeader
GetSetCookieHeader(HttpContext context
) {
420 StringBuilder s
= new StringBuilder();
423 if (!String
.IsNullOrEmpty(_name
)) {
429 if (_multiValue
!= null)
430 s
.Append(_multiValue
.ToString(false));
431 else if (_stringValue
!= null)
432 s
.Append(_stringValue
);
435 if (!String
.IsNullOrEmpty(_domain
)) {
436 s
.Append("; domain=");
441 if (_expirationSet
&& _expires
!= DateTime
.MinValue
) {
442 s
.Append("; expires=");
443 s
.Append(HttpUtility
.FormatHttpCookieDateTime(_expires
));
447 if (!String
.IsNullOrEmpty(_path
)) {
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