[corlib] Improve CancellationTokenSource test
[mono.git] / mcs / class / System.Net / System.Net / WebHeaderCollection_2_1.cs
1 //
2 // System.Net.WebHeaderCollection (for 2.1 profile)
3 //
4 // Authors:
5 //      Jb Evain  <jbevain@novell.com>
6 //      Sebastien Pouliot  <sebastien@ximian.com>
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 // (c) 2007, 2009-2010 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 #if NET_2_1
37
38 using System;
39 using System.Collections;
40 using System.Collections.Generic;
41
42 namespace System.Net {
43
44         public sealed class WebHeaderCollection : IEnumerable {
45
46                 Dictionary<string, string> headers;
47                 bool validate;
48
49                 public WebHeaderCollection ()
50                         : this (false)
51                 {
52                 }
53
54                 internal WebHeaderCollection (bool restrict)
55                 {
56                         validate = restrict;
57                         headers = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
58                 }
59
60                 public int Count {
61                         get { return headers.Count; }
62                 }
63
64                 public string [] AllKeys {
65                         get {
66                                 var keys = new string [headers.Count];
67                                 headers.Keys.CopyTo (keys, 0);
68                                 return keys;
69                         }
70                 }
71
72                 public string this [string header] {
73                         get {
74                                 if (header == null)
75                                         throw new ArgumentNullException ("header");
76
77                                 string value = null;
78                                 headers.TryGetValue (header, out value);
79                                 return value;
80                         }
81                         set {
82                                 if (header == null)
83                                         throw new ArgumentNullException ("header");
84                                 if (!IsHeaderName (header))
85                                         throw new ArgumentException ("header");
86
87                                 if (validate)
88                                         ValidateHeader (header);
89
90                                 // null value are ignored (see moon-unit)
91                                 if (value == null)
92                                         return;
93                                 if (!IsHeaderValue (value))
94                                         throw new ArgumentException ("value");
95
96                                 headers [header] = value;
97                         }
98                 }
99
100                 public string this [HttpRequestHeader header] {
101                         get { return this [HttpRequestHeaderToString (header)]; }
102                         set {
103                                 string h = HttpRequestHeaderToString (header);
104                                 if (validate)
105                                         ValidateHeader (h);
106                                 headers [h] = value;
107                         }
108                 }
109
110                 // some headers cannot be set using the "this" property but by using
111                 // the right property of the Web[Request|Response]. However the value 
112                 // does end up in the collection (and can be read safely from there)
113                 internal void SetHeader (string header, string value)
114                 {
115                         if (String.IsNullOrEmpty (value))
116                                 headers.Remove (header);
117                         else
118                                 headers [header] = value;
119                 }
120
121                 internal void Clear ()
122                 {
123                         headers.Clear ();
124                 }
125
126                 internal bool ContainsKey (string key)
127                 {
128                         return headers.ContainsKey (key);
129                 }
130
131                 IEnumerator IEnumerable.GetEnumerator ()
132                 {
133                         return headers.Keys.GetEnumerator ();
134                 }
135
136                 static string HttpResponseHeaderToString (HttpResponseHeader header)
137                 {
138                         switch (header) {
139                         case HttpResponseHeader.CacheControl:           return "Cache-Control";
140                         case HttpResponseHeader.Connection:             return "Connection";
141                         case HttpResponseHeader.Date:                   return "Date";
142                         case HttpResponseHeader.KeepAlive:              return "Keep-Alive";
143                         case HttpResponseHeader.Pragma:                 return "Pragma";
144                         case HttpResponseHeader.Trailer:                return "Trailer";
145                         case HttpResponseHeader.TransferEncoding:       return "Transfer-Encoding";
146                         case HttpResponseHeader.Upgrade:                return "Upgrade";
147                         case HttpResponseHeader.Via:                    return "Via";
148                         case HttpResponseHeader.Warning:                return "Warning";
149                         case HttpResponseHeader.Allow:                  return "Allow";
150                         case HttpResponseHeader.ContentLength:          return "Content-Length";
151                         case HttpResponseHeader.ContentType:            return "Content-Type";
152                         case HttpResponseHeader.ContentEncoding:        return "Content-Encoding";
153                         case HttpResponseHeader.ContentLanguage:        return "Content-Language";
154                         case HttpResponseHeader.ContentLocation:        return "Content-Location";
155                         case HttpResponseHeader.ContentMd5:             return "Content-MD5";
156                         case HttpResponseHeader.ContentRange:           return "Content-Range";
157                         case HttpResponseHeader.Expires:                return "Expires";
158                         case HttpResponseHeader.LastModified:           return "Last-Modified";
159                         case HttpResponseHeader.AcceptRanges:           return "Accept-Ranges";
160                         case HttpResponseHeader.Age:                    return "Age";
161                         case HttpResponseHeader.ETag:                   return "ETag";
162                         case HttpResponseHeader.Location:               return "Location";
163                         case HttpResponseHeader.ProxyAuthenticate:      return "Proxy-Authenticate";
164                         case HttpResponseHeader.RetryAfter:             return "Retry-After";
165                         case HttpResponseHeader.Server:                 return "Server";
166                         case HttpResponseHeader.SetCookie:              return "Set-Cookie";
167                         case HttpResponseHeader.Vary:                   return "Vary";
168                         case HttpResponseHeader.WwwAuthenticate:        return "WWW-Authenticate";
169                         default:
170                                 throw new IndexOutOfRangeException ();
171                         }
172                 }
173
174                 static string HttpRequestHeaderToString (HttpRequestHeader header)
175                 {
176                         switch (header) {
177                         case HttpRequestHeader.CacheControl:            return "Cache-Control";
178                         case HttpRequestHeader.Connection:              return "Connection";
179                         case HttpRequestHeader.Date:                    return "Date";
180                         case HttpRequestHeader.KeepAlive:               return "Keep-Alive";
181                         case HttpRequestHeader.Pragma:                  return "Pragma";
182                         case HttpRequestHeader.Trailer:                 return "Trailer";
183                         case HttpRequestHeader.TransferEncoding:        return "Transfer-Encoding";
184                         case HttpRequestHeader.Upgrade:                 return "Upgrade";
185                         case HttpRequestHeader.Via:                     return "Via";
186                         case HttpRequestHeader.Warning:                 return "Warning";
187                         case HttpRequestHeader.Allow:                   return "Allow";
188                         case HttpRequestHeader.ContentLength:           return "Content-Length";
189                         case HttpRequestHeader.ContentType:             return "Content-Type";
190                         case HttpRequestHeader.ContentEncoding:         return "Content-Encoding";
191                         case HttpRequestHeader.ContentLanguage:         return "Content-Language";
192                         case HttpRequestHeader.ContentLocation:         return "Content-Location";
193                         case HttpRequestHeader.ContentMd5:              return "Content-MD5";
194                         case HttpRequestHeader.ContentRange:            return "Content-Range";
195                         case HttpRequestHeader.Expires:                 return "Expires";
196                         case HttpRequestHeader.LastModified:            return "Last-Modified";
197                         case HttpRequestHeader.Accept:                  return "Accept";
198                         case HttpRequestHeader.AcceptCharset:           return "Accept-Charset";
199                         case HttpRequestHeader.AcceptEncoding:          return "Accept-Encoding";
200                         case HttpRequestHeader.AcceptLanguage:          return "Accept-Language";
201                         case HttpRequestHeader.Authorization:           return "Authorization";
202                         case HttpRequestHeader.Cookie:                  return "Cookie";
203                         case HttpRequestHeader.Expect:                  return "Expect";
204                         case HttpRequestHeader.From:                    return "From";
205                         case HttpRequestHeader.Host:                    return "Host";
206                         case HttpRequestHeader.IfMatch:                 return "If-Match";
207                         case HttpRequestHeader.IfModifiedSince:         return "If-Modified-Since";
208                         case HttpRequestHeader.IfNoneMatch:             return "If-None-Match";
209                         case HttpRequestHeader.IfRange:                 return "If-Range";
210                         case HttpRequestHeader.IfUnmodifiedSince:       return "If-Unmodified-Since";
211                         case HttpRequestHeader.MaxForwards:             return "Max-Forwards";
212                         case HttpRequestHeader.ProxyAuthorization:      return "Proxy-Authorization";
213                         case HttpRequestHeader.Referer:                 return "Referer";
214                         case HttpRequestHeader.Range:                   return "Range";
215                         case HttpRequestHeader.Te:                      return "TE";
216                         case HttpRequestHeader.Translate:               return "Translate";
217                         case HttpRequestHeader.UserAgent:               return "User-Agent";
218                         default:
219                                 throw new IndexOutOfRangeException ();
220                         }
221                 }
222
223                 internal static void ValidateHeader (string header)
224                 {
225                         switch (header.ToLowerInvariant ()) {
226                         case "connection":
227                         case "date":
228                         case "keep-alive":
229                         case "trailer":
230                         case "transfer-encoding":
231                         case "upgrade":
232                         case "via":
233                         case "warning":
234                         case "allow":
235                         case "content-length":          // accepted by WebClient, copied to WebRequest.ContentLength (not headers)
236                         case "content-type":            // accepted by WebClient, copied to WebRequest.ContentType
237                         case "content-location":
238                         case "content-range":
239                         case "last-modified":
240                         case "accept":                  // accepted by WebClient, copied to HttpWebRequest.Accept
241                         case "accept-charset":
242                         case "accept-encoding":
243                         case "accept-language":
244                         case "cookie":
245                         case "expect":
246                         case "host":
247                         case "if-modified-since":
248                         case "max-forwards":
249                         case "referer":
250                         case "te":
251                         case "user-agent":
252                         // extra (not HttpRequestHeader defined) headers that are not accepted by SL2
253                         // note: the HttpResponseHeader enum is not available in SL2
254                         case "accept-ranges":
255                         case "age":
256                         case "allowed":
257                         case "connect":
258                         case "content-transfer-encoding":
259                         case "delete":
260                         case "etag":
261                         case "get":
262                         case "head":
263                         case "location":
264                         case "options":
265                         case "post":
266                         case "proxy-authenticate":
267                         case "proxy-connection":
268                         case "public":
269                         case "put":
270                         case "request-range":
271                         case "retry-after":
272                         case "server":
273                         case "sec-headertest":
274                         case "sec-":
275                         case "trace":
276                         case "uri":
277                         case "vary":
278                         case "www-authenticate":
279                         case "x-flash-version":
280                                 string msg = String.Format ("'{0}' cannot be modified", header);
281                                 throw new ArgumentException (msg, "header");
282                         default:
283                                 return;
284                         }
285                 }
286
287                 internal static bool IsHeaderValue (string value)
288                 {
289                         // TEXT any 8 bit value except CTL's (0-31 and 127)
290                         //      but including \r\n space and \t
291                         //      after a newline at least one space or \t must follow
292                         //      certain header fields allow comments ()
293                                 
294                         int len = value.Length;
295                         for (int i = 0; i < len; i++) {                 
296                                 char c = value [i];
297                                 if (c == 127)
298                                         return false;
299                                 if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
300                                         return false;
301                                 if (c == '\r' && ++i < len) {
302                                         c = value [i];
303                                         if (c != '\n')
304                                                 return false;
305                                 }
306                                 if (c == '\n' && ++i < len) {
307                                         c = value [i];
308                                         if (c != ' ' && c != '\t')
309                                                 return false;
310                                 }
311                         }
312                         
313                         return true;
314                 }
315                 
316                 internal static bool IsHeaderName (string name)
317                 {
318                         if (name == null || name.Length == 0)
319                                 return false;
320
321                         int len = name.Length;
322                         for (int i = 0; i < len; i++) {                 
323                                 char c = name [i];
324                                 if (c > 126 || !allowed_chars [(int) c])
325                                         return false;
326                         }
327                         
328                         return true;
329                 }
330
331                 static bool [] allowed_chars = new bool [126] {
332                         false, false, false, false, false, false, false, false, false, false, false, false, false, false,
333                         false, false, false, false, false, false, false, false, false, false, false, false, false, false,
334                         false, false, false, false, false, true, false, true, true, true, true, false, false, false, true,
335                         true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false,
336                         false, false, false, false, false, false, true, true, true, true, true, true, true, true, true,
337                         true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
338                         false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
339                         true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
340                         false, true, false
341                         };
342         }
343 }
344
345 #endif