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