Merge pull request #2377 from joelmartinez/docs-multiassembly-extension-fix
[mono.git] / mcs / class / referencesource / System / net / System / Net / WebHeaderCollection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="WebHeaderCollection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Net.Cache;
9     using System.Collections;
10     using System.Collections.Specialized;
11     using System.Text;
12     using System.Runtime.InteropServices;
13     using System.Runtime.Serialization;
14     using System.Globalization;
15     using System.Security.Permissions;
16     using System.Diagnostics.CodeAnalysis;
17
18     internal enum WebHeaderCollectionType : ushort {
19         Unknown,
20         WebRequest,
21         WebResponse,
22         HttpWebRequest,
23         HttpWebResponse,
24         HttpListenerRequest,
25         HttpListenerResponse,
26         FtpWebRequest,
27         FtpWebResponse,
28         FileWebRequest,
29         FileWebResponse,
30     }
31
32     //
33     // HttpHeaders - this is our main HttpHeaders object,
34     //  which is a simple collection of name-value pairs,
35     //  along with additional methods that provide HTTP parsing
36     //  collection to sendable buffer capablities and other enhansments
37     //  We also provide validation of what headers are allowed to be added.
38     //
39
40     /// <devdoc>
41     ///    <para>
42     ///       Contains protocol headers associated with a
43     ///       request or response.
44     ///    </para>
45     /// </devdoc>
46     [ComVisible(true), Serializable]
47     public class WebHeaderCollection : NameValueCollection, ISerializable {
48         //
49         // Data and Constants
50         //
51         private const int ApproxAveHeaderLineSize = 30;
52         private const int ApproxHighAvgNumHeaders = 16;
53         private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();
54
55         //
56         // Common Headers - used only when receiving a response, and internally.  If the user ever requests a header,
57         // all the common headers are moved into the hashtable.
58         //
59         private string[] m_CommonHeaders;
60         private int m_NumCommonHeaders;
61
62         // Grouped by first character, so lookup is faster.  The table s_CommonHeaderHints maps first letters to indexes in this array.
63         // After first character, sort by decreasing length.  It's ok if two headers have the same first character and length.
64         private static readonly string[] s_CommonHeaderNames = new string[] {
65             HttpKnownHeaderNames.AcceptRanges,      // "Accept-Ranges"       13
66             HttpKnownHeaderNames.ContentLength,     // "Content-Length"      14
67             HttpKnownHeaderNames.CacheControl,      // "Cache-Control"       13
68             HttpKnownHeaderNames.ContentType,       // "Content-Type"        12
69             HttpKnownHeaderNames.Date,              // "Date"                 4 
70             HttpKnownHeaderNames.Expires,           // "Expires"              7
71             HttpKnownHeaderNames.ETag,              // "ETag"                 4
72             HttpKnownHeaderNames.LastModified,      // "Last-Modified"       13
73             HttpKnownHeaderNames.Location,          // "Location"             8
74             HttpKnownHeaderNames.ProxyAuthenticate, // "Proxy-Authenticate"  18
75             HttpKnownHeaderNames.P3P,               // "P3P"                  3
76             HttpKnownHeaderNames.SetCookie2,        // "Set-Cookie2"         11
77             HttpKnownHeaderNames.SetCookie,         // "Set-Cookie"          10
78             HttpKnownHeaderNames.Server,            // "Server"               6
79             HttpKnownHeaderNames.Via,               // "Via"                  3
80             HttpKnownHeaderNames.WWWAuthenticate,   // "WWW-Authenticate"    16
81             HttpKnownHeaderNames.XAspNetVersion,    // "X-AspNet-Version"    16
82             HttpKnownHeaderNames.XPoweredBy,        // "X-Powered-By"        12
83             "[" };  // This sentinel will never match.  (This character isn't in the hint table.)
84
85         // Mask off all but the bottom five bits, and look up in this array.
86         private static readonly sbyte[] s_CommonHeaderHints = new sbyte[] {
87             -1,  0, -1,  1,  4,  5, -1, -1,   // - a b c d e f g
88             -1, -1, -1, -1,  7, -1, -1, -1,   // h i j k l m n o
89              9, -1, -1, 11, -1, -1, 14, 15,   // p q r s t u v w
90             16, -1, -1, -1, -1, -1, -1, -1 }; // x y z [ - - - -
91
92         private const int c_AcceptRanges      =  0;
93         private const int c_ContentLength     =  1;
94         private const int c_CacheControl      =  2;
95         private const int c_ContentType       =  3;
96         private const int c_Date              =  4;
97         private const int c_Expires           =  5;
98         private const int c_ETag              =  6;
99         private const int c_LastModified      =  7;
100         private const int c_Location          =  8;
101         private const int c_ProxyAuthenticate =  9;
102         private const int c_P3P               = 10;
103         private const int c_SetCookie2        = 11;
104         private const int c_SetCookie         = 12;
105         private const int c_Server            = 13;
106         private const int c_Via               = 14;
107         private const int c_WwwAuthenticate   = 15;
108         private const int c_XAspNetVersion    = 16;
109         private const int c_XPoweredBy        = 17;
110
111         // Easy fast lookups for common headers.  More can be added.
112         internal string ContentLength
113         {
114             get
115             {
116                 return m_CommonHeaders != null ? m_CommonHeaders[c_ContentLength] : Get(s_CommonHeaderNames[c_ContentLength]);
117             }
118         }
119
120         internal string CacheControl
121         {
122             get
123             {
124                 return m_CommonHeaders != null ? m_CommonHeaders[c_CacheControl] : Get(s_CommonHeaderNames[c_CacheControl]);
125             }
126         }
127
128         internal string ContentType
129         {
130             get
131             {
132                 return m_CommonHeaders != null ? m_CommonHeaders[c_ContentType] : Get(s_CommonHeaderNames[c_ContentType]);
133             }
134         }
135
136         internal string Date
137         {
138             get
139             {
140                 return m_CommonHeaders != null ? m_CommonHeaders[c_Date] : Get(s_CommonHeaderNames[c_Date]);
141             }
142         }
143
144         internal string Expires
145         {
146             get
147             {
148                 return m_CommonHeaders != null ? m_CommonHeaders[c_Expires] : Get(s_CommonHeaderNames[c_Expires]);
149             }
150         }
151
152         internal string ETag
153         {
154             get
155             {
156                 return m_CommonHeaders != null ? m_CommonHeaders[c_ETag] : Get(s_CommonHeaderNames[c_ETag]);
157             }
158         }
159
160         internal string LastModified
161         {
162             get
163             {
164                 return m_CommonHeaders != null ? m_CommonHeaders[c_LastModified] : Get(s_CommonHeaderNames[c_LastModified]);
165             }
166         }
167
168         internal string Location
169         {
170             get
171             {
172                 string location = m_CommonHeaders != null
173                     ? m_CommonHeaders[c_Location] : Get(s_CommonHeaderNames[c_Location]);
174                 // The normal header parser just casts bytes to chars. Check if there is a UTF8 host name.
175                 return HeaderEncoding.DecodeUtf8FromString(location);
176             }
177         }
178
179         internal string ProxyAuthenticate
180         {
181             get
182             {
183                 return m_CommonHeaders != null ? m_CommonHeaders[c_ProxyAuthenticate] : Get(s_CommonHeaderNames[c_ProxyAuthenticate]);
184             }
185         }
186
187         internal string SetCookie2
188         {
189             get
190             {
191                 return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie2] : Get(s_CommonHeaderNames[c_SetCookie2]);
192             }
193         }
194
195         internal string SetCookie
196         {
197             get
198             {
199                 return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie] : Get(s_CommonHeaderNames[c_SetCookie]);
200             }
201         }
202
203         internal string Server
204         {
205             get
206             {
207                 return m_CommonHeaders != null ? m_CommonHeaders[c_Server] : Get(s_CommonHeaderNames[c_Server]);
208             }
209         }
210
211         internal string Via
212         {
213             get
214             {
215                 return m_CommonHeaders != null ? m_CommonHeaders[c_Via] : Get(s_CommonHeaderNames[c_Via]);
216             }
217         }
218
219         private void NormalizeCommonHeaders()
220         {
221             if (m_CommonHeaders == null)
222                 return;
223             for (int i = 0; i < m_CommonHeaders.Length; i++)
224                 if (m_CommonHeaders[i] != null)
225                     InnerCollection.Add(s_CommonHeaderNames[i], m_CommonHeaders[i]);
226
227             m_CommonHeaders = null;
228             m_NumCommonHeaders = 0;
229         }
230
231         //
232         // To ensure C++ and IL callers can't pollute the underlying collection by calling overridden base members directly, we
233         // will use a member collection instead.
234         private NameValueCollection m_InnerCollection;
235
236         private NameValueCollection InnerCollection
237         {
238             get
239             {
240                 if (m_InnerCollection == null)
241                     m_InnerCollection = new NameValueCollection(ApproxHighAvgNumHeaders, CaseInsensitiveAscii.StaticInstance);
242                 return m_InnerCollection;
243             }
244         }
245
246         // this is the object that created the header collection.
247         private WebHeaderCollectionType m_Type;
248
249 #if MONO
250         internal bool AllowMultiValues (string name)
251         {
252             return HInfo[name].AllowMultiValues;
253         }
254 #endif
255
256 #if !FEATURE_PAL || MONO
257         private bool AllowHttpRequestHeader {
258             get {
259                 if (m_Type==WebHeaderCollectionType.Unknown) {
260                     m_Type = WebHeaderCollectionType.WebRequest;
261                 }
262                 return m_Type==WebHeaderCollectionType.WebRequest || m_Type==WebHeaderCollectionType.HttpWebRequest || m_Type==WebHeaderCollectionType.HttpListenerRequest;
263             }
264         }
265
266         internal bool AllowHttpResponseHeader {
267             get {
268                 if (m_Type==WebHeaderCollectionType.Unknown) {
269                     m_Type = WebHeaderCollectionType.WebResponse;
270                 }
271                 return m_Type==WebHeaderCollectionType.WebResponse || m_Type==WebHeaderCollectionType.HttpWebResponse || m_Type==WebHeaderCollectionType.HttpListenerResponse;
272             }
273         }
274
275         public string this[HttpRequestHeader header] {
276             get {
277                 if (!AllowHttpRequestHeader) {
278                     throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
279                 }
280                 return this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)];
281             }
282             set {
283                 if (!AllowHttpRequestHeader) {
284                     throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
285                 }
286                 this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)] = value;
287             }
288         }
289         public string this[HttpResponseHeader header] {
290             get {
291                 if (!AllowHttpResponseHeader) {
292                     throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
293                 }
294
295                 // Some of these can be mapped to Common Headers.  Other cases can be added as needed for perf.
296                 if (m_CommonHeaders != null)
297                 {
298                     switch (header)
299                     {
300                         case HttpResponseHeader.ProxyAuthenticate:
301                             return m_CommonHeaders[c_ProxyAuthenticate];
302
303                         case HttpResponseHeader.WwwAuthenticate:
304                             return m_CommonHeaders[c_WwwAuthenticate];
305                     }
306                 }
307
308                 return this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)];
309             }
310             set {
311                 if (!AllowHttpResponseHeader) {
312                     throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
313                 }
314                 if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
315                     if (value!=null && value.Length>ushort.MaxValue) {
316                         throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
317                     }
318                 }
319                 this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)] = value;
320             }
321         }
322
323         public void Add(HttpRequestHeader header, string value) {
324             if (!AllowHttpRequestHeader) {
325                 throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
326             }
327             this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
328         }
329
330         public void Add(HttpResponseHeader header, string value) {
331             if (!AllowHttpResponseHeader) {
332                 throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
333             }
334             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
335                 if (value!=null && value.Length>ushort.MaxValue) {
336                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
337                 }
338             }
339             this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value);
340         }
341
342         public void Set(HttpRequestHeader header, string value) {
343             if (!AllowHttpRequestHeader) {
344                 throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
345             }
346             this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
347         }
348
349         public void Set(HttpResponseHeader header, string value) {
350             if (!AllowHttpResponseHeader) {
351                 throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
352             }
353             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
354                 if (value!=null && value.Length>ushort.MaxValue) {
355                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
356                 }
357             }
358             this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value);
359         }
360
361
362         internal void SetInternal(HttpResponseHeader header, string value) {
363             if (!AllowHttpResponseHeader) {
364                 throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
365             }
366             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
367                 if (value!=null && value.Length>ushort.MaxValue) {
368                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
369                 }
370             }
371             this.SetInternal(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value);
372         }
373
374
375         public void Remove(HttpRequestHeader header) {
376             if (!AllowHttpRequestHeader) {
377                 throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
378             }
379             this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header));
380         }
381
382         public void Remove(HttpResponseHeader header) {
383             if (!AllowHttpResponseHeader) {
384                 throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
385             }
386             this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header));
387         }
388 #endif // !FEATURE_PAL
389
390         // In general, HttpWebResponse headers aren't modified, so these methods don't support common headers.
391
392         /// <devdoc>
393         ///    <para>[To be supplied.]</para>
394         /// </devdoc>
395         protected void AddWithoutValidate(string headerName, string headerValue) {
396             headerName = CheckBadChars(headerName, false);
397             headerValue = CheckBadChars(headerValue, true);
398             GlobalLog.Print("WebHeaderCollection::AddWithoutValidate() calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]");
399             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
400                 if (headerValue!=null && headerValue.Length>ushort.MaxValue) {
401                     throw new ArgumentOutOfRangeException("headerValue", headerValue, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
402                 }
403             }
404             NormalizeCommonHeaders();
405             InvalidateCachedArrays();
406             InnerCollection.Add(headerName, headerValue);
407         }
408
409         internal void SetAddVerified(string name, string value) {
410             if(HInfo[name].AllowMultiValues) {
411                 GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
412                 NormalizeCommonHeaders();
413                 InvalidateCachedArrays();
414                 InnerCollection.Add(name, value);
415             }
416             else {
417                 GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
418                 NormalizeCommonHeaders();
419                 InvalidateCachedArrays();
420                 InnerCollection.Set(name, value);
421             }
422         }
423
424         // Below three methods are for fast headers manipulation, bypassing all the checks
425         internal void AddInternal(string name, string value) {
426             GlobalLog.Print("WebHeaderCollection::AddInternal() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
427             NormalizeCommonHeaders();
428             InvalidateCachedArrays();
429             InnerCollection.Add(name, value);
430         }
431
432         internal void ChangeInternal(string name, string value) {
433             GlobalLog.Print("WebHeaderCollection::ChangeInternal() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
434             NormalizeCommonHeaders();
435             InvalidateCachedArrays();
436             InnerCollection.Set(name, value);
437         }
438
439
440         internal void RemoveInternal(string name) {
441             GlobalLog.Print("WebHeaderCollection::RemoveInternal() calling InnerCollection.Remove() key:[" + name + "]");
442             NormalizeCommonHeaders();
443             if (m_InnerCollection != null)
444             {
445                 InvalidateCachedArrays();
446                 m_InnerCollection.Remove(name);
447             }
448         }
449
450         internal void CheckUpdate(string name, string value) {
451             value = CheckBadChars(value, true);
452             ChangeInternal(name, value);
453         }
454
455         // This even faster one can be used to add headers when it's known not to be a common header or that common headers aren't active.
456         private void AddInternalNotCommon(string name, string value)
457         {
458             GlobalLog.Print("WebHeaderCollection::AddInternalNotCommon() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
459             InvalidateCachedArrays();
460             InnerCollection.Add(name, value);
461         }
462
463
464         private static readonly char[] HttpTrimCharacters = new char[]{(char)0x09,(char)0xA,(char)0xB,(char)0xC,(char)0xD,(char)0x20};
465
466         //
467         // CheckBadChars - throws on invalid chars to be not found in header name/value
468         //
469         internal static string CheckBadChars(string name, bool isHeaderValue) {
470
471             if (name == null || name.Length == 0) {
472                 // emtpy name is invlaid
473                 if (!isHeaderValue) {
474                     throw name == null ? new ArgumentNullException("name") :
475                         new ArgumentException(SR.GetString(SR.net_emptystringcall, "name"), "name");
476                 }
477                 //empty value is OK
478                 return string.Empty;
479             }
480
481             if (isHeaderValue) {
482                 // VALUE check
483                 //Trim spaces from both ends
484                 name = name.Trim(HttpTrimCharacters);
485
486                 //First, check for correctly formed multi-line value
487                 //Second, check for absenece of CTL characters
488                 int crlf = 0;
489                 for(int i = 0; i < name.Length; ++i) {
490                     char c = (char) (0x000000ff & (uint) name[i]);
491                     switch (crlf)
492                     {
493                         case 0:
494                             if (c == '\r')
495                             {
496                                 crlf = 1;
497                             }
498                             else if (c == '\n')
499                             {
500                                 // Technically this is bad HTTP.  But it would be a breaking change to throw here.
501                                 // Is there an exploit?
502                                 crlf = 2;
503                             }
504                             else if (c == 127 || (c < ' ' && c != '\t'))
505                             {
506                                 throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidControlChars), "value");
507                             }
508                             break;
509
510                         case 1:
511                             if (c == '\n')
512                             {
513                                 crlf = 2;
514                                 break;
515                             }
516                             throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");
517
518                         case 2:
519                             if (c == ' ' || c == '\t')
520                             {
521                                 crlf = 0;
522                                 break;
523                             }
524                             throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");
525                     }
526                 }
527                 if (crlf != 0)
528                 {
529                     throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");
530                 }
531             }
532             else {
533                 // NAME check
534                 //First, check for absence of separators and spaces
535                 if (name.IndexOfAny(ValidationHelper.InvalidParamChars) != -1) {
536                     throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidHeaderChars), "name");
537                 }
538
539                 //Second, check for non CTL ASCII-7 characters (32-126)
540                 if (ContainsNonAsciiChars(name)) {
541                     throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidNonAsciiChars), "name");
542                 }
543             }
544             return name;
545         }
546
547         internal static bool IsValidToken(string token) {
548             return (token.Length > 0)
549                 && (token.IndexOfAny(ValidationHelper.InvalidParamChars) == -1)
550                 && !ContainsNonAsciiChars(token);
551         }
552
553         internal static bool ContainsNonAsciiChars(string token) {
554             for (int i = 0; i < token.Length; ++i) {
555                 if ((token[i] < 0x20) || (token[i] > 0x7e)) {
556                     return true;
557                 }
558             }
559             return false;
560         }
561
562         //
563         // ThrowOnRestrictedHeader - generates an error if the user,
564         //  passed in a reserved string as the header name
565         //
566         internal void ThrowOnRestrictedHeader(string headerName)
567         {
568             if (m_Type == WebHeaderCollectionType.HttpWebRequest)
569             {
570                 if (HInfo[headerName].IsRequestRestricted)
571                 {
572                     throw new ArgumentException(SR.GetString(SR.net_headerrestrict, headerName), "name");
573                 }
574             }
575             else if (m_Type == WebHeaderCollectionType.HttpListenerResponse)
576             {
577                 if (HInfo[headerName].IsResponseRestricted)
578                 {
579                     throw new ArgumentException(SR.GetString(SR.net_headerrestrict, headerName), "name");
580                 }
581             }
582         }
583
584         //
585         // Our Public METHOD set, most are inherited from NameValueCollection,
586         //  not all methods from NameValueCollection are listed, even though usable -
587         //
588         //  this includes
589         //  Add(name, value)
590         //  Add(header)
591         //  this[name] {set, get}
592         //  Remove(name), returns bool
593         //  Remove(name), returns void
594         //  Set(name, value)
595         //  ToString()
596         //
597         //  SplitValue(name, value)
598         //  ToByteArray()
599         //  ParseHeaders(char [], ...)
600         //  ParseHeaders(byte [], ...)
601         //
602
603         // Add more headers; if "name" already exists it will
604         // add concatenated value
605
606
607         // Add -
608         //  Routine Description:
609         //      Adds headers with validation to see if they are "proper" headers.
610         //      Will cause header to be concat to existing if already found.
611         //      If the header is a special header, listed in RestrictedHeaders object,
612         //      then this call will cause an exception indication as such.
613         //  Arguments:
614         //      name - header-name to add
615         //      value - header-value to add, a header is already there, will concat this value
616         //  Return Value:
617         //      None
618
619         /// <devdoc>
620         ///    <para>
621         ///       Adds a new header with the indicated name and value.
622         ///    </para>
623         /// </devdoc>
624         public override void Add(string name, string value) {
625             name = CheckBadChars(name, false);
626             ThrowOnRestrictedHeader(name);
627             value = CheckBadChars(value, true);
628             GlobalLog.Print("WebHeaderCollection::Add() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
629             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
630                 if (value!=null && value.Length>ushort.MaxValue) {
631                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
632                 }
633             }
634             NormalizeCommonHeaders();
635             InvalidateCachedArrays();
636             InnerCollection.Add(name, value);
637         }
638
639
640         // Add -
641         // Routine Description:
642         //     Adds headers with validation to see if they are "proper" headers.
643         //     Assumes a combined a "Name: Value" string, and parses the two parts out.
644         //     Will cause header to be concat to existing if already found.
645         //     If the header is a speical header, listed in RestrictedHeaders object,
646         //     then this call will cause an exception indication as such.
647         // Arguments:
648         //     header - header name: value pair
649         // Return Value:
650         //     None
651
652         /// <devdoc>
653         ///    <para>
654         ///       Adds the indicated header.
655         ///    </para>
656         /// </devdoc>
657         public void Add(string header) {
658             if ( ValidationHelper.IsBlankString(header) ) {
659                 throw new ArgumentNullException("header");
660             }
661             int colpos = header.IndexOf(':');
662             // check for badly formed header passed in
663             if (colpos<0) {
664                 throw new ArgumentException(SR.GetString(SR.net_WebHeaderMissingColon), "header");
665             }
666             string name = header.Substring(0, colpos);
667             string value = header.Substring(colpos+1);
668             name = CheckBadChars(name, false);
669             ThrowOnRestrictedHeader(name);
670             value = CheckBadChars(value, true);
671             GlobalLog.Print("WebHeaderCollection::Add(" + header + ") calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
672             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
673                 if (value!=null && value.Length>ushort.MaxValue) {
674                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
675                 }
676             }
677             NormalizeCommonHeaders();
678             InvalidateCachedArrays();
679             InnerCollection.Add(name, value);
680         }
681
682         // Set -
683         // Routine Description:
684         //     Sets headers with validation to see if they are "proper" headers.
685         //     If the header is a special header, listed in RestrictedHeaders object,
686         //     then this call will cause an exception indication as such.
687         // Arguments:
688         //     name - header-name to set
689         //     value - header-value to set
690         // Return Value:
691         //     None
692
693         /// <devdoc>
694         ///    <para>
695         ///       Sets the specified header to the specified value.
696         ///    </para>
697         /// </devdoc>
698         public override void Set(string name, string value) {
699             if (ValidationHelper.IsBlankString(name)) {
700                 throw new ArgumentNullException("name");
701             }
702             name = CheckBadChars(name, false);
703             ThrowOnRestrictedHeader(name);
704             value = CheckBadChars(value, true);
705             GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
706             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
707                 if (value!=null && value.Length>ushort.MaxValue) {
708                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
709                 }
710             }
711             NormalizeCommonHeaders();
712             InvalidateCachedArrays();
713             InnerCollection.Set(name, value);
714         }
715
716
717         internal void SetInternal(string name, string value) {
718             if (ValidationHelper.IsBlankString(name)) {
719                 throw new ArgumentNullException("name");
720             }
721             name = CheckBadChars(name, false);
722             value = CheckBadChars(value, true);
723             GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
724             if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
725                 if (value!=null && value.Length>ushort.MaxValue) {
726                     throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
727                 }
728             }
729             NormalizeCommonHeaders();
730             InvalidateCachedArrays();
731             InnerCollection.Set(name, value);
732         }
733
734
735         // Remove -
736         // Routine Description:
737         //     Removes give header with validation to see if they are "proper" headers.
738         //     If the header is a speical header, listed in RestrictedHeaders object,
739         //     then this call will cause an exception indication as such.
740         // Arguments:
741         //     name - header-name to remove
742         // Return Value:
743         //     None
744
745         /// <devdoc>
746         ///    <para>Removes the specified header.</para>
747         /// </devdoc>
748         public override void Remove(string name) {
749             if ( ValidationHelper.IsBlankString(name) ) {
750                 throw new ArgumentNullException("name");
751             }
752             ThrowOnRestrictedHeader(name);
753             name = CheckBadChars(name,  false);
754             GlobalLog.Print("WebHeaderCollection::Remove() calling InnerCollection.Remove() key:[" + name + "]");
755             NormalizeCommonHeaders();
756             if (m_InnerCollection != null)
757             {
758                 InvalidateCachedArrays();
759                 m_InnerCollection.Remove(name);
760             }
761         }
762
763
764         // GetValues
765         // Routine Description:
766         //     This method takes a header name and returns a string array representing
767         //     the individual values for that headers. For example, if the headers
768         //     contained the line Accept: text/plain, text/html then
769         //     GetValues("Accept") would return an array of two strings: "text/plain"
770         //     and "text/html".
771         // Arguments:
772         //     header      - Name of the header.
773         // Return Value:
774         //     string[] - array of parsed string objects
775
776         /// <devdoc>
777         ///    <para>
778         ///       Gets an array of header values stored in a
779         ///       header.
780         ///    </para>
781         /// </devdoc>
782         public override string[] GetValues(string header) {
783             // This method doesn't work with common headers.  Dump common headers into the pool.
784             NormalizeCommonHeaders();
785
786             // First get the information about the header and the values for
787             // the header.
788             HeaderInfo Info = HInfo[header];
789             string[] Values = InnerCollection.GetValues(header);
790             // If we have no information about the header or it doesn't allow
791             // multiple values, just return the values.
792             if (Info == null || Values == null || !Info.AllowMultiValues) {
793                 return Values;
794             }
795             // Here we have a multi value header. We need to go through
796             // each entry in the multi values array, and if an entry itself
797             // has multiple values we'll need to combine those in.
798             //
799             // We do some optimazation here, where we try not to copy the
800             // values unless there really is one that have multiple values.
801             string[] TempValues;
802             ArrayList ValueList = null;
803             int i;
804             for (i = 0; i < Values.Length; i++) {
805                 // Parse this value header.
806                 TempValues = Info.Parser(Values[i]);
807                 // If we don't have an array list yet, see if this
808                 // value has multiple values.
809                 if (ValueList == null) {
810                     // See if it has multiple values.
811                     if (TempValues.Length > 1) {
812                         // It does, so we need to create an array list that
813                         // represents the Values, then trim out this one and
814                         // the ones after it that haven't been parsed yet.
815                         ValueList = new ArrayList(Values);
816                         ValueList.RemoveRange(i, Values.Length - i);
817                         ValueList.AddRange(TempValues);
818                     }
819                 }
820                 else {
821                     // We already have an ArrayList, so just add the values.
822                     ValueList.AddRange(TempValues);
823                 }
824             }
825             // See if we have an ArrayList. If we don't, just return the values.
826             // Otherwise convert the ArrayList to a string array and return that.
827             if (ValueList != null) {
828                 string[] ReturnArray = new string[ValueList.Count];
829                 ValueList.CopyTo(ReturnArray);
830                 return ReturnArray;
831             }
832             return Values;
833         }
834
835
836         // ToString()  -
837         // Routine Description:
838         //     Generates a string representation of the headers, that is ready to be sent except for it being in string format:
839         //     the format looks like:
840         //
841         //     Header-Name: Header-Value\r\n
842         //     Header-Name2: Header-Value2\r\n
843         //     ...
844         //     Header-NameN: Header-ValueN\r\n
845         //     \r\n
846         //
847         //     Uses the string builder class to Append the elements together.
848         // Arguments:
849         //     None.
850         // Return Value:
851         //     string
852
853         /// <internalonly/>
854         /// <devdoc>
855         ///    <para>
856         ///       Obsolete.
857         ///    </para>
858         /// </devdoc>
859         public override string ToString() {
860             string result = GetAsString(this, false, false);
861             GlobalLog.Print("WebHeaderCollection::ToString: \r\n" + result);
862             return result;
863         }
864
865         internal string ToString(bool forTrace)
866         {
867             return GetAsString(this, false, true);
868         }
869             
870
871         //
872         // if winInetCompat = true then it will not insert spaces after ':'
873         // and it will output "~U" header first
874         //
875         internal static string GetAsString(NameValueCollection cc, 
876                                            bool                winInetCompat,
877                                            bool                forTrace) {
878 #if FEATURE_PAL
879             if (winInetCompat) {
880                 throw new InvalidOperationException();
881             }
882 #endif // FEATURE_PAL
883
884             if (cc == null || cc.Count == 0) {
885                 return "\r\n";
886             }
887             StringBuilder sb = new StringBuilder(ApproxAveHeaderLineSize*cc.Count);
888             string statusLine;
889             statusLine = cc[string.Empty];
890             if (statusLine != null) {
891                 sb.Append(statusLine).Append("\r\n");
892             }
893             for (int i = 0; i < cc.Count ; i++) {
894                 string key = cc.GetKey(i) as string;
895                 string val = cc.Get(i) as string;
896                 /*
897                 if (forTrace)
898                 {
899                     // Put a condition here that if we are using basic auth, 
900                     // we shouldn't put the authorization header. Otherwise
901                     // the password will get saved in the trace.
902                     if (using basic)
903                         continue;
904                 }
905                 */
906                 if (ValidationHelper.IsBlankString(key)) {
907                     continue;
908                 }
909                 sb.Append(key);
910                 if (winInetCompat) {
911                     sb.Append(':');
912                 }
913                 else {
914                     sb.Append(": ");
915                 }
916                 sb.Append(val).Append("\r\n");
917             }
918             if (!forTrace)
919                 sb.Append("\r\n");
920             return sb.ToString();
921         }
922
923
924         // ToByteArray()  -
925         // Routine Description:
926         //     Generates a byte array representation of the headers, that is ready to be sent.
927         //     So it Serializes our headers into a byte array suitable for sending over the net.
928         //
929         //     the format looks like:
930         //
931         //     Header-Name1: Header-Value1\r\n
932         //     Header-Name2: Header-Value2\r\n
933         //     ...
934         //     Header-NameN: Header-ValueN\r\n
935         //     \r\n
936         //
937         //     Uses the ToString() method to generate, and then performs conversion.
938         //
939         //     Performance Note:  Why are we not doing a single copy/covert run?
940         //     As the code before used to know the size of the output!
941         //     Because according to Demitry, its cheaper to copy the headers twice,
942         //     then it is to call the UNICODE to ANSI conversion code many times.
943         // Arguments:
944         //     None.
945         // Return Value:
946         //     byte [] - array of bytes values
947
948         /// <internalonly/>
949         /// <devdoc>
950         ///    <para>
951         ///       Obsolete.
952         ///    </para>
953         /// </devdoc>
954         public byte[] ToByteArray() {
955             // Make sure the buffer is big enough.
956             string tempStr = ToString();
957             //
958             // Use the string of headers, convert to Char Array,
959             //  then convert to Bytes,
960             //  serializing finally into the buffer, along the way.
961             //
962             byte[] buffer = HeaderEncoding.GetBytes(tempStr);
963             return buffer;
964         }
965
966         /// <devdoc>
967         ///    <para>Tests if access to the HTTP header with the provided name is accessible for setting.</para>
968         /// </devdoc>
969         public static bool IsRestricted(string headerName)
970         {
971             return IsRestricted(headerName, false);
972         }
973
974         public static bool IsRestricted(string headerName, bool response)
975         {
976             return response ? HInfo[CheckBadChars(headerName, false)].IsResponseRestricted : HInfo[CheckBadChars(headerName, false)].IsRequestRestricted;
977         }
978
979
980         /// <devdoc>
981         ///    <para>
982         ///       Initializes a new instance of the <see cref='System.Net.WebHeaderCollection'/>
983         ///       class.
984         ///    </para>
985         /// </devdoc>
986         public WebHeaderCollection() : base(DBNull.Value)
987         {
988         }
989
990         internal WebHeaderCollection(WebHeaderCollectionType type) : base(DBNull.Value)
991         {
992             m_Type = type;
993             if (type == WebHeaderCollectionType.HttpWebResponse)
994                 m_CommonHeaders = new string[s_CommonHeaderNames.Length - 1];  // Minus one for the sentinel.
995         }
996
997         //This is for Cache
998         internal WebHeaderCollection(NameValueCollection cc): base(DBNull.Value)
999         {
1000             m_InnerCollection = new NameValueCollection(cc.Count + 2, CaseInsensitiveAscii.StaticInstance);
1001             int len = cc.Count;
1002             for (int i = 0; i < len; ++i) {
1003                 String key = cc.GetKey(i);
1004                 String[] values = cc.GetValues(i);
1005                 if (values != null) {
1006                     for (int j = 0; j < values.Length; j++) {
1007                         InnerCollection.Add(key, values[j]);
1008                     }
1009                 }
1010                 else {
1011                     InnerCollection.Add(key, null);
1012                 }
1013             }
1014         }
1015
1016         //
1017         // ISerializable constructor
1018         //
1019         /// <devdoc>
1020         ///    <para>[To be supplied.]</para>
1021         /// </devdoc>
1022         protected WebHeaderCollection(SerializationInfo serializationInfo, StreamingContext streamingContext) :
1023             base(DBNull.Value)
1024         {
1025             int count = serializationInfo.GetInt32("Count");
1026             m_InnerCollection = new NameValueCollection(count + 2, CaseInsensitiveAscii.StaticInstance);
1027             for (int i = 0; i < count; i++) {
1028                 string headerName = serializationInfo.GetString(i.ToString(NumberFormatInfo.InvariantInfo));
1029                 string headerValue = serializationInfo.GetString((i+count).ToString(NumberFormatInfo.InvariantInfo));
1030                 GlobalLog.Print("WebHeaderCollection::.ctor(ISerializable) calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]");
1031                 InnerCollection.Add(headerName, headerValue);
1032             }
1033         }
1034
1035         public override void OnDeserialization(object sender) {
1036             // 
1037
1038
1039         }
1040
1041         //
1042         // ISerializable method
1043         //
1044         /// <internalonly/>
1045         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]           
1046         public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) {
1047             //
1048             // for now disregard streamingContext.
1049             //
1050             NormalizeCommonHeaders();
1051             serializationInfo.AddValue("Count", Count);
1052             for (int i = 0; i < Count; i++)
1053             {
1054                 serializationInfo.AddValue(i.ToString(NumberFormatInfo.InvariantInfo), GetKey(i));
1055                 serializationInfo.AddValue((i + Count).ToString(NumberFormatInfo.InvariantInfo), Get(i));
1056             }
1057         }
1058
1059
1060         // we use this static class as a helper class to encode/decode HTTP headers.
1061         // what we need is a 1-1 correspondence between a char in the range U+0000-U+00FF
1062         // and a byte in the range 0x00-0xFF (which is the range that can hit the network).
1063         // The Latin-1 encoding (ISO-88591-1) (GetEncoding(28591)) works for byte[] to string, but is a little slow.
1064         // It doesn't work for string -> byte[] because of best-fit-mapping problems.
1065         internal static class HeaderEncoding
1066         {
1067             internal static unsafe string GetString(byte[] bytes, int byteIndex, int byteCount)
1068             {
1069                 fixed(byte* pBytes = bytes)
1070                     return GetString(pBytes + byteIndex, byteCount);
1071             }
1072
1073             internal static unsafe string GetString(byte* pBytes, int byteCount)
1074             {
1075                 if (byteCount < 1)
1076                     return "";
1077
1078                 string s = new String('\0', byteCount);
1079
1080                 fixed (char* pStr = s)
1081                 {
1082                     char* pString = pStr;
1083                     while (byteCount >= 8)
1084                     {
1085                         pString[0] = (char) pBytes[0];
1086                         pString[1] = (char) pBytes[1];
1087                         pString[2] = (char) pBytes[2];
1088                         pString[3] = (char) pBytes[3];
1089                         pString[4] = (char) pBytes[4];
1090                         pString[5] = (char) pBytes[5];
1091                         pString[6] = (char) pBytes[6];
1092                         pString[7] = (char) pBytes[7];
1093                         pString += 8;
1094                         pBytes += 8;
1095                         byteCount -= 8;
1096                     }
1097                     for (int i = 0; i < byteCount; i++)
1098                     {
1099                         pString[i] = (char) pBytes[i];
1100                     }
1101                 }
1102
1103                 return s;
1104             }
1105
1106             internal static int GetByteCount(string myString) {
1107                 return myString.Length;
1108             }
1109             internal unsafe static void GetBytes(string myString, int charIndex, int charCount, byte[] bytes, int byteIndex) {
1110                 if (myString.Length==0) {
1111                     return;
1112                 }
1113                 fixed (byte *bufferPointer = bytes) {
1114                     byte* newBufferPointer = bufferPointer + byteIndex;
1115                     int finalIndex = charIndex + charCount;
1116                     while (charIndex<finalIndex) {
1117                         *newBufferPointer++ = (byte)myString[charIndex++];
1118                     }
1119                 }
1120             }
1121             internal unsafe static byte[] GetBytes(string myString) {
1122                 byte[] bytes = new byte[myString.Length];
1123                 if (myString.Length!=0) {
1124                     GetBytes(myString, 0, myString.Length, bytes, 0);
1125                 }
1126                 return bytes;
1127             }
1128
1129             // The normal client header parser just casts bytes to chars (see GetString).
1130             // Check if those bytes were actually utf-8 instead of ASCII.
1131             // If not, just return the input value.
1132             [System.Runtime.CompilerServices.FriendAccessAllowed]
1133             internal static string DecodeUtf8FromString(string input) {
1134                 if (string.IsNullOrWhiteSpace(input)) {
1135                     return input;
1136                 }
1137
1138                 bool possibleUtf8 = false;
1139                 for (int i = 0; i < input.Length; i++) {
1140                     if (input[i] > (char)255) {
1141                         return input; // This couldn't have come from the wire, someone assigned it directly.
1142                     }
1143                     else if (input[i] > (char)127) {
1144                         possibleUtf8 = true;
1145                         break;
1146                     }
1147                 }
1148                 if (possibleUtf8) {
1149                     byte[] rawBytes = new byte[input.Length];
1150                     for (int i = 0; i < input.Length; i++) {
1151                         if (input[i] > (char)255) {
1152                             return input; // This couldn't have come from the wire, someone assigned it directly.
1153                         }
1154                         rawBytes[i] = (byte)input[i];
1155                     }
1156                     try {
1157                         // We don't want '?' replacement characters, just fail.
1158                         Encoding decoder = Encoding.GetEncoding("utf-8", EncoderFallback.ExceptionFallback,
1159                             DecoderFallback.ExceptionFallback);
1160                         return decoder.GetString(rawBytes);
1161                     }
1162                     catch (ArgumentException) { } // Not actually Utf-8
1163                 }
1164                 return input;
1165             }
1166         }
1167
1168
1169         // ParseHeaders -
1170         // Routine Description:
1171         //
1172         //     This code is optimized for the case in which all the headers fit in the buffer.
1173         //     we support multiple re-entrance, but we won't save intermediate
1174         //     state, we will just roll back all the parsing done for the current header if we can't
1175         //     parse a whole one (including multiline) or decide something else ("invalid data" or "done parsing").
1176         //
1177         //     we're going to cycle through the loop until we
1178         //
1179         //     1) find an HTTP violation (in this case we return DataParseStatus.Invalid)
1180         //     2) we need more data (in this case we return DataParseStatus.NeedMoreData)
1181         //     3) we found the end of the headers and the beginning of the entity body (in this case we return DataParseStatus.Done)
1182         //
1183         //
1184         // Arguments:
1185         //
1186         //     buffer      - buffer containing the data to be parsed
1187         //     size        - size of the buffer
1188         //     unparsed    - offset of data yet to be parsed
1189         //
1190         // Return Value:
1191         //
1192         //     DataParseStatus - status of parsing
1193         //
1194         // Revision:
1195         //
1196         //     02/13/2001 rewrote the method from scratch.
1197         //
1198         // BreakPoint:
1199         //
1200         //     b system.dll!System.Net.WebHeaderCollection::ParseHeaders
1201         internal unsafe DataParseStatus ParseHeaders(
1202                 byte[] buffer, 
1203                 int size, 
1204                 ref int unparsed, 
1205                 ref int totalResponseHeadersLength, 
1206                 int maximumResponseHeadersLength, 
1207                 ref WebParseError parseError) {
1208
1209             fixed (byte * byteBuffer = buffer) {
1210
1211             char ch;
1212
1213             // quick check in the boundaries (as we use unsafe pointer)
1214             if (buffer.Length < size) {
1215                 return DataParseStatus.NeedMoreData;
1216             }
1217
1218             int headerNameStartOffset = -1;
1219             int headerNameEndOffset = -1;
1220             int headerValueStartOffset = -1;
1221             int headerValueEndOffset = -1;
1222             int numberOfLf = -1;
1223             int index = unparsed;
1224             bool spaceAfterLf;
1225             string headerMultiLineValue;
1226             string headerName;
1227             string headerValue;
1228
1229             // we need this because this method is entered multiple times.
1230             int localTotalResponseHeadersLength = totalResponseHeadersLength;
1231
1232             WebParseErrorCode parseErrorCode = WebParseErrorCode.Generic;
1233             DataParseStatus parseStatus = DataParseStatus.Invalid;
1234 #if TRAVE
1235             GlobalLog.Enter("WebHeaderCollection::ParseHeaders(): ANSI size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size-unparsed)) + "]");
1236 #endif
1237
1238             //
1239             // according to RFC216 a header can have the following syntax:
1240             //
1241             // message-header = field-name ":" [ field-value ]
1242             // field-name     = token
1243             // field-value    = *( field-content | LWS )
1244             // field-content  = <the OCTETs making up the field-value and consisting of either *TEXT or combinations of token, separators, and quoted-string>
1245             // TEXT           = <any OCTET except CTLs, but including LWS>
1246             // CTL            = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
1247             // SP             = <US-ASCII SP, space (32)>
1248             // HT             = <US-ASCII HT, horizontal-tab (9)>
1249             // CR             = <US-ASCII CR, carriage return (13)>
1250             // LF             = <US-ASCII LF, linefeed (10)>
1251             // LWS            = [CR LF] 1*( SP | HT )
1252             // CHAR           = <any US-ASCII character (octets 0 - 127)>
1253             // token          = 1*<any CHAR except CTLs or separators>
1254             // separators     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
1255             // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
1256             // qdtext         = <any TEXT except <">>
1257             // quoted-pair    = "\" CHAR
1258             //
1259
1260             //
1261             // At each iteration of the following loop we expect to parse a single HTTP header entirely.
1262             //
1263             for (;;) {
1264                 //
1265                 // trim leading whitespaces (LWS) just for extra robustness, in fact if there are leading white spaces then:
1266                 // 1) it could be that after the status line we might have spaces. handle this.
1267                 // 2) this should have been detected to be a multiline header so there'll be no spaces and we'll spend some time here.
1268                 //
1269                 headerName = string.Empty;
1270                 headerValue = string.Empty;
1271                 spaceAfterLf = false;
1272                 headerMultiLineValue = null;
1273
1274                 if (Count == 0) {
1275                     //
1276                     // so, restrict this extra trimming only on the first header line
1277                     //
1278                     while (index < size) {
1279                          ch = (char) byteBuffer[index];
1280                          if (ch == ' ' || ch == '\t') {
1281                              ++index;
1282                             if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1283                                 parseStatus = DataParseStatus.DataTooBig;
1284                                 goto quit;
1285                             }
1286                         }
1287                         else {
1288                             break;
1289                         }
1290                     }
1291
1292                     if (index==size) {
1293                         //
1294                         // we reached the end of the buffer. ask for more data.
1295                         //
1296                         parseStatus = DataParseStatus.NeedMoreData;
1297                         goto quit;
1298                     }
1299                 }
1300
1301                 //
1302                 // what we have here is the beginning of a new header
1303                 //
1304                 headerNameStartOffset = index;
1305
1306                 while (index < size) {
1307                     ch = (char) byteBuffer[index];
1308                     if (ch != ':' && ch != '\n') {
1309                         if (ch > ' ') {
1310                             //
1311                             // if there's an illegal character we should return DataParseStatus.Invalid
1312                             // instead we choose to be flexible, try to trim it, but include it in the string
1313                             //
1314                             headerNameEndOffset = index;
1315                         }
1316                         ++index;
1317                         if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1318                             parseStatus = DataParseStatus.DataTooBig;
1319                             goto quit;
1320                         }
1321                     }
1322                     else {
1323                         if (ch == ':') {
1324                             ++index;
1325                             if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1326                                 parseStatus = DataParseStatus.DataTooBig;
1327                                 goto quit;
1328                             }
1329                         }
1330                         break;
1331                     }
1332                 }
1333                 if (index==size) {
1334                     //
1335                     // we reached the end of the buffer. ask for more data.
1336                     //
1337                     parseStatus = DataParseStatus.NeedMoreData;
1338                     goto quit;
1339                 }
1340
1341 startOfValue:
1342                 //
1343                 // skip all [' ','\t','\r','\n'] characters until HeaderValue starts
1344                 // if we didn't find any headers yet, we set numberOfLf to 1
1345                 // so that we take the '\n' from the status line into account
1346                 //
1347
1348                 numberOfLf = (Count == 0 && headerNameEndOffset < 0) ? 1 : 0;
1349                 while (index<size && numberOfLf<2) {
1350                     ch = (char) byteBuffer[index];
1351                     if (ch <= ' ') {
1352                         if (ch=='\n') {
1353                             numberOfLf++;
1354                             // In this case, need to check for a space.
1355                             if (numberOfLf == 1)
1356                             {
1357                                 if (index + 1 == size)
1358                                 {
1359                                     //
1360                                     // we reached the end of the buffer. ask for more data.
1361                                     // need to be able to peek after the \n and see if there's some space.
1362                                     //
1363                                     parseStatus = DataParseStatus.NeedMoreData;
1364                                     goto quit;
1365                                 }
1366                                 spaceAfterLf = (char) byteBuffer[index + 1] == ' ' || (char) byteBuffer[index + 1] == '\t';
1367                             }
1368                         }
1369                         ++index;
1370                         if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1371                             parseStatus = DataParseStatus.DataTooBig;
1372                             goto quit;
1373                         }
1374                     }
1375                     else {
1376                         break;
1377                     }
1378                 }
1379                 if (numberOfLf==2 || (numberOfLf==1 && !spaceAfterLf)) {
1380                     //
1381                     // if we've counted two '\n' we got at the end of the headers even if we're past the end of the buffer
1382                     // if we've counted one '\n' and the first character after that was a ' ' or a '\t'
1383                     // no matter if we found a ':' or not, treat this as an empty header name.
1384                     //
1385                     goto addHeader;
1386                 }
1387                 if (index==size) {
1388                     //
1389                     // we reached the end of the buffer. ask for more data.
1390                     //
1391                     parseStatus = DataParseStatus.NeedMoreData;
1392                     goto quit;
1393                 }
1394
1395                 headerValueStartOffset = index;
1396
1397                 while (index<size) {
1398                     ch = (char) byteBuffer[index];
1399                     if (ch != '\n') {
1400                         if (ch > ' ') {
1401                             headerValueEndOffset = index;
1402                         }
1403                         ++index;
1404                         if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1405                             parseStatus = DataParseStatus.DataTooBig;
1406                             goto quit;
1407                         }
1408                     }
1409                     else {
1410                         break;
1411                     }
1412                 }
1413                 if (index==size) {
1414                     //
1415                     // we reached the end of the buffer. ask for more data.
1416                     //
1417                     parseStatus = DataParseStatus.NeedMoreData;
1418                     goto quit;
1419                 }
1420
1421                 //
1422                 // at this point we found either a '\n' or the end of the headers
1423                 // hence we are at the end of the Header Line. 4 options:
1424                 // 1) need more data
1425                 // 2) if we find two '\n' => end of headers
1426                 // 3) if we find one '\n' and a ' ' or a '\t' => multiline header
1427                 // 4) if we find one '\n' and a valid char => next header
1428                 //
1429                 numberOfLf = 0;
1430                 while (index<size && numberOfLf<2) {
1431                     ch = (char) byteBuffer[index];
1432                     if (ch =='\r' || ch == '\n') {
1433                         if (ch == '\n') {
1434                             numberOfLf++;
1435                         }
1436                         ++index;
1437                         if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1438                             parseStatus = DataParseStatus.DataTooBig;
1439                             goto quit;
1440                         }
1441                     }
1442                     else {
1443                         break;
1444                     }
1445                 }
1446                 if (index==size && numberOfLf<2) {
1447                     //
1448                     // we reached the end of the buffer but not of the headers. ask for more data.
1449                     //
1450                     parseStatus = DataParseStatus.NeedMoreData;
1451                     goto quit;
1452                 }
1453
1454 addHeader:
1455                 if (headerValueStartOffset>=0 && headerValueStartOffset>headerNameEndOffset && headerValueEndOffset>=headerValueStartOffset) {
1456                     //
1457                     // Encoding fastest way to build the UNICODE string off the byte[]
1458                     //
1459                     headerValue = HeaderEncoding.GetString(byteBuffer + headerValueStartOffset, headerValueEndOffset - headerValueStartOffset + 1);
1460                 }
1461
1462                 //
1463                 // if we got here from the beginning of the for loop, headerMultiLineValue will be null
1464                 // otherwise it will contain the headerValue constructed for the multiline header
1465                 // add this line as well to it, separated by a single space
1466                 //
1467                 headerMultiLineValue = (headerMultiLineValue==null ? headerValue : headerMultiLineValue + " " + headerValue);
1468
1469                 if (index < size && numberOfLf == 1) {
1470                     ch = (char) byteBuffer[index];
1471                     if (ch == ' ' || ch == '\t') {
1472                         //
1473                         // since we found only one Lf and the next header line begins with a Lws,
1474                         // this is the beginning of a multiline header.
1475                         // parse the next line into headerValue later it will be added to headerMultiLineValue
1476                         //
1477                         ++index;
1478                         if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
1479                             parseStatus = DataParseStatus.DataTooBig;
1480                             goto quit;
1481                         }
1482                         goto startOfValue;
1483                     }
1484                 }
1485
1486                 if (headerNameStartOffset>=0 && headerNameEndOffset>=headerNameStartOffset) {
1487                     //
1488                     // Encoding is the fastest way to build the UNICODE string off the byte[]
1489                     //
1490                     headerName = HeaderEncoding.GetString(byteBuffer + headerNameStartOffset, headerNameEndOffset - headerNameStartOffset + 1);
1491                 }
1492
1493                 //
1494                 // now it's finally safe to add the header if we have a name for it
1495                 //
1496                 if (headerName.Length>0) {
1497                     //
1498                     // the base clasee will check for pre-existing headerValue and append
1499                     // it using commas as indicated in the RFC
1500                     //
1501                     GlobalLog.Print("WebHeaderCollection::ParseHeaders() calling AddInternal() key:[" + headerName + "], value:[" + headerMultiLineValue + "]");
1502                     AddInternal(headerName, headerMultiLineValue);
1503                 }
1504
1505                 //
1506                 // and update unparsed
1507                 //
1508                 totalResponseHeadersLength = localTotalResponseHeadersLength;
1509                 unparsed = index;
1510
1511                 if (numberOfLf==2) {
1512                     parseStatus = DataParseStatus.Done;
1513                     goto quit;
1514                 }
1515
1516             } // for (;;)
1517
1518 quit:
1519             GlobalLog.Leave("WebHeaderCollection::ParseHeaders() returning parseStatus:" + parseStatus.ToString());
1520             if (parseStatus == DataParseStatus.Invalid) {
1521                 parseError.Section = WebParseErrorSection.ResponseHeader; 
1522                 parseError.Code    = parseErrorCode;
1523             }
1524
1525             return parseStatus;
1526             }
1527         }
1528
1529         //
1530         // Alternative parsing that follows RFC2616.  Like the above, this trims both sides of the header value and replaces
1531         // folding with a single space.
1532         //
1533         private enum RfcChar : byte
1534         {
1535             High = 0,
1536             Reg,
1537             Ctl,
1538             CR,
1539             LF,
1540             WS,
1541             Colon,
1542             Delim
1543         }
1544
1545         private static RfcChar[] RfcCharMap = new RfcChar[128]
1546         {
1547             RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
1548             RfcChar.Ctl,   RfcChar.WS,    RfcChar.LF,    RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.CR,    RfcChar.Ctl,   RfcChar.Ctl,
1549             RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
1550             RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
1551             RfcChar.WS,    RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1552             RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim,
1553             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1554             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Colon, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim,
1555             RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1556             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1557             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1558             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,
1559             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1560             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1561             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
1562             RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Ctl,
1563         };
1564
1565         internal unsafe DataParseStatus ParseHeadersStrict(
1566                 byte[] buffer, 
1567                 int size, 
1568                 ref int unparsed, 
1569                 ref int totalResponseHeadersLength, 
1570                 int maximumResponseHeadersLength, 
1571                 ref WebParseError parseError)
1572         {
1573             GlobalLog.Enter("WebHeaderCollection::ParseHeadersStrict(): size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size-unparsed)) + "]");
1574
1575             WebParseErrorCode parseErrorCode = WebParseErrorCode.Generic;
1576             DataParseStatus parseStatus = DataParseStatus.Invalid;
1577
1578             int i = unparsed;
1579             RfcChar ch;
1580             int effectiveSize = maximumResponseHeadersLength <= 0 ? Int32.MaxValue : maximumResponseHeadersLength - totalResponseHeadersLength + i;
1581             DataParseStatus sizeError = DataParseStatus.DataTooBig;
1582             if (size < effectiveSize)
1583             {
1584                 effectiveSize = size;
1585                 sizeError = DataParseStatus.NeedMoreData;
1586             }
1587
1588             // Verify the size.
1589             if (i >= effectiveSize)
1590             {
1591                 parseStatus = sizeError;
1592                 goto quit;
1593             }
1594
1595             fixed (byte* byteBuffer = buffer)
1596             {
1597                 while (true)
1598                 {
1599                     // If this is CRLF, actually we're done.
1600                     if (byteBuffer[i] == '\r')
1601                     {
1602                         if (++i == effectiveSize)
1603                         {
1604                             parseStatus = sizeError;
1605                             goto quit;
1606                         }
1607
1608                         if (byteBuffer[i++] == '\n')
1609                         {
1610                             totalResponseHeadersLength += i - unparsed;
1611                             unparsed = i;
1612                             parseStatus = DataParseStatus.Done;
1613                             goto quit;
1614                         }
1615
1616                         parseStatus = DataParseStatus.Invalid;
1617                         parseErrorCode = WebParseErrorCode.CrLfError;
1618                         goto quit;
1619                     }
1620
1621                     // Find the header name; only regular characters allowed.
1622                     int iBeginName = i;
1623                     for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.Reg; i++);
1624                     if (i == effectiveSize)
1625                     {
1626                         parseStatus = sizeError;
1627                         goto quit;
1628                     }
1629                     if (i == iBeginName)
1630                     {
1631                         parseStatus = DataParseStatus.Invalid;
1632                         parseErrorCode = WebParseErrorCode.InvalidHeaderName;
1633                         goto quit;
1634                     }
1635
1636                     // Read to a colon.
1637                     int iEndName = i - 1;
1638                     int crlf = 0;  // 1 = cr, 2 = crlf
1639                     for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) != RfcChar.Colon; i++)
1640                     {
1641                         switch (ch)
1642                         {
1643                             case RfcChar.WS:
1644                                 if (crlf == 1)
1645                                 {
1646                                     break;
1647                                 }
1648                                 crlf = 0;
1649                                 continue;
1650
1651                             case RfcChar.CR:
1652                                 if (crlf == 0)
1653                                 {
1654                                     crlf = 1;
1655                                     continue;
1656                                 }
1657                                 break;
1658
1659                             case RfcChar.LF:
1660                                 if (crlf == 1)
1661                                 {
1662                                     crlf = 2;
1663                                     continue;
1664                                 }
1665                                 break;
1666                         }
1667                         parseStatus = DataParseStatus.Invalid;
1668                         parseErrorCode = WebParseErrorCode.CrLfError;
1669                         goto quit;
1670                     }
1671                     if (i == effectiveSize)
1672                     {
1673                         parseStatus = sizeError;
1674                         goto quit;
1675                     }
1676                     if (crlf != 0)                        
1677                     {
1678                         parseStatus = DataParseStatus.Invalid;
1679                         parseErrorCode = WebParseErrorCode.IncompleteHeaderLine;
1680                         goto quit;
1681                     }
1682
1683                     // Skip the colon.
1684                     if (++i == effectiveSize)
1685                     {
1686                         parseStatus = sizeError;
1687                         goto quit;
1688                     }
1689
1690                     // Read the value.  crlf = 3 means in the whitespace after a CRLF
1691                     int iBeginValue = -1;
1692                     int iEndValue = -1;
1693                     StringBuilder valueAccumulator = null;
1694                     for (; i < effectiveSize && ((ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.WS || crlf != 2); i++)
1695                     {
1696                         switch (ch)
1697                         {
1698                             case RfcChar.WS:
1699                                 if (crlf == 1)
1700                                 {
1701                                     break;
1702                                 }
1703                                 if (crlf == 2)
1704                                 {
1705                                     crlf = 3;
1706                                 }
1707                                 continue;
1708
1709                             case RfcChar.CR:
1710                                 if (crlf == 0)
1711                                 {
1712                                     crlf = 1;
1713                                     continue;
1714                                 }
1715                                 break;
1716
1717                             case RfcChar.LF:
1718                                 if (crlf == 1)
1719                                 {
1720                                     crlf = 2;
1721                                     continue;
1722                                 }
1723                                 break;
1724
1725                             case RfcChar.High:
1726                             case RfcChar.Colon:
1727                             case RfcChar.Delim:
1728                             case RfcChar.Reg:
1729                                 if (crlf == 1)
1730                                 {
1731                                     break;
1732                                 }
1733                                 if (crlf == 3)
1734                                 {
1735                                     crlf = 0;
1736                                     if (iBeginValue != -1)
1737                                     {
1738                                         string s = HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1);
1739                                         if (valueAccumulator == null)
1740                                         {
1741                                             valueAccumulator = new StringBuilder(s, s.Length * 5);
1742                                         }
1743                                         else
1744                                         {
1745                                             valueAccumulator.Append(" ");
1746                                             valueAccumulator.Append(s);
1747                                         }
1748                                     }
1749                                     iBeginValue = -1;
1750                                 }
1751                                 if (iBeginValue == -1)
1752                                 {
1753                                     iBeginValue = i;
1754                                 }
1755                                 iEndValue = i;
1756                                 continue;
1757                         }
1758                         parseStatus = DataParseStatus.Invalid;
1759                         parseErrorCode = WebParseErrorCode.CrLfError;
1760                         goto quit;
1761                     }
1762                     if (i == effectiveSize)
1763                     {
1764                         parseStatus = sizeError;
1765                         goto quit;
1766                     }
1767
1768                     // Make the value.
1769                     string sValue = iBeginValue == -1 ? "" : HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1);
1770                     if (valueAccumulator != null)
1771                     {
1772                         if (sValue.Length != 0)
1773                         {
1774                             valueAccumulator.Append(" ");
1775                             valueAccumulator.Append(sValue);
1776                         }
1777                         sValue = valueAccumulator.ToString();
1778                     }
1779
1780                     // Make the name.  See if it's a common header first.
1781                     string sName = null;
1782                     int headerNameLength = iEndName - iBeginName + 1;
1783                     if (m_CommonHeaders != null)
1784                     {
1785                         int iHeader = s_CommonHeaderHints[byteBuffer[iBeginName] & 0x1f];
1786                         if (iHeader >= 0)
1787                         {
1788                             while (true)
1789                             {
1790                                 string s = s_CommonHeaderNames[iHeader++];
1791
1792                                 // Not found if we get to a shorter header or one with a different first character.
1793                                 if (s.Length < headerNameLength || CaseInsensitiveAscii.AsciiToLower[byteBuffer[iBeginName]] != CaseInsensitiveAscii.AsciiToLower[s[0]])
1794                                     break;
1795
1796                                 // Keep looking if the common header is too long.
1797                                 if (s.Length > headerNameLength)
1798                                     continue;
1799
1800                                 int j;
1801                                 byte* pBuffer = byteBuffer + iBeginName + 1;
1802                                 for (j = 1; j < s.Length; j++)
1803                                 {
1804                                     // Avoid the case-insensitive compare in the common case where they match.
1805                                     if (*(pBuffer++) != s[j] && CaseInsensitiveAscii.AsciiToLower[*(pBuffer - 1)] != CaseInsensitiveAscii.AsciiToLower[s[j]])
1806                                         break;
1807                                 }
1808                                 if (j == s.Length)
1809                                 {
1810                                     // Set it to the appropriate index.
1811                                     m_NumCommonHeaders++;
1812                                     iHeader--;
1813                                     if (m_CommonHeaders[iHeader] == null)
1814                                     {
1815                                         m_CommonHeaders[iHeader] = sValue;
1816                                     }
1817                                     else
1818                                     {
1819                                         // Don't currently handle combining multiple header instances in the common header case.
1820                                         // Nothing to do but punt them all to the NameValueCollection.
1821                                         NormalizeCommonHeaders();
1822                                         AddInternalNotCommon(s, sValue);
1823                                     }
1824
1825                                     sName = s;
1826                                     break;
1827                                 }
1828                             }
1829                         }
1830                     }
1831
1832                     // If it wasn't a common header, add it to the hash.
1833                     if (sName == null)
1834                     {
1835                         sName = HeaderEncoding.GetString(byteBuffer + iBeginName, headerNameLength);
1836                         AddInternalNotCommon(sName, sValue);
1837                     }
1838
1839                     totalResponseHeadersLength += i - unparsed;
1840                     unparsed = i;
1841                 }
1842             }
1843
1844 quit:
1845             GlobalLog.Leave("WebHeaderCollection::ParseHeadersStrict() returning parseStatus:" + parseStatus.ToString());
1846
1847             if (parseStatus == DataParseStatus.Invalid) {
1848                 parseError.Section = WebParseErrorSection.ResponseHeader; 
1849                 parseError.Code    = parseErrorCode;
1850             }
1851
1852             return parseStatus;
1853         }
1854
1855         //
1856         // Keeping this version for backwards compatibility (mostly with reflection).  Remove some day, along with the interface
1857         // explicit reimplementation.
1858         //
1859         /// <internalonly/>
1860         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
1861         [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)]
1862         void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
1863         {
1864             GetObjectData(serializationInfo, streamingContext);
1865         }
1866
1867         // Override Get() to check the common headers.
1868         public override string Get(string name)
1869         {
1870             // In this case, need to make sure name doesn't have any Unicode since it's being used as an index into tables.
1871             if (m_CommonHeaders != null && name != null && name.Length > 0 && name[0] < 256)
1872             {
1873                 int iHeader = s_CommonHeaderHints[name[0] & 0x1f];
1874                 if (iHeader >= 0)
1875                 {
1876                     while (true)
1877                     {
1878                         string s = s_CommonHeaderNames[iHeader++];
1879
1880                         // Not found if we get to a shorter header or one with a different first character.
1881                         if (s.Length < name.Length || CaseInsensitiveAscii.AsciiToLower[name[0]] != CaseInsensitiveAscii.AsciiToLower[s[0]])
1882                             break;
1883
1884                         // Keep looking if the common header is too long.
1885                         if (s.Length > name.Length)
1886                             continue;
1887
1888                         int j;
1889                         for (j = 1; j < s.Length; j++)
1890                         {
1891                             // Avoid the case-insensitive compare in the common case where they match.
1892                             if (name[j] != s[j] && (name[j] > 255 || CaseInsensitiveAscii.AsciiToLower[name[j]] != CaseInsensitiveAscii.AsciiToLower[s[j]]))
1893                                 break;
1894                         }
1895                         if (j == s.Length)
1896                         {
1897                             // Get the appropriate header.
1898                             return m_CommonHeaders[iHeader - 1];
1899                         }
1900                     }
1901                 }
1902             }
1903
1904             // Fall back to normal lookup.
1905             if (m_InnerCollection == null)
1906                 return null;
1907             return m_InnerCollection.Get(name);
1908         }
1909
1910
1911         //
1912         // Additional overrides required to fully orphan the base implementation.
1913         //
1914         public override IEnumerator GetEnumerator()
1915         {
1916             NormalizeCommonHeaders();
1917             return new NameObjectKeysEnumerator(InnerCollection);
1918         }
1919
1920         public override int Count
1921         {
1922             get
1923             {
1924                 return (m_InnerCollection == null ? 0 : m_InnerCollection.Count) + m_NumCommonHeaders;
1925             }
1926         }
1927
1928         public override KeysCollection Keys
1929         {
1930             get
1931             {
1932                 NormalizeCommonHeaders();
1933                 return InnerCollection.Keys;
1934             }
1935         }
1936
1937         internal override bool InternalHasKeys()
1938         {
1939             NormalizeCommonHeaders();
1940             if (m_InnerCollection == null)
1941                 return false;
1942             return m_InnerCollection.HasKeys();
1943         }
1944
1945         public override string Get(int index)
1946         {
1947             NormalizeCommonHeaders();
1948             return InnerCollection.Get(index);
1949         }
1950
1951         public override string[] GetValues(int index)
1952         {
1953             NormalizeCommonHeaders();
1954             return InnerCollection.GetValues(index);
1955         }
1956
1957         public override string GetKey(int index)
1958         {
1959             NormalizeCommonHeaders();
1960             return InnerCollection.GetKey(index);
1961         }
1962
1963         public override string[] AllKeys
1964         {
1965             get
1966             {
1967                 NormalizeCommonHeaders();
1968                 return InnerCollection.AllKeys;
1969             }
1970         }
1971
1972         public override void Clear()
1973         {
1974             m_CommonHeaders = null;
1975             m_NumCommonHeaders = 0;
1976             InvalidateCachedArrays();
1977             if (m_InnerCollection != null)
1978                 m_InnerCollection.Clear();
1979         }
1980     } // class WebHeaderCollection
1981
1982
1983     internal class CaseInsensitiveAscii : IEqualityComparer, IComparer{
1984         // ASCII char ToLower table
1985         internal static readonly CaseInsensitiveAscii StaticInstance = new CaseInsensitiveAscii();
1986         internal static readonly byte[] AsciiToLower = new byte[] {
1987               0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
1988              10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
1989              20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1990              30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1991              40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
1992              50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1993              60, 61, 62, 63, 64, 97, 98, 99,100,101, //  60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1994             102,103,104,105,106,107,108,109,110,111, //  70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
1995             112,113,114,115,116,117,118,119,120,121, //  80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1996             122, 91, 92, 93, 94, 95, 96, 97, 98, 99, //  90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1997             100,101,102,103,104,105,106,107,108,109,
1998             110,111,112,113,114,115,116,117,118,119,
1999             120,121,122,123,124,125,126,127,128,129,
2000             130,131,132,133,134,135,136,137,138,139,
2001             140,141,142,143,144,145,146,147,148,149,
2002             150,151,152,153,154,155,156,157,158,159,
2003             160,161,162,163,164,165,166,167,168,169,
2004             170,171,172,173,174,175,176,177,178,179,
2005             180,181,182,183,184,185,186,187,188,189,
2006             190,191,192,193,194,195,196,197,198,199,
2007             200,201,202,203,204,205,206,207,208,209,
2008             210,211,212,213,214,215,216,217,218,219,
2009             220,221,222,223,224,225,226,227,228,229,
2010             230,231,232,233,234,235,236,237,238,239,
2011             240,241,242,243,244,245,246,247,248,249,
2012             250,251,252,253,254,255
2013         };
2014
2015         // ASCII string case insensitive hash function
2016         public int GetHashCode(object myObject) {
2017             string myString = myObject as string;
2018             if (myObject == null) {
2019                 return 0;
2020             }
2021             int myHashCode = myString.Length;
2022             if (myHashCode == 0) {
2023                 return 0;
2024             }
2025             myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16;
2026             return myHashCode;
2027         }
2028
2029         // ASCII string case insensitive comparer
2030         public int Compare(object firstObject, object secondObject) {
2031             string firstString = firstObject as string;
2032             string secondString = secondObject as string;
2033             if (firstString==null) {
2034                 return secondString == null ? 0 : -1;
2035             }
2036             if (secondString == null) {
2037                 return 1;
2038             }
2039             int result = firstString.Length - secondString.Length;
2040             int comparisons = result > 0 ? secondString.Length : firstString.Length;
2041             int difference, index = 0;
2042             while ( index < comparisons ) {
2043                 difference = (int)(AsciiToLower[ firstString[index] ] - AsciiToLower[ secondString[index] ]);
2044                 if ( difference != 0 ) {
2045                     result = difference;
2046                     break;
2047                 }
2048                 index++;
2049             }
2050             return result;
2051         }
2052
2053         // ASCII string case insensitive hash function
2054         int FastGetHashCode(string myString) {
2055             int myHashCode = myString.Length;
2056             if (myHashCode!=0) {
2057                 myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16;
2058             }
2059             return myHashCode;
2060         }
2061
2062         // ASCII string case insensitive comparer
2063         public new bool Equals(object firstObject, object secondObject) {
2064             string firstString = firstObject as string;
2065             string secondString = secondObject as string;
2066             if (firstString==null) {
2067                 return secondString==null;
2068             }
2069             if (secondString!=null) {
2070                 int index = firstString.Length;
2071                 if (index==secondString.Length) {
2072                     if (FastGetHashCode(firstString)==FastGetHashCode(secondString)) {
2073                         int comparisons = firstString.Length;
2074                         while (index>0) {
2075                             index--;
2076                             if (AsciiToLower[firstString[index]]!=AsciiToLower[secondString[index]]) {
2077                                 return false;
2078                             }
2079                         }
2080                         return true;
2081                     }
2082                 }
2083             }
2084             return false;
2085         }
2086     }
2087     
2088     internal class HostHeaderString {
2089
2090         private bool m_Converted;
2091         private string m_String;
2092         private byte[] m_Bytes;
2093
2094         internal HostHeaderString() {
2095             Init(null);
2096         }
2097
2098         internal HostHeaderString(string s) {
2099             Init(s);
2100         }
2101
2102         private void Init(string s) {
2103             m_String = s;
2104             m_Converted = false;
2105             m_Bytes = null;
2106         }
2107
2108         private void Convert() {
2109             if (m_String != null && !m_Converted) {
2110                 m_Bytes = Encoding.Default.GetBytes(m_String);
2111                 string copy = Encoding.Default.GetString(m_Bytes);
2112                 if (!(string.Compare(m_String, copy, StringComparison.Ordinal) == 0)) {
2113                     m_Bytes = Encoding.UTF8.GetBytes(m_String);
2114                 }
2115             }
2116         }
2117
2118         internal string String {
2119             get { return m_String; }
2120             set {
2121                 Init(value);
2122             }
2123         }
2124
2125         internal int ByteCount {
2126             get {
2127                 Convert();
2128                 return m_Bytes.Length;
2129             }
2130         }
2131
2132         internal byte[] Bytes {
2133             get {
2134                 Convert();
2135                 return m_Bytes;
2136             }
2137         }
2138
2139         internal void Copy(byte[] destBytes, int destByteIndex) {
2140             Convert();
2141             Array.Copy(m_Bytes, 0, destBytes, destByteIndex, m_Bytes.Length);
2142         }
2143
2144     }
2145 }