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