2 // System.Net.WebClient
\r
5 // Lawrence Pit (loz@cable.a2000.nl)
\r
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
\r
8 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
\r
12 using System.Collections.Specialized;
\r
13 using System.ComponentModel;
\r
15 using System.Runtime.InteropServices;
\r
16 using System.Runtime.Serialization;
\r
19 namespace System.Net
\r
22 public sealed class WebClient : Component
\r
24 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
\r
25 static byte [] hexBytes;
\r
26 ICredentials credentials;
\r
27 WebHeaderCollection headers;
\r
28 WebHeaderCollection responseHeaders;
\r
31 NameValueCollection queryString;
\r
36 hexBytes = new byte [16];
\r
38 for (int i = '0'; i <= '9'; i++, index++)
\r
39 hexBytes [index] = (byte) i;
\r
41 for (int i = 'A'; i <= 'F'; i++, index++)
\r
42 hexBytes [index] = (byte) i;
\r
51 public string BaseAddress {
\r
53 if (baseString == null) {
\r
54 if (baseAddress == null)
\r
58 baseString = baseAddress.ToString ();
\r
63 if (value == null || value == "") {
\r
66 baseAddress = new Uri (value);
\r
71 public ICredentials Credentials {
\r
72 get { return credentials; }
\r
73 set { credentials = value; }
\r
76 public WebHeaderCollection Headers {
\r
78 if (headers == null)
\r
79 headers = new WebHeaderCollection ();
\r
83 set { headers = value; }
\r
86 public NameValueCollection QueryString {
\r
88 if (queryString == null)
\r
89 queryString = new NameValueCollection ();
\r
93 set { queryString = value; }
\r
96 public WebHeaderCollection ResponseHeaders {
\r
97 get { return responseHeaders; }
\r
102 public byte [] DownloadData (string address)
\r
104 WebRequest request = SetupRequest (address);
\r
105 WebResponse response = request.GetResponse ();
\r
106 Stream st = ProcessResponse (response);
\r
107 return ReadAll (st, (int) response.ContentLength);
\r
110 public void DownloadFile (string address, string fileName)
\r
112 WebRequest request = SetupRequest (address);
\r
113 WebResponse response = request.GetResponse ();
\r
114 Stream st = ProcessResponse (response);
\r
116 int cLength = (int) response.ContentLength;
\r
117 int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
\r
118 byte [] buffer = new byte [length];
\r
119 FileStream f = new FileStream (fileName, FileMode.CreateNew);
\r
122 while ((nread = st.Read (buffer, 0, length)) != 0)
\r
123 f.Write (buffer, 0, nread);
\r
128 public Stream OpenRead (string address)
\r
130 WebRequest request = SetupRequest (address);
\r
131 WebResponse response = request.GetResponse ();
\r
132 return ProcessResponse (response);
\r
135 public Stream OpenWrite (string address)
\r
137 return OpenWrite (address, "POST");
\r
140 public Stream OpenWrite (string address, string method)
\r
142 WebRequest request = SetupRequest (address, method);
\r
143 return request.GetRequestStream ();
\r
146 public byte [] UploadData (string address, byte [] data)
\r
148 return UploadData (address, "POST", data);
\r
151 public byte [] UploadData (string address, string method, byte [] data)
\r
154 throw new ArgumentNullException ("data");
\r
156 int contentLength = data.Length;
\r
157 WebRequest request = SetupRequest (address, method, contentLength);
\r
158 using (Stream stream = request.GetRequestStream ()) {
\r
159 stream.Write (data, 0, contentLength);
\r
162 WebResponse response = request.GetResponse ();
\r
163 Stream st = ProcessResponse (response);
\r
164 return ReadAll (st, (int) response.ContentLength);
\r
167 public byte [] UploadFile (string address, string fileName)
\r
169 return UploadFile (address, "POST", fileName);
\r
172 public byte [] UploadFile (string address, string method, string fileName)
\r
174 string fileCType = Headers ["Content-Type"];
\r
175 if (fileCType != null) {
\r
176 string lower = fileCType.ToLower ();
\r
177 if (lower.StartsWith ("multipart/"))
\r
178 throw new WebException ("Content-Type cannot be set to a multipart" +
\r
179 " type for this request.");
\r
181 fileCType = "application/octet-stream";
\r
184 string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
\r
185 Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
\r
186 WebRequest request = SetupRequest (address, method);
\r
187 Stream reqStream = null;
\r
188 Stream fStream = null;
\r
189 byte [] resultBytes = null;
\r
192 fStream = File.OpenRead (fileName);
\r
193 reqStream = request.GetRequestStream ();
\r
194 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
\r
195 reqStream.Write (realBoundary, 0, realBoundary.Length);
\r
196 string partHeaders = String.Format ("Content-Disposition: form-data; " +
\r
197 "name=\"file\"; filename=\"{0}\"\r\n" +
\r
198 "Content-Type: {1}\r\n\r\n",
\r
199 Path.GetFileName (fileName), fileCType);
\r
201 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
\r
202 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
\r
204 byte [] buffer = new byte [4096];
\r
205 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
\r
206 reqStream.Write (buffer, 0, nread);
\r
208 reqStream.WriteByte ((byte) '\r');
\r
209 reqStream.WriteByte ((byte) '\n');
\r
210 reqStream.Write (realBoundary, 0, realBoundary.Length);
\r
211 reqStream.Close ();
\r
213 WebResponse response = request.GetResponse ();
\r
214 Stream st = ProcessResponse (response);
\r
215 resultBytes = ReadAll (st, (int) response.ContentLength);
\r
216 } catch (WebException) {
\r
218 } catch (Exception e) {
\r
219 throw new WebException ("Error uploading file.", e);
\r
221 if (fStream != null)
\r
224 if (reqStream != null)
\r
225 reqStream.Close ();
\r
228 return resultBytes;
\r
231 public byte[] UploadValues (string address, NameValueCollection data)
\r
233 return UploadValues (address, "POST", data);
\r
236 public byte[] UploadValues (string address, string method, NameValueCollection data)
\r
239 throw new ArgumentNullException ("data"); // MS throws a nullref
\r
241 string cType = Headers ["Content-Type"];
\r
242 if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
\r
243 throw new WebException ("Content-Type header cannot be changed from its default " +
\r
244 "value for this request.");
\r
246 Headers ["Content-Type"] = urlEncodedCType;
\r
247 WebRequest request = SetupRequest (address, method);
\r
248 Stream rqStream = request.GetRequestStream ();
\r
249 MemoryStream tmpStream = new MemoryStream ();
\r
250 foreach (string key in data) {
\r
251 byte [] bytes = Encoding.ASCII.GetBytes (key);
\r
252 UrlEncodeAndWrite (tmpStream, bytes);
\r
253 tmpStream.WriteByte ((byte) '=');
\r
254 bytes = Encoding.ASCII.GetBytes (data [key]);
\r
255 UrlEncodeAndWrite (tmpStream, bytes);
\r
256 tmpStream.WriteByte ((byte) '&');
\r
259 int length = (int) tmpStream.Length;
\r
261 tmpStream.SetLength (--length); // remove trailing '&'
\r
263 tmpStream.WriteByte ((byte) '\r');
\r
264 tmpStream.WriteByte ((byte) '\n');
\r
266 byte [] buf = tmpStream.GetBuffer ();
\r
267 rqStream.Write (buf, 0, length + 2);
\r
269 tmpStream.Close ();
\r
271 WebResponse response = request.GetResponse ();
\r
272 Stream st = ProcessResponse (response);
\r
273 return ReadAll (st, (int) response.ContentLength);
\r
276 Uri MakeUri (string path)
\r
278 string query = null;
\r
279 if (queryString != null && queryString.Count != 0) {
\r
280 // This is not the same as UploadValues, because these 'keys' are not
\r
281 // urlencoded here.
\r
282 StringBuilder sb = new StringBuilder ();
\r
284 foreach (string key in queryString)
\r
285 sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
\r
287 if (sb.Length != 0) {
\r
288 sb.Length--; // remove trailing '&'
\r
289 query = sb.ToString ();
\r
294 if (baseAddress == null && query == null) {
\r
296 return new Uri (path);
\r
298 catch (System.UriFormatException) {
\r
299 if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
\r
300 return new Uri ("file://" + path);
\r
303 return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
\r
308 if (baseAddress == null)
\r
309 return new Uri (path + query, (query != null));
\r
312 return new Uri (baseAddress, path);
\r
314 return new Uri (baseAddress, path + query, (query != null));
\r
317 WebRequest SetupRequest (string address)
\r
319 Uri uri = MakeUri (address);
\r
320 WebRequest request = WebRequest.Create (uri);
\r
321 request.Credentials = credentials;
\r
323 // Special headers. These are properties of HttpWebRequest.
\r
324 // What do we do with other requests differnt from HttpWebRequest?
\r
325 if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
\r
326 HttpWebRequest req = (HttpWebRequest) request;
\r
327 string expect = headers ["Expect"];
\r
328 string contentType = headers ["Content-Type"];
\r
329 string accept = headers ["Accept"];
\r
330 string connection = headers ["Connection"];
\r
331 string userAgent = headers ["User-Agent"];
\r
332 string referer = headers ["Referer"];
\r
333 headers.RemoveInternal ("Expect");
\r
334 headers.RemoveInternal ("Content-Type");
\r
335 headers.RemoveInternal ("Accept");
\r
336 headers.RemoveInternal ("Connection");
\r
337 headers.RemoveInternal ("Referer");
\r
338 headers.RemoveInternal ("User-Agent");
\r
339 request.Headers = headers;
\r
341 if (expect != null && expect != "")
\r
342 req.Expect = expect;
\r
344 if (accept != null && accept != "")
\r
345 req.Accept = accept;
\r
347 if (contentType != null && contentType != "")
\r
348 req.ContentType = contentType;
\r
350 if (connection != null && connection != "")
\r
351 req.Connection = connection;
\r
353 if (userAgent != null && userAgent != "")
\r
354 req.UserAgent = userAgent;
\r
356 if (referer != null && referer != "")
\r
357 req.Referer = referer;
\r
360 responseHeaders = null;
\r
364 WebRequest SetupRequest (string address, string method)
\r
366 WebRequest request = SetupRequest (address);
\r
367 request.Method = method;
\r
371 WebRequest SetupRequest (string address, string method, int contentLength)
\r
373 WebRequest request = SetupRequest (address, method);
\r
374 request.ContentLength = contentLength;
\r
378 Stream ProcessResponse (WebResponse response)
\r
380 responseHeaders = response.Headers;
\r
381 return response.GetResponseStream ();
\r
384 static byte [] ReadAll (Stream stream, int length)
\r
386 MemoryStream ms = null;
\r
388 bool nolength = (length == -1);
\r
389 int size = ((nolength) ? 8192 : length);
\r
391 ms = new MemoryStream ();
\r
395 byte [] buffer = new byte [size];
\r
396 while ((nread = stream.Read (buffer, offset, size)) != 0) {
\r
398 ms.Write (buffer, 0, nread);
\r
406 return ms.ToArray ();
\r
411 string UrlEncode (string str)
\r
413 StringBuilder result = new StringBuilder ();
\r
415 int len = str.Length;
\r
416 for (int i = 0; i < len; i++) {
\r
419 result.Append ('+');
\r
420 else if ((c < '0' && c != '-' && c != '.') ||
\r
421 (c < 'A' && c > '9') ||
\r
422 (c > 'Z' && c < 'a' && c != '_') ||
\r
424 result.Append ('%');
\r
425 int idx = ((int) c) >> 4;
\r
426 result.Append ((char) hexBytes [idx]);
\r
427 idx = ((int) c) & 0x0F;
\r
428 result.Append ((char) hexBytes [idx]);
\r
434 return result.ToString ();
\r
437 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
\r
442 int len = bytes.Length;
\r
446 for (int i = 0; i < len; i++) {
\r
447 char c = (char) bytes [i];
\r
449 stream.WriteByte ((byte) '+');
\r
450 else if ((c < '0' && c != '-' && c != '.') ||
\r
451 (c < 'A' && c > '9') ||
\r
452 (c > 'Z' && c < 'a' && c != '_') ||
\r
454 stream.WriteByte ((byte) '%');
\r
455 int idx = ((int) c) >> 4;
\r
456 stream.WriteByte (hexBytes [idx]);
\r
457 idx = ((int) c) & 0x0F;
\r
458 stream.WriteByte (hexBytes [idx]);
\r
460 stream.WriteByte ((byte) c);
\r