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 ();
67 public string BaseAddress {
68 get { return baseAddress; }
70 if (String.IsNullOrEmpty (value)) {
71 baseAddress = String.Empty;
74 if (!Uri.TryCreate (value, UriKind.Absolute, out uri))
75 throw new ArgumentException ("Invalid URI");
77 baseAddress = Uri.UnescapeDataString (uri.AbsoluteUri);
82 [MonoTODO ("provide credentials to the client stack")]
83 public ICredentials Credentials { get; set; }
85 // this is an unvalidated collection, HttpWebRequest is responsable to validate it
86 public WebHeaderCollection Headers {
89 headers = new WebHeaderCollection ();
93 set { headers = value; }
96 public WebHeaderCollection ResponseHeaders {
97 get { return responseHeaders; }
100 public Encoding Encoding {
101 get { return encoding; }
104 throw new ArgumentNullException ("value");
110 get { return is_busy; }
113 [MonoTODO ("value is unused, current implementation always works like it's true (default)")]
114 public bool AllowReadStreamBuffering {
115 get { return allow_read_buffering; }
116 set { allow_read_buffering = value; }
120 [MonoTODO ("value is unused, current implementation always works like it's true (default)")]
121 public bool AllowWriteStreamBuffering {
122 get { return allow_write_buffering; }
123 set { allow_write_buffering = value; }
126 public bool UseDefaultCredentials {
135 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
146 private string DetermineMethod (Uri address, string method)
151 if (address.Scheme == Uri.UriSchemeFtp)
156 public event DownloadProgressChangedEventHandler DownloadProgressChanged;
157 public event DownloadStringCompletedEventHandler DownloadStringCompleted;
158 public event OpenReadCompletedEventHandler OpenReadCompleted;
159 public event OpenWriteCompletedEventHandler OpenWriteCompleted;
160 public event UploadProgressChangedEventHandler UploadProgressChanged;
161 public event UploadStringCompletedEventHandler UploadStringCompleted;
162 public event WriteStreamClosedEventHandler WriteStreamClosed;
164 WebRequest SetupRequest (Uri uri, string method, CallbackData callbackData)
166 callback_data = callbackData;
167 WebRequest request = GetWebRequest (uri);
168 request.Method = DetermineMethod (uri, method);
169 foreach (string header in Headers.AllKeys)
170 request.Headers.SetHeader (header, Headers [header]);
174 Stream ProcessResponse (WebResponse response)
176 responseHeaders = response.Headers;
177 HttpWebResponse hwr = (response as HttpWebResponse);
179 throw new NotSupportedException ();
181 HttpStatusCode status_code = HttpStatusCode.NotFound;
184 status_code = hwr.StatusCode;
185 if (status_code == HttpStatusCode.OK)
186 s = response.GetResponseStream ();
188 catch (Exception e) {
189 throw new WebException ("NotFound", e, WebExceptionStatus.UnknownError, response);
192 if (status_code != HttpStatusCode.OK)
193 throw new WebException ("NotFound", null, WebExceptionStatus.UnknownError, response);
198 public void CancelAsync ()
204 void CompleteAsync ()
210 public object user_token;
211 public SynchronizationContext sync_context;
213 public CallbackData (object user_token, byte [] data)
215 this.user_token = user_token;
217 this.sync_context = SynchronizationContext.Current ?? new SynchronizationContext ();
219 public CallbackData (object user_token) : this (user_token, null)
224 // DownloadStringAsync
226 public void DownloadStringAsync (Uri address)
228 DownloadStringAsync (address, null);
231 public void DownloadStringAsync (Uri address, object userToken)
234 throw new ArgumentNullException ("address");
240 request = SetupRequest (address, "GET", new CallbackData (userToken));
241 request.BeginGetResponse (new AsyncCallback (DownloadStringAsyncCallback), null);
243 catch (Exception e) {
244 WebException wex = new WebException ("Could not start operation.", e);
245 OnDownloadStringCompleted (
246 new DownloadStringCompletedEventArgs (null, wex, false, userToken));
251 private void DownloadStringAsyncCallback (IAsyncResult result)
257 WebResponse response = request.EndGetResponse (result);
258 Stream stream = ProcessResponse (response);
260 using (StreamReader sr = new StreamReader (stream, encoding, true)) {
261 data = sr.ReadToEnd ();
264 catch (WebException web) {
265 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
268 catch (Exception e) {
272 callback_data.sync_context.Post (delegate (object sender) {
273 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (data, ex, cancel, callback_data.user_token));
280 public void OpenReadAsync (Uri address)
282 OpenReadAsync (address, null);
285 public void OpenReadAsync (Uri address, object userToken)
288 throw new ArgumentNullException ("address");
294 request = SetupRequest (address, "GET", new CallbackData (userToken));
295 request.BeginGetResponse (new AsyncCallback (OpenReadAsyncCallback), null);
297 catch (Exception e) {
298 WebException wex = new WebException ("Could not start operation.", e);
299 OnOpenReadCompleted (
300 new OpenReadCompletedEventArgs (null, wex, false, userToken));
305 private void OpenReadAsyncCallback (IAsyncResult result)
307 Stream stream = null;
311 WebResponse response = request.EndGetResponse (result);
312 stream = ProcessResponse (response);
314 catch (WebException web) {
315 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
318 catch (Exception e) {
322 callback_data.sync_context.Post (delegate (object sender) {
323 OnOpenReadCompleted (new OpenReadCompletedEventArgs (stream, ex, cancel, callback_data.user_token));
330 public void OpenWriteAsync (Uri address)
332 OpenWriteAsync (address, null);
335 public void OpenWriteAsync (Uri address, string method)
337 OpenWriteAsync (address, method, null);
340 public void OpenWriteAsync (Uri address, string method, object userToken)
343 throw new ArgumentNullException ("address");
349 request = SetupRequest (address, method, new CallbackData (userToken));
350 request.BeginGetRequestStream (new AsyncCallback (OpenWriteAsyncCallback), null);
352 catch (Exception e) {
353 WebException wex = new WebException ("Could not start operation.", e);
354 OnOpenWriteCompleted (
355 new OpenWriteCompletedEventArgs (null, wex, false, userToken));
360 private void OpenWriteAsyncCallback (IAsyncResult result)
362 Stream stream = null;
365 InternalWebRequestStreamWrapper internal_stream;
368 stream = request.EndGetRequestStream (result);
369 internal_stream = (InternalWebRequestStreamWrapper) stream;
370 internal_stream.WebClient = this;
371 internal_stream.WebClientData = callback_data;
373 catch (WebException web) {
374 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
377 catch (Exception e) {
381 callback_data.sync_context.Post (delegate (object sender) {
382 OnOpenWriteCompleted (new OpenWriteCompletedEventArgs (stream, ex, cancel, callback_data.user_token));
387 internal void WriteStreamClosedCallback (object WebClientData)
390 request.BeginGetResponse (OpenWriteAsyncResponseCallback, WebClientData);
392 catch (Exception e) {
393 callback_data.sync_context.Post (delegate (object sender) {
394 OnWriteStreamClosed (new WriteStreamClosedEventArgs (e));
399 private void OpenWriteAsyncResponseCallback (IAsyncResult result)
402 WebResponse response = request.EndGetResponse (result);
403 ProcessResponse (response);
405 catch (Exception e) {
406 callback_data.sync_context.Post (delegate (object sender) {
407 OnWriteStreamClosed (new WriteStreamClosedEventArgs (e));
414 public void UploadStringAsync (Uri address, string data)
416 UploadStringAsync (address, null, data);
419 public void UploadStringAsync (Uri address, string method, string data)
421 UploadStringAsync (address, method, data, null);
424 public void UploadStringAsync (Uri address, string method, string data, object userToken)
427 throw new ArgumentNullException ("address");
429 throw new ArgumentNullException ("data");
435 request = SetupRequest (address, method, new CallbackData (userToken, encoding.GetBytes (data)));
436 request.BeginGetRequestStream (new AsyncCallback (UploadStringRequestAsyncCallback), null);
438 catch (Exception e) {
439 WebException wex = new WebException ("Could not start operation.", e);
440 OnUploadStringCompleted (
441 new UploadStringCompletedEventArgs (null, wex, false, userToken));
446 private void UploadStringRequestAsyncCallback (IAsyncResult result)
449 Stream stream = request.EndGetRequestStream (result);
450 stream.Write (callback_data.data, 0, callback_data.data.Length);
451 request.BeginGetResponse (new AsyncCallback (UploadStringResponseAsyncCallback), null);
459 private void UploadStringResponseAsyncCallback (IAsyncResult result)
465 WebResponse response = request.EndGetResponse (result);
466 Stream stream = ProcessResponse (response);
468 using (StreamReader sr = new StreamReader (stream, encoding, true)) {
469 data = sr.ReadToEnd ();
472 catch (WebException web) {
473 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
476 catch (InvalidOperationException ioe) {
477 ex = new WebException ("An exception occurred during a WebClient request", ioe);
479 catch (Exception e) {
483 callback_data.sync_context.Post (delegate (object sender) {
484 OnUploadStringCompleted (new UploadStringCompletedEventArgs (data, ex, cancel, callback_data.user_token));
489 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
491 DownloadProgressChangedEventHandler handler = DownloadProgressChanged;
496 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
499 OpenReadCompletedEventHandler handler = OpenReadCompleted;
501 handler (this, args);
504 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
507 DownloadStringCompletedEventHandler handler = DownloadStringCompleted;
509 handler (this, args);
512 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
515 OpenWriteCompletedEventHandler handler = OpenWriteCompleted;
517 handler (this, args);
520 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
522 UploadProgressChangedEventHandler handler = UploadProgressChanged;
527 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
530 UploadStringCompletedEventHandler handler = UploadStringCompleted;
532 handler (this, args);
535 protected virtual void OnWriteStreamClosed (WriteStreamClosedEventArgs e)
538 WriteStreamClosedEventHandler handler = WriteStreamClosed;
543 protected virtual WebRequest GetWebRequest (Uri address)
546 throw new ArgumentNullException ("address");
548 // if the URI is relative then we use our base address URI to make an absolute one
549 Uri uri = address.IsAbsoluteUri ? address : new Uri (new Uri (baseAddress), address);
551 WebRequest request = WebRequest.Create (uri);
553 request.progress = delegate (long read, long length) {
554 callback_data.sync_context.Post (delegate (object sender) {
555 OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (read, length, callback_data.user_token));
562 protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
564 return request.EndGetResponse (result);