[Mono-list] more correct and more complete HttpWebResponse/Request patch

Shahms E. King shahms@shahms.com
02 Sep 2002 18:18:01 -0700


--=-T5PD3OCKPHzS+WWyTKwW
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

This patch adds Cookie handling and more correct header handling
(doesn't assume that the next character after a \r is a \n being the
most important).

could someone please look over this and 1) tell me what I did wrong or
2) apply it?
Thanks

--Shahms

--=-T5PD3OCKPHzS+WWyTKwW
Content-Disposition: attachment; filename=mcs-HttpWeb.patch
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; name=mcs-HttpWeb.patch; charset=ISO-8859-1

? .HttpWebResponse.cs.swp
Index: HttpWebRequest.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/mcs/class/System/System.Net/HttpWebRequest.cs,v
retrieving revision 1.5
diff -u -r1.5 HttpWebRequest.cs
--- HttpWebRequest.cs	28 May 2002 18:36:53 -0000	1.5
+++ HttpWebRequest.cs	3 Sep 2002 01:17:56 -0000
@@ -56,9 +56,9 @@
 			this.requestUri =3D uri;
 			this.actualUri =3D uri;
 			this.webHeaders =3D new WebHeaderCollection (true);
-			// this.webHeaders.SetInternal ("Host", uri.Authority);
-			// this.webHeaders.SetInternal ("Date", DateTime.Now.ToUniversalTime ()=
.ToString ("r", null));
-			// this.webHeaders.SetInternal ("Expect", "100-continue");
+			this.webHeaders.SetInternal ("Host", uri.Authority);
+			this.webHeaders.SetInternal ("Date", DateTime.Now.ToUniversalTime ().To=
String ("r", null));
+			this.webHeaders.SetInternal ("Expect", "100-continue");
 			this.method =3D "GET";
 			this.version =3D HttpVersion.Version11;
 			this.proxy =3D GlobalProxySelection.Select;
@@ -451,7 +451,7 @@
 	=09
 		internal Stream GetRequestStreamInternal ()
 		{
-			this.requestStream =3D null;   // TODO: new HttpWebStream (this);
+			this.requestStream =3D new HttpWebStream (this);
 			return this.requestStream;
 		}
 	=09
@@ -512,7 +512,7 @@
 			if (requestEndEvent !=3D null) {
 				requestEndEvent.WaitOne ();
 			}
-			Stream responseStream =3D null; // TODO: new HttpWebStream (this);
+			Stream responseStream =3D new HttpWebStream (this);
  			this.webResponse =3D new HttpWebResponse (this.actualUri, method, resp=
onseStream);
  			return (WebResponse) this.webResponse;
 		}
@@ -557,16 +557,41 @@
 		// Private Classes
 	=09
 		// to catch the Close called on the NetworkStream
-		/*
-		internal class HttpWebStream : Stream
+		internal class HttpWebStream : NetworkStream
 		{
 			HttpWebRequest webRequest;
 		=09
-			internal HttpWebStream (HttpWebRequest webRequest)
-				: base (webRequest.RequestUri)
+			internal HttpWebStream (HttpWebRequest webRequest)=20
+				: base (HttpWebStream.CreateSocket (webRequest), true)
 			{
+				StreamWriter webWriter =3D null;
+				string headerValue =3D null;
+
+
+				webWriter =3D new StreamWriter (this);
+=09
+				webWriter.Write (webRequest.Method + " " + webRequest.actualUri.Absolu=
tePath + " HTTP/1.1\r\n");
+
+				foreach (string header in webRequest.webHeaders) {
+					headerValue =3D header + ": " + webRequest.webHeaders[header] + "\r\n=
";
+					webWriter.Write (headerValue);
+				}
+				webWriter.Write ("\r\n");
+				webWriter.Flush();
+
 				this.webRequest =3D webRequest;
 			}
+	=09
+			private static Socket CreateSocket (HttpWebRequest webRequest)
+			{
+				IPAddress hostAddr =3D Dns.Resolve (webRequest.actualUri.Host).Address=
List[0];
+				IPEndPoint endPoint =3D new IPEndPoint (hostAddr, webRequest.actualUri=
.Port);
+				Socket socket =3D new Socket(AddressFamily.InterNetwork, SocketType.St=
ream,
+					ProtocolType.Tcp);
+
+				socket.Connect (endPoint);
+				return socket;
+			}
 					   =09
 			public override void Close()=20
 			{
@@ -574,6 +599,5 @@
 				webRequest.Close ();
 			}
 		}	=09
-		*/
 	}
-}
\ No newline at end of file
+}
Index: HttpWebResponse.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /mono/mcs/class/System/System.Net/HttpWebResponse.cs,v
retrieving revision 1.4
diff -u -r1.4 HttpWebResponse.cs
--- HttpWebResponse.cs	28 May 2002 18:36:53 -0000	1.4
+++ HttpWebResponse.cs	3 Sep 2002 01:17:56 -0000
@@ -29,13 +29,54 @@
 	=09
 		internal HttpWebResponse (Uri uri, string method, Stream responseStream)=
=20
 		{=20
+			Text.StringBuilder value =3D null;
+			string last =3D null;
+			string line =3D null;
+			string[] protocol, header;
+
 			this.uri =3D uri;
 			this.method =3D method;
 			this.responseStream =3D responseStream;
+			this.webHeaders =3D new WebHeaderCollection();
+
+			line =3D ReadHttpLine(responseStream);
+			protocol =3D line.Split (' ');
+		=09
+			switch (protocol[0]) {
+				case "HTTP/1.0":
+					this.version =3D HttpVersion.Version10;
+					break;
+				case "HTTP/1.1":
+					this.version =3D HttpVersion.Version11;
+					break;
+				default:
+					throw new WebException ("Unrecognized HTTP Version");
+			}
 		=09
-			// TODO: parse headers from responseStream
+			this.statusCode =3D Int32.Parse (protocol[1]);
+
+			while ((line =3D ReadHttpLine(responseStream)).Length !=3D 0) {
+				if (!Char.IsWhiteSpace (line[0])) { // new header
+					header =3D line.Split (new char[] {':'}, 2);
+					if (header.Length !=3D 2)
+						throw new WebException ("Bad HTTP Header");
+					if (last !=3D 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] =3D value.ToString();
+					}
+					last =3D header[0];
+					value =3D new Text.StringBuilder (header[1].Trim());
+				}
+				else
+					value.Append (header[0].Trim());
+			}
 		=09
-			this.statusCode =3D HttpStatusCode.OK;
+			this.webHeaders[last] =3D value.ToString(); // otherwise we miss the la=
st header
+			// TODO: parse cookies from headers
 		}
 	=09
 		protected HttpWebResponse (SerializationInfo serializationInfo, Streamin=
gContext streamingContext)
@@ -100,11 +141,6 @@
 			get {=20
 				CheckDisposed ();
 			=09
-				// LAMESPEC: a simple test reveal this always=20
-				// returns an empty collection. It is not filled=20
-				// with the values from the Set-Cookie or=20
-				// Set-Cookie2 response headers, which is a bit
-				// of a shame..
 				if (cookieCollection =3D=3D null)
 					cookieCollection =3D new CookieCollection ();
 				return cookieCollection;
@@ -267,5 +303,98 @@
 			if (disposed)
 				throw new ObjectDisposedException (GetType ().FullName);
 		}
+
+		private static string ReadHttpLine (Stream stream)
+		{
+			Text.StringBuilder line =3D new Text.StringBuilder();
+			byte last =3D (byte)'\n';
+			bool read_last =3D false;
+			byte[] buf =3D new byte[1]; // one at a time to not snarf too much
+		=09
+			while (stream.Read (buf, 0, buf.Length) !=3D 0) {
+				if (buf[0] =3D=3D '\r') {
+					if ((last =3D (byte)stream.ReadByte ()) =3D=3D '\n') // headers; not =
at EOS
+						break;
+					read_last =3D true;
+				}
+
+				line.Append (Convert.ToChar(buf[0]));
+				if (read_last) {
+					line.Append (Convert.ToChar (last));
+					read_last =3D false;
+				}
+			}
+		=09
+			return line.ToString();
+		}
+
+		private void SetCookie (string cookie_str)
+		{
+			string[] parts =3D null;
+			Collections.Queue options =3D null;
+			Cookie cookie =3D null;
+
+			options =3D new Collections.Queue (cookie_str.Split (';'));
+			parts =3D ((string)options.Dequeue()).Split ('=3D'); // NAME=3DVALUE mu=
st be first
+
+			cookie =3D new Cookie (parts[0], parts[1]);
+
+			while (options.Count > 0) {
+				parts =3D ((string)options.Dequeue()).Split ('=3D');
+				switch (parts[0].ToUpper()) { // cookie options are case-insensitive
+					case "COMMENT":
+						if (cookie.Comment =3D=3D String.Empty)
+							cookie.Comment =3D parts[1];
+					break;
+					case "COMMENTURL":
+						if (cookie.CommentUri =3D=3D null)
+							cookie.CommentUri =3D new Uri(parts[1]);
+					break;
+					case "DISCARD":
+						cookie.Discard =3D true;
+					break;
+					case "DOMAIN":
+						if (cookie.Domain =3D=3D String.Empty)
+							cookie.Domain =3D parts[1];
+					break;
+					case "MAX-AGE": // RFC Style Set-Cookie2
+						if (cookie.Expires =3D=3D DateTime.MinValue)
+							cookie.Expires =3D cookie.TimeStamp.AddSeconds (Int32.Parse (parts[=
1]));
+					break;
+					case "EXPIRES": // Netscape Style Set-Cookie
+						if (cookie.Expires =3D=3D DateTime.MinValue)
+							cookie.Expires =3D DateTime.Parse (parts[1]);
+					break;
+					case "PATH":
+						if (cookie.Path =3D=3D String.Empty)
+							cookie.Path =3D parts[1];
+					break;
+					case "PORT":
+						if (cookie.Port =3D=3D String.Empty)
+							cookie.Port =3D parts[1];
+					break;
+					case "SECURE":
+						cookie.Secure =3D true;
+					break;
+					case "VERSION":
+						cookie.Version =3D Int32.Parse (parts[1]);
+					break;
+				} // switch
+			} // while
+
+			if (cookieCollection =3D=3D null)
+				cookieCollection =3D new CookieCollection();
+
+			cookieCollection.Add (cookie);
+		}
+
+		private void SetCookie2 (string cookies_str)
+		{
+			string[] cookies =3D cookies_str.Split (',');
+=09
+			foreach (string cookie_str in cookies)
+				SetCookie (cookie_str);
+
+		}
 	}=09
-}
\ No newline at end of file
+}

--=-T5PD3OCKPHzS+WWyTKwW


--=-T5PD3OCKPHzS+WWyTKwW--