2 // System.Net.WebClient
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 // Stephane Delcroix (sdelcroix@novell.com)
11 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
12 // Copyright 2006, 2008, 2009-2010 Novell, Inc. (http://www.novell.com)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
37 using System
.Threading
;
39 namespace System
.Net
{
41 // note: this type is effectively sealed to transparent code since it's default .ctor is marked with [SecuritySafeCritical]
42 public class WebClient
{
44 WebHeaderCollection headers
;
45 WebHeaderCollection responseHeaders
;
48 Encoding encoding
= Encoding
.UTF8
;
49 bool allow_read_buffering
= true;
50 bool allow_write_buffering
= true;
53 CallbackData callback_data
;
57 // kind of calling NativeMethods.plugin_instance_get_source_location (PluginHost.Handle)
58 // but without adding dependency on System.Windows.dll. GetData is [SecurityCritical]
59 // this makes the default .ctor [SecuritySafeCritical] which would be a problem (inheritance)
60 // but it happens that MS SL2 also has this default .ctor as SSC :-)
61 baseAddress
= (AppDomain
.CurrentDomain
.GetData ("xap_uri") as string);
62 locker
= new object ();
63 UseDefaultCredentials
= true;
68 public string BaseAddress
{
69 get { return baseAddress; }
71 if (String
.IsNullOrEmpty (value)) {
72 baseAddress
= String
.Empty
;
75 if (!Uri
.TryCreate (value, UriKind
.Absolute
, out uri
))
76 throw new ArgumentException ("Invalid URI");
78 baseAddress
= Uri
.UnescapeDataString (uri
.AbsoluteUri
);
83 [MonoTODO ("provide credentials to the client stack")]
84 public ICredentials Credentials { get; set; }
86 // this is an unvalidated collection, HttpWebRequest is responsable to validate it
87 public WebHeaderCollection Headers
{
90 headers
= new WebHeaderCollection ();
94 set { headers = value; }
97 public WebHeaderCollection ResponseHeaders
{
98 get { return responseHeaders; }
101 public Encoding Encoding
{
102 get { return encoding; }
105 throw new ArgumentNullException ("value");
111 get { return is_busy; }
114 [MonoTODO ("value is unused, current implementation always works like it's true (default)")]
115 public bool AllowReadStreamBuffering
{
116 get { return allow_read_buffering; }
117 set { allow_read_buffering = value; }
121 [MonoTODO ("value is unused, current implementation always works like it's true (default)")]
122 public bool AllowWriteStreamBuffering
{
123 get { return allow_write_buffering; }
124 set { allow_write_buffering = value; }
127 public bool UseDefaultCredentials
{
136 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
147 private string DetermineMethod (Uri address
, string method
)
152 if (address
.Scheme
== Uri
.UriSchemeFtp
)
157 public event DownloadProgressChangedEventHandler DownloadProgressChanged
;
158 public event DownloadStringCompletedEventHandler DownloadStringCompleted
;
159 public event OpenReadCompletedEventHandler OpenReadCompleted
;
160 public event OpenWriteCompletedEventHandler OpenWriteCompleted
;
161 public event UploadProgressChangedEventHandler UploadProgressChanged
;
162 public event UploadStringCompletedEventHandler UploadStringCompleted
;
163 public event WriteStreamClosedEventHandler WriteStreamClosed
;
165 WebRequest
SetupRequest (Uri uri
, string method
, CallbackData callbackData
)
167 callback_data
= callbackData
;
168 WebRequest request
= GetWebRequest (uri
);
169 request
.Method
= DetermineMethod (uri
, method
);
170 foreach (string header
in Headers
.AllKeys
)
171 request
.Headers
.SetHeader (header
, Headers
[header
]);
175 Stream
ProcessResponse (WebResponse response
)
177 responseHeaders
= response
.Headers
;
178 HttpWebResponse hwr
= (response
as HttpWebResponse
);
180 throw new NotSupportedException ();
182 HttpStatusCode status_code
= HttpStatusCode
.NotFound
;
185 status_code
= hwr
.StatusCode
;
186 if (status_code
== HttpStatusCode
.OK
)
187 s
= response
.GetResponseStream ();
189 catch (Exception e
) {
190 throw new WebException ("NotFound", e
, WebExceptionStatus
.UnknownError
, response
);
193 if (status_code
!= HttpStatusCode
.OK
)
194 throw new WebException ("NotFound", null, WebExceptionStatus
.UnknownError
, response
);
199 public void CancelAsync ()
205 void CompleteAsync ()
211 public object user_token
;
212 public SynchronizationContext sync_context
;
214 public CallbackData (object user_token
, byte [] data
)
216 this.user_token
= user_token
;
218 this.sync_context
= SynchronizationContext
.Current
?? new SynchronizationContext ();
220 public CallbackData (object user_token
) : this (user_token
, null)
225 // DownloadStringAsync
227 public void DownloadStringAsync (Uri address
)
229 DownloadStringAsync (address
, null);
232 public void DownloadStringAsync (Uri address
, object userToken
)
235 throw new ArgumentNullException ("address");
241 request
= SetupRequest (address
, "GET", new CallbackData (userToken
));
242 request
.BeginGetResponse (new AsyncCallback (DownloadStringAsyncCallback
), null);
244 catch (Exception e
) {
245 WebException wex
= new WebException ("Could not start operation.", e
);
246 OnDownloadStringCompleted (
247 new DownloadStringCompletedEventArgs (null, wex
, false, userToken
));
252 private void DownloadStringAsyncCallback (IAsyncResult result
)
258 WebResponse response
= request
.EndGetResponse (result
);
259 Stream stream
= ProcessResponse (response
);
261 using (StreamReader sr
= new StreamReader (stream
, encoding
, true)) {
262 data
= sr
.ReadToEnd ();
265 catch (WebException web
) {
266 cancel
= (web
.Status
== WebExceptionStatus
.RequestCanceled
);
269 catch (Exception e
) {
273 callback_data
.sync_context
.Post (delegate (object sender
) {
274 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (data
, ex
, cancel
, callback_data
.user_token
));
281 public void OpenReadAsync (Uri address
)
283 OpenReadAsync (address
, null);
286 public void OpenReadAsync (Uri address
, object userToken
)
289 throw new ArgumentNullException ("address");
295 request
= SetupRequest (address
, "GET", new CallbackData (userToken
));
296 request
.BeginGetResponse (new AsyncCallback (OpenReadAsyncCallback
), null);
298 catch (Exception e
) {
299 WebException wex
= new WebException ("Could not start operation.", e
);
300 OnOpenReadCompleted (
301 new OpenReadCompletedEventArgs (null, wex
, false, userToken
));
306 private void OpenReadAsyncCallback (IAsyncResult result
)
308 Stream stream
= null;
312 WebResponse response
= request
.EndGetResponse (result
);
313 stream
= ProcessResponse (response
);
315 catch (WebException web
) {
316 cancel
= (web
.Status
== WebExceptionStatus
.RequestCanceled
);
319 catch (Exception e
) {
323 callback_data
.sync_context
.Post (delegate (object sender
) {
324 OnOpenReadCompleted (new OpenReadCompletedEventArgs (stream
, ex
, cancel
, callback_data
.user_token
));
331 public void OpenWriteAsync (Uri address
)
333 OpenWriteAsync (address
, null);
336 public void OpenWriteAsync (Uri address
, string method
)
338 OpenWriteAsync (address
, method
, null);
341 public void OpenWriteAsync (Uri address
, string method
, object userToken
)
344 throw new ArgumentNullException ("address");
350 request
= SetupRequest (address
, method
, new CallbackData (userToken
));
351 request
.BeginGetRequestStream (new AsyncCallback (OpenWriteAsyncCallback
), null);
353 catch (Exception e
) {
354 WebException wex
= new WebException ("Could not start operation.", e
);
355 OnOpenWriteCompleted (
356 new OpenWriteCompletedEventArgs (null, wex
, false, userToken
));
361 private void OpenWriteAsyncCallback (IAsyncResult result
)
363 Stream stream
= null;
366 InternalWebRequestStreamWrapper internal_stream
;
369 stream
= request
.EndGetRequestStream (result
);
370 internal_stream
= (InternalWebRequestStreamWrapper
) stream
;
371 internal_stream
.WebClient
= this;
372 internal_stream
.WebClientData
= callback_data
;
374 catch (WebException web
) {
375 cancel
= (web
.Status
== WebExceptionStatus
.RequestCanceled
);
378 catch (Exception e
) {
382 callback_data
.sync_context
.Post (delegate (object sender
) {
383 OnOpenWriteCompleted (new OpenWriteCompletedEventArgs (stream
, ex
, cancel
, callback_data
.user_token
));
388 internal void WriteStreamClosedCallback (object WebClientData
)
391 request
.BeginGetResponse (OpenWriteAsyncResponseCallback
, WebClientData
);
393 catch (Exception e
) {
394 callback_data
.sync_context
.Post (delegate (object sender
) {
395 OnWriteStreamClosed (new WriteStreamClosedEventArgs (e
));
400 private void OpenWriteAsyncResponseCallback (IAsyncResult result
)
403 WebResponse response
= request
.EndGetResponse (result
);
404 ProcessResponse (response
);
406 catch (Exception e
) {
407 callback_data
.sync_context
.Post (delegate (object sender
) {
408 OnWriteStreamClosed (new WriteStreamClosedEventArgs (e
));
415 public void UploadStringAsync (Uri address
, string data
)
417 UploadStringAsync (address
, null, data
);
420 public void UploadStringAsync (Uri address
, string method
, string data
)
422 UploadStringAsync (address
, method
, data
, null);
425 public void UploadStringAsync (Uri address
, string method
, string data
, object userToken
)
428 throw new ArgumentNullException ("address");
430 throw new ArgumentNullException ("data");
436 request
= SetupRequest (address
, method
, new CallbackData (userToken
, encoding
.GetBytes (data
)));
437 request
.BeginGetRequestStream (new AsyncCallback (UploadStringRequestAsyncCallback
), null);
439 catch (Exception e
) {
440 WebException wex
= new WebException ("Could not start operation.", e
);
441 OnUploadStringCompleted (
442 new UploadStringCompletedEventArgs (null, wex
, false, userToken
));
447 private void UploadStringRequestAsyncCallback (IAsyncResult result
)
450 Stream stream
= request
.EndGetRequestStream (result
);
451 stream
.Write (callback_data
.data
, 0, callback_data
.data
.Length
);
452 request
.BeginGetResponse (new AsyncCallback (UploadStringResponseAsyncCallback
), null);
460 private void UploadStringResponseAsyncCallback (IAsyncResult result
)
466 WebResponse response
= request
.EndGetResponse (result
);
467 Stream stream
= ProcessResponse (response
);
469 using (StreamReader sr
= new StreamReader (stream
, encoding
, true)) {
470 data
= sr
.ReadToEnd ();
473 catch (WebException web
) {
474 cancel
= (web
.Status
== WebExceptionStatus
.RequestCanceled
);
477 catch (InvalidOperationException ioe
) {
478 ex
= new WebException ("An exception occurred during a WebClient request", ioe
);
480 catch (Exception e
) {
484 callback_data
.sync_context
.Post (delegate (object sender
) {
485 OnUploadStringCompleted (new UploadStringCompletedEventArgs (data
, ex
, cancel
, callback_data
.user_token
));
490 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e
)
492 DownloadProgressChangedEventHandler handler
= DownloadProgressChanged
;
497 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args
)
500 OpenReadCompletedEventHandler handler
= OpenReadCompleted
;
502 handler (this, args
);
505 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args
)
508 DownloadStringCompletedEventHandler handler
= DownloadStringCompleted
;
510 handler (this, args
);
513 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args
)
516 OpenWriteCompletedEventHandler handler
= OpenWriteCompleted
;
518 handler (this, args
);
521 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e
)
523 UploadProgressChangedEventHandler handler
= UploadProgressChanged
;
528 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args
)
531 UploadStringCompletedEventHandler handler
= UploadStringCompleted
;
533 handler (this, args
);
536 protected virtual void OnWriteStreamClosed (WriteStreamClosedEventArgs e
)
539 WriteStreamClosedEventHandler handler
= WriteStreamClosed
;
544 protected virtual WebRequest
GetWebRequest (Uri address
)
547 throw new ArgumentNullException ("address");
549 // if the URI is relative then we use our base address URI to make an absolute one
550 Uri uri
= address
.IsAbsoluteUri
? address
: new Uri (new Uri (baseAddress
), address
);
552 HttpWebRequest request
= (HttpWebRequest
) WebRequest
.Create (uri
);
553 request
.AllowReadStreamBuffering
= AllowReadStreamBuffering
;
554 request
.AllowWriteStreamBuffering
= AllowWriteStreamBuffering
;
555 request
.UseDefaultCredentials
= UseDefaultCredentials
;
557 request
.progress
= delegate (long read
, long length
) {
558 callback_data
.sync_context
.Post (delegate (object sender
) {
559 OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (read
, length
, callback_data
.user_token
));
566 protected virtual WebResponse
GetWebResponse (WebRequest request
, IAsyncResult result
)
568 return request
.EndGetResponse (result
);