Просмотр исходного кода

2003-06-08 Gonzalo Paniagua Javier <[email protected]>

	* HttpWebRequest.cs: don't send "Expect: 100-continue" is the server
	is known not to respond to that.

	* ServicePoint.cs: added SendContinue property.

	* WebConnection.cs: only wait 2 seconds for a continue reply. If it
	timeouts, set SendContinue to false and proceeed sending data.

	* WebConnectionStream.cs: if 100-continue is not received and instead we
	get a 417 or anything else, don't send the data.

	* WebClient.cs: implemented all missing properties and methods except
	UploadFile.

svn path=/trunk/mcs/; revision=15180
Gonzalo Paniagua Javier 22 лет назад
Родитель
Сommit
ddf5ef623a

+ 16 - 0
mcs/class/System/System.Net/ChangeLog

@@ -1,3 +1,19 @@
+2003-06-08  Gonzalo Paniagua Javier <[email protected]>
+
+	* HttpWebRequest.cs: don't send "Expect: 100-continue" is the server
+	is known not to respond to that.
+
+	* ServicePoint.cs: added SendContinue property.
+
+	* WebConnection.cs: only wait 2 seconds for a continue reply. If it
+	timeouts, set SendContinue to false and proceeed sending data.
+
+	* WebConnectionStream.cs: if 100-continue is not received and instead we
+	get a 417 or anything else, don't send the data.
+
+	* WebClient.cs: implemented all missing properties and methods except
+	UploadFile.
+
 2003-06-06  Gonzalo Paniagua Javier <[email protected]>
 
 	* FileWebRequestCreator.cs: splitted from WebRequest.

+ 1 - 1
mcs/class/System/System.Net/HttpWebRequest.cs

@@ -691,7 +691,7 @@ namespace System.Net
 				webHeaders.SetInternal ("Transfer-Encoding", "chunked");
 			}
 
-			if (continue100) // RFC2616 8.2.3
+			if (continue100 && servicePoint.SendContinue) // RFC2616 8.2.3
 				webHeaders.SetInternal ("Expect" , "100-continue");
 
 			if (keepAlive && version == HttpVersion.Version10)

+ 5 - 0
mcs/class/System/System.Net/ServicePoint.cs

@@ -30,6 +30,7 @@ namespace System.Net
 		IPHostEntry host;
 		bool usesProxy;
 		Hashtable groups;
+		bool sendContinue = true;
 		
 		// Constructors
 
@@ -95,6 +96,10 @@ namespace System.Net
 			get { return HttpVersion.Version11.Equals (protocolVersion); }
 		}
 		
+		internal bool SendContinue {
+			get { return sendContinue; }
+			set { sendContinue = value; }
+		}
 		// Methods
 		
 		public override int GetHashCode() 

+ 285 - 63
mcs/class/System/System.Net/WebClient.cs

@@ -1,27 +1,46 @@
 //
 // System.Net.WebClient
 //
-// Author:
-//   Lawrence Pit ([email protected])
+// Authors:
+// 	Lawrence Pit ([email protected])
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// (c) 2003 Ximian, Inc. (http://www.ximian.com)
 //
 
 using System;
-using System.Collections;
 using System.Collections.Specialized;
 using System.ComponentModel;
 using System.IO;
 using System.Runtime.InteropServices;
 using System.Runtime.Serialization;
+using System.Text;
 
 namespace System.Net 
 {
 	[ComVisible(true)]
 	public sealed class WebClient : Component
 	{
-
+		static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
+		static byte [] hexBytes;
 		ICredentials credentials;
+		WebHeaderCollection headers;
+		WebHeaderCollection responseHeaders;
+		Uri baseAddress;
+		string baseString;
+		NameValueCollection queryString;
 	
 		// Constructors
+		static WebClient ()
+		{
+			hexBytes = new byte [16];
+			int index = 0;
+			for (int i = '0'; i < '9'; i++, index++)
+				hexBytes [index] = (byte) i;
+
+			for (int i = 'A'; i < 'F'; i++, index++)
+				hexBytes [index] = (byte) i;
+		}
 		
 		public WebClient ()
 		{
@@ -29,10 +48,24 @@ namespace System.Net
 		
 		// Properties
 		
-		[MonoTODO]
 		public string BaseAddress {
-			get { throw new NotImplementedException (); }
-			set { throw new NotImplementedException (); }
+			get {
+				if (baseString == null) {
+					if (baseAddress == null)
+						return "";
+				}
+
+				baseString = baseAddress.ToString ();
+				return baseString;
+			}
+			
+			set {
+				if (value == null || value == "") {
+					baseAddress = null;
+				} else {
+					baseAddress = new Uri (value);
+				}
+			}
 		}
 		
 		public ICredentials Credentials {
@@ -40,77 +73,63 @@ namespace System.Net
 			set { credentials = value; }
 		}
 		
-		[MonoTODO]
 		public WebHeaderCollection Headers {
-			get { throw new NotImplementedException (); }
-			set { throw new NotImplementedException (); }
+			get {
+				if (headers == null)
+					headers = new WebHeaderCollection ();
+
+				return headers;
+			}
+			set { headers = value; }
 		}
 		
-		[MonoTODO]
 		public NameValueCollection QueryString {
-			get { throw new NotImplementedException (); }
-			set { throw new NotImplementedException (); }
+			get {
+				if (queryString == null)
+					queryString = new NameValueCollection ();
+
+				return queryString;
+			}
+			set { queryString = value; }
 		}
 		
-		[MonoTODO]
 		public WebHeaderCollection ResponseHeaders {
-			get { throw new NotImplementedException (); }
+			get { return responseHeaders; }
 		}
 
 		// Methods
 		
 		public byte [] DownloadData (string address)
 		{
-			const int readSize = 8192;
-			Stream networkStream = OpenRead (address);
-			ArrayList chunks = new ArrayList ();
-			byte[] buf = new byte [readSize];
-			int size = 0;
-			int total_size = 0;
-
-			try {
-				do {
-					size = networkStream.Read (buf, 0, readSize);
-					byte [] copy = new byte [size];
-					Array.Copy (buf, 0, copy,0, size);
-					chunks.Add (copy);
-					total_size += size;
-				} while (size != 0);
-			} finally {
-				networkStream.Close ();
-			}
-			
-			byte [] result = new byte [total_size];
-			int target = 0;
-			foreach (byte [] block in chunks){
-				int len = block.Length;
-				Array.Copy (block, 0, result, target, len);
-				target += len;
-			}
-			return result;
+			WebRequest request = SetupRequest (address);
+			WebResponse response = request.GetResponse ();
+			Stream st = ProcessResponse (response);
+			return ReadAll (st, (int) response.ContentLength);
 		}
 		
 		public void DownloadFile (string address, string fileName)
 		{
-			byte[] buf = DownloadData (address);
-			using (FileStream f = new FileStream (fileName, FileMode.CreateNew)){
-				f.Write (buf, 0, buf.Length);
-			}
+			WebRequest request = SetupRequest (address);
+			WebResponse response = request.GetResponse ();
+			Stream st = ProcessResponse (response);
+
+			int cLength = (int) response.ContentLength;
+			int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
+			byte [] buffer = new byte [length];
+			FileStream f = new FileStream (fileName, FileMode.CreateNew);
+
+			int nread = 0;
+			while ((nread = st.Read (buffer, 0, length)) != 0)
+				f.Write (buffer, 0, nread);
+
+			f.Close ();
 		}
 		
 		public Stream OpenRead (string address)
 		{
-			Uri uri = new Uri (address);
-			WebRequest request = null;
-
-			if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
-				request = new HttpWebRequest (uri);
-			else if(uri.Scheme == Uri.UriSchemeFile)
-				request = new FileWebRequest (uri);
-			else
-				throw new NotImplementedException ();
-
-			return request.GetResponse ().GetResponseStream ();
+			WebRequest request = SetupRequest (address);
+			WebResponse response = request.GetResponse ();
+			return ProcessResponse (response);
 		}
 		
 		public Stream OpenWrite (string address)
@@ -118,10 +137,10 @@ namespace System.Net
 			return OpenWrite (address, "POST");
 		}
 		
-		[MonoTODO]
 		public Stream OpenWrite (string address, string method)
 		{
-			throw new NotImplementedException ();
+			WebRequest request = SetupRequest (address, method);
+			return request.GetRequestStream ();
 		}
 				
 		public byte [] UploadData (string address, byte [] data)
@@ -129,10 +148,20 @@ namespace System.Net
 			return UploadData (address, "POST", data);
 		}
 		
-		[MonoTODO]
 		public byte [] UploadData (string address, string method, byte [] data)
 		{
-			throw new NotImplementedException ();
+			if (data == null)
+				throw new ArgumentNullException ("data");
+
+			int contentLength = data.Length;
+			WebRequest request = SetupRequest (address, method, contentLength);
+			using (Stream stream = request.GetRequestStream ()) {
+				stream.Write (data, 0, contentLength);
+			}
+
+			WebResponse response = request.GetResponse ();
+			Stream st = ProcessResponse (response);
+			return ReadAll (st, (int) response.ContentLength);
 		}
 		
 		public byte [] UploadFile (string address, string fileName)
@@ -151,10 +180,203 @@ namespace System.Net
 			return UploadValues (address, "POST", data);
 		}
 		
-		[MonoTODO]
 		public byte[] UploadValues (string address, string method, NameValueCollection data)
 		{
-			throw new NotImplementedException ();
+			if (data == null)
+				throw new ArgumentNullException ("data"); // MS throws a nullref
+
+			string cType = Headers ["Content-Type"];
+			if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
+				throw new WebException ("Content-Type header cannot be changed from its default " +
+							"value for this request.");
+
+			Headers ["Content-Type"] = urlEncodedCType;
+			WebRequest request = SetupRequest (address, method);
+			Stream rqStream = request.GetRequestStream ();
+			MemoryStream tmpStream = new MemoryStream ();
+			foreach (string key in data) {
+				byte [] bytes = Encoding.ASCII.GetBytes (key);
+				UrlEncodeAndWrite (tmpStream, bytes);
+				tmpStream.WriteByte ((byte) '=');
+				bytes = Encoding.ASCII.GetBytes (data [key]);
+				UrlEncodeAndWrite (tmpStream, bytes);
+				tmpStream.WriteByte ((byte) '&');
+			}
+
+			int length = (int) tmpStream.Length;
+			if (length > 0)
+				tmpStream.SetLength (--length); // remove trailing '&'
+
+			tmpStream.WriteByte ((byte) '\r');
+			tmpStream.WriteByte ((byte) '\n');
+
+			byte [] bytes = tmpStream.GetBuffer ();
+			rqStream.Write (bytes, 0, length + 2);
+			rqStream.Close ();
+			tmpStream.Close ();
+
+			WebResponse response = request.GetResponse ();
+			Stream st = ProcessResponse (response);
+			return ReadAll (st, (int) response.ContentLength);
+		}
+
+		Uri MakeUri (string path)
+		{
+			string query = null;
+			if (queryString != null && queryString.Count != 0) {
+				// This is not the same as UploadValues, because these 'keys' are not
+				// urlencoded here.
+				StringBuilder sb = new StringBuilder ();
+				sb.Append ('?');
+				foreach (string key in queryString)
+					sb.AppendFormat ("{0}={1}&", key, queryString [key]);
+
+				if (sb.Length != 0) {
+					sb.Length--; // remove trailing '&'
+					query = sb.ToString ();
+				}
+			}
+			
+
+			if (baseAddress == null && query == null)
+				return new Uri (path);
+
+			if (baseAddress == null)
+				return new Uri (path + query);
+
+			if (query == null)
+				return new Uri (baseAddress, path);
+
+			return new Uri (baseAddress, path + query);
+		}
+		
+		WebRequest SetupRequest (string address)
+		{
+			Uri uri = MakeUri (address);
+			WebRequest request = WebRequest.Create (uri);
+			request.Credentials = credentials;
+
+			// Special headers. These are properties of HttpWebRequest.
+			// What do we do with other requests differnt from HttpWebRequest?
+			if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
+				HttpWebRequest req = (HttpWebRequest) request;
+				string val = headers ["Expect"];
+				if (val != null && val != "") {
+					req.Expect = val;
+					headers.RemoveInternal ("Expect");
+				}
+
+				val = headers ["Accept"];
+				if (val != null && val != "") {
+					req.Accept = val;
+					headers.RemoveInternal ("Accept");
+				}
+
+				val = headers ["Content-Type"];
+				if (val != null && val != "") {
+					req.ContentType = val;
+					headers.RemoveInternal ("Content-Type");
+				}
+
+				val = headers ["Connection"];
+				if (val != null && val != "") {
+					req.Connection = val;
+					headers.RemoveInternal ("Connection");
+				}
+
+				val = headers ["User-Agent"];
+				if (val != null && val != "") {
+					req.UserAgent = val;
+					headers.RemoveInternal ("User-Agent");
+				}
+
+				val = headers ["Referer"];
+				if (val != null && val != "") {
+					req.Referer = val;
+					headers.RemoveInternal ("Referer");
+				}
+
+				request.Headers = headers;
+			}
+
+			responseHeaders = null;
+			return request;
+		}
+
+		WebRequest SetupRequest (string address, string method)
+		{
+			WebRequest request = SetupRequest (address);
+			request.Method = method;
+			return request;
+		}
+
+		WebRequest SetupRequest (string address, string method, int contentLength)
+		{
+			WebRequest request = SetupRequest (address, method);
+			request.ContentLength = contentLength;
+			return request;
+		}
+
+		Stream ProcessResponse (WebResponse response)
+		{
+			responseHeaders = response.Headers;
+			return response.GetResponseStream ();
+		}
+
+		static byte [] ReadAll (Stream stream, int length)
+		{
+			MemoryStream ms = null;
+			
+			bool nolength = (length == -1);
+			int size = ((nolength) ? 8192 : length);
+			if (nolength)
+				ms = new MemoryStream ();
+
+			int nread = 0;
+			int offset = 0;
+			byte [] buffer = new byte [size];
+			while ((nread = stream.Read (buffer, offset, size)) != 0) {
+				if (nolength) {
+					ms.Write (buffer, 0, nread);
+				} else {
+					offset += nread;
+					size -= nread;
+				}
+			}
+
+			if (nolength)
+				return ms.ToArray ();
+
+			return buffer;
+		}
+
+		static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
+		{
+			if (bytes == null)
+				return;
+
+			int len = bytes.Length;
+			if (len == 0)
+				return;
+
+			for (int i = 0; i < len; i++) {
+				char c = (char) bytes [i];
+				if (c == ' ')
+					stream.WriteByte ((byte) '+');
+				else if ((c < '0' && c != '-' && c != '.') ||
+					 (c < 'A' && c > '9') ||
+					 (c > 'Z' && c < 'a' && c != '_') ||
+					 (c > 'z')) {
+					stream.WriteByte ((byte) '%');
+					int idx = ((int) c) >> 4;
+					stream.WriteByte (hexBytes [idx]);
+					idx = ((int) c) & 0x0F;
+					stream.WriteByte (hexBytes [idx]);
+				} else {
+					stream.WriteByte ((byte) c);
+				}
+			}
 		}
 	}
 }
+

+ 11 - 2
mcs/class/System/System.Net/WebConnection.cs

@@ -108,15 +108,24 @@ namespace System.Net
 		
 		internal bool WaitForContinue (byte [] headers, int offset, int size)
 		{
+			Data.StatusCode = 0;
+			if (sPoint.SendContinue == false)
+				return false;
+
 			if (waitForContinue == null)
 				waitForContinue = new AutoResetEvent (false);
 
 			Write (headers, offset, size);
 			waitingForContinue = true;
-			bool result = waitForContinue.WaitOne (Timeout.Infinite, false);
+			bool result = waitForContinue.WaitOne (2000, false);
 			waitingForContinue = false;
-			if (result)
+			if (result) {
 				Data.request.DoContinueDelegate (Data.StatusCode, Data.Headers);
+				sPoint.SendContinue = true;
+			} else {
+				sPoint.SendContinue = false;
+				waitForContinue = null;
+			}
 
 			return result;
 		}

+ 3 - 2
mcs/class/System/System.Net/WebConnectionStream.cs

@@ -288,8 +288,9 @@ namespace System.Net
 
 			request.InternalContentLength = length;
 			request.SendRequestHeaders ();
-			if (!cnc.WaitForContinue (headers, 0, headers.Length))
-				return; // the error is reported in WebConnection (timeout or 417 or whatever)
+			cnc.WaitForContinue (headers, 0, headers.Length);
+			if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100)
+				return;
 
 			cnc.Write (bytes, 0, length);
 			requestWritten = true;