asp.net cookie Processing Process In-depth analysis _ practical skills

Source: Internet
Author: User
Tags flush httpcontext set cookie

When it comes to cookies, I think we should all know that it is a save on the client, when the browser requests a URL, the browser will carry the relevant cookies to the server side, so the server can operate cookies, in response, the cookie information will be exported to the customer service side. Let's look at a demo bar with the following code:

The results of the first request are as follows:

The results of the second request are as follows:

Here we can see that the second request to the incoming cookie is exactly the first time the cookie information is returned, where the maintenance of the cookie information is mainly our client's browser, However, in the ASP.net program development, cookies are often written in the server program, as my case code, rarely useful customer service terminal JS implementation. Now let's look at how the ASP.net server implements a read-write cookie.

First, let's look at how the HttpRequest cookie is defined:

Copy Code code as follows:

Public HttpCookieCollection Cookies {
get {
Ensurecookies ();
if (_flags[needtovalidatecookies]) {
_flags. Clear (needtovalidatecookies);
Validatecookiecollection (_cookies);
}
return _cookies;
}
}



The cookie here is mainly called a ensurecookies method, Ensurecookies is mainly called


Copy Code code as follows:

Populates the Cookies property but does is not a hook up validation.
Internal HttpCookieCollection ensurecookies () {
if (_cookies = = null) {
_cookies = new HttpCookieCollection (null, FALSE);
if (_WR!= null)
Fillincookiescollection (_cookies, true/*includeresponse*/);

if (hastransitionedtowebsocketrequest)//Cookies can ' t be modified after the WebSocket handshake is complete
_cookies. Makereadonly ();
}
return _cookies;
}

public sealed class Httpcookiecollection:nameobjectcollectionbase
{
Internal HttpCookieCollection (HttpResponse response, BOOL readOnly): Base (Stringcomparer.ordinalignorecase)
{
This._response = response;
Base. IsReadOnly = readOnly;
}
}



Here the Fillincookiescollection method implementation is also more complex:


Copy Code code as follows:



internal void Fillincookiescollection (HttpCookieCollection cookiecollection, bool includeresponse) {


if (_WR = null)


Return





String s = _wr. Getknownrequestheader (Httpworkerrequest.headercookie);





Parse the cookie server variable.


format:c1=k1=v1&k2=v2; C2= ...





int L = (s!= null)? s.length:0;


int i = 0;


Int J;


Char ch;





HttpCookie Lastcookie = null;





while (I < L) {


Find Next '; ' (Don ' t look to ', ' as per 91884)


j = i;


while (J < L) {


ch = s[j];


if (ch = = '; ')


Break


j + +;


}





Create Cookie form string


String cookiestring = s.substring (i, j-i). Trim ();


i = j+1; Next Cookie Start





if (cookiestring.length = 0)


Continue





HttpCookie cookie = createcookiefromstring (cookiestring);





Some cookies starting with ' $ ' are really attributes to the last cookie


if (Lastcookie!= null) {


String name = cookie. Name;





Add known to the "Last cookie" (if any)


if (name!= null && name. Length > 0 && name[0] = = ' $ ') {


if (stringutil.equalsignorecase (name, $Path))


Lastcookie.path = cookie. Value;


else if (stringutil.equalsignorecase (name, $Domain))


Lastcookie.domain = cookie. Value;





Continue


}


}





Regular cookies


Cookiecollection.addcookie (Cookie, true);


Lastcookie = cookie;





Goto Next Cookie


}





Append Response Cookies


if (includeresponse) {


If we have a reference to the response cookie collection, use it directly


Rather than going through the Response object (which might, not is available, e.g.


If we have already transitioned to a websockets request).


HttpCookieCollection storedresponsecookies = _storedresponsecookies;


if (storedresponsecookies = = NULL &&!) Hastransitionedtowebsocketrequest && Response!= null) {


Storedresponsecookies = Response.getcookiesnocreate ();


}





if (storedresponsecookies!= null && storedresponsecookies.count > 0) {


httpcookie[] Responsecookiearray = new Httpcookie[storedresponsecookies.count];


Storedresponsecookies.copyto (Responsecookiearray, 0);


for (int icookie = 0; Icookie < responsecookiearray.length; icookie++)


Cookiecollection.addcookie (Responsecookiearray[icookie], append:true);


}





Release any stored reference to the response cookie collection


_storedresponsecookies = null;


}


}





Let's just say it's simpler. It mainly calls the HttpWorkerRequest Getknownrequestheader method to get the cookie string information that the browser passes in, and then the information is separated into multiple HttpCookie instances. Add these HttpCookie instances to the httpcookiecollection parameters that are passed in.

Here the results of the HttpWorkerRequest inheritance are as follows:

Copy Code code as follows:

Internal class ISAPIWORKERREQUESTINPROCFORIIS7:ISAPIWORKERREQUESTINPROCFORIIS6
Internal class Isapiworkerrequestinprocforiis6:isapiworkerrequestinproc
Internal class Isapiworkerrequestinproc:isapiworkerrequest
Internal abstract class Isapiworkerrequest:httpworkerrequest



The implementation of Getknownrequestheader method is mainly in Isapiworkerrequest, whose getknownrequestheader mainly calls its readrequestheaders private method, In the Readrequestheaders method it is primarily called the this.getservervariable ("All_raw") method, so we can think of this. GetServerVariable ("All_raw") This method is to obtain the cookie parameters from the client, and the GetServerVariable method is implemented mainly in the Isapiworkerrequestinproc class, The concrete implementation is very complex.


The Getknownrequestheader method here is very complex and we don't go deep into it, we just know that calling this method returns all the string information for the cookie. In this method, a Createcookiefromstring method is also called to create our HttpCookie instance based on the string. The Createcookiefromstring method is implemented as follows:

Copy Code code as follows:



Internal static HttpCookie createcookiefromstring (String s) {


HttpCookie C = new HttpCookie ();





int L = (s!= null)? s.length:0;


int i = 0;


int ai, ei;


bool Firstvalue = true;


int numvalues = 1;





Format:cookiename[=key1=val2&key2=val2&amp ...]





while (I < L) {


Find Next &


ai = S.indexof (' & ', i);


if (AI < 0)


AI = l;





The might contain cookie name before =


if (firstvalue) {


ei = s.indexof (' = ', i);





if (ei >= 0 && ei < ai) {


C.name = s.substring (i, ei-i);


i = ei+1;


}


else if (ai = = L) {


The whole cookie is just a name


C.name = s;


Break


}





Firstvalue = false;


}





Find ' = '


ei = s.indexof (' = ', i);





if (ei < 0 && ai = = L && numvalues = 0) {


Simple cookies with simple value


C.value = s.substring (i, l-i);


}


else if (ei >= 0 && ei < ai) {


Key=value


C.values.add (S.substring (i, Ei-i), s.substring (ei+1, ai-ei-1));


numvalues++;


}


else {


Value without key


C.values.add (NULL, s.substring (i, ai-i));


numvalues++;


}





i = ai+1;


}





return C;


}





We usually rarely use the HttpCookie values attribute, so this property should be noted, this method is to convert a cookie string into the corresponding HttpCookie instance.


Now we're back to HttpRequest's Cookie property, and here's a simple validation of cookies


Copy Code code as follows:

private void Validatecookiecollection (HttpCookieCollection cc) {
if (_enablegranularvalidation) {
Granular request validation is Enabled-validate collection entries only as they ' re accessed.
Cc. Enablegranularvalidation ((key, value) => validatestring (value, key, requestvalidationsource.cookies));
}
else {
Granular request validation is disabled-eagerly validate all collection entries.
int c = cc. Count;

for (int i = 0; i < C; i++) {
String key = cc. Getkey (i);
String val = cc. Get (i). Value;

if (! String.IsNullOrEmpty (Val))
Validatestring (Val, key, Requestvalidationsource.cookies);
}
}
}




One of the HttpCookieCollection's enablegranularvalidation implementations is as follows:


Copy Code code as follows:

internal void enablegranularvalidation (Validatestringcallback validationcallback)
{
This._keysawaitingvalidation = new Hashset<string> (this. Keys.cast<string> (), stringcomparer.ordinalignorecase);
This._validationcallback = ValidationCallback;
}

private void ensurekeyvalidated (string key, String value)
{
if ((this._keysawaitingvalidation!= null) && This._keysawaitingvalidation.contains (key))
{
if (!string. IsNullOrEmpty (value))
{
This._validationcallback (key, value);
}
This._keysawaitingvalidation.remove (key);
}
}





Here we know that the default cookies sent from the browser to the server end need to be validated. Here's the validatestring method of concrete implementation we're not going to say that But you need to know that it is called the RequestValidator.Current.IsValidRequestString method to implement validation, information about Requestvalidator you can view the QueryString properties of HttpRequest A little understanding. Now we have basically finished getting the cookie. So let's take a look at how to add cookies.

First let's look at the HttpResponse cookie properties:

Copy Code code as follows:

Public HttpCookieCollection Cookies
{
Get
{
if (this._cookies = null)
{
This._cookies = new HttpCookieCollection (this, false);
}
return this._cookies;
}
}




Let's take a look at HttpCookie's implementation as follows:


Copy Code code as follows:



public sealed class HttpCookie {


Private String _name;


Private String _path = "/";


private bool _secure;


private bool _httponly;


Private String _domain;


private bool _expirationset;


Private DateTime _expires;


Private String _stringvalue;


Private Httpvaluecollection _multivalue;


private bool _changed;


private bool _added;





Internal HttpCookie () {


_changed = true;


}





/*


* Constructor-empty Cookie with Name


*/





&lt;devdoc&gt;


&lt;para&gt;


Initializes a new instance of the &lt;see cref= ' System.Web.HttpCookie '/&gt;


Class.


&lt;/para&gt;


&lt;/devdoc&gt;


Public HttpCookie (String name) {


_name = name;





Setdefaultsfromconfig ();


_changed = true;


}





/*


* Constructor-cookie with Name and value


*/





&lt;devdoc&gt;


&lt;para&gt;


Initializes a new instance of the &lt;see cref= ' System.Web.HttpCookie '/&gt;


Class.


&lt;/para&gt;


&lt;/devdoc&gt;


Public HttpCookie (string name, String value) {


_name = name;


_stringvalue = value;





Setdefaultsfromconfig ();


_changed = true;


}





private void Setdefaultsfromconfig () {


httpcookiessection config = Runtimeconfig.getconfig (). httpcookies;


_secure = config. requireSSL;


_httponly = config. Httponlycookies;





if (config. Domain!= null &amp;&amp; CONFIG. Domain.length &gt; 0)


_domain = config. Domain;


}





/*


* Whether The cookie contents have changed


*/


internal BOOL Changed {


get {return _changed;}


set {_changed = value;}


}





/*


* Whether The cookie has been added


*/


internal BOOL Added {


get {return _added;}


set {_added = value;}


}





Devid 251951 Cookie is getting duplicated by asp.net when they are added via a native module


This flag was used to remember the This cookie came the from a IIS set-header flag,


So we don ' t duplicate it and send it back to IIS


internal BOOL Fromheader {


Get


Set


}





/*


* Cookie Name


*/





&lt;devdoc&gt;


&lt;para&gt;


Gets


or sets the name of cookie.


&lt;/para&gt;


&lt;/devdoc&gt;


Public String Name {


get {return _name;}


set {


_name = value;


_changed = true;


}


}





/*


* Cookie Path


*/





&lt;devdoc&gt;


&lt;para&gt;


Gets or sets the URL prefix to transmit with the


Current cookie.


&lt;/para&gt;


&lt;/devdoc&gt;


Public String Path {


get {return _path;}


set {


_path = value;


_changed = true;


}


}





/*


* ' Secure ' flag


*/





&lt;devdoc&gt;


&lt;para&gt;


Indicates whether the cookie should is transmitted only over HTTPS.


&lt;/para&gt;


&lt;/devdoc&gt;


public bool Secure {


get {return _secure;}


set {


_secure = value;


_changed = true;


}


}





&lt;summary&gt;


Determines whether this cookies are allowed to participate in output caching.


&lt;/summary&gt;


&lt;remarks&gt;


If a given httpresponse contains one or more outbound cookies with shareable = False (the default value),


Output caching'll be suppressed to that response. This prevents cookie that contain potentially


sensitive information, e.g. FormsAuth cookies, from being cached in the response and sent to multiple


Clients. If A developer wants to allow a response containing cookies are cached, he should configure


Caching as normal for the response, e.g. via the OutputCache directive, MVC ' s [OutputCache] attribute,


etc., and he should make sure so all outbound cookies are marked shareable = True.


&lt;/remarks&gt;


public bool Shareable {


Get


Set Don ' t need to set _changed flag since Set-cookie header isn ' t affected by value of shareable


}





&lt;devdoc&gt;


&lt;para&gt;


Indicates whether the cookie should have HttpOnly attribute


&lt;/para&gt;


&lt;/devdoc&gt;


public bool HttpOnly {


get {return _httponly;}


set {


_httponly = value;


_changed = true;


}


}





/*


* Cookie Domain


*/





&lt;devdoc&gt;


&lt;para&gt;


Restricts domain cookie is used with.


&lt;/para&gt;


&lt;/devdoc&gt;


Public String Domain {


get {return _domain;}


set {


_domain = value;


_changed = true;


}


}





/*


* Cookie Expiration


*/





&lt;devdoc&gt;


&lt;para&gt;


Expiration time for cookies (in minutes).


&lt;/para&gt;


&lt;/devdoc&gt;


Public DateTime Expires {


get {


Return (_expirationset _expires:datetime.minvalue);


}





set {


_expires = value;


_expirationset = true;


_changed = true;


}


}





/*


* Cookie Value As String


*/





&lt;devdoc&gt;


&lt;para&gt;


Gets


Or


Sets an individual cookie value.


&lt;/para&gt;


&lt;/devdoc&gt;


Public String Value {


get {


if (_multivalue!= null)


return _multivalue.tostring (FALSE);


Else


return _stringvalue;


}





set {


if (_multivalue!= null) {


Reset Multivalue collection to contain


Single Keyless value


_multivalue.reset ();


_multivalue.add (null, value);


}


else {


Remember as String


_stringvalue = value;


}


_changed = true;


}


}





/*


* Checks is Cookie has sub-keys


*/





&lt;devdoc&gt;


&lt;para&gt;gets A


value indicating whether the cookie has sub-keys.&lt;/para&gt;


&lt;/devdoc&gt;


public bool HasKeys {


get {return Values.haskeys ();}


}





private bool Supportshttponly (HttpContext context) {


If (context!= null &amp;&amp; context. Request!= null) {


HttpBrowserCapabilities Browser = context. Request.Browser;


return (browser!= null &amp;&amp; (browser). Type!= "IE5" | | Browser. Platform!= "MACPPC"));


}


return false;


}





/*


* Cookie values as Multivalue collection


*/





&lt;devdoc&gt;


&lt;para&gt;gets individual key:value pairs within a single cookie object.&lt;/para&gt;


&lt;/devdoc&gt;


Public NameValueCollection Values {


get {


if (_multivalue = = null) {


Create collection On Demand


_multivalue = new Httpvaluecollection ();





Convert existing string value into Multivalue


if (_stringvalue!= null) {


if (_stringvalue.indexof (' &amp; ') &gt;= 0 | | | _stringvalue.indexof (' = ') &gt;= 0)


_multivalue.fillfromstring (_stringvalue);


Else


_multivalue.add (null, _stringvalue);





_stringvalue = null;


}


}





_changed = true;





return _multivalue;


}


}





/*


* Default Indexed Property--Lookup The Multivalue collection


*/





&lt;devdoc&gt;


&lt;para&gt;


Shortcut for Httpcookie$values[key]. Required for ASP compatibility.


&lt;/para&gt;


&lt;/devdoc&gt;


Public String this[string Key]


{


get {


return Values[key];


}





set {


Values[key] = value;


_changed = true;


}


}





/*


* Construct Set-cookie Header


*/


Internal Httpresponseheader Getsetcookieheader (HttpContext context) {


StringBuilder s = new StringBuilder ();





Cookiename=


if (! String.IsNullOrEmpty (_name)) {


S.append (_name);


S.append (' = ');


}





Key=value&amp;


if (_multivalue!= null)


S.append (_multivalue.tostring (false));


else if (_stringvalue!= null)


S.append (_stringvalue);





Domain


if (! String.IsNullOrEmpty (_domain)) {


S.append ("; Domain= ");


S.append (_domain);


}





Expiration


if (_expirationset &amp;&amp; _expires!= datetime.minvalue) {


S.append ("; Expires= ");


S.append (Httputility.formathttpcookiedatetime (_expires));


}





Path


if (! String.IsNullOrEmpty (_path)) {


S.append ("; Path= ");


S.append (_path);


}





Secure


if (_secure)


S.append ("; Secure ");





HttpOnly, note:ie5 on the Mac doesn ' t support this


if (_httponly &amp;&amp; supportshttponly (context)) {


S.append ("; HttpOnly ");


}





return as Httpresponseheader


return new Httpresponseheader (Httpworkerrequest.headersetcookie, s.tostring ());


}


}





Now we go back to HttpCookieCollection's Add method to see,


Copy Code code as follows:



public void Add (HttpCookie cookie) {


if (_response!= null)


_response. Beforecookiecollectionchange ();





Addcookie (Cookie, true);





if (_response!= null)


_response. Oncookieadd (cookie);


}





public sealed class HttpResponse


{


internal void Beforecookiecollectionchange ()


{


if (This._headerswritten)


{


throw new HttpException (SR. GetString ("Cannot_modify_cookies_after_headers_sent"));


}


}


internal void Oncookieadd (HttpCookie cookie)


{


This. Request.addresponsecookie (cookie);


}


}


public sealed class HttpRequest


{


internal void Addresponsecookie (HttpCookie cookie)


{


if (this._cookies!= null)


{


This._cookies. Addcookie (Cookie, true);


}


if (this._params!= null)


{


This._params. Makereadwrite ();


This._params. ADD (Cookie. Name, Cookie. Value);


This._params. Makereadonly ();


}


}


}








Here we should know that every addition or modification of a cookie will invoke the HttpResponse Beforecookiecollectionchange and Oncookieadd method. Beforecookiecollectionchange is to confirm that our cookies can be added, previously in the project encountered the error message here said "after header send can not modify cookies", see By default _ Headerswritten is false, then where it is usually set to true, in Httpreaponse Beginexecuteurlforentireresponse, Flush, The Endflush method is set to true, and the flush method is our most common contact. The Oncookieadd method here ensures that the cookie instance is also added to the HttpRequest.


Copy Code code as follows:



internal void Addcookie (HttpCookie cookie, bool append) {


Throwifmaxhttpcollectionkeysexceeded ();





_all = null;


_allkeys = null;





if (append) {


Devid 251951 Cookie is getting duplicated by asp.net when they are added via a native module


Need to don't double add response cookies from native modules


if (!cookie. Fromheader) {


Mark Cookie as New


Cookie. Added = true;


}


Baseadd (cookies. Name, cookie);


}


else {


if (baseget) (cookie. Name)!= null) {


Mark the cookie as changed because we are overriding the existing one


Cookie. Changed = true;


}


Baseset (cookies. Name, cookie);


}


}


private void throwifmaxhttpcollectionkeysexceeded () {


if (Count &gt;= appsettings.maxhttpcollectionkeys) {


throw new InvalidOperationException (SR. GetString (SR. Collectioncountexceeded_httpvaluecollection, Appsettings.maxhttpcollectionkeys));


}


}








The Addcookie method here is also very simple, but every time you add it, you will check to see if the cookie exceeds the maximum value. In fact, adding cookies can also invoke the HttpResponse AppendCookie method,


Copy Code code as follows:

public void AppendCookie (HttpCookie cookie)
{
if (This._headerswritten)
{
throw new HttpException (SR. GetString ("Cannot_append_cookie_after_headers_sent"));
}
This. Cookies.addcookie (Cookie, true);
This. Oncookieadd (cookie);
}




This implementation is consistent with the httpcookiecollection public void Add (HttpCookie cookie) method.


We also know that these cookies are used in the Generateresponseheadersforcookies method of HttpResponse,


The implementation of the Generateresponseheadersforcookies method is as follows:


Copy Code code as follows:



internal void generateresponseheadersforcookies ()


{


if (_cookies = null | | (_cookies. Count = = 0 &amp;&amp;!_cookies. Changed))


Return No cookies exist





Httpheadercollection headers = headers as httpheadercollection;


Httpresponseheader cookieheader = null;


HttpCookie cookie = null;


BOOL Needtoreset = false;





Go through all cookies, and check whether any have been added


or changed. If A cookie was added, we can simply generate a new


Set cookie header for it. If the cookie collection has been


Changed (cleared or cookies removed), or a existing cookie was


Changed, we have to regenerate all Set-cookie headers due to a IIS


Limitation that prevents us from being able to delete specific


Set-cookie headers for items that changed.


if (!_cookies. Changed)


{


for (int c = 0; c &lt; _cookies. Count; C + +)


{


cookie = _cookies[c];


if (cookie). Added) {


If a cookie is added, we generate a Set-cookie header for it


Cookieheader = cookie. Getsetcookieheader (_context);


Headers. SetHeader (Cookieheader.name, Cookieheader.value, false);


Cookie. Added = false;


Cookie. Changed = false;


}


else if (cookie. Changed) {


If a cookie has changed, we need to clear all cookies


Headers and re-write them all since we cant delete


Specific existing cookies


Needtoreset = true;


Break


}


}


}








if (_cookies. Changed | | Needtoreset)


{


Delete all set cookie headers


Headers. Remove ("Set-cookie");





Write all the cookies again


for (int c = 0; c &lt; _cookies. Count; C + +)


{


Generate a Set-cookie header for each Cookie


cookie = _cookies[c];


Cookieheader = cookie. Getsetcookieheader (_context);


Headers. SetHeader (Cookieheader.name, Cookieheader.value, false);


Cookie. Added = false;


Cookie. Changed = false;


}





_cookies. Changed = false;


}


}





Let's summarize here: in HttpWorkerRequest we call the Getknownrequestheader method to get the string form of the cookie and then convert the string here into a HttpCookie set for HttpRequest use, the Generateresponseheadersforcookies method in HttpResponse will process our cookie instance, The Getsetcookieheader method that invokes the cookie obtains the corresponding string value of HttpCookie, and then adds the value to the Httpheadercollection collection (or modifies an existing value).   In getting the cookie there's a validation here's what we need to be aware of is the RequestValidator.Current.IsValidRequestString method. To add or modify cookies there are 2 places to check (1) Check whether the number of cookies is up to the maximum number of cookies we configure, (2) whether the header is now written, or if the header information is already written, the cookie cannot be manipulated.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.