2004-12-12 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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.Globalization;
35 using System.IO;
36 using System.Net.Sockets;
37 using System.Runtime.Serialization;
38 using System.Text;
39
40 namespace System.Net 
41 {
42         [Serializable]
43         public class HttpWebResponse : WebResponse, ISerializable, IDisposable
44         {
45                 Uri uri;
46                 WebHeaderCollection webHeaders;
47                 CookieCollection cookieCollection;
48                 string method;
49                 Version version;
50                 HttpStatusCode statusCode;
51                 string statusDescription;
52                 long contentLength = -1;
53                 string contentType;
54
55                 bool disposed = false;
56                 Stream stream;
57                 
58                 // Constructors
59                 
60                 internal HttpWebResponse (Uri uri, string method, WebConnectionData data, bool cookiesSet)
61                 {
62                         this.uri = uri;
63                         this.method = method;
64                         webHeaders = data.Headers;
65                         version = data.Version;
66                         statusCode = (HttpStatusCode) data.StatusCode;
67                         statusDescription = data.StatusDescription;
68                         stream = data.stream;
69                         if (cookiesSet) {
70                                 FillCookies ();
71                         } else if (webHeaders != null) {
72                                 webHeaders.RemoveInternal ("Set-Cookie");
73                                 webHeaders.RemoveInternal ("Set-Cookie2");
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                                 return webHeaders ["Content-Encoding"];
119                         }
120                 }
121                 
122                 public override long ContentLength {            
123                         get {
124                                 CheckDisposed ();
125                                 if (contentLength != -1)
126                                         return contentLength;
127
128                                 try {
129                                         contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]); 
130                                 } catch (Exception) {
131                                         return -1;
132                                 }
133
134                                 return contentLength;
135                         }
136                 }
137                 
138                 public override string ContentType {            
139                         get {
140                                 CheckDisposed ();
141                                 if (contentType == null)
142                                         contentType = webHeaders ["Content-Type"];
143
144                                 return contentType;
145                         }
146                 }
147                 
148                 public CookieCollection Cookies {
149                         get { 
150                                 CheckDisposed ();
151                                 
152                                 if (cookieCollection == null)
153                                         cookieCollection = new CookieCollection ();
154                                 return cookieCollection;
155                         }
156                         set {
157                                 CheckDisposed ();
158                                 cookieCollection = value;
159                         }
160                 }
161                 
162                 public override WebHeaderCollection Headers {           
163                         get { 
164                                 CheckDisposed ();
165                                 return webHeaders; 
166                         }
167                 }
168                 
169                 public DateTime LastModified {
170                         get {
171                                 CheckDisposed ();
172                                 try {
173                                         string dtStr = webHeaders ["Last-Modified"];
174                                         return MonoHttpDate.Parse (dtStr);
175                                 } catch (Exception) {
176                                         return DateTime.Now;    
177                                 }
178                         }
179                 }
180                 
181                 public string Method {
182                         get { 
183                                 CheckDisposed ();
184                                 return method; 
185                         }
186                 }
187                 
188                 public Version ProtocolVersion {
189                         get { 
190                                 CheckDisposed ();
191                                 return version; 
192                         }
193                 }
194                 
195                 public override Uri ResponseUri {               
196                         get { 
197                                 CheckDisposed ();
198                                 return uri; 
199                         }
200                 }               
201                 
202                 public string Server {
203                         get { 
204                                 CheckDisposed ();
205                                 return webHeaders ["Server"]; 
206                         }
207                 }
208                 
209                 public HttpStatusCode StatusCode {
210                         get { 
211                                 CheckDisposed ();
212                                 return statusCode; 
213                         }
214                 }
215                 
216                 public string StatusDescription {
217                         get { 
218                                 CheckDisposed ();
219                                 return statusDescription; 
220                         }
221                 }
222
223                 // Methods
224                 
225                 public override int GetHashCode ()
226                 {
227                         CheckDisposed ();
228                         return base.GetHashCode ();
229                 }
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                 protected virtual void Dispose (bool disposing) 
290                 {
291                         if (this.disposed)
292                                 return;
293                         this.disposed = true;
294                         
295                         if (disposing) {
296                                 // release managed resources
297                                 uri = null;
298                                 webHeaders = null;
299                                 cookieCollection = null;
300                                 method = null;
301                                 version = null;
302                                 statusDescription = null;
303                         }
304                         
305                         // release unmanaged resources
306                         Stream st = stream;
307                         stream = null;
308                         if (st != null) {
309                                 WebConnectionStream wce = st as WebConnectionStream;
310                                 if (wce != null) {
311                                         try {
312                                                 wce.ReadAll ();
313                                         } catch {}
314                                 }
315                                 st.Close ();
316                         }
317                 }
318                 
319                 private void CheckDisposed () 
320                 {
321                         if (disposed)
322                                 throw new ObjectDisposedException (GetType ().FullName);
323                 }
324
325                 void FillCookies ()
326                 {
327                         if (webHeaders == null)
328                                 return;
329
330                         string [] values = webHeaders.GetValues ("Set-Cookie");
331                         if (values != null) {
332                                 foreach (string va in values)
333                                         SetCookie (va);
334                         }
335
336                         values = webHeaders.GetValues ("Set-Cookie2");
337                         if (values != null) {
338                                 foreach (string va in values)
339                                         SetCookie2 (va);
340                         }
341                 }
342                 
343                 void SetCookie (string header)
344                 {
345                         string [] name_values = header.Trim ().Split (';');
346                         int length = name_values.Length;
347                         Cookie cookie = null;
348                         int pos;
349                         for (int i = 0; i < length; i++) {
350                                 pos = 0;
351                                 string name_value = name_values [i].Trim ();
352                                 if (name_value == "")
353                                         continue;
354
355                                 string name = GetCookieName (name_value, name_value.Length, ref pos);
356                                 string value = GetCookieValue (name_value, name_value.Length, ref pos);
357                                 if (cookie == null) {
358                                         cookie = new Cookie (name, value);
359                                         continue;
360                                 }
361
362                                 name = name.ToUpper ();
363                                 switch (name) {
364                                 case "COMMENT":
365                                         if (cookie.Comment == null)
366                                                 cookie.Comment = value;
367                                         break;
368                                 case "COMMENTURL":
369                                         if (cookie.CommentUri == null)
370                                                 cookie.CommentUri = new Uri (value);
371                                         break;
372                                 case "DISCARD":
373                                         cookie.Discard = true;
374                                         break;
375                                 case "DOMAIN":
376                                         if (cookie.Domain == "")
377                                                 cookie.Domain = value;
378                                         break;
379                                 case "MAX-AGE": // RFC Style Set-Cookie2
380                                         if (cookie.Expires == DateTime.MinValue)
381                                                 cookie.Expires = cookie.TimeStamp.AddSeconds (Int32.Parse (value));
382                                         break;
383                                 case "EXPIRES": // Netscape Style Set-Cookie
384                                         if (cookie.Expires != DateTime.MinValue)
385                                                 break;
386                                         try {
387                                                 cookie.Expires = DateTime.ParseExact (value, "r", CultureInfo.InvariantCulture);
388                                         } catch {
389                                                 try { 
390                                                 cookie.Expires = DateTime.ParseExact (value,
391                                                                 "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
392                                                                 CultureInfo.InvariantCulture);
393                                                 } catch {
394                                                         cookie.Expires = DateTime.Now.AddDays (1);
395                                                 }
396                                         }
397                                         break;
398                                 case "PATH":
399                                         cookie.Path = value;
400                                         break;
401                                 case "PORT":
402                                         if (cookie.Port == null)
403                                                 cookie.Port = value;
404                                         break;
405                                 case "SECURE":
406                                         cookie.Secure = true;
407                                         break;
408                                 case "VERSION":
409                                         cookie.Version = Int32.Parse (value);
410                                         break;
411                                 }
412                         }
413
414                         if (cookieCollection == null)
415                                 cookieCollection = new CookieCollection ();
416
417                         if (cookie.Domain == "")
418                                 cookie.Domain = uri.Host;
419
420                         cookieCollection.Add (cookie);
421                 }
422
423                 void SetCookie2 (string cookies_str)
424                 {
425                         string [] cookies = cookies_str.Split (',');
426         
427                         foreach (string cookie_str in cookies)
428                                 SetCookie (cookie_str);
429                 }
430
431                 static string GetCookieValue (string str, int length, ref int i)
432                 {
433                         if (i >= length)
434                                 return null;
435
436                         int k = i;
437                         while (k < length && Char.IsWhiteSpace (str [k]))
438                                 k++;
439
440                         int begin = k;
441                         while (k < length && str [k] != ';')
442                                 k++;
443
444                         i = k;
445                         return str.Substring (begin, i - begin).Trim ();
446                 }
447
448                 static string GetCookieName (string str, int length, ref int i)
449                 {
450                         if (i >= length)
451                                 return null;
452
453                         int k = i;
454                         while (k < length && Char.IsWhiteSpace (str [k]))
455                                 k++;
456
457                         int begin = k;
458                         while (k < length && str [k] != ';' &&  str [k] != '=')
459                                 k++;
460
461                         i = k + 1;
462                         return str.Substring (begin, k - begin).Trim ();
463                 }
464         }       
465 }
466