1 #define EMBEDDED_IN_1_0
4 // System.Net.WebHeaderCollection
7 // Lawrence Pit (loz@cable.a2000.nl)
8 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 // Miguel de Icaza (miguel@novell.com)
11 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
12 // Copyright 2007 Novell, Inc. (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Collections;
39 using System.Collections.Generic;
41 using System.Collections.Specialized;
42 using System.Runtime.InteropServices;
43 using System.Runtime.Serialization;
46 // See RFC 2068 par 4.2 Message Headers
48 using System; using System.Net; namespace MonoHttp
52 internal class WebHeaderCollection : NameValueCollection, ISerializable
54 private static readonly Hashtable restricted;
55 private static readonly Hashtable multiValue;
56 private bool internallyCreated = false;
60 static WebHeaderCollection ()
62 // the list of restricted header names as defined
64 restricted = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
65 CaseInsensitiveComparer.DefaultInvariant);
67 restricted.Add ("accept", true);
68 restricted.Add ("connection", true);
69 restricted.Add ("content-length", true);
70 restricted.Add ("content-type", true);
71 restricted.Add ("date", true);
72 restricted.Add ("expect", true);
73 restricted.Add ("host", true);
74 restricted.Add ("if-modified-since", true);
75 restricted.Add ("range", true);
76 restricted.Add ("referer", true);
77 restricted.Add ("transfer-encoding", true);
78 restricted.Add ("user-agent", true);
79 restricted.Add ("proxy-connection", true);
82 // see par 14 of RFC 2068 to see which header names
83 // accept multiple values each separated by a comma
84 multiValue = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
85 CaseInsensitiveComparer.DefaultInvariant);
87 multiValue.Add ("accept", true);
88 multiValue.Add ("accept-charset", true);
89 multiValue.Add ("accept-encoding", true);
90 multiValue.Add ("accept-language", true);
91 multiValue.Add ("accept-ranges", true);
92 multiValue.Add ("allow", true);
93 multiValue.Add ("authorization", true);
94 multiValue.Add ("cache-control", true);
95 multiValue.Add ("connection", true);
96 multiValue.Add ("content-encoding", true);
97 multiValue.Add ("content-language", true);
98 multiValue.Add ("expect", true);
99 multiValue.Add ("if-match", true);
100 multiValue.Add ("if-none-match", true);
101 multiValue.Add ("proxy-authenticate", true);
102 multiValue.Add ("public", true);
103 multiValue.Add ("range", true);
104 multiValue.Add ("transfer-encoding", true);
105 multiValue.Add ("upgrade", true);
106 multiValue.Add ("vary", true);
107 multiValue.Add ("via", true);
108 multiValue.Add ("warning", true);
109 multiValue.Add ("www-authenticate", true);
112 multiValue.Add ("set-cookie", true);
113 multiValue.Add ("set-cookie2", true);
118 public WebHeaderCollection () { }
120 protected WebHeaderCollection (SerializationInfo serializationInfo,
121 StreamingContext streamingContext)
126 count = serializationInfo.GetInt32("Count");
127 for (int i = 0; i < count; i++)
128 this.Add (serializationInfo.GetString (i.ToString ()),
129 serializationInfo.GetString ((count + i).ToString ()));
130 } catch (SerializationException){
131 count = serializationInfo.GetInt32("count");
132 for (int i = 0; i < count; i++)
133 this.Add (serializationInfo.GetString ("k" + i),
134 serializationInfo.GetString ("v" + i));
139 internal WebHeaderCollection (bool internallyCreated)
141 this.internallyCreated = internallyCreated;
146 public void Add (string header)
149 throw new ArgumentNullException ("header");
150 int pos = header.IndexOf (':');
152 throw new ArgumentException ("no colon found", "header");
153 this.Add (header.Substring (0, pos),
154 header.Substring (pos + 1));
157 public override void Add (string name, string value)
160 throw new ArgumentNullException ("name");
161 if (internallyCreated && IsRestricted (name))
162 throw new ArgumentException ("This header must be modified with the appropiate property.");
163 this.AddWithoutValidate (name, value);
166 protected void AddWithoutValidate (string headerName, string headerValue)
168 if (!IsHeaderName (headerName))
169 throw new ArgumentException ("invalid header name: " + headerName, "headerName");
170 if (headerValue == null)
171 headerValue = String.Empty;
173 headerValue = headerValue.Trim ();
174 if (!IsHeaderValue (headerValue))
175 throw new ArgumentException ("invalid header value: " + headerValue, "headerValue");
176 base.Add (headerName, headerValue);
179 public override string [] GetValues (string header)
182 throw new ArgumentNullException ("header");
184 string [] values = base.GetValues (header);
185 if (values == null || values.Length == 0)
189 if (IsMultiValue (header)) {
190 values = GetMultipleValues (values);
197 public override string[] GetValues (int index)
199 string[] values = base.GetValues (index);
200 if (values == null || values.Length == 0) {
207 /* Now i wonder why this is here...
208 static string [] GetMultipleValues (string [] values)
210 ArrayList mvalues = new ArrayList (values.Length);
211 StringBuilder sb = null;
212 for (int i = 0; i < values.Length; ++i) {
213 string val = values [i];
214 if (val.IndexOf (',') == -1) {
220 sb = new StringBuilder ();
223 for (int k = 0; k < val.Length; k++) {
227 } else if (!quote && c == ',') {
228 mvalues.Add (sb.ToString ().Trim ());
236 mvalues.Add (sb.ToString ().Trim ());
241 return (string []) mvalues.ToArray (typeof (string));
245 public static bool IsRestricted (string headerName)
247 if (headerName == null)
248 throw new ArgumentNullException ("headerName");
250 if (headerName == "") // MS throw nullexception here!
251 throw new ArgumentException ("empty string", "headerName");
253 if (!IsHeaderName (headerName))
254 throw new ArgumentException ("Invalid character in header");
256 return restricted.ContainsKey (headerName);
260 public static bool IsRestricted (string headerName, bool response)
262 if (headerName == null || headerName.Length == 0)
263 throw new ArgumentNullException ("headerName");
265 if (!IsHeaderName (headerName))
266 throw new ArgumentException ("Invalid character in header");
271 return restricted.ContainsKey (headerName);
275 public override void OnDeserialization (object sender)
279 public override void Remove (string name)
282 throw new ArgumentNullException ("name");
283 if (internallyCreated && IsRestricted (name))
284 throw new ArgumentException ("restricted header");
288 public override void Set (string name, string value)
291 throw new ArgumentNullException ("name");
292 if (internallyCreated && IsRestricted (name))
293 throw new ArgumentException ("restricted header");
294 if (!IsHeaderName (name))
295 throw new ArgumentException ("invalid header name");
297 value = String.Empty;
299 value = value.Trim ();
300 if (!IsHeaderValue (value))
301 throw new ArgumentException ("invalid header value");
302 base.Set (name, value);
305 public byte[] ToByteArray ()
307 return Encoding.UTF8.GetBytes(ToString ());
310 public override string ToString ()
312 StringBuilder sb = new StringBuilder();
314 int count = base.Count;
315 for (int i = 0; i < count ; i++)
316 sb.Append (GetKey (i))
321 return sb.Append("\r\n").ToString();
324 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
325 StreamingContext streamingContext)
327 GetObjectData (serializationInfo, streamingContext);
330 public override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
332 int count = base.Count;
333 serializationInfo.AddValue ("Count", count);
334 for (int i = 0; i < count; i++) {
335 serializationInfo.AddValue (i.ToString (), GetKey (i));
336 serializationInfo.AddValue ((count + i).ToString (), Get (i));
340 public override string[] AllKeys
343 return(base.AllKeys);
347 public override int Count
354 public override KeysCollection Keys
361 public override string Get (int index)
363 return(base.Get (index));
366 public override string Get (string name)
368 return(base.Get (name));
371 public override string GetKey (int index)
373 return(base.GetKey (index));
377 public void Add (HttpRequestHeader header, string value)
379 Add (RequestHeaderToString (header), value);
382 public void Remove (HttpRequestHeader header)
384 Remove (RequestHeaderToString (header));
387 public void Set (HttpRequestHeader header, string value)
389 Set (RequestHeaderToString (header), value);
392 public void Add (HttpResponseHeader header, string value)
394 Add (ResponseHeaderToString (header), value);
397 public void Remove (HttpResponseHeader header)
399 Remove (ResponseHeaderToString (header));
402 public void Set (HttpResponseHeader header, string value)
404 Set (ResponseHeaderToString (header), value);
407 string RequestHeaderToString (HttpRequestHeader value)
410 case HttpRequestHeader.CacheControl:
411 return "cache-control";
412 case HttpRequestHeader.Connection:
414 case HttpRequestHeader.Date:
416 case HttpRequestHeader.KeepAlive:
418 case HttpRequestHeader.Pragma:
420 case HttpRequestHeader.Trailer:
422 case HttpRequestHeader.TransferEncoding:
423 return "transfer-encoding";
424 case HttpRequestHeader.Upgrade:
426 case HttpRequestHeader.Via:
428 case HttpRequestHeader.Warning:
430 case HttpRequestHeader.Allow:
432 case HttpRequestHeader.ContentLength:
433 return "content-length";
434 case HttpRequestHeader.ContentType:
435 return "content-type";
436 case HttpRequestHeader.ContentEncoding:
437 return "content-encoding";
438 case HttpRequestHeader.ContentLanguage:
439 return "content-language";
440 case HttpRequestHeader.ContentLocation:
441 return "content-location";
442 case HttpRequestHeader.ContentMd5:
443 return "content-md5";
444 case HttpRequestHeader.ContentRange:
445 return "content-range";
446 case HttpRequestHeader.Expires:
448 case HttpRequestHeader.LastModified:
449 return "last-modified";
450 case HttpRequestHeader.Accept:
452 case HttpRequestHeader.AcceptCharset:
453 return "accept-charset";
454 case HttpRequestHeader.AcceptEncoding:
455 return "accept-encoding";
456 case HttpRequestHeader.AcceptLanguage:
457 return "accept-language";
458 case HttpRequestHeader.Authorization:
459 return "authorization";
460 case HttpRequestHeader.Cookie:
462 case HttpRequestHeader.Expect:
464 case HttpRequestHeader.From:
466 case HttpRequestHeader.Host:
468 case HttpRequestHeader.IfMatch:
470 case HttpRequestHeader.IfModifiedSince:
471 return "if-modified-since";
472 case HttpRequestHeader.IfNoneMatch:
473 return "if-none-match";
474 case HttpRequestHeader.IfRange:
476 case HttpRequestHeader.IfUnmodifiedSince:
477 return "if-unmodified-since";
478 case HttpRequestHeader.MaxForwards:
479 return "max-forwards";
480 case HttpRequestHeader.ProxyAuthorization:
481 return "proxy-authorization";
482 case HttpRequestHeader.Referer:
484 case HttpRequestHeader.Range:
486 case HttpRequestHeader.Te:
488 case HttpRequestHeader.Translate:
490 case HttpRequestHeader.UserAgent:
493 throw new InvalidOperationException ();
498 public string this[HttpRequestHeader hrh]
501 return Get (RequestHeaderToString (hrh));
505 Add (RequestHeaderToString (hrh), value);
509 string ResponseHeaderToString (HttpResponseHeader value)
512 case HttpResponseHeader.CacheControl:
513 return "cache-control";
514 case HttpResponseHeader.Connection:
516 case HttpResponseHeader.Date:
518 case HttpResponseHeader.KeepAlive:
520 case HttpResponseHeader.Pragma:
522 case HttpResponseHeader.Trailer:
524 case HttpResponseHeader.TransferEncoding:
525 return "transfer-encoding";
526 case HttpResponseHeader.Upgrade:
528 case HttpResponseHeader.Via:
530 case HttpResponseHeader.Warning:
532 case HttpResponseHeader.Allow:
534 case HttpResponseHeader.ContentLength:
535 return "content-length";
536 case HttpResponseHeader.ContentType:
537 return "content-type";
538 case HttpResponseHeader.ContentEncoding:
539 return "content-encoding";
540 case HttpResponseHeader.ContentLanguage:
541 return "content-language";
542 case HttpResponseHeader.ContentLocation:
543 return "content-location";
544 case HttpResponseHeader.ContentMd5:
545 return "content-md5";
546 case HttpResponseHeader.ContentRange:
547 return "content-range";
548 case HttpResponseHeader.Expires:
550 case HttpResponseHeader.LastModified:
551 return "last-modified";
552 case HttpResponseHeader.AcceptRanges:
553 return "accept-ranges";
554 case HttpResponseHeader.Age:
556 case HttpResponseHeader.ETag:
558 case HttpResponseHeader.Location:
560 case HttpResponseHeader.ProxyAuthenticate:
561 return "proxy-authenticate";
562 case HttpResponseHeader.RetryAfter:
564 case HttpResponseHeader.Server:
566 case HttpResponseHeader.SetCookie:
568 case HttpResponseHeader.Vary:
570 case HttpResponseHeader.WwwAuthenticate:
571 return "www-authenticate";
573 throw new InvalidOperationException ();
576 public string this[HttpResponseHeader hrh]
580 return Get (ResponseHeaderToString (hrh));
585 Add (ResponseHeaderToString (hrh), value);
590 public override void Clear ()
596 public override IEnumerator GetEnumerator ()
598 return(base.GetEnumerator ());
605 // With this we don't check for invalid characters in header. See bug #55994.
606 internal void SetInternal (string header)
608 int pos = header.IndexOf (':');
610 throw new ArgumentException ("no colon found", "header");
612 SetInternal (header.Substring (0, pos), header.Substring (pos + 1));
615 internal void SetInternal (string name, string value)
618 value = String.Empty;
620 value = value.Trim ();
621 if (!IsHeaderValue (value))
622 throw new ArgumentException ("invalid header value");
624 if (IsMultiValue (name)) {
625 base.Add (name, value);
628 base.Set (name, value);
632 internal void RemoveAndAdd (string name, string value)
635 value = String.Empty;
637 value = value.Trim ();
640 base.Set (name, value);
643 internal void RemoveInternal (string name)
646 throw new ArgumentNullException ("name");
652 internal static bool IsMultiValue (string headerName)
654 if (headerName == null || headerName == "")
657 return multiValue.ContainsKey (headerName);
660 internal static bool IsHeaderValue (string value)
662 // TEXT any 8 bit value except CTL's (0-31 and 127)
663 // but including \r\n space and \t
664 // after a newline at least one space or \t must follow
665 // certain header fields allow comments ()
667 int len = value.Length;
668 for (int i = 0; i < len; i++) {
672 if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
674 if (c == '\n' && ++i < len) {
676 if (c != ' ' && c != '\t')
684 internal static bool IsHeaderName (string name)
686 if (name == null || name.Length == 0)
689 int len = name.Length;
690 for (int i = 0; i < len; i++) {
692 if (c > 126 || !allowed_chars [(int) c])
699 static bool [] allowed_chars = new bool [126] {
700 false, false, false, false, false, false, false, false, false, false, false, false, false, false,
701 false, false, false, false, false, false, false, false, false, false, false, false, false, false,
702 false, false, false, false, false, true, false, true, true, true, true, false, false, false, true,
703 true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false,
704 false, false, false, false, false, false, true, true, true, true, true, true, true, true, true,
705 true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
706 false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
707 true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,