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& ...]
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
*/
<devdoc>
<para>
Initializes a new instance of the <see cref= ' System.Web.HttpCookie '/>
Class.
</para>
</devdoc>
Public HttpCookie (String name) {
_name = name;
Setdefaultsfromconfig ();
_changed = true;
}
/*
* Constructor-cookie with Name and value
*/
<devdoc>
<para>
Initializes a new instance of the <see cref= ' System.Web.HttpCookie '/>
Class.
</para>
</devdoc>
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 && CONFIG. Domain.length > 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
*/
<devdoc>
<para>
Gets
or sets the name of cookie.
</para>
</devdoc>
Public String Name {
get {return _name;}
set {
_name = value;
_changed = true;
}
}
/*
* Cookie Path
*/
<devdoc>
<para>
Gets or sets the URL prefix to transmit with the
Current cookie.
</para>
</devdoc>
Public String Path {
get {return _path;}
set {
_path = value;
_changed = true;
}
}
/*
* ' Secure ' flag
*/
<devdoc>
<para>
Indicates whether the cookie should is transmitted only over HTTPS.
</para>
</devdoc>
public bool Secure {
get {return _secure;}
set {
_secure = value;
_changed = true;
}
}
<summary>
Determines whether this cookies are allowed to participate in output caching.
</summary>
<remarks>
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.
</remarks>
public bool Shareable {
Get
Set Don ' t need to set _changed flag since Set-cookie header isn ' t affected by value of shareable
}
<devdoc>
<para>
Indicates whether the cookie should have HttpOnly attribute
</para>
</devdoc>
public bool HttpOnly {
get {return _httponly;}
set {
_httponly = value;
_changed = true;
}
}
/*
* Cookie Domain
*/
<devdoc>
<para>
Restricts domain cookie is used with.
</para>
</devdoc>
Public String Domain {
get {return _domain;}
set {
_domain = value;
_changed = true;
}
}
/*
* Cookie Expiration
*/
<devdoc>
<para>
Expiration time for cookies (in minutes).
</para>
</devdoc>
Public DateTime Expires {
get {
Return (_expirationset _expires:datetime.minvalue);
}
set {
_expires = value;
_expirationset = true;
_changed = true;
}
}
/*
* Cookie Value As String
*/
<devdoc>
<para>
Gets
Or
Sets an individual cookie value.
</para>
</devdoc>
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
*/
<devdoc>
<para>gets A
value indicating whether the cookie has sub-keys.</para>
</devdoc>
public bool HasKeys {
get {return Values.haskeys ();}
}
private bool Supportshttponly (HttpContext context) {
If (context!= null && context. Request!= null) {
HttpBrowserCapabilities Browser = context. Request.Browser;
return (browser!= null && (browser). Type!= "IE5" | | Browser. Platform!= "MACPPC"));
}
return false;
}
/*
* Cookie values as Multivalue collection
*/
<devdoc>
<para>gets individual key:value pairs within a single cookie object.</para>
</devdoc>
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 (' & ') >= 0 | | | _stringvalue.indexof (' = ') >= 0)
_multivalue.fillfromstring (_stringvalue);
Else
_multivalue.add (null, _stringvalue);
_stringvalue = null;
}
}
_changed = true;
return _multivalue;
}
}
/*
* Default Indexed Property--Lookup The Multivalue collection
*/
<devdoc>
<para>
Shortcut for Httpcookie$values[key]. Required for ASP compatibility.
</para>
</devdoc>
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&
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 && _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 && 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 >= 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 &&!_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 < _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 < _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.