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