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