* Makefile: Add a System.Core.dll reference; embed monodoc.xml as a
[mono.git] / mcs / class / System.Runtime.Remoting / MonoHttp / WebHeaderCollection.cs
1 #define EMBEDDED_IN_1_0
2
3 //
4 // System.Net.WebHeaderCollection
5 //
6 // Authors:
7 //      Lawrence Pit (loz@cable.a2000.nl)
8 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 //      Miguel de Icaza (miguel@novell.com)
10 //
11 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
12 // Copyright 2007 Novell, Inc. (http://www.novell.com)
13 //
14 //
15 //
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:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
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.
34 //
35
36 using System;
37 using System.Collections;
38 #if EMBEDDED_IN_1_0
39 using System.Collections.Generic;
40 #endif
41 using System.Collections.Specialized;
42 using System.Runtime.InteropServices;
43 using System.Runtime.Serialization;
44 using System.Text;
45     
46 // See RFC 2068 par 4.2 Message Headers
47     
48 using System; using System.Net; namespace MonoHttp 
49 {
50         [Serializable]
51         [ComVisible(true)]
52         internal class WebHeaderCollection : NameValueCollection, ISerializable
53         {
54                 private static readonly Hashtable restricted;
55                 private static readonly Hashtable multiValue;
56                 private bool internallyCreated = false;
57                 
58                 // Static Initializer
59                 
60                 static WebHeaderCollection () 
61                 {
62                         // the list of restricted header names as defined 
63                         // by the ms.net spec
64                         restricted = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
65                                                     CaseInsensitiveComparer.DefaultInvariant);
66
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);                      
80
81                         //
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);
86
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);
110
111                         // Extra
112                         multiValue.Add ("set-cookie", true);
113                         multiValue.Add ("set-cookie2", true);
114                 }
115                 
116                 // Constructors
117                 
118                 public WebHeaderCollection () { }       
119                 
120                 protected WebHeaderCollection (SerializationInfo serializationInfo, 
121                                                StreamingContext streamingContext)
122                 {
123                         int count;
124
125                         try {
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));
135                         }
136                         
137                 }
138                 
139                 internal WebHeaderCollection (bool internallyCreated)
140                 {       
141                         this.internallyCreated = internallyCreated;
142                 }               
143                 
144                 // Methods
145                 
146                 public void Add (string header)
147                 {
148                         if (header == null)
149                                 throw new ArgumentNullException ("header");
150                         int pos = header.IndexOf (':');
151                         if (pos == -1)
152                                 throw new ArgumentException ("no colon found", "header");                               
153                         this.Add (header.Substring (0, pos), 
154                                   header.Substring (pos + 1));
155                 }
156                 
157                 public override void Add (string name, string value)
158                 {
159                         if (name == null)
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);
164                 }
165
166                 protected void AddWithoutValidate (string headerName, string headerValue)
167                 {
168                         if (!IsHeaderName (headerName))
169                                 throw new ArgumentException ("invalid header name: " + headerName, "headerName");
170                         if (headerValue == null)
171                                 headerValue = String.Empty;
172                         else
173                                 headerValue = headerValue.Trim ();
174                         if (!IsHeaderValue (headerValue))
175                                 throw new ArgumentException ("invalid header value: " + headerValue, "headerValue");
176                         base.Add (headerName, headerValue);                     
177                 }
178
179                 public override string [] GetValues (string header)
180                 {
181                         if (header == null)
182                                 throw new ArgumentNullException ("header");
183
184                         string [] values = base.GetValues (header);
185                         if (values == null || values.Length == 0)
186                                 return null;
187
188                         /*
189                         if (IsMultiValue (header)) {
190                                 values = GetMultipleValues (values);
191                         }
192                         */
193
194                         return values;
195                 }
196
197                 public override string[] GetValues (int index)
198                 {
199                         string[] values = base.GetValues (index);
200                         if (values == null || values.Length == 0) {
201                                 return(null);
202                         }
203                         
204                         return(values);
205                 }
206
207                 /* Now i wonder why this is here...
208                 static string [] GetMultipleValues (string [] values)
209                 {
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) {
215                                         mvalues.Add (val);
216                                         continue;
217                                 }
218
219                                 if (sb == null)
220                                         sb = new StringBuilder ();
221
222                                 bool quote = false;
223                                 for (int k = 0; k < val.Length; k++) {
224                                         char c = val [k];
225                                         if (c == '"') {
226                                                 quote = !quote;
227                                         } else if (!quote && c == ',') {
228                                                 mvalues.Add (sb.ToString ().Trim ());
229                                                 sb.Length = 0;
230                                                 continue;
231                                         }
232                                         sb.Append (c);
233                                 }
234
235                                 if (sb.Length > 0) {
236                                         mvalues.Add (sb.ToString ().Trim ());
237                                         sb.Length = 0;
238                                 }
239                         }
240
241                         return (string []) mvalues.ToArray (typeof (string));
242                 }
243                 */
244
245                 public static bool IsRestricted (string headerName)
246                 {
247                         if (headerName == null)
248                                 throw new ArgumentNullException ("headerName");
249
250                         if (headerName == "") // MS throw nullexception here!
251                                 throw new ArgumentException ("empty string", "headerName");
252
253                         if (!IsHeaderName (headerName))
254                                 throw new ArgumentException ("Invalid character in header");
255
256                         return restricted.ContainsKey (headerName);
257                 }
258
259 #if EMBEDDED_IN_1_0
260                 public static bool IsRestricted (string headerName, bool response)
261                 {
262                         if (headerName == null || headerName.Length == 0)
263                                 throw new ArgumentNullException ("headerName");
264
265                         if (!IsHeaderName (headerName))
266                                 throw new ArgumentException ("Invalid character in header");
267
268
269                         if (response)
270                                 return false;
271                         return restricted.ContainsKey (headerName);
272                 }
273 #endif
274
275                 public override void OnDeserialization (object sender)
276                 {
277                 }
278
279                 public override void Remove (string name)
280                 {
281                         if (name == null)
282                                 throw new ArgumentNullException ("name");
283                         if (internallyCreated && IsRestricted (name))
284                                 throw new ArgumentException ("restricted header");
285                         base.Remove (name);
286                 }
287
288                 public override void Set (string name, string value)
289                 {
290                         if (name == null)
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");
296                         if (value == null)
297                                 value = String.Empty;
298                         else
299                                 value = value.Trim ();
300                         if (!IsHeaderValue (value))
301                                 throw new ArgumentException ("invalid header value");
302                         base.Set (name, value);                 
303                 }
304
305                 public byte[] ToByteArray ()
306                 {
307                         return Encoding.UTF8.GetBytes(ToString ());
308                 }
309
310                 public override string ToString ()
311                 {
312                         StringBuilder sb = new StringBuilder();
313
314                         int count = base.Count;
315                         for (int i = 0; i < count ; i++)
316                                 sb.Append (GetKey (i))
317                                   .Append (": ")
318                                   .Append (Get (i))
319                                   .Append ("\r\n");
320                                   
321                         return sb.Append("\r\n").ToString();
322                 }
323 #if !TARGET_JVM
324                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
325                                                   StreamingContext streamingContext)
326                 {
327                         GetObjectData (serializationInfo, streamingContext);
328                 }
329 #endif
330                 public override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
331                 {
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));
337                         }
338                 }
339
340                 public override string[] AllKeys
341                 {
342                         get {
343                                 return(base.AllKeys);
344                         }
345                 }
346                 
347                 public override int Count 
348                 {
349                         get {
350                                 return(base.Count);
351                         }
352                 }
353
354                 public override KeysCollection Keys
355                 {
356                         get {
357                                 return(base.Keys);
358                         }
359                 }
360
361                 public override string Get (int index)
362                 {
363                         return(base.Get (index));
364                 }
365                 
366                 public override string Get (string name)
367                 {
368                         return(base.Get (name));
369                 }
370                 
371                 public override string GetKey (int index)
372                 {
373                         return(base.GetKey (index));
374                 }
375
376 #if EMBEDDED_IN_1_0
377                 public void Add (HttpRequestHeader header, string value)
378                 {
379                         Add (RequestHeaderToString (header), value);
380                 }
381
382                 public void Remove (HttpRequestHeader header)
383                 {
384                         Remove (RequestHeaderToString (header));
385                 }
386
387                 public void Set (HttpRequestHeader header, string value)
388                 {
389                         Set (RequestHeaderToString (header), value);
390                 }
391
392                 public void Add (HttpResponseHeader header, string value)
393                 {
394                         Add (ResponseHeaderToString (header), value);
395                 }
396
397                 public void Remove (HttpResponseHeader header)
398                 {
399                         Remove (ResponseHeaderToString (header));
400                 }
401
402                 public void Set (HttpResponseHeader header, string value)
403                 {
404                         Set (ResponseHeaderToString (header), value);
405                 }
406
407                 string RequestHeaderToString (HttpRequestHeader value)
408                 {
409                         switch (value){
410                         case HttpRequestHeader.CacheControl:
411                                 return "cache-control";
412                         case HttpRequestHeader.Connection:
413                                 return "connection";
414                         case HttpRequestHeader.Date:
415                                 return "date";
416                         case HttpRequestHeader.KeepAlive:
417                                 return "keep-alive";
418                         case HttpRequestHeader.Pragma:
419                                 return "pragma";
420                         case HttpRequestHeader.Trailer:
421                                 return "trailer";
422                         case HttpRequestHeader.TransferEncoding:
423                                 return "transfer-encoding";
424                         case HttpRequestHeader.Upgrade:
425                                 return "upgrade";
426                         case HttpRequestHeader.Via:
427                                 return "via";
428                         case HttpRequestHeader.Warning:
429                                 return "warning";
430                         case HttpRequestHeader.Allow:
431                                 return "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:
447                                 return "expires";
448                         case HttpRequestHeader.LastModified:
449                                 return "last-modified";
450                         case HttpRequestHeader.Accept:
451                                 return "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:
461                                 return "cookie";
462                         case HttpRequestHeader.Expect:
463                                 return "expect";
464                         case HttpRequestHeader.From:
465                                 return "from";
466                         case HttpRequestHeader.Host:
467                                 return "host";
468                         case HttpRequestHeader.IfMatch:
469                                 return "if-match";
470                         case HttpRequestHeader.IfModifiedSince:
471                                 return "if-modified-since";
472                         case HttpRequestHeader.IfNoneMatch:
473                                 return "if-none-match";
474                         case HttpRequestHeader.IfRange:
475                                 return "if-range";
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:
483                                 return "referer";
484                         case HttpRequestHeader.Range:
485                                 return "range";
486                         case HttpRequestHeader.Te:
487                                 return "te";
488                         case HttpRequestHeader.Translate:
489                                 return "translate";
490                         case HttpRequestHeader.UserAgent:
491                                 return "user-agent";
492                         default:
493                                 throw new InvalidOperationException ();
494                         }
495                 }
496                 
497                 
498                 public string this[HttpRequestHeader hrh]
499                 {
500                         get {
501                                 return Get (RequestHeaderToString (hrh));
502                         }
503                         
504                         set {
505                                 Add (RequestHeaderToString (hrh), value);
506                         }
507                 }
508
509                 string ResponseHeaderToString (HttpResponseHeader value)
510                 {
511                         switch (value){
512                         case HttpResponseHeader.CacheControl:
513                                 return "cache-control";
514                         case HttpResponseHeader.Connection:
515                                 return "connection";
516                         case HttpResponseHeader.Date:
517                                 return "date";
518                         case HttpResponseHeader.KeepAlive:
519                                 return "keep-alive";
520                         case HttpResponseHeader.Pragma:
521                                 return "pragma";
522                         case HttpResponseHeader.Trailer:
523                                 return "trailer";
524                         case HttpResponseHeader.TransferEncoding:
525                                 return "transfer-encoding";
526                         case HttpResponseHeader.Upgrade:
527                                 return "upgrade";
528                         case HttpResponseHeader.Via:
529                                 return "via";
530                         case HttpResponseHeader.Warning:
531                                 return "warning";
532                         case HttpResponseHeader.Allow:
533                                 return "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:
549                                 return "expires";
550                         case HttpResponseHeader.LastModified:
551                                 return "last-modified";
552                         case HttpResponseHeader.AcceptRanges:
553                                 return "accept-ranges";
554                         case HttpResponseHeader.Age:
555                                 return "age";
556                         case HttpResponseHeader.ETag:
557                                 return "etag";
558                         case HttpResponseHeader.Location:
559                                 return "location";
560                         case HttpResponseHeader.ProxyAuthenticate:
561                                 return "proxy-authenticate";
562                         case HttpResponseHeader.RetryAfter:
563                                 return "RetryAfter";
564                         case HttpResponseHeader.Server:
565                                 return "server";
566                         case HttpResponseHeader.SetCookie:
567                                 return "set-cookie";
568                         case HttpResponseHeader.Vary:
569                                 return "vary";
570                         case HttpResponseHeader.WwwAuthenticate:
571                                 return "www-authenticate";
572                         default:
573                                 throw new InvalidOperationException ();
574                         }
575                 }
576                 public string this[HttpResponseHeader hrh]
577                 {
578                         get
579                         {
580                                 return Get (ResponseHeaderToString (hrh));
581                         }
582
583                         set
584                         {
585                                 Add (ResponseHeaderToString (hrh), value);
586                         }
587                 }
588
589 #if !EMBEDDED_IN_1_0
590                 public override void Clear ()
591                 {
592                         base.Clear ();
593                 }
594
595
596                 public override IEnumerator GetEnumerator ()
597                 {
598                         return(base.GetEnumerator ());
599                 }
600 #endif
601 #endif
602
603                 // Internal Methods
604                 
605                 // With this we don't check for invalid characters in header. See bug #55994.
606                 internal void SetInternal (string header)
607                 {
608                         int pos = header.IndexOf (':');
609                         if (pos == -1)
610                                 throw new ArgumentException ("no colon found", "header");                               
611
612                         SetInternal (header.Substring (0, pos), header.Substring (pos + 1));
613                 }
614
615                 internal void SetInternal (string name, string value)
616                 {
617                         if (value == null)
618                                 value = String.Empty;
619                         else
620                                 value = value.Trim ();
621                         if (!IsHeaderValue (value))
622                                 throw new ArgumentException ("invalid header value");
623
624                         if (IsMultiValue (name)) {
625                                 base.Add (name, value);
626                         } else {
627                                 base.Remove (name);
628                                 base.Set (name, value); 
629                         }
630                 }
631
632                 internal void RemoveAndAdd (string name, string value)
633                 {
634                         if (value == null)
635                                 value = String.Empty;
636                         else
637                                 value = value.Trim ();
638
639                         base.Remove (name);
640                         base.Set (name, value);
641                 }
642
643                 internal void RemoveInternal (string name)
644                 {
645                         if (name == null)
646                                 throw new ArgumentNullException ("name");
647                         base.Remove (name);
648                 }               
649                 
650                 // Private Methods
651                 
652                 internal static bool IsMultiValue (string headerName)
653                 {
654                         if (headerName == null || headerName == "")
655                                 return false;
656
657                         return multiValue.ContainsKey (headerName);
658                 }               
659                 
660                 internal static bool IsHeaderValue (string value)
661                 {
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 ()
666                                 
667                         int len = value.Length;
668                         for (int i = 0; i < len; i++) {                 
669                                 char c = value [i];
670                                 if (c == 127)
671                                         return false;
672                                 if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
673                                         return false;
674                                 if (c == '\n' && ++i < len) {
675                                         c = value [i];
676                                         if (c != ' ' && c != '\t')
677                                                 return false;
678                                 }
679                         }
680                         
681                         return true;
682                 }
683                 
684                 internal static bool IsHeaderName (string name)
685                 {
686                         if (name == null || name.Length == 0)
687                                 return false;
688
689                         int len = name.Length;
690                         for (int i = 0; i < len; i++) {                 
691                                 char c = name [i];
692                                 if (c > 126 || !allowed_chars [(int) c])
693                                         return false;
694                         }
695                         
696                         return true;
697                 }
698
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,
708                         false, true, false
709                         };
710         }
711 }
712
713