2003-07-01 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System / System.Net / HttpWebResponse.cs
index 10d66d87d09bb94cd8c0a781f5a34d164719c7ed..bb4393d9e419c1aa91bf1830c15284f978ae46e6 100644 (file)
@@ -1,85 +1,69 @@
 //
 // System.Net.HttpWebResponse
 //
-// Author:
-//   Lawrence Pit (loz@cable.a2000.nl)
+// Authors:
+//     Lawrence Pit (loz@cable.a2000.nl)
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2002 Lawrence Pit
+// (c) 2003 Ximian, Inc. (http://www.ximian.com)
 //
 
 using System;
 using System.IO;
+using System.Net.Sockets;
 using System.Runtime.Serialization;
+using System.Text;
 
 namespace System.Net 
 {
        [Serializable]
        public class HttpWebResponse : WebResponse, ISerializable, IDisposable
        {
-               private Uri uri;
-               private WebHeaderCollection webHeaders;
-               private CookieCollection cookieCollection = null;
-               private string method = null;
-               private Version version = null;
-               private HttpStatusCode statusCode;
-               private string statusDescription = null;
+               Uri uri;
+               WebHeaderCollection webHeaders;
+               CookieCollection cookieCollection;
+               string method;
+               Version version;
+               HttpStatusCode statusCode;
+               string statusDescription;
+               long contentLength = -1;
+               string contentType;
 
-               private Stream responseStream;          
-               private bool disposed = false;
+               bool disposed = false;
+               WebConnectionStream stream;
                
                // Constructors
                
-               internal HttpWebResponse (Uri uri, string method, Stream responseStream) 
-               { 
-                       Text.StringBuilder value = null;
-                       string last = null;
-                       string line = null;
-                       string[] protocol, header;
-
+               internal HttpWebResponse (Uri uri, string method, WebConnectionData data, bool cookiesSet)
+               {
                        this.uri = uri;
                        this.method = method;
-                       this.responseStream = responseStream;
-                       this.webHeaders = new WebHeaderCollection();
-
-                       line = ReadHttpLine(responseStream);
-                       protocol = line.Split (' ');
-                       
-                       switch (protocol[0]) {
-                               case "HTTP/1.0":
-                                       this.version = HttpVersion.Version10;
-                                       break;
-                               case "HTTP/1.1":
-                                       this.version = HttpVersion.Version11;
-                                       break;
-                               default:
-                                       throw new WebException ("Unrecognized HTTP Version");
+                       webHeaders = data.Headers;
+                       version = data.Version;
+                       statusCode = (HttpStatusCode) data.StatusCode;
+                       statusDescription = data.StatusDescription;
+                       stream = data.stream;
+                       if (cookiesSet) {
+                               FillCookies ();
+                       } else if (webHeaders != null) {
+                               webHeaders.RemoveInternal ("Set-Cookie");
+                               webHeaders.RemoveInternal ("Set-Cookie2");
                        }
-                       
-                       this.statusCode = (HttpStatusCode) Int32.Parse (protocol[1]);
-                       while ((line = ReadHttpLine(responseStream)).Length != 0) {
-                               if (!Char.IsWhiteSpace (line[0])) { // new header
-                                       header = line.Split (new char[] {':'}, 2);
-                                       if (header.Length != 2)
-                                               throw new WebException ("Bad HTTP Header");
-                                       if (last != null) { // not the first header
-                                               if (last.Equals ("Set-Cookie"))
-                                                       SetCookie (value.ToString());
-                                               else if (last.Equals ("Set-Cookie2"))
-                                                       SetCookie2 (value.ToString());
-                                               else //don't save Set-Cookie headers
-                                                       this.webHeaders[last] = value.ToString();
-                                       }
-                                       last = header[0];
-                                       value = new Text.StringBuilder (header[1].Trim());
-                               }
-                               else
-                                       value.Append (line.Trim());
-                       }
-                       
-                       this.webHeaders[last] = value.ToString(); // otherwise we miss the last header
                }
-               
+
                protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
                {
-                       throw new NotSupportedException ();
+                       SerializationInfo info = serializationInfo;
+
+                       uri = (Uri) info.GetValue ("uri", typeof (Uri));
+                       contentLength = info.GetInt64 ("contentLength");
+                       contentType = info.GetString ("contentType");
+                       method = info.GetString ("method");
+                       statusDescription = info.GetString ("statusDescription");
+                       cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
+                       version = (Version) info.GetValue ("version", typeof (Version));
+                       statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
                }
                
                // Properties
@@ -90,48 +74,52 @@ namespace System.Net
                        // parameter      = attribute "=" value
                        // 3.7.1. default is ISO-8859-1
                        get { 
-                               try {
-                                       string contentType = ContentType;
-                                       if (contentType == null)
-                                               return "ISO-8859-1";
-                                       string val = contentType.ToLower ();                                    
-                                       int pos = val.IndexOf ("charset=");
-                                       if (pos == -1)
-                                               return "ISO-8859-1";
-                                       pos += 8;
-                                       int pos2 = val.IndexOf (';', pos);
-                                       return (pos2 == -1)
-                                            ? contentType.Substring (pos) 
-                                            : contentType.Substring (pos, pos2 - pos);
-                               } finally {
-                                       CheckDisposed ();
-                               }
+                               CheckDisposed ();
+                               string contentType = ContentType;
+                               if (contentType == null)
+                                       return "ISO-8859-1";
+                               string val = contentType.ToLower ();                                    
+                               int pos = val.IndexOf ("charset=");
+                               if (pos == -1)
+                                       return "ISO-8859-1";
+                               pos += 8;
+                               int pos2 = val.IndexOf (';', pos);
+                               return (pos2 == -1)
+                                    ? contentType.Substring (pos) 
+                                    : contentType.Substring (pos, pos2 - pos);
                        }
                }
                
                public string ContentEncoding {
                        get { 
-                               try { return webHeaders ["Content-Encoding"]; }
-                               finally { CheckDisposed (); }
+                               CheckDisposed ();
+                               return webHeaders ["Content-Encoding"];
                        }
                }
                
                public override long ContentLength {            
-                       get { 
+                       get {
+                               CheckDisposed ();
+                               if (contentLength != -1)
+                                       return contentLength;
+
                                try {
-                                       return Int64.Parse (webHeaders ["Content-Length"]); 
+                                       contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]); 
                                } catch (Exception) {
                                        return -1;
-                               } finally {
-                                       CheckDisposed ();
                                }
+
+                               return contentLength;
                        }
                }
                
                public override string ContentType {            
-                       get { 
-                               try { return webHeaders ["Content-Type"]; }
-                               finally { CheckDisposed (); }
+                       get {
+                               CheckDisposed ();
+                               if (contentType == null)
+                                       contentType = webHeaders ["Content-Type"];
+
+                               return contentType;
                        }
                }
                
@@ -145,8 +133,7 @@ namespace System.Net
                        }
                        set {
                                CheckDisposed ();
-                               // ?? don't understand how you can set cookies on a response.
-                               throw new NotSupportedException ();
+                               cookieCollection = value;
                        }
                }
                
@@ -192,11 +179,8 @@ namespace System.Net
                
                public string Server {
                        get { 
-                               try {
-                                       return webHeaders ["Server"]; 
-                               } finally {
-                                       CheckDisposed ();
-                               }
+                               CheckDisposed ();
+                               return webHeaders ["Server"]; 
                        }
                }
                
@@ -218,49 +202,44 @@ namespace System.Net
                
                public override int GetHashCode ()
                {
-                       try {
-                               return base.GetHashCode ();
-                       } finally {
-                               CheckDisposed ();
-                       }
+                       CheckDisposed ();
+                       return base.GetHashCode ();
                }
                
                public string GetResponseHeader (string headerName)
                {
-                       try {
-                               return webHeaders [headerName];
-                       } finally {
-                               CheckDisposed ();
-                       }
+                       CheckDisposed ();
+                       string value = webHeaders [headerName];
+                       return (value != null) ? value : "";
                }
                
                public override Stream GetResponseStream ()
                {
-                       try {
-                               if (method.Equals ("HEAD")) // see par 4.3 & 9.4
-                                       return Stream.Null;  
-                               return responseStream;
-                       } finally {
-                               CheckDisposed ();
-                       }
+                       CheckDisposed ();
+                       if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
+                               return Stream.Null;  
+
+                       return stream;
                }
                
-               [MonoTODO]
                void ISerializable.GetObjectData (SerializationInfo serializationInfo,
                                                  StreamingContext streamingContext)
                {
-                       CheckDisposed ();
-                       throw new NotImplementedException ();
+                       SerializationInfo info = serializationInfo;
+
+                       info.AddValue ("uri", uri);
+                       info.AddValue ("contentLength", contentLength);
+                       info.AddValue ("contentType", contentType);
+                       info.AddValue ("method", method);
+                       info.AddValue ("statusDescription", statusDescription);
+                       info.AddValue ("cookieCollection", cookieCollection);
+                       info.AddValue ("version", version);
+                       info.AddValue ("statusCode", statusCode);
                }               
 
 
                // Cleaning up stuff
 
-               ~HttpWebResponse ()
-               {
-                       Dispose (false);
-               }               
-               
                public override void Close ()
                {
                        ((IDisposable) this).Dispose ();
@@ -285,15 +264,14 @@ namespace System.Net
                                cookieCollection = null;
                                method = null;
                                version = null;
-                               // statusCode = null;
                                statusDescription = null;
                        }
                        
                        // release unmanaged resources
-                       Stream stream = responseStream;
-                       responseStream = null;
-                       if (stream != null)
-                               stream.Close ();  // also closes webRequest                     
+                       Stream st = stream;
+                       stream = null;
+                       if (st != null)
+                               st.Close ();
                }
                
                private void CheckDisposed () 
@@ -302,44 +280,49 @@ namespace System.Net
                                throw new ObjectDisposedException (GetType ().FullName);
                }
 
-               private static string ReadHttpLine (Stream stream)
+               void FillCookies ()
                {
-                       Text.StringBuilder line = new Text.StringBuilder();
-                       byte last = (byte)'\n';
-                       bool read_last = false;
-                       byte[] buf = new byte[1]; // one at a time to not snarf too much
-                       
-                       while (stream.Read (buf, 0, buf.Length) != 0) {
-                               if (buf[0] == '\r') {
-                                       if ((last = (byte)stream.ReadByte ()) == '\n') // headers; not at EOS
-                                               break;
-                                       read_last = true;
-                               }
+                       if (webHeaders == null)
+                               return;
 
-                               line.Append (Convert.ToChar(buf[0]));
-                               if (read_last) {
-                                       line.Append (Convert.ToChar (last));
-                                       read_last = false;
-                               }
+                       string val = webHeaders ["Set-Cookie"];
+                       if (val != null && val.Trim () != "")
+                               SetCookie (val);
+
+                       val = webHeaders ["Set-Cookie2"];
+                       if (val != null && val.Trim () != "")
+                               SetCookie2 (val);
+               }
+               
+               static string [] SplitValue (string input)
+               {
+                       string [] result = new string [2];
+                       int eq = input.IndexOf ('=');
+                       if (eq == -1) {
+                               result [0] = "invalid";
+                       } else {
+                               result [0] = input.Substring (0, eq).Trim ().ToUpper ();
+                               result [1] = input.Substring (eq + 1);
                        }
                        
-                       return line.ToString();
+                       return result;
                }
-
-               private void SetCookie (string cookie_str)
+               
+               [MonoTODO ("Parse dates")]
+               void SetCookie (string cookie_str)
                {
                        string[] parts = null;
                        Collections.Queue options = null;
                        Cookie cookie = null;
 
                        options = new Collections.Queue (cookie_str.Split (';'));
-                       parts = ((string)options.Dequeue()).Split ('='); // NAME=VALUE must be first
+                       parts = SplitValue ((string) options.Dequeue()); // NAME=VALUE must be first
 
                        cookie = new Cookie (parts[0], parts[1]);
 
                        while (options.Count > 0) {
-                               parts = ((string)options.Dequeue()).Split ('=');
-                               switch (parts[0].ToUpper()) { // cookie options are case-insensitive
+                               parts = SplitValue ((string) options.Dequeue());
+                               switch (parts [0]) {
                                        case "COMMENT":
                                                if (cookie.Comment == null)
                                                        cookie.Comment = parts[1];
@@ -352,7 +335,7 @@ namespace System.Net
                                                cookie.Discard = true;
                                        break;
                                        case "DOMAIN":
-                                               if (cookie.Domain == null)
+                                               if (cookie.Domain == "")
                                                        cookie.Domain = parts[1];
                                        break;
                                        case "MAX-AGE": // RFC Style Set-Cookie2
@@ -360,11 +343,13 @@ namespace System.Net
                                                        cookie.Expires = cookie.TimeStamp.AddSeconds (Int32.Parse (parts[1]));
                                        break;
                                        case "EXPIRES": // Netscape Style Set-Cookie
-                                               if (cookie.Expires == DateTime.MinValue)
-                                                       cookie.Expires = DateTime.Parse (parts[1]);
+                                               if (cookie.Expires == DateTime.MinValue) {
+                                                       //FIXME: Does DateTime parse something like: "Sun, 17-Jan-2038 19:14:07 GMT"?
+                                                       //cookie.Expires = DateTime.ParseExact (parts[1]);
+                                                       cookie.Expires = DateTime.Now.AddDays (1);
+                                               }
                                        break;
                                        case "PATH":
-                                               if (cookie.Path == null)
                                                        cookie.Path = parts[1];
                                        break;
                                        case "PORT":
@@ -383,19 +368,19 @@ namespace System.Net
                        if (cookieCollection == null)
                                cookieCollection = new CookieCollection();
 
-                       if (cookie.Domain == null)
+                       if (cookie.Domain == "")
                                cookie.Domain = uri.Host;
 
                        cookieCollection.Add (cookie);
                }
 
-               private void SetCookie2 (string cookies_str)
+               void SetCookie2 (string cookies_str)
                {
-                       string[] cookies = cookies_str.Split (',');
+                       string [] cookies = cookies_str.Split (',');
        
                        foreach (string cookie_str in cookies)
                                SetCookie (cookie_str);
-
                }
        }       
 }
+