* NetworkAccess.cs: Only mark as flags enum on 2.0.
[mono.git] / mcs / class / System / System.Net / HttpWebResponse.cs
1 //
2 // System.Net.HttpWebResponse
3 //
4 // Authors:
5 //      Lawrence Pit (loz@cable.a2000.nl)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002 Lawrence Pit
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Globalization;
36 using System.IO;
37 using System.Net.Sockets;
38 using System.Runtime.Serialization;
39 using System.Text;
40
41 namespace System.Net 
42 {
43         [Serializable]
44         public class HttpWebResponse : WebResponse, ISerializable, IDisposable
45         {
46                 Uri uri;
47                 WebHeaderCollection webHeaders;
48                 CookieCollection cookieCollection;
49                 string method;
50                 Version version;
51                 HttpStatusCode statusCode;
52                 string statusDescription;
53                 long contentLength = -1;
54                 string contentType;
55                 CookieContainer cookie_container;
56
57                 bool disposed = false;
58                 Stream stream;
59                 
60                 // Constructors
61                 
62                 internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container)
63                 {
64                         this.uri = uri;
65                         this.method = method;
66                         webHeaders = data.Headers;
67                         version = data.Version;
68                         statusCode = (HttpStatusCode) data.StatusCode;
69                         statusDescription = data.StatusDescription;
70                         stream = data.stream;
71                         if (container != null) {
72                                 this.cookie_container = container;      
73                                 FillCookies ();
74                         }
75                 }
76
77 #if NET_2_0
78                 [Obsolete ("Serialization is obsoleted for this type", false)]
79 #endif
80                 protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
81                 {
82                         SerializationInfo info = serializationInfo;
83
84                         uri = (Uri) info.GetValue ("uri", typeof (Uri));
85                         contentLength = info.GetInt64 ("contentLength");
86                         contentType = info.GetString ("contentType");
87                         method = info.GetString ("method");
88                         statusDescription = info.GetString ("statusDescription");
89                         cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
90                         version = (Version) info.GetValue ("version", typeof (Version));
91                         statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
92                 }
93                 
94                 // Properties
95                 
96                 public string CharacterSet {
97                         // Content-Type   = "Content-Type" ":" media-type
98                         // media-type     = type "/" subtype *( ";" parameter )
99                         // parameter      = attribute "=" value
100                         // 3.7.1. default is ISO-8859-1
101                         get { 
102                                 CheckDisposed ();
103                                 string contentType = ContentType;
104                                 if (contentType == null)
105                                         return "ISO-8859-1";
106                                 string val = contentType.ToLower ();                                    
107                                 int pos = val.IndexOf ("charset=");
108                                 if (pos == -1)
109                                         return "ISO-8859-1";
110                                 pos += 8;
111                                 int pos2 = val.IndexOf (';', pos);
112                                 return (pos2 == -1)
113                                      ? contentType.Substring (pos) 
114                                      : contentType.Substring (pos, pos2 - pos);
115                         }
116                 }
117                 
118                 public string ContentEncoding {
119                         get { 
120                                 CheckDisposed ();
121                                 string h = webHeaders ["Content-Encoding"];
122                                 return h != null ? h : "";
123                         }
124                 }
125                 
126                 public override long ContentLength {            
127                         get {
128                                 CheckDisposed ();
129                                 if (contentLength != -1)
130                                         return contentLength;
131
132                                 try {
133                                         contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]); 
134                                 } catch (Exception) {
135                                         return -1;
136                                 }
137
138                                 return contentLength;
139                         }
140                 }
141                 
142                 public override string ContentType {            
143                         get {
144                                 CheckDisposed ();
145                                 if (contentType == null)
146                                         contentType = webHeaders ["Content-Type"];
147
148                                 return contentType;
149                         }
150                 }
151                 
152                 public CookieCollection Cookies {
153                         get { 
154                                 CheckDisposed ();
155                                 
156                                 if (cookieCollection == null)
157                                         cookieCollection = new CookieCollection ();
158                                 return cookieCollection;
159                         }
160                         set {
161                                 CheckDisposed ();
162                                 cookieCollection = value;
163                         }
164                 }
165                 
166                 public override WebHeaderCollection Headers {           
167                         get { 
168                                 CheckDisposed ();
169                                 return webHeaders; 
170                         }
171                 }
172
173 #if NET_2_0
174                 static Exception GetMustImplement ()
175                 {
176                         return new NotImplementedException ();
177                 }
178                 
179                 [MonoTODO]
180                 public override bool IsMutuallyAuthenticated
181                 {
182                         get {
183                                 throw GetMustImplement ();
184                         }
185                 }
186 #endif
187                 
188                 public DateTime LastModified {
189                         get {
190                                 CheckDisposed ();
191                                 try {
192                                         string dtStr = webHeaders ["Last-Modified"];
193                                         return MonoHttpDate.Parse (dtStr);
194                                 } catch (Exception) {
195                                         return DateTime.Now;    
196                                 }
197                         }
198                 }
199                 
200                 public string Method {
201                         get { 
202                                 CheckDisposed ();
203                                 return method; 
204                         }
205                 }
206                 
207                 public Version ProtocolVersion {
208                         get { 
209                                 CheckDisposed ();
210                                 return version; 
211                         }
212                 }
213                 
214                 public override Uri ResponseUri {               
215                         get { 
216                                 CheckDisposed ();
217                                 return uri; 
218                         }
219                 }               
220                 
221                 public string Server {
222                         get { 
223                                 CheckDisposed ();
224                                 return webHeaders ["Server"]; 
225                         }
226                 }
227                 
228                 public HttpStatusCode StatusCode {
229                         get { 
230                                 CheckDisposed ();
231                                 return statusCode; 
232                         }
233                 }
234                 
235                 public string StatusDescription {
236                         get { 
237                                 CheckDisposed ();
238                                 return statusDescription; 
239                         }
240                 }
241
242                 // Methods
243 #if !NET_2_0
244                 public override int GetHashCode ()
245                 {
246                         CheckDisposed ();
247                         return base.GetHashCode ();
248                 }
249 #endif
250                 
251                 public string GetResponseHeader (string headerName)
252                 {
253                         CheckDisposed ();
254                         string value = webHeaders [headerName];
255                         return (value != null) ? value : "";
256                 }
257
258                 internal void ReadAll ()
259                 {
260                         WebConnectionStream wce = stream as WebConnectionStream;
261                         if (wce == null)
262                                 return;
263                                 
264                         try {
265                                 wce.ReadAll ();
266                         } catch {}
267                 }
268
269                 public override Stream GetResponseStream ()
270                 {
271                         CheckDisposed ();
272                         if (stream == null)
273                                 return Stream.Null;  
274                         if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
275                                 return Stream.Null;  
276
277                         return stream;
278                 }
279                 
280                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
281                                                   StreamingContext streamingContext)
282                 {
283                         GetObjectData (serializationInfo, streamingContext);
284                 }
285
286 #if NET_2_0
287                 protected override
288 #endif
289                 void GetObjectData (SerializationInfo serializationInfo,
290                                     StreamingContext streamingContext)
291                 {
292                         SerializationInfo info = serializationInfo;
293
294                         info.AddValue ("uri", uri);
295                         info.AddValue ("contentLength", contentLength);
296                         info.AddValue ("contentType", contentType);
297                         info.AddValue ("method", method);
298                         info.AddValue ("statusDescription", statusDescription);
299                         info.AddValue ("cookieCollection", cookieCollection);
300                         info.AddValue ("version", version);
301                         info.AddValue ("statusCode", statusCode);
302                 }
303
304                 // Cleaning up stuff
305
306                 public override void Close ()
307                 {
308                         ((IDisposable) this).Dispose ();
309                 }
310                 
311                 void IDisposable.Dispose ()
312                 {
313                         Dispose (true);
314                         GC.SuppressFinalize (this);  
315                 }
316
317 #if !NET_2_0
318                 protected virtual
319 #endif
320                 void Dispose (bool disposing) 
321                 {
322                         if (this.disposed)
323                                 return;
324                         this.disposed = true;
325                         
326                         if (disposing) {
327                                 // release managed resources
328                                 uri = null;
329                                 webHeaders = null;
330                                 cookieCollection = null;
331                                 method = null;
332                                 version = null;
333                                 statusDescription = null;
334                         }
335                         
336                         // release unmanaged resources
337                         Stream st = stream;
338                         stream = null;
339                         if (st != null)
340                                 st.Close ();
341                 }
342                 
343                 private void CheckDisposed () 
344                 {
345                         if (disposed)
346                                 throw new ObjectDisposedException (GetType ().FullName);
347                 }
348
349                 void FillCookies ()
350                 {
351                         if (webHeaders == null)
352                                 return;
353
354                         string [] values = webHeaders.GetValues ("Set-Cookie");
355                         if (values != null) {
356                                 foreach (string va in values)
357                                         SetCookie (va);
358                         }
359
360                         values = webHeaders.GetValues ("Set-Cookie2");
361                         if (values != null) {
362                                 foreach (string va in values)
363                                         SetCookie2 (va);
364                         }
365                 }
366
367                 void SetCookie (string header)
368                 {
369                         string name, val;
370                         Cookie cookie = null;
371                         CookieParser parser = new CookieParser (header);
372
373                         while (parser.GetNextNameValue (out name, out val)) {
374                                 if ((name == null || name == "") && cookie == null)
375                                         continue;
376
377                                 if (cookie == null) {
378                                         cookie = new Cookie (name, val);
379                                         continue;
380                                 }
381
382                                 name = name.ToUpper ();
383                                 switch (name) {
384                                 case "COMMENT":
385                                         if (cookie.Comment == null)
386                                                 cookie.Comment = val;
387                                         break;
388                                 case "COMMENTURL":
389                                         if (cookie.CommentUri == null)
390                                                 cookie.CommentUri = new Uri (val);
391                                         break;
392                                 case "DISCARD":
393                                         cookie.Discard = true;
394                                         break;
395                                 case "DOMAIN":
396                                         if (cookie.Domain == "")
397                                                 cookie.Domain = val;
398                                         break;
399                                 case "MAX-AGE": // RFC Style Set-Cookie2
400                                         if (cookie.Expires == DateTime.MinValue) {
401                                                 try {
402                                                 cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
403                                                 } catch {}
404                                         }
405                                         break;
406                                 case "EXPIRES": // Netscape Style Set-Cookie
407                                         if (cookie.Expires != DateTime.MinValue)
408                                                 break;
409                                         try {
410                                                 cookie.Expires = DateTime.ParseExact (val, "r", CultureInfo.InvariantCulture);
411                                         } catch {
412                                                 try { 
413                                                 cookie.Expires = DateTime.ParseExact (val,
414                                                                 "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
415                                                                 CultureInfo.InvariantCulture);
416                                                 } catch {
417                                                         cookie.Expires = DateTime.Now.AddDays (1);
418                                                 }
419                                         }
420                                         break;
421                                 case "PATH":
422                                         cookie.Path = val;
423                                         break;
424                                 case "PORT":
425                                         if (cookie.Port == null)
426                                                 cookie.Port = val;
427                                         break;
428                                 case "SECURE":
429                                         cookie.Secure = true;
430                                         break;
431                                 case "VERSION":
432                                         try {
433                                                 cookie.Version = (int) UInt32.Parse (val);
434                                         } catch {}
435                                         break;
436                                 }
437                         }
438
439                         if (cookieCollection == null)
440                                 cookieCollection = new CookieCollection ();
441
442                         if (cookie.Domain == "")
443                                 cookie.Domain = uri.Host;
444
445                         cookieCollection.Add (cookie);
446                         if (cookie_container != null)
447                                 cookie_container.Add (uri, cookie);
448                 }
449
450                 void SetCookie2 (string cookies_str)
451                 {
452                         string [] cookies = cookies_str.Split (',');
453         
454                         foreach (string cookie_str in cookies)
455                                 SetCookie (cookie_str);
456                 }
457         }       
458
459         class CookieParser {
460                 string header;
461                 int pos;
462                 int length;
463
464                 public CookieParser (string header) : this (header, 0)
465                 {
466                 }
467
468                 public CookieParser (string header, int position)
469                 {
470                         this.header = header;
471                         this.pos = position;
472                         this.length = header.Length;
473                 }
474
475                 public bool GetNextNameValue (out string name, out string val)
476                 {
477                         name = null;
478                         val = null;
479
480                         if (pos >= length)
481                                 return false;
482
483                         name = GetCookieName ();
484                         if (pos < header.Length && header [pos] == '=') {
485                                 pos++;
486                                 val = GetCookieValue ();
487                         }
488
489                         if (pos < length && header [pos] == ';')
490                                 pos++;
491
492                         return true;
493                 }
494
495                 string GetCookieName ()
496                 {
497                         int k = pos;
498                         while (k < length && Char.IsWhiteSpace (header [k]))
499                                 k++;
500
501                         int begin = k;
502                         while (k < length && header [k] != ';' &&  header [k] != '=')
503                                 k++;
504
505                         pos = k;
506                         return header.Substring (begin, k - begin).Trim ();
507                 }
508
509                 string GetCookieValue ()
510                 {
511                         if (pos >= length)
512                                 return null;
513
514                         int k = pos;
515                         while (k < length && Char.IsWhiteSpace (header [k]))
516                                 k++;
517
518                         int begin;
519                         if (header [k] == '"'){
520                                 int j;
521                                 begin = ++k;
522
523                                 while (k < length && header [k] != '"')
524                                         k++;
525
526                                 for (j = k; j < length && header [j] != ';'; j++)
527                                         ;
528                                 pos = j;
529                         } else {
530                                 begin = k;
531                                 while (k < length && header [k] != ';')
532                                         k++;
533                                 pos = k;
534                         }
535                                 
536                         return header.Substring (begin, k - begin).Trim ();
537                 }
538         }
539 }
540