* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System / System.Net / HttpWebResponse.cs
index 918b97bc93020af9079d0c3a04e51f0a9a27e40b..d86e3fd5065d12ac0d474a5644b30f17304221e0 100644 (file)
-//\r
-// System.Net.HttpWebResponse\r
-//\r
-// Author:\r
-//   Lawrence Pit (loz@cable.a2000.nl)\r
-//\r
-\r
-using System;\r
-using System.IO;\r
-using System.Runtime.Serialization;\r
-\r
-namespace System.Net \r
-{\r
-       [Serializable]\r
-       public class HttpWebResponse : WebResponse, ISerializable, IDisposable\r
-       {\r
-               private Uri uri;\r
-               private WebHeaderCollection webHeaders;\r
-               private CookieCollection cookieCollection = null;\r
-               private string method = null;\r
-               private Version version = null;\r
-               private HttpStatusCode statusCode;\r
-               private string statusDescription = null;\r
-\r
-               private Stream responseStream;          \r
-               private bool disposed = false;\r
-               \r
-               // Constructors\r
-               \r
-               internal HttpWebResponse (Uri uri, string method, Stream responseStream) \r
-               { \r
-                       this.uri = uri;\r
-                       this.method = method;\r
-                       this.responseStream = responseStream;\r
-                       \r
-                       // TODO: parse headers from responseStream\r
-                       \r
-                       this.statusCode = HttpStatusCode.OK;\r
-               }\r
-               \r
-               protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)\r
-               {\r
-                       throw new NotSupportedException ();\r
-               }\r
-               \r
-               // Properties\r
-               \r
-               public string CharacterSet {\r
-                       // Content-Type   = "Content-Type" ":" media-type\r
-                       // media-type     = type "/" subtype *( ";" parameter )\r
-                       // parameter      = attribute "=" value\r
-                       // 3.7.1. default is ISO-8859-1\r
-                       get { \r
-                               try {\r
-                                       string contentType = ContentType;\r
-                                       if (contentType == null)\r
-                                               return "ISO-8859-1";\r
-                                       string val = contentType.ToLower ();                                    \r
-                                       int pos = val.IndexOf ("charset=");\r
-                                       if (pos == -1)\r
-                                               return "ISO-8859-1";\r
-                                       pos += 8;\r
-                                       int pos2 = val.IndexOf (';', pos);\r
-                                       return (pos2 == -1)\r
-                                            ? contentType.Substring (pos) \r
-                                            : contentType.Substring (pos, pos2 - pos);\r
-                               } finally {\r
-                                       CheckDisposed ();\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               public string ContentEncoding {\r
-                       get { \r
-                               try { return webHeaders ["Content-Encoding"]; }\r
-                               finally { CheckDisposed (); }\r
-                       }\r
-               }\r
-               \r
-               public override long ContentLength {            \r
-                       get { \r
-                               try {\r
-                                       return Int64.Parse (webHeaders ["Content-Length"]); \r
-                               } catch (Exception) {\r
-                                       return -1;\r
-                               } finally {\r
-                                       CheckDisposed ();\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               public override string ContentType {            \r
-                       get { \r
-                               try { return webHeaders ["Content-Type"]; }\r
-                               finally { CheckDisposed (); }\r
-                       }\r
-               }\r
-               \r
-               public CookieCollection Cookies {\r
-                       get { \r
-                               CheckDisposed ();\r
-                               \r
-                               // LAMESPEC: a simple test reveal this always \r
-                               // returns an empty collection. It is not filled \r
-                               // with the values from the Set-Cookie or \r
-                               // Set-Cookie2 response headers, which is a bit\r
-                               // of a shame..\r
-                               if (cookieCollection == null)\r
-                                       cookieCollection = new CookieCollection ();\r
-                               return cookieCollection;\r
-                       }\r
-                       set {\r
-                               CheckDisposed ();\r
-                               // ?? don't understand how you can set cookies on a response.\r
-                               throw new NotSupportedException ();\r
-                       }\r
-               }\r
-               \r
-               public override WebHeaderCollection Headers {           \r
-                       get { \r
-                               CheckDisposed ();\r
-                               return webHeaders; \r
-                       }\r
-               }\r
-               \r
-               public DateTime LastModified {\r
-                       get {\r
-                               CheckDisposed ();\r
-                               try {\r
-                                       string dtStr = webHeaders ["Last-Modified"];\r
-                                       // TODO: accept more than rfc1123 dates\r
-                                       DateTime dt = DateTime.ParseExact (dtStr, "r", null);\r
-                                       return dt;\r
-                               } catch (Exception) {\r
-                                       return DateTime.Now;    \r
-                               }\r
-                       }\r
-               }\r
-               \r
-               public string Method {\r
-                       get { \r
-                               CheckDisposed ();\r
-                               return method; \r
-                       }\r
-               }\r
-               \r
-               public Version ProtocolVersion {\r
-                       get { \r
-                               CheckDisposed ();\r
-                               return version; \r
-                       }\r
-               }\r
-               \r
-               public override Uri ResponseUri {               \r
-                       get { \r
-                               CheckDisposed ();\r
-                               return uri; \r
-                       }\r
-               }               \r
-               \r
-               public string Server {\r
-                       get { \r
-                               try {\r
-                                       return webHeaders ["Server"]; \r
-                               } finally {\r
-                                       CheckDisposed ();\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               public HttpStatusCode StatusCode {\r
-                       get { \r
-                               CheckDisposed ();\r
-                               return statusCode; \r
-                       }\r
-               }\r
-               \r
-               public string StatusDescription {\r
-                       get { \r
-                               CheckDisposed ();\r
-                               return statusDescription; \r
-                       }\r
-               }\r
-\r
-               // Methods\r
-               \r
-               public override int GetHashCode ()\r
-               {\r
-                       try {\r
-                               return base.GetHashCode ();\r
-                       } finally {\r
-                               CheckDisposed ();\r
-                       }\r
-               }\r
-               \r
-               public string GetResponseHeader (string headerName)\r
-               {\r
-                       try {\r
-                               return webHeaders [headerName];\r
-                       } finally {\r
-                               CheckDisposed ();\r
-                       }\r
-               }\r
-               \r
-               public override Stream GetResponseStream ()\r
-               {\r
-                       try {\r
-                               if (method.Equals ("HEAD")) // see par 4.3 & 9.4\r
-                                       return Stream.Null;  \r
-                               return responseStream;\r
-                       } finally {\r
-                               CheckDisposed ();\r
-                       }\r
-               }\r
-               \r
-               [MonoTODO]\r
-               void ISerializable.GetObjectData (SerializationInfo serializationInfo,\r
-                                                 StreamingContext streamingContext)\r
-               {\r
-                       CheckDisposed ();\r
-                       throw new NotImplementedException ();\r
-               }               \r
-\r
-\r
-               // Cleaning up stuff\r
-\r
-               ~HttpWebResponse ()\r
-               {\r
-                       Dispose (false);\r
-               }               \r
-               \r
-               public override void Close ()\r
-               {\r
-                       ((IDisposable) this).Dispose ();\r
-               }\r
-               \r
-               void IDisposable.Dispose ()\r
-               {\r
-                       Dispose (true);\r
-                       GC.SuppressFinalize (this);  \r
-               }\r
-               \r
-               protected virtual void Dispose (bool disposing) \r
-               {\r
-                       if (this.disposed)\r
-                               return;\r
-                       this.disposed = true;\r
-                       \r
-                       if (disposing) {\r
-                               // release managed resources\r
-                               uri = null;\r
-                               webHeaders = null;\r
-                               cookieCollection = null;\r
-                               method = null;\r
-                               version = null;\r
-                               // statusCode = null;\r
-                               statusDescription = null;\r
-                       }\r
-                       \r
-                       // release unmanaged resources\r
-                       Stream stream = responseStream;\r
-                       responseStream = null;\r
-                       if (stream != null)\r
-                               stream.Close ();  // also closes webRequest                     \r
-               }\r
-               \r
-               private void CheckDisposed () \r
-               {\r
-                       if (disposed)\r
-                               throw new ObjectDisposedException (GetType ().FullName);\r
-               }\r
-       }       \r
-}
\ No newline at end of file
+//
+// System.Net.HttpWebResponse
+//
+// 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)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Net.Sockets;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace System.Net 
+{
+       [Serializable]
+       public class HttpWebResponse : WebResponse, ISerializable, IDisposable
+       {
+               Uri uri;
+               WebHeaderCollection webHeaders;
+               CookieCollection cookieCollection;
+               string method;
+               Version version;
+               HttpStatusCode statusCode;
+               string statusDescription;
+               long contentLength = -1;
+               string contentType;
+               CookieContainer cookieContainer;
+
+               bool disposed = false;
+               Stream stream;
+               
+               // Constructors
+               
+               internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container)
+               {
+                       this.uri = uri;
+                       this.method = method;
+                       webHeaders = data.Headers;
+                       version = data.Version;
+                       statusCode = (HttpStatusCode) data.StatusCode;
+                       statusDescription = data.StatusDescription;
+                       stream = data.stream;
+                       if (container != null) {
+                               this.cookieContainer = container;
+                               FillCookies ();
+                       }
+               }
+
+               protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
+               {
+                       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
+               
+               public string CharacterSet {
+                       // Content-Type   = "Content-Type" ":" media-type
+                       // media-type     = type "/" subtype *( ";" parameter )
+                       // parameter      = attribute "=" value
+                       // 3.7.1. default is ISO-8859-1
+                       get { 
+                               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 { 
+                               CheckDisposed ();
+                               string h = webHeaders ["Content-Encoding"];
+                               return h != null ? h : "";
+                       }
+               }
+               
+               public override long ContentLength {            
+                       get {
+                               CheckDisposed ();
+                               if (contentLength != -1)
+                                       return contentLength;
+
+                               try {
+                                       contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]); 
+                               } catch (Exception) {
+                                       return -1;
+                               }
+
+                               return contentLength;
+                       }
+               }
+               
+               public override string ContentType {            
+                       get {
+                               CheckDisposed ();
+                               if (contentType == null)
+                                       contentType = webHeaders ["Content-Type"];
+
+                               return contentType;
+                       }
+               }
+               
+               public CookieCollection Cookies {
+                       get { 
+                               CheckDisposed ();
+                               
+                               if (cookieCollection == null)
+                                       cookieCollection = new CookieCollection ();
+                               return cookieCollection;
+                       }
+                       set {
+                               CheckDisposed ();
+                               cookieCollection = value;
+                       }
+               }
+               
+               public override WebHeaderCollection Headers {           
+                       get { 
+                               CheckDisposed ();
+                               return webHeaders; 
+                       }
+               }
+               
+               public DateTime LastModified {
+                       get {
+                               CheckDisposed ();
+                               try {
+                                       string dtStr = webHeaders ["Last-Modified"];
+                                       return MonoHttpDate.Parse (dtStr);
+                               } catch (Exception) {
+                                       return DateTime.Now;    
+                               }
+                       }
+               }
+               
+               public string Method {
+                       get { 
+                               CheckDisposed ();
+                               return method; 
+                       }
+               }
+               
+               public Version ProtocolVersion {
+                       get { 
+                               CheckDisposed ();
+                               return version; 
+                       }
+               }
+               
+               public override Uri ResponseUri {               
+                       get { 
+                               CheckDisposed ();
+                               return uri; 
+                       }
+               }               
+               
+               public string Server {
+                       get { 
+                               CheckDisposed ();
+                               return webHeaders ["Server"]; 
+                       }
+               }
+               
+               public HttpStatusCode StatusCode {
+                       get { 
+                               CheckDisposed ();
+                               return statusCode; 
+                       }
+               }
+               
+               public string StatusDescription {
+                       get { 
+                               CheckDisposed ();
+                               return statusDescription; 
+                       }
+               }
+
+               // Methods
+#if !NET_2_0
+               public override int GetHashCode ()
+               {
+                       CheckDisposed ();
+                       return base.GetHashCode ();
+               }
+#endif
+               
+               public string GetResponseHeader (string headerName)
+               {
+                       CheckDisposed ();
+                       string value = webHeaders [headerName];
+                       return (value != null) ? value : "";
+               }
+
+               internal void ReadAll ()
+               {
+                       WebConnectionStream wce = stream as WebConnectionStream;
+                       if (wce == null)
+                               return;
+                               
+                       try {
+                               wce.ReadAll ();
+                       } catch {}
+               }
+
+               public override Stream GetResponseStream ()
+               {
+                       CheckDisposed ();
+                       if (stream == null)
+                               return Stream.Null;  
+                       if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
+                               return Stream.Null;  
+
+                       return stream;
+               }
+               
+               void ISerializable.GetObjectData (SerializationInfo serializationInfo,
+                                                 StreamingContext streamingContext)
+               {
+                       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
+
+               public override void Close ()
+               {
+                       ((IDisposable) this).Dispose ();
+               }
+               
+               void IDisposable.Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);  
+               }
+
+#if !NET_2_0
+               protected virtual
+#endif
+               void Dispose (bool disposing) 
+               {
+                       if (this.disposed)
+                               return;
+                       this.disposed = true;
+                       
+                       if (disposing) {
+                               // release managed resources
+                               uri = null;
+                               webHeaders = null;
+                               cookieCollection = null;
+                               method = null;
+                               version = null;
+                               statusDescription = null;
+                       }
+                       
+                       // release unmanaged resources
+                       Stream st = stream;
+                       stream = null;
+                       if (st != null)
+                               st.Close ();
+               }
+               
+               private void CheckDisposed () 
+               {
+                       if (disposed)
+                               throw new ObjectDisposedException (GetType ().FullName);
+               }
+
+               void FillCookies ()
+               {
+                       if (webHeaders == null)
+                               return;
+
+                       string [] values = webHeaders.GetValues ("Set-Cookie");
+                       if (values != null) {
+                               foreach (string va in values)
+                                       SetCookie (va);
+                       }
+
+                       values = webHeaders.GetValues ("Set-Cookie2");
+                       if (values != null) {
+                               foreach (string va in values)
+                                       SetCookie2 (va);
+                       }
+               }
+
+               void SetCookie (string header)
+               {
+                       string name, val;
+                       Cookie cookie = null;
+                       CookieParser parser = new CookieParser (header);
+
+                       while (parser.GetNextNameValue (out name, out val)) {
+                               if ((name == null || name == "") && cookie == null)
+                                       continue;
+
+                               if (cookie == null) {
+                                       cookie = new Cookie (name, val);
+                                       continue;
+                               }
+
+                               name = name.ToUpper ();
+                               switch (name) {
+                               case "COMMENT":
+                                       if (cookie.Comment == null)
+                                               cookie.Comment = val;
+                                       break;
+                               case "COMMENTURL":
+                                       if (cookie.CommentUri == null)
+                                               cookie.CommentUri = new Uri (val);
+                                       break;
+                               case "DISCARD":
+                                       cookie.Discard = true;
+                                       break;
+                               case "DOMAIN":
+                                       if (cookie.Domain == "")
+                                               cookie.Domain = val;
+                                       break;
+                               case "MAX-AGE": // RFC Style Set-Cookie2
+                                       if (cookie.Expires == DateTime.MinValue) {
+                                               try {
+                                               cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
+                                               } catch {}
+                                       }
+                                       break;
+                               case "EXPIRES": // Netscape Style Set-Cookie
+                                       if (cookie.Expires != DateTime.MinValue)
+                                               break;
+                                       try {
+                                               cookie.Expires = DateTime.ParseExact (val, "r", CultureInfo.InvariantCulture);
+                                       } catch {
+                                               try { 
+                                               cookie.Expires = DateTime.ParseExact (val,
+                                                               "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
+                                                               CultureInfo.InvariantCulture);
+                                               } catch {
+                                                       cookie.Expires = DateTime.Now.AddDays (1);
+                                               }
+                                       }
+                                       break;
+                               case "PATH":
+                                       cookie.Path = val;
+                                       break;
+                               case "PORT":
+                                       if (cookie.Port == null)
+                                               cookie.Port = val;
+                                       break;
+                               case "SECURE":
+                                       cookie.Secure = true;
+                                       break;
+                               case "VERSION":
+                                       try {
+                                               cookie.Version = (int) UInt32.Parse (val);
+                                       } catch {}
+                                       break;
+                               }
+                       }
+
+                       if (cookieCollection == null)
+                               cookieCollection = new CookieCollection ();
+
+                       if (cookie.Domain == "")
+                               cookie.Domain = uri.Host;
+
+                       cookieCollection.Add (cookie);
+                       if (cookieContainer != null)
+                               cookieContainer.Add (uri, cookie);
+               }
+
+               void SetCookie2 (string cookies_str)
+               {
+                       string [] cookies = cookies_str.Split (',');
+       
+                       foreach (string cookie_str in cookies)
+                               SetCookie (cookie_str);
+               }
+       }       
+
+       class CookieParser {
+               string header;
+               int pos;
+               int length;
+
+               public CookieParser (string header) : this (header, 0)
+               {
+               }
+
+               public CookieParser (string header, int position)
+               {
+                       this.header = header;
+                       this.pos = position;
+                       this.length = header.Length;
+               }
+
+               public bool GetNextNameValue (out string name, out string val)
+               {
+                       name = null;
+                       val = null;
+
+                       if (pos >= length)
+                               return false;
+
+                       name = GetCookieName ();
+                       if (pos < header.Length && header [pos] == '=') {
+                               pos++;
+                               val = GetCookieValue ();
+                       }
+
+                       if (pos < length && header [pos] == ';')
+                               pos++;
+
+                       return true;
+               }
+
+               string GetCookieName ()
+               {
+                       int k = pos;
+                       while (k < length && Char.IsWhiteSpace (header [k]))
+                               k++;
+
+                       int begin = k;
+                       while (k < length && header [k] != ';' &&  header [k] != '=')
+                               k++;
+
+                       pos = k;
+                       return header.Substring (begin, k - begin).Trim ();
+               }
+
+               string GetCookieValue ()
+               {
+                       if (pos >= length)
+                               return null;
+
+                       int k = pos;
+                       while (k < length && Char.IsWhiteSpace (header [k]))
+                               k++;
+
+                       int begin;
+                       if (header [k] == '"'){
+                               int j;
+                               begin = ++k;
+
+                               while (k < length && header [k] != '"')
+                                       k++;
+
+                               for (j = k; j < length && header [j] != ';'; j++)
+                                       ;
+                               pos = j;
+                       } else {
+                               begin = k;
+                               while (k < length && header [k] != ';')
+                                       k++;
+                               pos = k;
+                       }
+                               
+                       return header.Substring (begin, k - begin).Trim ();
+               }
+       }
+}
+