2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Net / System.Net / WebClient_2_1.cs
blob1487adc40b62382f791f2bfd94b73d82e2829099
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 // 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:
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.
35 using System.IO;
36 using System.Text;
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;
46 string baseAddress;
47 bool is_busy;
48 Encoding encoding = Encoding.UTF8;
49 bool allow_read_buffering = true;
50 bool allow_write_buffering = true;
51 WebRequest request;
52 object locker;
53 CallbackData callback_data;
55 public WebClient ()
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;
66 // Properties
68 public string BaseAddress {
69 get { return baseAddress; }
70 set {
71 if (String.IsNullOrEmpty (value)) {
72 baseAddress = String.Empty;
73 } else {
74 Uri uri = null;
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 {
88 get {
89 if (headers == null)
90 headers = new WebHeaderCollection ();
92 return headers;
94 set { headers = value; }
97 public WebHeaderCollection ResponseHeaders {
98 get { return responseHeaders; }
101 public Encoding Encoding {
102 get { return encoding; }
103 set {
104 if (value == null)
105 throw new ArgumentNullException ("value");
106 encoding = value;
110 public bool IsBusy {
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; }
120 // new in SL4 RC
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 {
128 get; set;
131 // Methods
133 void CheckBusy ()
135 if (IsBusy)
136 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
139 void SetBusy ()
141 lock (locker) {
142 CheckBusy ();
143 is_busy = true;
147 private string DetermineMethod (Uri address, string method)
149 if (method != null)
150 return method;
152 if (address.Scheme == Uri.UriSchemeFtp)
153 return "RETR";
154 return "POST";
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]);
172 return request;
175 Stream ProcessResponse (WebResponse response)
177 responseHeaders = response.Headers;
178 HttpWebResponse hwr = (response as HttpWebResponse);
179 if (hwr == null)
180 throw new NotSupportedException ();
182 HttpStatusCode status_code = HttpStatusCode.NotFound;
183 Stream s = null;
184 try {
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);
192 finally {
193 if (status_code != HttpStatusCode.OK)
194 throw new WebException ("NotFound", null, WebExceptionStatus.UnknownError, response);
196 return s;
199 public void CancelAsync ()
201 if (request != null)
202 request.Abort ();
205 void CompleteAsync ()
207 is_busy = false;
210 class CallbackData {
211 public object user_token;
212 public SynchronizationContext sync_context;
213 public byte [] data;
214 public CallbackData (object user_token, byte [] data)
216 this.user_token = user_token;
217 this.data = data;
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)
234 if (address == null)
235 throw new ArgumentNullException ("address");
237 lock (locker) {
238 SetBusy ();
240 try {
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)
254 string data = null;
255 Exception ex = null;
256 bool cancel = false;
257 try {
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);
267 ex = web;
269 catch (Exception e) {
270 ex = e;
272 finally {
273 callback_data.sync_context.Post (delegate (object sender) {
274 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (data, ex, cancel, callback_data.user_token));
275 }, null);
279 // OpenReadAsync
281 public void OpenReadAsync (Uri address)
283 OpenReadAsync (address, null);
286 public void OpenReadAsync (Uri address, object userToken)
288 if (address == null)
289 throw new ArgumentNullException ("address");
291 lock (locker) {
292 SetBusy ();
294 try {
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;
309 Exception ex = null;
310 bool cancel = false;
311 try {
312 WebResponse response = request.EndGetResponse (result);
313 stream = ProcessResponse (response);
315 catch (WebException web) {
316 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
317 ex = web;
319 catch (Exception e) {
320 ex = e;
322 finally {
323 callback_data.sync_context.Post (delegate (object sender) {
324 OnOpenReadCompleted (new OpenReadCompletedEventArgs (stream, ex, cancel, callback_data.user_token));
325 }, null);
329 // OpenWriteAsync
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)
343 if (address == null)
344 throw new ArgumentNullException ("address");
346 lock (locker) {
347 SetBusy ();
349 try {
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;
364 Exception ex = null;
365 bool cancel = false;
366 InternalWebRequestStreamWrapper internal_stream;
368 try {
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);
376 ex = web;
378 catch (Exception e) {
379 ex = e;
381 finally {
382 callback_data.sync_context.Post (delegate (object sender) {
383 OnOpenWriteCompleted (new OpenWriteCompletedEventArgs (stream, ex, cancel, callback_data.user_token));
384 }, null);
388 internal void WriteStreamClosedCallback (object WebClientData)
390 try {
391 request.BeginGetResponse (OpenWriteAsyncResponseCallback, WebClientData);
393 catch (Exception e) {
394 callback_data.sync_context.Post (delegate (object sender) {
395 OnWriteStreamClosed (new WriteStreamClosedEventArgs (e));
396 }, null);
400 private void OpenWriteAsyncResponseCallback (IAsyncResult result)
402 try {
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));
409 }, null);
413 // UploadStringAsync
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)
427 if (address == null)
428 throw new ArgumentNullException ("address");
429 if (data == null)
430 throw new ArgumentNullException ("data");
432 lock (locker) {
433 SetBusy ();
435 try {
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)
449 try {
450 Stream stream = request.EndGetRequestStream (result);
451 stream.Write (callback_data.data, 0, callback_data.data.Length);
452 request.BeginGetResponse (new AsyncCallback (UploadStringResponseAsyncCallback), null);
454 catch {
455 request.Abort ();
456 throw;
460 private void UploadStringResponseAsyncCallback (IAsyncResult result)
462 string data = null;
463 Exception ex = null;
464 bool cancel = false;
465 try {
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);
475 ex = web;
477 catch (InvalidOperationException ioe) {
478 ex = new WebException ("An exception occurred during a WebClient request", ioe);
480 catch (Exception e) {
481 ex = e;
483 finally {
484 callback_data.sync_context.Post (delegate (object sender) {
485 OnUploadStringCompleted (new UploadStringCompletedEventArgs (data, ex, cancel, callback_data.user_token));
486 }, null);
490 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
492 DownloadProgressChangedEventHandler handler = DownloadProgressChanged;
493 if (handler != null)
494 handler (this, e);
497 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
499 CompleteAsync ();
500 OpenReadCompletedEventHandler handler = OpenReadCompleted;
501 if (handler != null)
502 handler (this, args);
505 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
507 CompleteAsync ();
508 DownloadStringCompletedEventHandler handler = DownloadStringCompleted;
509 if (handler != null)
510 handler (this, args);
513 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
515 CompleteAsync ();
516 OpenWriteCompletedEventHandler handler = OpenWriteCompleted;
517 if (handler != null)
518 handler (this, args);
521 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
523 UploadProgressChangedEventHandler handler = UploadProgressChanged;
524 if (handler != null)
525 handler (this, e);
528 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
530 CompleteAsync ();
531 UploadStringCompletedEventHandler handler = UploadStringCompleted;
532 if (handler != null)
533 handler (this, args);
536 protected virtual void OnWriteStreamClosed (WriteStreamClosedEventArgs e)
538 CompleteAsync ();
539 WriteStreamClosedEventHandler handler = WriteStreamClosed;
540 if (handler != null)
541 handler (this, e);
544 protected virtual WebRequest GetWebRequest (Uri address)
546 if (address == null)
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));
560 }, null);
563 return request;
566 protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
568 return request.EndGetResponse (result);