New test.
[mono.git] / mcs / class / System / System.Net / WebClient.cs
1 //
2 // System.Net.WebClient
3 //
4 // Authors:
5 //      Lawrence Pit (loz@cable.a2000.nl)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Atsushi Enomoto (atsushi@ximian.com)
8 //      Miguel de Icaza (miguel@ximian.com)
9 //
10 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
11 // Copyright 2006, 2010 Novell, Inc. (http://www.novell.com)
12 //
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 //
34 // Notes on CancelAsync and Async methods:
35 //
36 //    WebClient.CancelAsync is implemented by calling Thread.Interrupt
37 //    in our helper thread.   The various async methods have to cancel
38 //    any ongoing requests by calling request.Abort () at that point.
39 //    In a few places (UploadDataCore, UploadValuesCore,
40 //    UploadFileCore) we catch the ThreadInterruptedException and
41 //    abort the request there.
42 //
43 //    Higher level routines (the async callbacks) also need to catch
44 //    the exception and raise the OnXXXXCompleted events there with
45 //    the "canceled" flag set to true. 
46 //
47 //    In a few other places where these helper routines are not used
48 //    (OpenReadAsync for example) catching the ThreadAbortException
49 //    also must abort the request.
50 //
51 //    The Async methods currently differ in their implementation from
52 //    the .NET implementation in that we manually catch any other
53 //    exceptions and correctly raise the OnXXXXCompleted passing the
54 //    Exception that caused the problem.   The .NET implementation
55 //    does not seem to have a mechanism to flag errors that happen
56 //    during downloads though.    We do this because we still need to
57 //    catch the exception on these helper threads, or we would
58 //    otherwise kill the application (on the 2.x profile, uncaught
59 //    exceptions in threads terminate the application).
60 //
61 using System;
62 using System.Collections.Specialized;
63 using System.ComponentModel;
64 using System.IO;
65 using System.Runtime.InteropServices;
66 using System.Runtime.Serialization;
67 using System.Text;
68 using System.Threading;
69 using System.Net.Cache;
70
71 namespace System.Net 
72 {
73         [ComVisible(true)]
74         public class WebClient : Component
75         {
76                 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
77                 static byte [] hexBytes;
78                 ICredentials credentials;
79                 WebHeaderCollection headers;
80                 WebHeaderCollection responseHeaders;
81                 Uri baseAddress;
82                 string baseString;
83                 NameValueCollection queryString;
84                 bool is_busy;
85                 bool async;
86                 Thread async_thread;
87                 Encoding encoding = Encoding.Default;
88                 IWebProxy proxy;
89
90                 // Constructors
91                 static WebClient ()
92                 {
93                         hexBytes = new byte [16];
94                         int index = 0;
95                         for (int i = '0'; i <= '9'; i++, index++)
96                                 hexBytes [index] = (byte) i;
97
98                         for (int i = 'a'; i <= 'f'; i++, index++)
99                                 hexBytes [index] = (byte) i;
100                 }
101                 
102                 public WebClient ()
103                 {
104                 }
105                 
106                 // Properties
107                 
108                 public string BaseAddress {
109                         get {
110                                 if (baseString == null) {
111                                         if (baseAddress == null)
112                                                 return string.Empty;
113                                 }
114
115                                 baseString = baseAddress.ToString ();
116                                 return baseString;
117                         }
118                         
119                         set {
120                                 if (value == null || value.Length == 0) {
121                                         baseAddress = null;
122                                 } else {
123                                         baseAddress = new Uri (value);
124                                 }
125                         }
126                 }
127
128                 static Exception GetMustImplement ()
129                 {
130                         return new NotImplementedException ();
131                 }
132                 
133                 [MonoTODO]
134                 public RequestCachePolicy CachePolicy
135                 {
136                         get {
137                                 throw GetMustImplement ();
138                         }
139                         set {
140                                 throw GetMustImplement ();
141                         }
142                 }
143
144                 [MonoTODO]
145                 public bool UseDefaultCredentials
146                 {
147                         get {
148                                 throw GetMustImplement ();
149                         }
150                         set {
151                                 throw GetMustImplement ();
152                         }
153                 }
154                 
155                 public ICredentials Credentials {
156                         get { return credentials; }
157                         set { credentials = value; }
158                 }
159
160                 public WebHeaderCollection Headers {
161                         get {
162                                 if (headers == null)
163                                         headers = new WebHeaderCollection ();
164
165                                 return headers;
166                         }
167                         set { headers = value; }
168                 }
169                 
170                 public NameValueCollection QueryString {
171                         get {
172                                 if (queryString == null)
173                                         queryString = new NameValueCollection ();
174
175                                 return queryString;
176                         }
177                         set { queryString = value; }
178                 }
179                 
180                 public WebHeaderCollection ResponseHeaders {
181                         get { return responseHeaders; }
182                 }
183
184                 public Encoding Encoding {
185                         get { return encoding; }
186                         set {
187                                 if (value == null)
188                                         throw new ArgumentNullException ("Encoding");
189                                 encoding = value;
190                         }
191                 }
192
193                 public IWebProxy Proxy {
194                         get { return proxy; }
195                         set { proxy = value; }
196                 }
197
198                 public bool IsBusy {
199                         get { return is_busy; } 
200                 }
201                 // Methods
202
203                 void CheckBusy ()
204                 {
205                         if (IsBusy)
206                                 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
207                 }
208
209                 void SetBusy ()
210                 {
211                         lock (this) {
212                                 CheckBusy ();
213                                 is_busy = true;
214                         }
215                 }
216
217                 //   DownloadData
218
219                 public byte [] DownloadData (string address)
220                 {
221                         if (address == null)
222                                 throw new ArgumentNullException ("address");
223
224                         return DownloadData (CreateUri (address));
225                 }
226
227                 public byte [] DownloadData (Uri address)
228                 {
229                         if (address == null)
230                                 throw new ArgumentNullException ("address");
231
232                         try {
233                                 SetBusy ();
234                                 async = false;
235                                 return DownloadDataCore (address, null);
236                         } finally {
237                                 is_busy = false;
238                         }
239                 }
240
241                 byte [] DownloadDataCore (Uri address, object userToken)
242                 {
243                         WebRequest request = null;
244                         
245                         try {
246                                 request = SetupRequest (address);
247                                 WebResponse response = GetWebResponse (request);
248                                 Stream st = response.GetResponseStream ();
249                                 return ReadAll (st, (int) response.ContentLength, userToken);
250                         } catch (ThreadInterruptedException){
251                                 if (request != null)
252                                         request.Abort ();
253                                 throw;
254                         } catch (WebException) {
255                                 throw;
256                         } catch (Exception ex) {
257                                 throw new WebException ("An error occurred " +
258                                         "performing a WebClient request.", ex);
259                         }
260                 }
261
262                 //   DownloadFile
263
264                 public void DownloadFile (string address, string fileName)
265                 {
266                         if (address == null)
267                                 throw new ArgumentNullException ("address");
268
269                         DownloadFile (CreateUri (address), fileName);
270                 }
271
272                 public void DownloadFile (Uri address, string fileName)
273                 {
274                         if (address == null)
275                                 throw new ArgumentNullException ("address");
276                         if (fileName == null)
277                                 throw new ArgumentNullException ("fileName");
278
279                         try {
280                                 SetBusy ();
281                                 async = false;
282                                 DownloadFileCore (address, fileName, null);
283                         } catch (WebException) {
284                                 throw;
285                         } catch (Exception ex) {
286                                 throw new WebException ("An error occurred " +
287                                         "performing a WebClient request.", ex);
288                         } finally {
289                                 is_busy = false;
290                         }
291                 }
292
293                 void DownloadFileCore (Uri address, string fileName, object userToken)
294                 {
295                         WebRequest request = null;
296                         
297                         using (FileStream f = new FileStream (fileName, FileMode.Create)) {
298                                 try {
299                                         request = SetupRequest (address);
300                                         WebResponse response = GetWebResponse (request);
301                                         Stream st = response.GetResponseStream ();
302                                         
303                                         int cLength = (int) response.ContentLength;
304                                         int length = (cLength <= -1 || cLength > 32*1024) ? 32*1024 : cLength;
305                                         byte [] buffer = new byte [length];
306                                         
307                                         int nread = 0;
308                                         long notify_total = 0;
309                                         while ((nread = st.Read (buffer, 0, length)) != 0){
310                                                 if (async){
311                                                         notify_total += nread;
312                                                         OnDownloadProgressChanged (
313                                                                 new DownloadProgressChangedEventArgs (notify_total, response.ContentLength, userToken));
314                                                                                                       
315                                                 }
316                                                 f.Write (buffer, 0, nread);
317                                         }
318                                 } catch (ThreadInterruptedException){
319                                         if (request != null)
320                                                 request.Abort ();
321                                         throw;
322                                 }
323                         }
324                 }
325
326                 //   OpenRead
327
328                 public Stream OpenRead (string address)
329                 {
330                         if (address == null)
331                                 throw new ArgumentNullException ("address");
332                         return OpenRead (CreateUri (address));
333                 }
334
335                 public Stream OpenRead (Uri address)
336                 {
337                         if (address == null)
338                                 throw new ArgumentNullException ("address");
339
340                         WebRequest request = null;
341                         try {
342                                 SetBusy ();
343                                 async = false;
344                                 request = SetupRequest (address);
345                                 WebResponse response = GetWebResponse (request);
346                                 return response.GetResponseStream ();
347                         } catch (WebException) {
348                                 throw;
349                         } catch (Exception ex) {
350                                 throw new WebException ("An error occurred " +
351                                         "performing a WebClient request.", ex);
352                         } finally {
353                                 is_busy = false;
354                         }
355                 }
356
357                 //   OpenWrite
358
359                 public Stream OpenWrite (string address)
360                 {
361                         if (address == null)
362                                 throw new ArgumentNullException ("address");
363
364                         return OpenWrite (CreateUri (address));
365                 }
366                 
367                 public Stream OpenWrite (string address, string method)
368                 {
369                         if (address == null)
370                                 throw new ArgumentNullException ("address");
371
372                         return OpenWrite (CreateUri (address), method);
373                 }
374
375                 public Stream OpenWrite (Uri address)
376                 {
377                         return OpenWrite (address, (string) null);
378                 }
379
380                 public Stream OpenWrite (Uri address, string method)
381                 {
382                         if (address == null)
383                                 throw new ArgumentNullException ("address");
384
385                         try {
386                                 SetBusy ();
387                                 async = false;
388                                 WebRequest request = SetupRequest (address, method, true);
389                                 return request.GetRequestStream ();
390                         } catch (WebException) {
391                                 throw;
392                         } catch (Exception ex) {
393                                 throw new WebException ("An error occurred " +
394                                         "performing a WebClient request.", ex);
395                         } finally {
396                                 is_busy = false;
397                         }
398                 }
399
400                 private string DetermineMethod (Uri address, string method, bool is_upload)
401                 {
402                         if (method != null)
403                                 return method;
404
405                         if (address.Scheme == Uri.UriSchemeFtp)
406                                 return (is_upload) ? "STOR" : "RETR";
407
408                         return (is_upload) ? "POST" : "GET";
409                 }
410
411                 //   UploadData
412
413                 public byte [] UploadData (string address, byte [] data)
414                 {
415                         if (address == null)
416                                 throw new ArgumentNullException ("address");
417
418                         return UploadData (CreateUri (address), data);
419                 }
420                 
421                 public byte [] UploadData (string address, string method, byte [] data)
422                 {
423                         if (address == null)
424                                 throw new ArgumentNullException ("address");
425
426                         return UploadData (CreateUri (address), method, data);
427                 }
428
429                 public byte [] UploadData (Uri address, byte [] data)
430                 {
431                         return UploadData (address, (string) null, data);
432                 }
433
434                 public byte [] UploadData (Uri address, string method, byte [] data)
435                 {
436                         if (address == null)
437                                 throw new ArgumentNullException ("address");
438                         if (data == null)
439                                 throw new ArgumentNullException ("data");
440
441                         try {
442                                 SetBusy ();
443                                 async = false;
444                                 return UploadDataCore (address, method, data, null);
445                         } catch (WebException) {
446                                 throw;
447                         } catch (Exception ex) {
448                                 throw new WebException ("An error occurred " +
449                                         "performing a WebClient request.", ex);
450                         } finally {
451                                 is_busy = false;
452                         }
453                 }
454
455                 byte [] UploadDataCore (Uri address, string method, byte [] data, object userToken)
456                 {
457                         WebRequest request = SetupRequest (address, method, true);
458                         try {
459                                 int contentLength = data.Length;
460                                 request.ContentLength = contentLength;
461                                 using (Stream stream = request.GetRequestStream ()) {
462                                         stream.Write (data, 0, contentLength);
463                                 }
464                                 
465                                 WebResponse response = GetWebResponse (request);
466                                 Stream st = response.GetResponseStream ();
467                                 return ReadAll (st, (int) response.ContentLength, userToken);
468                         } catch (ThreadInterruptedException){
469                                 if (request != null)
470                                         request.Abort ();
471                                 throw;
472                         }
473                 }
474
475                 //   UploadFile
476
477                 public byte [] UploadFile (string address, string fileName)
478                 {
479                         if (address == null)
480                                 throw new ArgumentNullException ("address");
481
482                         return UploadFile (CreateUri (address), fileName);
483                 }
484
485                 public byte [] UploadFile (Uri address, string fileName)
486                 {
487                         return UploadFile (address, (string) null, fileName);
488                 }
489                 
490                 public byte [] UploadFile (string address, string method, string fileName)
491                 {
492                         return UploadFile (CreateUri (address), method, fileName);
493                 }
494
495                 public byte [] UploadFile (Uri address, string method, string fileName)
496                 {
497                         if (address == null)
498                                 throw new ArgumentNullException ("address");
499                         if (fileName == null)
500                                 throw new ArgumentNullException ("fileName");
501
502                         try {
503                                 SetBusy ();
504                                 async = false;
505                                 return UploadFileCore (address, method, fileName, null);
506                         } catch (WebException) {
507                                 throw;
508                         } catch (Exception ex) {
509                                 throw new WebException ("An error occurred " +
510                                         "performing a WebClient request.", ex);
511                         } finally {
512                                 is_busy = false;
513                         }
514                 }
515
516                 byte [] UploadFileCore (Uri address, string method, string fileName, object userToken)
517                 {
518                         string fileCType = Headers ["Content-Type"];
519                         if (fileCType != null) {
520                                 string lower = fileCType.ToLower ();
521                                 if (lower.StartsWith ("multipart/"))
522                                         throw new WebException ("Content-Type cannot be set to a multipart" +
523                                                                 " type for this request.");
524                         } else {
525                                 fileCType = "application/octet-stream";
526                         }
527
528                         string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
529                         Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
530                         Stream reqStream = null;
531                         Stream fStream = null;
532                         byte [] resultBytes = null;
533
534                         fileName = Path.GetFullPath (fileName);
535
536                         WebRequest request = null;
537                         try {
538                                 fStream = File.OpenRead (fileName);
539                                 request = SetupRequest (address, method, true);
540                                 reqStream = request.GetRequestStream ();
541                                 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
542                                 reqStream.Write (realBoundary, 0, realBoundary.Length);
543                                 string partHeaders = String.Format ("Content-Disposition: form-data; " +
544                                                                     "name=\"file\"; filename=\"{0}\"\r\n" +
545                                                                     "Content-Type: {1}\r\n\r\n",
546                                                                     Path.GetFileName (fileName), fileCType);
547
548                                 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
549                                 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
550                                 int nread;
551                                 byte [] buffer = new byte [4096];
552                                 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
553                                         reqStream.Write (buffer, 0, nread);
554
555                                 reqStream.WriteByte ((byte) '\r');
556                                 reqStream.WriteByte ((byte) '\n');
557                                 reqStream.Write (realBoundary, 0, realBoundary.Length);
558                                 reqStream.Close ();
559                                 reqStream = null;
560                                 WebResponse response = GetWebResponse (request);
561                                 Stream st = response.GetResponseStream ();
562                                 resultBytes = ReadAll (st, (int) response.ContentLength, userToken);
563                         } catch (ThreadInterruptedException){
564                                 if (request != null)
565                                         request.Abort ();
566                                 throw;
567                         } finally {
568                                 if (fStream != null)
569                                         fStream.Close ();
570
571                                 if (reqStream != null)
572                                         reqStream.Close ();
573                         }
574                         
575                         return resultBytes;
576                 }
577                 
578                 public byte[] UploadValues (string address, NameValueCollection data)
579                 {
580                         if (address == null)
581                                 throw new ArgumentNullException ("address");
582
583                         return UploadValues (CreateUri (address), data);
584                 }
585                 
586                 public byte[] UploadValues (string address, string method, NameValueCollection data)
587                 {
588                         if (address == null)
589                                 throw new ArgumentNullException ("address");
590                         return UploadValues (CreateUri (address), method, data);
591                 }
592
593                 public byte[] UploadValues (Uri address, NameValueCollection data)
594                 {
595                         return UploadValues (address, (string) null, data);
596                 }
597
598                 public byte[] UploadValues (Uri address, string method, NameValueCollection data)
599                 {
600                         if (address == null)
601                                 throw new ArgumentNullException ("address");
602                         if (data == null)
603                                 throw new ArgumentNullException ("data");
604
605                         try {
606                                 SetBusy ();
607                                 async = false;
608                                 return UploadValuesCore (address, method, data, null);
609                         } catch (WebException) {
610                                 throw;
611                         } catch (Exception ex) {
612                                 throw new WebException ("An error occurred " +
613                                         "performing a WebClient request.", ex);
614                         } finally {
615                                 is_busy = false;
616                         }
617                 }
618
619                 byte[] UploadValuesCore (Uri uri, string method, NameValueCollection data, object userToken)
620                 {
621                         string cType = Headers ["Content-Type"];
622                         if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
623                                 throw new WebException ("Content-Type header cannot be changed from its default " +
624                                                         "value for this request.");
625
626                         Headers ["Content-Type"] = urlEncodedCType;
627                         WebRequest request = SetupRequest (uri, method, true);
628                         try {
629                                 MemoryStream tmpStream = new MemoryStream ();
630                                 foreach (string key in data) {
631                                         byte [] bytes = Encoding.UTF8.GetBytes (key);
632                                         UrlEncodeAndWrite (tmpStream, bytes);
633                                         tmpStream.WriteByte ((byte) '=');
634                                         bytes = Encoding.UTF8.GetBytes (data [key]);
635                                         UrlEncodeAndWrite (tmpStream, bytes);
636                                         tmpStream.WriteByte ((byte) '&');
637                                 }
638                                 
639                                 int length = (int) tmpStream.Length;
640                                 if (length > 0)
641                                         tmpStream.SetLength (--length); // remove trailing '&'
642                                 
643                                 byte [] buf = tmpStream.GetBuffer ();
644                                 request.ContentLength = length;
645                                 using (Stream rqStream = request.GetRequestStream ()) {
646                                         rqStream.Write (buf, 0, length);
647                                 }
648                                 tmpStream.Close ();
649                                 
650                                 WebResponse response = GetWebResponse (request);
651                                 Stream st = response.GetResponseStream ();
652                                 return ReadAll (st, (int) response.ContentLength, userToken);
653                         } catch (ThreadInterruptedException) {
654                                 request.Abort ();
655                                 throw;
656                         }
657                 }
658
659                 public string DownloadString (string address)
660                 {
661                         if (address == null)
662                                 throw new ArgumentNullException ("address");
663
664                         return encoding.GetString (DownloadData (CreateUri (address)));
665                 }
666
667                 public string DownloadString (Uri address)
668                 {
669                         if (address == null)
670                                 throw new ArgumentNullException ("address");
671
672                         return encoding.GetString (DownloadData (CreateUri (address)));
673                 }
674
675                 public string UploadString (string address, string data)
676                 {
677                         if (address == null)
678                                 throw new ArgumentNullException ("address");
679                         if (data == null)
680                                 throw new ArgumentNullException ("data");
681
682                         byte [] resp = UploadData (address, encoding.GetBytes (data));
683                         return encoding.GetString (resp);
684                 }
685
686                 public string UploadString (string address, string method, string data)
687                 {
688                         if (address == null)
689                                 throw new ArgumentNullException ("address");
690                         if (data == null)
691                                 throw new ArgumentNullException ("data");
692
693                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
694                         return encoding.GetString (resp);
695                 }
696
697                 public string UploadString (Uri address, string data)
698                 {
699                         if (address == null)
700                                 throw new ArgumentNullException ("address");
701                         if (data == null)
702                                 throw new ArgumentNullException ("data");
703
704                         byte [] resp = UploadData (address, encoding.GetBytes (data));
705                         return encoding.GetString (resp);
706                 }
707
708                 public string UploadString (Uri address, string method, string data)
709                 {
710                         if (address == null)
711                                 throw new ArgumentNullException ("address");
712                         if (data == null)
713                                 throw new ArgumentNullException ("data");
714
715                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
716                         return encoding.GetString (resp);
717                 }
718
719                 public event DownloadDataCompletedEventHandler DownloadDataCompleted;
720                 public event AsyncCompletedEventHandler DownloadFileCompleted;
721                 public event DownloadProgressChangedEventHandler DownloadProgressChanged;
722                 public event DownloadStringCompletedEventHandler DownloadStringCompleted;
723                 public event OpenReadCompletedEventHandler OpenReadCompleted;
724                 public event OpenWriteCompletedEventHandler OpenWriteCompleted;
725                 public event UploadDataCompletedEventHandler UploadDataCompleted;
726                 public event UploadFileCompletedEventHandler UploadFileCompleted;
727                 public event UploadProgressChangedEventHandler UploadProgressChanged;
728                 public event UploadStringCompletedEventHandler UploadStringCompleted;
729                 public event UploadValuesCompletedEventHandler UploadValuesCompleted;
730
731                 Uri CreateUri (string address)
732                 {
733                         Uri uri;
734                         try {
735                                 if (baseAddress == null)
736                                         uri = new Uri (address);
737                                 else
738                                         uri = new Uri (baseAddress, address);
739                                 return CreateUri (uri);
740                         } catch {
741                         }
742                         return new Uri (Path.GetFullPath (address));
743                 }
744
745                 Uri CreateUri (Uri address)
746                 {
747                         Uri result = address;
748                         if (baseAddress != null && !result.IsAbsoluteUri) {
749                                 try {
750                                         result = new Uri (baseAddress, result.OriginalString);
751                                 } catch {
752                                         return result; // Not much we can do here.
753                                 }
754                         }
755
756                         string query = result.Query;
757                         if (String.IsNullOrEmpty (query))
758                                 query = GetQueryString (true);
759                         UriBuilder builder = new UriBuilder (address);
760                         if (!String.IsNullOrEmpty (query))
761                                 builder.Query = query.Substring (1);
762                         return builder.Uri;
763                 }
764
765                 string GetQueryString (bool add_qmark)
766                 {
767                         if (queryString == null || queryString.Count == 0)
768                                 return null;
769
770                         StringBuilder sb = new StringBuilder ();
771                         if (add_qmark)
772                                 sb.Append ('?');
773
774                         foreach (string key in queryString)
775                                 sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
776
777                         if (sb.Length != 0)
778                                 sb.Length--; // removes last '&' or the '?' if empty.
779
780                         if (sb.Length == 0)
781                                 return null;
782
783                         return sb.ToString ();
784                 }
785
786                 WebRequest SetupRequest (Uri uri)
787                 {
788                         WebRequest request = GetWebRequest (uri);
789                         if (Proxy != null)
790                                 request.Proxy = Proxy;
791                         request.Credentials = credentials;
792
793                         // Special headers. These are properties of HttpWebRequest.
794                         // What do we do with other requests differnt from HttpWebRequest?
795                         if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
796                                 HttpWebRequest req = (HttpWebRequest) request;
797                                 string expect = headers ["Expect"];
798                                 string contentType = headers ["Content-Type"];
799                                 string accept = headers ["Accept"];
800                                 string connection = headers ["Connection"];
801                                 string userAgent = headers ["User-Agent"];
802                                 string referer = headers ["Referer"];
803                                 headers.RemoveInternal ("Expect");
804                                 headers.RemoveInternal ("Content-Type");
805                                 headers.RemoveInternal ("Accept");
806                                 headers.RemoveInternal ("Connection");
807                                 headers.RemoveInternal ("Referer");
808                                 headers.RemoveInternal ("User-Agent");
809                                 request.Headers = headers;
810
811                                 if (expect != null && expect.Length > 0)
812                                         req.Expect = expect;
813
814                                 if (accept != null && accept.Length > 0)
815                                         req.Accept = accept;
816
817                                 if (contentType != null && contentType.Length > 0)
818                                         req.ContentType = contentType;
819
820                                 if (connection != null && connection.Length > 0)
821                                         req.Connection = connection;
822
823                                 if (userAgent != null && userAgent.Length > 0)
824                                         req.UserAgent = userAgent;
825
826                                 if (referer != null && referer.Length > 0)
827                                         req.Referer = referer;
828                         }
829
830                         responseHeaders = null;
831                         return request;
832                 }
833
834                 WebRequest SetupRequest (Uri uri, string method, bool is_upload)
835                 {
836                         WebRequest request = SetupRequest (uri);
837                         request.Method = DetermineMethod (uri, method, is_upload);
838                         return request;
839                 }
840
841                 byte [] ReadAll (Stream stream, int length, object userToken)
842                 {
843                         MemoryStream ms = null;
844                         
845                         bool nolength = (length == -1);
846                         int size = ((nolength) ? 8192 : length);
847                         if (nolength)
848                                 ms = new MemoryStream ();
849
850 //                      long total = 0;
851                         int nread = 0;
852                         int offset = 0;
853                         byte [] buffer = new byte [size];
854                         while ((nread = stream.Read (buffer, offset, size)) != 0) {
855                                 if (nolength) {
856                                         ms.Write (buffer, 0, nread);
857                                 } else {
858                                         offset += nread;
859                                         size -= nread;
860                                 }
861                                 if (async){
862 //                                      total += nread;
863                                         OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (nread, length, userToken));
864                                 }
865                         }
866
867                         if (nolength)
868                                 return ms.ToArray ();
869
870                         return buffer;
871                 }
872
873                 string UrlEncode (string str)
874                 {
875                         StringBuilder result = new StringBuilder ();
876
877                         int len = str.Length;
878                         for (int i = 0; i < len; i++) {
879                                 char c = str [i];
880                                 if (c == ' ')
881                                         result.Append ('+');
882                                 else if ((c < '0' && c != '-' && c != '.') ||
883                                          (c < 'A' && c > '9') ||
884                                          (c > 'Z' && c < 'a' && c != '_') ||
885                                          (c > 'z')) {
886                                         result.Append ('%');
887                                         int idx = ((int) c) >> 4;
888                                         result.Append ((char) hexBytes [idx]);
889                                         idx = ((int) c) & 0x0F;
890                                         result.Append ((char) hexBytes [idx]);
891                                 } else {
892                                         result.Append (c);
893                                 }
894                         }
895
896                         return result.ToString ();
897                 }
898
899                 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
900                 {
901                         if (bytes == null)
902                                 return;
903
904                         int len = bytes.Length;
905                         if (len == 0)
906                                 return;
907
908                         for (int i = 0; i < len; i++) {
909                                 char c = (char) bytes [i];
910                                 if (c == ' ')
911                                         stream.WriteByte ((byte) '+');
912                                 else if ((c < '0' && c != '-' && c != '.') ||
913                                          (c < 'A' && c > '9') ||
914                                          (c > 'Z' && c < 'a' && c != '_') ||
915                                          (c > 'z')) {
916                                         stream.WriteByte ((byte) '%');
917                                         int idx = ((int) c) >> 4;
918                                         stream.WriteByte (hexBytes [idx]);
919                                         idx = ((int) c) & 0x0F;
920                                         stream.WriteByte (hexBytes [idx]);
921                                 } else {
922                                         stream.WriteByte ((byte) c);
923                                 }
924                         }
925                 }
926
927                 public void CancelAsync ()
928                 {
929                         lock (this){
930                                 if (async_thread == null)
931                                         return;
932
933                                 //
934                                 // We first flag things as done, in case the Interrupt hangs
935                                 // or the thread decides to hang in some other way inside the
936                                 // event handlers, or if we are stuck somewhere else.  This
937                                 // ensures that the WebClient object is reusable immediately
938                                 //
939                                 Thread t = async_thread;
940                                 CompleteAsync ();
941                                 t.Interrupt ();
942                         }
943                 }
944
945                 void CompleteAsync ()
946                 {
947                         lock (this){
948                                 is_busy = false;
949                                 async_thread = null;
950                         }
951                 }
952
953                 //    DownloadDataAsync
954
955                 public void DownloadDataAsync (Uri address)
956                 {
957                         DownloadDataAsync (address, null);
958                 }
959
960                 public void DownloadDataAsync (Uri address, object userToken)
961                 {
962                         if (address == null)
963                                 throw new ArgumentNullException ("address");
964                         
965                         lock (this) {
966                                 SetBusy ();
967                                 async = true;
968                                 
969                                 async_thread = new Thread (delegate (object state) {
970                                         object [] args = (object []) state;
971                                         try {
972                                                 byte [] data = DownloadDataCore ((Uri) args [0], args [1]);
973                                                 OnDownloadDataCompleted (
974                                                         new DownloadDataCompletedEventArgs (data, null, false, args [1]));
975                                         } catch (ThreadInterruptedException){
976                                                 OnDownloadDataCompleted (
977                                                         new DownloadDataCompletedEventArgs (null, null, true, args [1]));
978                                                 throw;
979                                         } catch (Exception e){
980                                                 OnDownloadDataCompleted (
981                                                         new DownloadDataCompletedEventArgs (null, e, false, args [1]));
982                                         }
983                                 });
984                                 object [] cb_args = new object [] {address, userToken};
985                                 async_thread.Start (cb_args);
986                         }
987                 }
988
989                 //    DownloadFileAsync
990
991                 public void DownloadFileAsync (Uri address, string fileName)
992                 {
993                         DownloadFileAsync (address, fileName, null);
994                 }
995
996                 public void DownloadFileAsync (Uri address, string fileName, object userToken)
997                 {
998                         if (address == null)
999                                 throw new ArgumentNullException ("address");
1000                         if (fileName == null)
1001                                 throw new ArgumentNullException ("fileName");
1002                         
1003                         lock (this) {
1004                                 SetBusy ();
1005                                 async = true;
1006
1007                                 async_thread = new Thread (delegate (object state) {
1008                                         object [] args = (object []) state;
1009                                         try {
1010                                                 DownloadFileCore ((Uri) args [0], (string) args [1], args [2]);
1011                                                 OnDownloadFileCompleted (
1012                                                         new AsyncCompletedEventArgs (null, false, args [2]));
1013                                         } catch (ThreadInterruptedException){
1014                                                 OnDownloadFileCompleted (
1015                                                         new AsyncCompletedEventArgs (null, true, args [2]));
1016                                         } catch (Exception e){
1017                                                 OnDownloadFileCompleted (
1018                                                         new AsyncCompletedEventArgs (e, false, args [2]));
1019                                         }});
1020                                 object [] cb_args = new object [] {address, fileName, userToken};
1021                                 async_thread.Start (cb_args);
1022                         }
1023                 }
1024
1025                 //    DownloadStringAsync
1026
1027                 public void DownloadStringAsync (Uri address)
1028                 {
1029                         DownloadStringAsync (address, null);
1030                 }
1031
1032                 public void DownloadStringAsync (Uri address, object userToken)
1033                 {
1034                         if (address == null)
1035                                 throw new ArgumentNullException ("address");
1036                         
1037                         lock (this) {
1038                                 SetBusy ();
1039                                 async = true;
1040
1041                                 async_thread = new Thread (delegate (object state) {
1042                                         object [] args = (object []) state;
1043                                         try {
1044                                                 string data = encoding.GetString (DownloadDataCore ((Uri) args [0], args [1]));
1045                                                 OnDownloadStringCompleted (
1046                                                         new DownloadStringCompletedEventArgs (data, null, false, args [1]));
1047                                         } catch (ThreadInterruptedException){
1048                                                 OnDownloadStringCompleted (
1049                                                         new DownloadStringCompletedEventArgs (null, null, true, args [1]));
1050                                         } catch (Exception e){
1051                                                 OnDownloadStringCompleted (
1052                                                         new DownloadStringCompletedEventArgs (null, e, false, args [1]));
1053                                         }});
1054                                 object [] cb_args = new object [] {address, userToken};
1055                                 async_thread.Start (cb_args);
1056                         }
1057                 }
1058
1059                 //    OpenReadAsync
1060
1061                 public void OpenReadAsync (Uri address)
1062                 {
1063                         OpenReadAsync (address, null);
1064                 }
1065
1066                 public void OpenReadAsync (Uri address, object userToken)
1067                 {
1068                         if (address == null)
1069                                 throw new ArgumentNullException ("address");
1070                         
1071                         lock (this) {
1072                                 SetBusy ();
1073                                 async = true;
1074
1075                                 async_thread = new Thread (delegate (object state) {
1076                                         object [] args = (object []) state;
1077                                         WebRequest request = null;
1078                                         try {
1079                                                 request = SetupRequest ((Uri) args [0]);
1080                                                 WebResponse response = GetWebResponse (request);
1081                                                 Stream stream = response.GetResponseStream ();
1082                                                 OnOpenReadCompleted (
1083                                                         new OpenReadCompletedEventArgs (stream, null, false, args [1]));
1084                                         } catch (ThreadInterruptedException){
1085                                                 if (request != null)
1086                                                         request.Abort ();
1087                                                 
1088                                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, null, true, args [1]));
1089                                         } catch (Exception e){
1090                                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, e, false, args [1]));
1091                                         } });
1092                                 object [] cb_args = new object [] {address, userToken};
1093                                 async_thread.Start (cb_args);
1094                         }
1095                 }
1096
1097                 //    OpenWriteAsync
1098
1099                 public void OpenWriteAsync (Uri address)
1100                 {
1101                         OpenWriteAsync (address, null);
1102                 }
1103
1104                 public void OpenWriteAsync (Uri address, string method)
1105                 {
1106                         OpenWriteAsync (address, method, null);
1107                 }
1108
1109                 public void OpenWriteAsync (Uri address, string method, object userToken)
1110                 {
1111                         if (address == null)
1112                                 throw new ArgumentNullException ("address");
1113
1114                         lock (this) {
1115                                 SetBusy ();
1116                                 async = true;
1117
1118                                 async_thread = new Thread (delegate (object state) {
1119                                         object [] args = (object []) state;
1120                                         WebRequest request = null;
1121                                         try {
1122                                                 request = SetupRequest ((Uri) args [0], (string) args [1], true);
1123                                                 Stream stream = request.GetRequestStream ();
1124                                                 OnOpenWriteCompleted (
1125                                                         new OpenWriteCompletedEventArgs (stream, null, false, args [2]));
1126                                         } catch (ThreadInterruptedException){
1127                                                 if (request != null)
1128                                                         request.Abort ();
1129                                                 OnOpenWriteCompleted (
1130                                                         new OpenWriteCompletedEventArgs (null, null, true, args [2]));
1131                                         } catch (Exception e){
1132                                                 OnOpenWriteCompleted (
1133                                                         new OpenWriteCompletedEventArgs (null, e, false, args [2]));
1134                                         }});
1135                                 object [] cb_args = new object [] {address, method, userToken};
1136                                 async_thread.Start (cb_args);
1137                         }
1138                 }
1139
1140                 //    UploadDataAsync
1141
1142                 public void UploadDataAsync (Uri address, byte [] data)
1143                 {
1144                         UploadDataAsync (address, null, data);
1145                 }
1146
1147                 public void UploadDataAsync (Uri address, string method, byte [] data)
1148                 {
1149                         UploadDataAsync (address, method, data, null);
1150                 }
1151
1152                 public void UploadDataAsync (Uri address, string method, byte [] data, object userToken)
1153                 {
1154                         if (address == null)
1155                                 throw new ArgumentNullException ("address");
1156                         if (data == null)
1157                                 throw new ArgumentNullException ("data");
1158                         
1159                         lock (this) {
1160                                 SetBusy ();
1161                                 async = true;
1162
1163                                 async_thread = new Thread (delegate (object state) {
1164                                         object [] args = (object []) state;
1165                                         byte [] data2;
1166
1167                                         try {
1168                                                 data2 = UploadDataCore ((Uri) args [0], (string) args [1], (byte []) args [2], args [3]);
1169                                         
1170                                                 OnUploadDataCompleted (
1171                                                         new UploadDataCompletedEventArgs (data2, null, false, args [3]));
1172                                         } catch (ThreadInterruptedException){
1173                                                 OnUploadDataCompleted (
1174                                                         new UploadDataCompletedEventArgs (null, null, true, args [3]));
1175                                         } catch (Exception e){
1176                                                 OnUploadDataCompleted (
1177                                                         new UploadDataCompletedEventArgs (null, e, false, args [3]));
1178                                         }});
1179                                 object [] cb_args = new object [] {address, method, data,  userToken};
1180                                 async_thread.Start (cb_args);
1181                         }
1182                 }
1183
1184                 //    UploadFileAsync
1185
1186                 public void UploadFileAsync (Uri address, string fileName)
1187                 {
1188                         UploadFileAsync (address, null, fileName);
1189                 }
1190
1191                 public void UploadFileAsync (Uri address, string method, string fileName)
1192                 {
1193                         UploadFileAsync (address, method, fileName, null);
1194                 }
1195
1196                 public void UploadFileAsync (Uri address, string method, string fileName, object userToken)
1197                 {
1198                         if (address == null)
1199                                 throw new ArgumentNullException ("address");
1200                         if (fileName == null)
1201                                 throw new ArgumentNullException ("fileName");
1202
1203                         lock (this) {
1204                                 SetBusy ();
1205                                 async = true;
1206
1207                                 async_thread = new Thread (delegate (object state) {
1208                                         object [] args = (object []) state;
1209                                         byte [] data;
1210
1211                                         try {
1212                                                 data = UploadFileCore ((Uri) args [0], (string) args [1], (string) args [2], args [3]);
1213                                                 OnUploadFileCompleted (
1214                                                         new UploadFileCompletedEventArgs (data, null, false, args [3]));
1215                                         } catch (ThreadInterruptedException){
1216                                                 OnUploadFileCompleted (
1217                                                         new UploadFileCompletedEventArgs (null, null, true, args [3]));
1218                                         } catch (Exception e){
1219                                                 OnUploadFileCompleted (
1220                                                         new UploadFileCompletedEventArgs (null, e, false, args [3]));
1221                                         }});
1222                                 object [] cb_args = new object [] {address, method, fileName,  userToken};
1223                                 async_thread.Start (cb_args);
1224                         }
1225                 }
1226
1227                 //    UploadStringAsync
1228
1229                 public void UploadStringAsync (Uri address, string data)
1230                 {
1231                         UploadStringAsync (address, null, data);
1232                 }
1233
1234                 public void UploadStringAsync (Uri address, string method, string data)
1235                 {
1236                         UploadStringAsync (address, method, data, null);
1237                 }
1238
1239                 public void UploadStringAsync (Uri address, string method, string data, object userToken)
1240                 {
1241                         if (address == null)
1242                                 throw new ArgumentNullException ("address");
1243                         if (data == null)
1244                                 throw new ArgumentNullException ("data");
1245                         
1246                         lock (this) {
1247                                 CheckBusy ();
1248                                 async = true;
1249                                 
1250                                 async_thread = new Thread (delegate (object state) {
1251                                         object [] args = (object []) state;
1252
1253                                         try {
1254                                                 string data2 = UploadString ((Uri) args [0], (string) args [1], (string) args [2]);
1255                                                 OnUploadStringCompleted (
1256                                                         new UploadStringCompletedEventArgs (data2, null, false, args [3]));
1257                                         } catch (ThreadInterruptedException){
1258                                                 OnUploadStringCompleted (
1259                                                         new UploadStringCompletedEventArgs (null, null, true, args [3]));
1260                                         } catch (Exception e){
1261                                                 OnUploadStringCompleted (
1262                                                         new UploadStringCompletedEventArgs (null, e, false, args [3]));
1263                                         }});
1264                                 object [] cb_args = new object [] {address, method, data, userToken};
1265                                 async_thread.Start (cb_args);
1266                         }
1267                 }
1268
1269                 //    UploadValuesAsync
1270
1271                 public void UploadValuesAsync (Uri address, NameValueCollection values)
1272                 {
1273                         UploadValuesAsync (address, null, values);
1274                 }
1275
1276                 public void UploadValuesAsync (Uri address, string method, NameValueCollection values)
1277                 {
1278                         UploadValuesAsync (address, method, values, null);
1279                 }
1280
1281                 public void UploadValuesAsync (Uri address, string method, NameValueCollection values, object userToken)
1282                 {
1283                         if (address == null)
1284                                 throw new ArgumentNullException ("address");
1285                         if (values == null)
1286                                 throw new ArgumentNullException ("values");
1287
1288                         lock (this) {
1289                                 CheckBusy ();
1290                                 async = true;
1291
1292                                 async_thread = new Thread (delegate (object state) {
1293                                         object [] args = (object []) state;
1294                                         try {
1295                                                 byte [] data = UploadValuesCore ((Uri) args [0], (string) args [1], (NameValueCollection) args [2], args [3]);
1296                                                 OnUploadValuesCompleted (
1297                                                         new UploadValuesCompletedEventArgs (data, null, false, args [3]));
1298                                         } catch (ThreadInterruptedException){
1299                                                 OnUploadValuesCompleted (
1300                                                         new UploadValuesCompletedEventArgs (null, null, true, args [3]));
1301                                         } catch (Exception e){
1302                                                 OnUploadValuesCompleted (
1303                                                         new UploadValuesCompletedEventArgs (null, e, false, args [3]));
1304                                         }});
1305                                 object [] cb_args = new object [] {address, method, values,  userToken};
1306                                 async_thread.Start (cb_args);
1307                         }
1308                 }
1309
1310                 protected virtual void OnDownloadDataCompleted (DownloadDataCompletedEventArgs args)
1311                 {
1312                         CompleteAsync ();
1313                         if (DownloadDataCompleted != null)
1314                                 DownloadDataCompleted (this, args);
1315                 }
1316
1317                 protected virtual void OnDownloadFileCompleted (AsyncCompletedEventArgs args)
1318                 {
1319                         CompleteAsync ();
1320                         if (DownloadFileCompleted != null)
1321                                 DownloadFileCompleted (this, args);
1322                 }
1323
1324                 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
1325                 {
1326                         if (DownloadProgressChanged != null)
1327                                 DownloadProgressChanged (this, e);
1328                 }
1329
1330                 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
1331                 {
1332                         CompleteAsync ();
1333                         if (DownloadStringCompleted != null)
1334                                 DownloadStringCompleted (this, args);
1335                 }
1336
1337                 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
1338                 {
1339                         CompleteAsync ();
1340                         if (OpenReadCompleted != null)
1341                                 OpenReadCompleted (this, args);
1342                 }
1343
1344                 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
1345                 {
1346                         CompleteAsync ();
1347                         if (OpenWriteCompleted != null)
1348                                 OpenWriteCompleted (this, args);
1349                 }
1350
1351                 protected virtual void OnUploadDataCompleted (UploadDataCompletedEventArgs args)
1352                 {
1353                         CompleteAsync ();
1354                         if (UploadDataCompleted != null)
1355                                 UploadDataCompleted (this, args);
1356                 }
1357
1358                 protected virtual void OnUploadFileCompleted (UploadFileCompletedEventArgs args)
1359                 {
1360                         CompleteAsync ();
1361                         if (UploadFileCompleted != null)
1362                                 UploadFileCompleted (this, args);
1363                 }
1364
1365                 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
1366                 {
1367                         if (UploadProgressChanged != null)
1368                                 UploadProgressChanged (this, e);
1369                 }
1370
1371                 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
1372                 {
1373                         CompleteAsync ();
1374                         if (UploadStringCompleted != null)
1375                                 UploadStringCompleted (this, args);
1376                 }
1377
1378                 protected virtual void OnUploadValuesCompleted (UploadValuesCompletedEventArgs args)
1379                 {
1380                         CompleteAsync ();
1381                         if (UploadValuesCompleted != null)
1382                                 UploadValuesCompleted (this, args);
1383                 }
1384
1385                 protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
1386                 {
1387                         WebResponse response = request.EndGetResponse (result);
1388                         responseHeaders = response.Headers;
1389                         return response;
1390                 }
1391
1392                 protected virtual WebRequest GetWebRequest (Uri address)
1393                 {
1394                         return WebRequest.Create (address);
1395                 }
1396
1397                 protected virtual WebResponse GetWebResponse (WebRequest request)
1398                 {
1399                         WebResponse response = request.GetResponse ();
1400                         responseHeaders = response.Headers;
1401                         return response;
1402                 }
1403
1404         }
1405 }
1406