forgot this
[mono.git] / mcs / class / System / System.Net / HttpWebResponse.cs
1 //
2 // System.Net.HttpWebResponse
3 //
4 // Author:
5 //   Lawrence Pit (loz@cable.a2000.nl)
6 //
7
8 using System;
9 using System.IO;
10 using System.Runtime.Serialization;
11
12 namespace System.Net 
13 {
14         [Serializable]
15         public class HttpWebResponse : WebResponse, ISerializable, IDisposable
16         {
17                 private Uri uri;
18                 private WebHeaderCollection webHeaders;
19                 private CookieCollection cookieCollection = null;
20                 private string method = null;
21                 private Version version = null;
22                 private HttpStatusCode statusCode;
23                 private string statusDescription = null;
24
25                 private Stream responseStream;          
26                 private bool disposed = false;
27                 
28                 // Constructors
29                 
30                 internal HttpWebResponse (Uri uri, string method, Stream responseStream) 
31                 { 
32                         Text.StringBuilder value = null;
33                         string last = null;
34                         string line = null;
35                         string[] protocol, header;
36
37                         this.uri = uri;
38                         this.method = method;
39                         this.responseStream = responseStream;
40                         this.webHeaders = new WebHeaderCollection();
41
42                         line = ReadHttpLine(responseStream);
43                         protocol = line.Split (' ');
44                         
45                         switch (protocol[0]) {
46                                 case "HTTP/1.0":
47                                         this.version = HttpVersion.Version10;
48                                         break;
49                                 case "HTTP/1.1":
50                                         this.version = HttpVersion.Version11;
51                                         break;
52                                 default:
53                                         throw new WebException ("Unrecognized HTTP Version");
54                         }
55                         
56                         this.statusCode = (HttpStatusCode) Int32.Parse (protocol[1]);
57                         while ((line = ReadHttpLine(responseStream)).Length != 0) {
58                                 if (!Char.IsWhiteSpace (line[0])) { // new header
59                                         header = line.Split (new char[] {':'}, 2);
60                                         if (header.Length != 2)
61                                                 throw new WebException ("Bad HTTP Header");
62                                         if (last != null) { // not the first header
63                                                 if (last.Equals ("Set-Cookie"))
64                                                         SetCookie (value.ToString());
65                                                 else if (last.Equals ("Set-Cookie2"))
66                                                         SetCookie2 (value.ToString());
67                                                 else //don't save Set-Cookie headers
68                                                         this.webHeaders[last] = value.ToString();
69                                         }
70                                         last = header[0];
71                                         value = new Text.StringBuilder (header[1].Trim());
72                                 }
73                                 else
74                                         value.Append (line.Trim());
75                         }
76                         
77                         this.webHeaders[last] = value.ToString(); // otherwise we miss the last header
78                 }
79                 
80                 protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
81                 {
82                         throw new NotSupportedException ();
83                 }
84                 
85                 // Properties
86                 
87                 public string CharacterSet {
88                         // Content-Type   = "Content-Type" ":" media-type
89                         // media-type     = type "/" subtype *( ";" parameter )
90                         // parameter      = attribute "=" value
91                         // 3.7.1. default is ISO-8859-1
92                         get { 
93                                 try {
94                                         string contentType = ContentType;
95                                         if (contentType == null)
96                                                 return "ISO-8859-1";
97                                         string val = contentType.ToLower ();                                    
98                                         int pos = val.IndexOf ("charset=");
99                                         if (pos == -1)
100                                                 return "ISO-8859-1";
101                                         pos += 8;
102                                         int pos2 = val.IndexOf (';', pos);
103                                         return (pos2 == -1)
104                                              ? contentType.Substring (pos) 
105                                              : contentType.Substring (pos, pos2 - pos);
106                                 } finally {
107                                         CheckDisposed ();
108                                 }
109                         }
110                 }
111                 
112                 public string ContentEncoding {
113                         get { 
114                                 try { return webHeaders ["Content-Encoding"]; }
115                                 finally { CheckDisposed (); }
116                         }
117                 }
118                 
119                 public override long ContentLength {            
120                         get { 
121                                 try {
122                                         return Int64.Parse (webHeaders ["Content-Length"]); 
123                                 } catch (Exception) {
124                                         return -1;
125                                 } finally {
126                                         CheckDisposed ();
127                                 }
128                         }
129                 }
130                 
131                 public override string ContentType {            
132                         get { 
133                                 try { return webHeaders ["Content-Type"]; }
134                                 finally { CheckDisposed (); }
135                         }
136                 }
137                 
138                 public CookieCollection Cookies {
139                         get { 
140                                 CheckDisposed ();
141                                 
142                                 if (cookieCollection == null)
143                                         cookieCollection = new CookieCollection ();
144                                 return cookieCollection;
145                         }
146                         set {
147                                 CheckDisposed ();
148                                 // ?? don't understand how you can set cookies on a response.
149                                 throw new NotSupportedException ();
150                         }
151                 }
152                 
153                 public override WebHeaderCollection Headers {           
154                         get { 
155                                 CheckDisposed ();
156                                 return webHeaders; 
157                         }
158                 }
159                 
160                 public DateTime LastModified {
161                         get {
162                                 CheckDisposed ();
163                                 try {
164                                         string dtStr = webHeaders ["Last-Modified"];
165                                         return MonoHttpDate.Parse (dtStr);
166                                 } catch (Exception) {
167                                         return DateTime.Now;    
168                                 }
169                         }
170                 }
171                 
172                 public string Method {
173                         get { 
174                                 CheckDisposed ();
175                                 return method; 
176                         }
177                 }
178                 
179                 public Version ProtocolVersion {
180                         get { 
181                                 CheckDisposed ();
182                                 return version; 
183                         }
184                 }
185                 
186                 public override Uri ResponseUri {               
187                         get { 
188                                 CheckDisposed ();
189                                 return uri; 
190                         }
191                 }               
192                 
193                 public string Server {
194                         get { 
195                                 try {
196                                         return webHeaders ["Server"]; 
197                                 } finally {
198                                         CheckDisposed ();
199                                 }
200                         }
201                 }
202                 
203                 public HttpStatusCode StatusCode {
204                         get { 
205                                 CheckDisposed ();
206                                 return statusCode; 
207                         }
208                 }
209                 
210                 public string StatusDescription {
211                         get { 
212                                 CheckDisposed ();
213                                 return statusDescription; 
214                         }
215                 }
216
217                 // Methods
218                 
219                 public override int GetHashCode ()
220                 {
221                         try {
222                                 return base.GetHashCode ();
223                         } finally {
224                                 CheckDisposed ();
225                         }
226                 }
227                 
228                 public string GetResponseHeader (string headerName)
229                 {
230                         try {
231                                 return webHeaders [headerName];
232                         } finally {
233                                 CheckDisposed ();
234                         }
235                 }
236                 
237                 public override Stream GetResponseStream ()
238                 {
239                         try {
240                                 if (method.Equals ("HEAD")) // see par 4.3 & 9.4
241                                         return Stream.Null;  
242                                 return responseStream;
243                         } finally {
244                                 CheckDisposed ();
245                         }
246                 }
247                 
248                 [MonoTODO]
249                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
250                                                   StreamingContext streamingContext)
251                 {
252                         CheckDisposed ();
253                         throw new NotImplementedException ();
254                 }               
255
256
257                 // Cleaning up stuff
258
259                 ~HttpWebResponse ()
260                 {
261                         Dispose (false);
262                 }               
263                 
264                 public override void Close ()
265                 {
266                         ((IDisposable) this).Dispose ();
267                 }
268                 
269                 void IDisposable.Dispose ()
270                 {
271                         Dispose (true);
272                         GC.SuppressFinalize (this);  
273                 }
274                 
275                 protected virtual void Dispose (bool disposing) 
276                 {
277                         if (this.disposed)
278                                 return;
279                         this.disposed = true;
280                         
281                         if (disposing) {
282                                 // release managed resources
283                                 uri = null;
284                                 webHeaders = null;
285                                 cookieCollection = null;
286                                 method = null;
287                                 version = null;
288                                 // statusCode = null;
289                                 statusDescription = null;
290                         }
291                         
292                         // release unmanaged resources
293                         Stream stream = responseStream;
294                         responseStream = null;
295                         if (stream != null)
296                                 stream.Close ();  // also closes webRequest                     
297                 }
298                 
299                 private void CheckDisposed () 
300                 {
301                         if (disposed)
302                                 throw new ObjectDisposedException (GetType ().FullName);
303                 }
304
305                 private static string ReadHttpLine (Stream stream)
306                 {
307                         Text.StringBuilder line = new Text.StringBuilder();
308                         byte last = (byte)'\n';
309                         bool read_last = false;
310                         byte[] buf = new byte[1]; // one at a time to not snarf too much
311                         
312                         while (stream.Read (buf, 0, buf.Length) != 0) {
313                                 if (buf[0] == '\r') {
314                                         if ((last = (byte)stream.ReadByte ()) == '\n') // headers; not at EOS
315                                                 break;
316                                         read_last = true;
317                                 }
318
319                                 line.Append (Convert.ToChar(buf[0]));
320                                 if (read_last) {
321                                         line.Append (Convert.ToChar (last));
322                                         read_last = false;
323                                 }
324                         }
325                         
326                         return line.ToString();
327                 }
328
329                 private void SetCookie (string cookie_str)
330                 {
331                         string[] parts = null;
332                         Collections.Queue options = null;
333                         Cookie cookie = null;
334
335                         options = new Collections.Queue (cookie_str.Split (';'));
336                         parts = ((string)options.Dequeue()).Split ('='); // NAME=VALUE must be first
337
338                         cookie = new Cookie (parts[0], parts[1]);
339
340                         while (options.Count > 0) {
341                                 parts = ((string)options.Dequeue()).Split ('=');
342                                 switch (parts[0].ToUpper()) { // cookie options are case-insensitive
343                                         case "COMMENT":
344                                                 if (cookie.Comment == null)
345                                                         cookie.Comment = parts[1];
346                                         break;
347                                         case "COMMENTURL":
348                                                 if (cookie.CommentUri == null)
349                                                         cookie.CommentUri = new Uri(parts[1]);
350                                         break;
351                                         case "DISCARD":
352                                                 cookie.Discard = true;
353                                         break;
354                                         case "DOMAIN":
355                                                 if (cookie.Domain == null)
356                                                         cookie.Domain = parts[1];
357                                         break;
358                                         case "MAX-AGE": // RFC Style Set-Cookie2
359                                                 if (cookie.Expires == DateTime.MinValue)
360                                                         cookie.Expires = cookie.TimeStamp.AddSeconds (Int32.Parse (parts[1]));
361                                         break;
362                                         case "EXPIRES": // Netscape Style Set-Cookie
363                                                 if (cookie.Expires == DateTime.MinValue)
364                                                         cookie.Expires = DateTime.Parse (parts[1]);
365                                         break;
366                                         case "PATH":
367                                                 if (cookie.Path == null)
368                                                         cookie.Path = parts[1];
369                                         break;
370                                         case "PORT":
371                                                 if (cookie.Port == null)
372                                                         cookie.Port = parts[1];
373                                         break;
374                                         case "SECURE":
375                                                 cookie.Secure = true;
376                                         break;
377                                         case "VERSION":
378                                                 cookie.Version = Int32.Parse (parts[1]);
379                                         break;
380                                 } // switch
381                         } // while
382
383                         if (cookieCollection == null)
384                                 cookieCollection = new CookieCollection();
385
386                         if (cookie.Domain == null)
387                                 cookie.Domain = uri.Host;
388
389                         cookieCollection.Add (cookie);
390                 }
391
392                 private void SetCookie2 (string cookies_str)
393                 {
394                         string[] cookies = cookies_str.Split (',');
395         
396                         foreach (string cookie_str in cookies)
397                                 SetCookie (cookie_str);
398
399                 }
400         }       
401 }