2 // System.Net.WebClient
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections.Specialized;
34 using System.ComponentModel;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
43 public sealed class WebClient : Component
45 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
46 static byte [] hexBytes;
47 ICredentials credentials;
48 WebHeaderCollection headers;
49 WebHeaderCollection responseHeaders;
52 NameValueCollection queryString;
57 hexBytes = new byte [16];
59 for (int i = '0'; i <= '9'; i++, index++)
60 hexBytes [index] = (byte) i;
62 for (int i = 'A'; i <= 'F'; i++, index++)
63 hexBytes [index] = (byte) i;
72 public string BaseAddress {
74 if (baseString == null) {
75 if (baseAddress == null)
79 baseString = baseAddress.ToString ();
84 if (value == null || value == "") {
87 baseAddress = new Uri (value);
92 public ICredentials Credentials {
93 get { return credentials; }
94 set { credentials = value; }
97 public WebHeaderCollection Headers {
100 headers = new WebHeaderCollection ();
104 set { headers = value; }
107 public NameValueCollection QueryString {
109 if (queryString == null)
110 queryString = new NameValueCollection ();
114 set { queryString = value; }
117 public WebHeaderCollection ResponseHeaders {
118 get { return responseHeaders; }
123 public byte [] DownloadData (string address)
125 WebRequest request = SetupRequest (address);
126 WebResponse response = request.GetResponse ();
127 Stream st = ProcessResponse (response);
128 return ReadAll (st, (int) response.ContentLength);
131 public void DownloadFile (string address, string fileName)
133 WebRequest request = SetupRequest (address);
134 WebResponse response = request.GetResponse ();
135 Stream st = ProcessResponse (response);
137 int cLength = (int) response.ContentLength;
138 int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
139 byte [] buffer = new byte [length];
140 FileStream f = new FileStream (fileName, FileMode.CreateNew);
143 while ((nread = st.Read (buffer, 0, length)) != 0)
144 f.Write (buffer, 0, nread);
149 public Stream OpenRead (string address)
151 WebRequest request = SetupRequest (address);
152 WebResponse response = request.GetResponse ();
153 return ProcessResponse (response);
156 public Stream OpenWrite (string address)
158 return OpenWrite (address, "POST");
161 public Stream OpenWrite (string address, string method)
163 WebRequest request = SetupRequest (address, method);
164 return request.GetRequestStream ();
167 public byte [] UploadData (string address, byte [] data)
169 return UploadData (address, "POST", data);
172 public byte [] UploadData (string address, string method, byte [] data)
175 throw new ArgumentNullException ("data");
177 int contentLength = data.Length;
178 WebRequest request = SetupRequest (address, method, contentLength);
179 using (Stream stream = request.GetRequestStream ()) {
180 stream.Write (data, 0, contentLength);
183 WebResponse response = request.GetResponse ();
184 Stream st = ProcessResponse (response);
185 return ReadAll (st, (int) response.ContentLength);
188 public byte [] UploadFile (string address, string fileName)
190 return UploadFile (address, "POST", fileName);
193 public byte [] UploadFile (string address, string method, string fileName)
195 string fileCType = Headers ["Content-Type"];
196 if (fileCType != null) {
197 string lower = fileCType.ToLower ();
198 if (lower.StartsWith ("multipart/"))
199 throw new WebException ("Content-Type cannot be set to a multipart" +
200 " type for this request.");
202 fileCType = "application/octet-stream";
205 string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
206 Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
207 WebRequest request = SetupRequest (address, method);
208 Stream reqStream = null;
209 Stream fStream = null;
210 byte [] resultBytes = null;
213 fStream = File.OpenRead (fileName);
214 reqStream = request.GetRequestStream ();
215 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
216 reqStream.Write (realBoundary, 0, realBoundary.Length);
217 string partHeaders = String.Format ("Content-Disposition: form-data; " +
218 "name=\"file\"; filename=\"{0}\"\r\n" +
219 "Content-Type: {1}\r\n\r\n",
220 Path.GetFileName (fileName), fileCType);
222 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
223 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
225 byte [] buffer = new byte [4096];
226 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
227 reqStream.Write (buffer, 0, nread);
229 reqStream.WriteByte ((byte) '\r');
230 reqStream.WriteByte ((byte) '\n');
231 reqStream.Write (realBoundary, 0, realBoundary.Length);
234 WebResponse response = request.GetResponse ();
235 Stream st = ProcessResponse (response);
236 resultBytes = ReadAll (st, (int) response.ContentLength);
237 } catch (WebException) {
239 } catch (Exception e) {
240 throw new WebException ("Error uploading file.", e);
245 if (reqStream != null)
252 public byte[] UploadValues (string address, NameValueCollection data)
254 return UploadValues (address, "POST", data);
257 public byte[] UploadValues (string address, string method, NameValueCollection data)
260 throw new ArgumentNullException ("data"); // MS throws a nullref
262 string cType = Headers ["Content-Type"];
263 if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
264 throw new WebException ("Content-Type header cannot be changed from its default " +
265 "value for this request.");
267 Headers ["Content-Type"] = urlEncodedCType;
268 WebRequest request = SetupRequest (address, method);
269 Stream rqStream = request.GetRequestStream ();
270 MemoryStream tmpStream = new MemoryStream ();
271 foreach (string key in data) {
272 byte [] bytes = Encoding.ASCII.GetBytes (key);
273 UrlEncodeAndWrite (tmpStream, bytes);
274 tmpStream.WriteByte ((byte) '=');
275 bytes = Encoding.ASCII.GetBytes (data [key]);
276 UrlEncodeAndWrite (tmpStream, bytes);
277 tmpStream.WriteByte ((byte) '&');
280 int length = (int) tmpStream.Length;
282 tmpStream.SetLength (--length); // remove trailing '&'
284 tmpStream.WriteByte ((byte) '\r');
285 tmpStream.WriteByte ((byte) '\n');
287 byte [] buf = tmpStream.GetBuffer ();
288 rqStream.Write (buf, 0, length + 2);
292 WebResponse response = request.GetResponse ();
293 Stream st = ProcessResponse (response);
294 return ReadAll (st, (int) response.ContentLength);
297 Uri MakeUri (string path)
300 if (queryString != null && queryString.Count != 0) {
301 // This is not the same as UploadValues, because these 'keys' are not
303 StringBuilder sb = new StringBuilder ();
305 foreach (string key in queryString)
306 sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
308 if (sb.Length != 0) {
309 sb.Length--; // remove trailing '&'
310 query = sb.ToString ();
315 if (baseAddress == null && query == null) {
317 return new Uri (path);
319 catch (System.UriFormatException) {
320 if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
321 return new Uri ("file://" + path);
324 return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
329 if (baseAddress == null)
330 return new Uri (path + query, (query != null));
333 return new Uri (baseAddress, path);
335 return new Uri (baseAddress, path + query, (query != null));
338 WebRequest SetupRequest (string address)
340 Uri uri = MakeUri (address);
341 WebRequest request = WebRequest.Create (uri);
342 request.Credentials = credentials;
344 // Special headers. These are properties of HttpWebRequest.
345 // What do we do with other requests differnt from HttpWebRequest?
346 if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
347 HttpWebRequest req = (HttpWebRequest) request;
348 string expect = headers ["Expect"];
349 string contentType = headers ["Content-Type"];
350 string accept = headers ["Accept"];
351 string connection = headers ["Connection"];
352 string userAgent = headers ["User-Agent"];
353 string referer = headers ["Referer"];
354 headers.RemoveInternal ("Expect");
355 headers.RemoveInternal ("Content-Type");
356 headers.RemoveInternal ("Accept");
357 headers.RemoveInternal ("Connection");
358 headers.RemoveInternal ("Referer");
359 headers.RemoveInternal ("User-Agent");
360 request.Headers = headers;
362 if (expect != null && expect != "")
365 if (accept != null && accept != "")
368 if (contentType != null && contentType != "")
369 req.ContentType = contentType;
371 if (connection != null && connection != "")
372 req.Connection = connection;
374 if (userAgent != null && userAgent != "")
375 req.UserAgent = userAgent;
377 if (referer != null && referer != "")
378 req.Referer = referer;
381 responseHeaders = null;
385 WebRequest SetupRequest (string address, string method)
387 WebRequest request = SetupRequest (address);
388 request.Method = method;
392 WebRequest SetupRequest (string address, string method, int contentLength)
394 WebRequest request = SetupRequest (address, method);
395 request.ContentLength = contentLength;
399 Stream ProcessResponse (WebResponse response)
401 responseHeaders = response.Headers;
402 return response.GetResponseStream ();
405 static byte [] ReadAll (Stream stream, int length)
407 MemoryStream ms = null;
409 bool nolength = (length == -1);
410 int size = ((nolength) ? 8192 : length);
412 ms = new MemoryStream ();
416 byte [] buffer = new byte [size];
417 while ((nread = stream.Read (buffer, offset, size)) != 0) {
419 ms.Write (buffer, 0, nread);
427 return ms.ToArray ();
432 string UrlEncode (string str)
434 StringBuilder result = new StringBuilder ();
436 int len = str.Length;
437 for (int i = 0; i < len; i++) {
441 else if ((c < '0' && c != '-' && c != '.') ||
442 (c < 'A' && c > '9') ||
443 (c > 'Z' && c < 'a' && c != '_') ||
446 int idx = ((int) c) >> 4;
447 result.Append ((char) hexBytes [idx]);
448 idx = ((int) c) & 0x0F;
449 result.Append ((char) hexBytes [idx]);
455 return result.ToString ();
458 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
463 int len = bytes.Length;
467 for (int i = 0; i < len; i++) {
468 char c = (char) bytes [i];
470 stream.WriteByte ((byte) '+');
471 else if ((c < '0' && c != '-' && c != '.') ||
472 (c < 'A' && c > '9') ||
473 (c > 'Z' && c < 'a' && c != '_') ||
475 stream.WriteByte ((byte) '%');
476 int idx = ((int) c) >> 4;
477 stream.WriteByte (hexBytes [idx]);
478 idx = ((int) c) & 0x0F;
479 stream.WriteByte (hexBytes [idx]);
481 stream.WriteByte ((byte) c);