New test.
[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                 protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
78                 {
79                         SerializationInfo info = serializationInfo;
80
81                         uri = (Uri) info.GetValue ("uri", typeof (Uri));
82                         contentLength = info.GetInt64 ("contentLength");
83                         contentType = info.GetString ("contentType");
84                         method = info.GetString ("method");
85                         statusDescription = info.GetString ("statusDescription");
86                         cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
87                         version = (Version) info.GetValue ("version", typeof (Version));
88                         statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
89                 }
90                 
91                 // Properties
92                 
93                 public string CharacterSet {
94                         // Content-Type   = "Content-Type" ":" media-type
95                         // media-type     = type "/" subtype *( ";" parameter )
96                         // parameter      = attribute "=" value
97                         // 3.7.1. default is ISO-8859-1
98                         get { 
99                                 CheckDisposed ();
100                                 string contentType = ContentType;
101                                 if (contentType == null)
102                                         return "ISO-8859-1";
103                                 string val = contentType.ToLower ();                                    
104                                 int pos = val.IndexOf ("charset=");
105                                 if (pos == -1)
106                                         return "ISO-8859-1";
107                                 pos += 8;
108                                 int pos2 = val.IndexOf (';', pos);
109                                 return (pos2 == -1)
110                                      ? contentType.Substring (pos) 
111                                      : contentType.Substring (pos, pos2 - pos);
112                         }
113                 }
114                 
115                 public string ContentEncoding {
116                         get { 
117                                 CheckDisposed ();
118                                 string h = webHeaders ["Content-Encoding"];
119                                 return h != null ? h : "";
120                         }
121                 }
122                 
123                 public override long ContentLength {            
124                         get {
125                                 CheckDisposed ();
126                                 if (contentLength != -1)
127                                         return contentLength;
128
129                                 try {
130                                         contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]); 
131                                 } catch (Exception) {
132                                         return -1;
133                                 }
134
135                                 return contentLength;
136                         }
137                 }
138                 
139                 public override string ContentType {            
140                         get {
141                                 CheckDisposed ();
142                                 if (contentType == null)
143                                         contentType = webHeaders ["Content-Type"];
144
145                                 return contentType;
146                         }
147                 }
148                 
149                 public CookieCollection Cookies {
150                         get { 
151                                 CheckDisposed ();
152                                 
153                                 if (cookieCollection == null)
154                                         cookieCollection = new CookieCollection ();
155                                 return cookieCollection;
156                         }
157                         set {
158                                 CheckDisposed ();
159                                 cookieCollection = value;
160                         }
161                 }
162                 
163                 public override WebHeaderCollection Headers {           
164                         get { 
165                                 CheckDisposed ();
166                                 return webHeaders; 
167                         }
168                 }
169                 
170                 public DateTime LastModified {
171                         get {
172                                 CheckDisposed ();
173                                 try {
174                                         string dtStr = webHeaders ["Last-Modified"];
175                                         return MonoHttpDate.Parse (dtStr);
176                                 } catch (Exception) {
177                                         return DateTime.Now;    
178                                 }
179                         }
180                 }
181                 
182                 public string Method {
183                         get { 
184                                 CheckDisposed ();
185                                 return method; 
186                         }
187                 }
188                 
189                 public Version ProtocolVersion {
190                         get { 
191                                 CheckDisposed ();
192                                 return version; 
193                         }
194                 }
195                 
196                 public override Uri ResponseUri {               
197                         get { 
198                                 CheckDisposed ();
199                                 return uri; 
200                         }
201                 }               
202                 
203                 public string Server {
204                         get { 
205                                 CheckDisposed ();
206                                 return webHeaders ["Server"]; 
207                         }
208                 }
209                 
210                 public HttpStatusCode StatusCode {
211                         get { 
212                                 CheckDisposed ();
213                                 return statusCode; 
214                         }
215                 }
216                 
217                 public string StatusDescription {
218                         get { 
219                                 CheckDisposed ();
220                                 return statusDescription; 
221                         }
222                 }
223
224                 // Methods
225 #if !NET_2_0
226                 public override int GetHashCode ()
227                 {
228                         CheckDisposed ();
229                         return base.GetHashCode ();
230                 }
231 #endif
232                 
233                 public string GetResponseHeader (string headerName)
234                 {
235                         CheckDisposed ();
236                         string value = webHeaders [headerName];
237                         return (value != null) ? value : "";
238                 }
239
240                 internal void ReadAll ()
241                 {
242                         WebConnectionStream wce = stream as WebConnectionStream;
243                         if (wce == null)
244                                 return;
245                                 
246                         try {
247                                 wce.ReadAll ();
248                         } catch {}
249                 }
250
251                 public override Stream GetResponseStream ()
252                 {
253                         CheckDisposed ();
254                         if (stream == null)
255                                 return Stream.Null;  
256                         if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
257                                 return Stream.Null;  
258
259                         return stream;
260                 }
261                 
262                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
263                                                   StreamingContext streamingContext)
264                 {
265                         SerializationInfo info = serializationInfo;
266
267                         info.AddValue ("uri", uri);
268                         info.AddValue ("contentLength", contentLength);
269                         info.AddValue ("contentType", contentType);
270                         info.AddValue ("method", method);
271                         info.AddValue ("statusDescription", statusDescription);
272                         info.AddValue ("cookieCollection", cookieCollection);
273                         info.AddValue ("version", version);
274                         info.AddValue ("statusCode", statusCode);
275                 }               
276
277
278                 // Cleaning up stuff
279
280                 public override void Close ()
281                 {
282                         ((IDisposable) this).Dispose ();
283                 }
284                 
285                 void IDisposable.Dispose ()
286                 {
287                         Dispose (true);
288                         GC.SuppressFinalize (this);  
289                 }
290
291 #if !NET_2_0
292                 protected virtual
293 #endif
294                 void Dispose (bool disposing) 
295                 {
296                         if (this.disposed)
297                                 return;
298                         this.disposed = true;
299                         
300                         if (disposing) {
301                                 // release managed resources
302                                 uri = null;
303                                 webHeaders = null;
304                                 cookieCollection = null;
305                                 method = null;
306                                 version = null;
307                                 statusDescription = null;
308                         }
309                         
310                         // release unmanaged resources
311                         Stream st = stream;
312                         stream = null;
313                         if (st != null)
314                                 st.Close ();
315                 }
316                 
317                 private void CheckDisposed () 
318                 {
319                         if (disposed)
320                                 throw new ObjectDisposedException (GetType ().FullName);
321                 }
322
323                 void FillCookies ()
324                 {
325                         if (webHeaders == null)
326                                 return;
327
328                         string [] values = webHeaders.GetValues ("Set-Cookie");
329                         if (values != null) {
330                                 foreach (string va in values)
331                                         SetCookie (va);
332                         }
333
334                         values = webHeaders.GetValues ("Set-Cookie2");
335                         if (values != null) {
336                                 foreach (string va in values)
337                                         SetCookie2 (va);
338                         }
339                 }
340
341                 void SetCookie (string header)
342                 {
343                         string name, val;
344                         Cookie cookie = null;
345                         CookieParser parser = new CookieParser (header);
346
347                         while (parser.GetNextNameValue (out name, out val)) {
348                                 if ((name == null || name == "") && cookie == null)
349                                         continue;
350
351                                 if (cookie == null) {
352                                         cookie = new Cookie (name, val);
353                                         continue;
354                                 }
355
356                                 name = name.ToUpper ();
357                                 switch (name) {
358                                 case "COMMENT":
359                                         if (cookie.Comment == null)
360                                                 cookie.Comment = val;
361                                         break;
362                                 case "COMMENTURL":
363                                         if (cookie.CommentUri == null)
364                                                 cookie.CommentUri = new Uri (val);
365                                         break;
366                                 case "DISCARD":
367                                         cookie.Discard = true;
368                                         break;
369                                 case "DOMAIN":
370                                         if (cookie.Domain == "")
371                                                 cookie.Domain = val;
372                                         break;
373                                 case "MAX-AGE": // RFC Style Set-Cookie2
374                                         if (cookie.Expires == DateTime.MinValue) {
375                                                 try {
376                                                 cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
377                                                 } catch {}
378                                         }
379                                         break;
380                                 case "EXPIRES": // Netscape Style Set-Cookie
381                                         if (cookie.Expires != DateTime.MinValue)
382                                                 break;
383                                         try {
384                                                 cookie.Expires = DateTime.ParseExact (val, "r", CultureInfo.InvariantCulture);
385                                         } catch {
386                                                 try { 
387                                                 cookie.Expires = DateTime.ParseExact (val,
388                                                                 "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
389                                                                 CultureInfo.InvariantCulture);
390                                                 } catch {
391                                                         cookie.Expires = DateTime.Now.AddDays (1);
392                                                 }
393                                         }
394                                         break;
395                                 case "PATH":
396                                         cookie.Path = val;
397                                         break;
398                                 case "PORT":
399                                         if (cookie.Port == null)
400                                                 cookie.Port = val;
401                                         break;
402                                 case "SECURE":
403                                         cookie.Secure = true;
404                                         break;
405                                 case "VERSION":
406                                         try {
407                                                 cookie.Version = (int) UInt32.Parse (val);
408                                         } catch {}
409                                         break;
410                                 }
411                         }
412
413                         if (cookieCollection == null)
414                                 cookieCollection = new CookieCollection ();
415
416                         if (cookie.Domain == "")
417                                 cookie.Domain = uri.Host;
418
419                         cookieCollection.Add (cookie);
420                         if (cookie_container != null)
421                                 cookie_container.Add (uri, cookie);
422                 }
423
424                 void SetCookie2 (string cookies_str)
425                 {
426                         string [] cookies = cookies_str.Split (',');
427         
428                         foreach (string cookie_str in cookies)
429                                 SetCookie (cookie_str);
430                 }
431         }       
432
433         class CookieParser {
434                 string header;
435                 int pos;
436                 int length;
437
438                 public CookieParser (string header) : this (header, 0)
439                 {
440                 }
441
442                 public CookieParser (string header, int position)
443                 {
444                         this.header = header;
445                         this.pos = position;
446                         this.length = header.Length;
447                 }
448
449                 public bool GetNextNameValue (out string name, out string val)
450                 {
451                         name = null;
452                         val = null;
453
454                         if (pos >= length)
455                                 return false;
456
457                         name = GetCookieName ();
458                         if (pos < header.Length && header [pos] == '=') {
459                                 pos++;
460                                 val = GetCookieValue ();
461                         }
462
463                         if (pos < length && header [pos] == ';')
464                                 pos++;
465
466                         return true;
467                 }
468
469                 string GetCookieName ()
470                 {
471                         int k = pos;
472                         while (k < length && Char.IsWhiteSpace (header [k]))
473                                 k++;
474
475                         int begin = k;
476                         while (k < length && header [k] != ';' &&  header [k] != '=')
477                                 k++;
478
479                         pos = k;
480                         return header.Substring (begin, k - begin).Trim ();
481                 }
482
483                 string GetCookieValue ()
484                 {
485                         if (pos >= length)
486                                 return null;
487
488                         int k = pos;
489                         while (k < length && Char.IsWhiteSpace (header [k]))
490                                 k++;
491
492                         int begin;
493                         if (header [k] == '"'){
494                                 int j;
495                                 begin = ++k;
496
497                                 while (k < length && header [k] != '"')
498                                         k++;
499
500                                 for (j = k; j < length && header [j] != ';'; j++)
501                                         ;
502                                 pos = j;
503                         } else {
504                                 begin = k;
505                                 while (k < length && header [k] != ';')
506                                         k++;
507                                 pos = k;
508                         }
509                                 
510                         return header.Substring (begin, k - begin).Trim ();
511                 }
512         }
513 }
514