2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System.Net / System.Net / WebClient_2_1.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 //      Stephane Delcroix (sdelcroix@novell.com)
10 //
11 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
12 // Copyright 2006, 2008, 2009 Novell, Inc. (http://www.novell.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 using System.IO;
36 using System.Text;
37
38 namespace System.Net {
39
40         // note: this type is effectively sealed to transparent code since it's default .ctor is marked with [SecuritySafeCritical]
41         public class WebClient {
42
43                 private delegate void ProgressChangedDelegate (long read, long length, object state);
44
45                 WebHeaderCollection headers;
46                 WebHeaderCollection responseHeaders;
47                 Uri baseAddress;
48                 string baseString;
49                 bool is_busy;
50                 Encoding encoding = Encoding.UTF8;
51                 bool allow_read_buffering = true;
52                 WebRequest request;
53                 object locker;
54
55                 public WebClient ()
56                 {
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 = new Uri (AppDomain.CurrentDomain.GetData ("xap_uri") as string);
62                         locker = new object ();
63                 }
64                 
65                 // Properties
66                 
67                 public string BaseAddress {
68                         get {
69                                 if (baseString == null) {
70                                         if (baseAddress == null)
71                                                 return String.Empty;
72                                         else
73                                                 baseString = baseAddress.ToString ();
74                                 }
75                                 return baseString;
76                         }
77                         
78                         set {
79                                 if (String.IsNullOrEmpty (value)) {
80                                         baseAddress = null;
81                                 } else {
82                                         baseAddress = new Uri (value);
83                                 }
84                         }
85                 }
86
87                 // this is an unvalidated collection, HttpWebRequest is responsable to validate it
88                 public WebHeaderCollection Headers {
89                         get {
90                                 if (headers == null)
91                                         headers = new WebHeaderCollection ();
92
93                                 return headers;
94                         }
95                         set { headers = value; }
96                 }
97
98                 // note: it's public in SL3 beta 1 - but we need it right now
99                 internal WebHeaderCollection ResponseHeaders {
100                         get { return responseHeaders; }
101                 }
102
103                 public Encoding Encoding {
104                         get { return encoding; }
105                         set {
106                                 if (value == null)
107                                         throw new ArgumentNullException ("value");
108                                 encoding = value;
109                         }
110                 }
111
112                 public bool IsBusy {
113                         get { return is_busy; }
114                 }
115
116                 [MonoTODO ("value is unused, current implementation always works like it's true (default)")]
117                 public bool AllowReadStreamBuffering {
118                         get { return allow_read_buffering; }
119                         set { allow_read_buffering = value; }
120                 }
121
122                 // Methods
123
124                 void CheckBusy ()
125                 {
126                         if (IsBusy)
127                                 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
128                 }
129
130                 void SetBusy ()
131                 {
132                         lock (locker) {
133                                 CheckBusy ();
134                                 is_busy = true;
135                         }
136                 }
137
138                 private string DetermineMethod (Uri address, string method)
139                 {
140                         if (method != null)
141                                 return method;
142
143                         if (address.Scheme == Uri.UriSchemeFtp)
144                                 return "RETR";
145                         return "POST";
146                 }
147
148                 public event DownloadProgressChangedEventHandler DownloadProgressChanged;
149                 public event DownloadStringCompletedEventHandler DownloadStringCompleted;
150                 public event OpenReadCompletedEventHandler OpenReadCompleted;
151                 public event OpenWriteCompletedEventHandler OpenWriteCompleted;
152                 public event UploadProgressChangedEventHandler UploadProgressChanged;
153                 public event UploadStringCompletedEventHandler UploadStringCompleted;
154                 public event WriteStreamClosedEventHandler WriteStreamClosed;
155
156                 WebRequest SetupRequest (Uri uri, string method)
157                 {
158                         WebRequest request = GetWebRequest (uri);
159                         request.Method = DetermineMethod (uri, method);
160                         return request;
161                 }
162
163                 Stream ProcessResponse (WebResponse response)
164                 {
165                         responseHeaders = response.Headers;
166                         HttpWebResponse hwr = (response as HttpWebResponse);
167                         if (hwr == null)
168                                 throw new NotSupportedException ();
169
170                         HttpStatusCode status_code = HttpStatusCode.NotFound;
171                         Stream s = null;
172                         try {
173                                 status_code = hwr.StatusCode;
174                                 if (status_code == HttpStatusCode.OK)
175                                         s = response.GetResponseStream ();
176                         }
177                         catch (Exception e) {
178                                 throw new WebException ("NotFound", e, WebExceptionStatus.UnknownError, response);
179                         }
180                         finally {
181                                 if (status_code != HttpStatusCode.OK)
182                                         throw new WebException ("NotFound", null, WebExceptionStatus.UnknownError, response);
183                         }
184                         return s;
185                 }
186
187                 public void CancelAsync ()
188                 {
189                         if (request != null)
190                                 request.Abort ();
191                 }
192
193                 void CompleteAsync ()
194                 {
195                         lock (locker) {
196                                 is_busy = false;
197                         }
198                 }
199
200                 //    DownloadStringAsync
201
202                 public void DownloadStringAsync (Uri address)
203                 {
204                         DownloadStringAsync (address, null);
205                 }
206
207                 public void DownloadStringAsync (Uri address, object userToken)
208                 {
209                         if (address == null)
210                                 throw new ArgumentNullException ("address");
211
212                         lock (locker) {
213                                 SetBusy ();
214
215                                 try {
216                                         request = SetupRequest (address, "GET");
217                                         request.BeginGetResponse (new AsyncCallback (DownloadStringAsyncCallback), userToken);
218                                 }
219                                 catch (Exception e) {
220                                         WebException wex = new WebException ("Could not start operation.", e);
221                                         OnDownloadStringCompleted (
222                                                 new DownloadStringCompletedEventArgs (null, wex, false, userToken));
223                                 }
224                         }
225                 }
226
227                 private void DownloadStringAsyncCallback (IAsyncResult result)
228                 {
229                         string data = null;
230                         Exception ex = null;
231                         bool cancel = false;
232                         try {
233                                 WebResponse response = request.EndGetResponse (result);
234                                 Stream stream = ProcessResponse (response);
235
236                                 using (StreamReader sr = new StreamReader (stream, encoding, true)) {
237                                         data = sr.ReadToEnd ();
238                                 }
239                         }
240                         catch (WebException web) {
241                                 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
242                                 ex = web;
243                         }
244                         catch (Exception e) {
245                                 ex = e;
246                         }
247                         finally {
248                                 OnDownloadStringCompleted (
249                                         new DownloadStringCompletedEventArgs (data, ex, cancel, result.AsyncState));
250                         }
251                 }
252
253                 //    OpenReadAsync
254
255                 public void OpenReadAsync (Uri address)
256                 {
257                         OpenReadAsync (address, null);
258                 }
259
260                 public void OpenReadAsync (Uri address, object userToken)
261                 {
262                         if (address == null)
263                                 throw new ArgumentNullException ("address");
264
265                         lock (locker) {
266                                 SetBusy ();
267
268                                 try {
269                                         request = SetupRequest (address, "GET");
270                                         request.BeginGetResponse (new AsyncCallback (OpenReadAsyncCallback), userToken);
271                                 }
272                                 catch (Exception e) {
273                                         WebException wex = new WebException ("Could not start operation.", e);
274                                         OnOpenReadCompleted (
275                                                 new OpenReadCompletedEventArgs (null, wex, false, userToken));
276                                 }
277                         }
278                 }
279
280                 private void OpenReadAsyncCallback (IAsyncResult result)
281                 {
282                         Stream stream = null;
283                         Exception ex = null;
284                         bool cancel = false;
285                         try {
286                                 WebResponse response = request.EndGetResponse (result);
287                                 stream = ProcessResponse (response);
288                         }
289                         catch (WebException web) {
290                                 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
291                                 ex = web;
292                         }
293                         catch (Exception e) {
294                                 ex = e;
295                         }
296                         finally {
297                                 OnOpenReadCompleted (
298                                         new OpenReadCompletedEventArgs (stream, ex, cancel, result.AsyncState));
299                         }
300                 }
301
302                 //    OpenWriteAsync
303
304                 public void OpenWriteAsync (Uri address)
305                 {
306                         OpenWriteAsync (address, null);
307                 }
308
309                 public void OpenWriteAsync (Uri address, string method)
310                 {
311                         OpenWriteAsync (address, method, null);
312                 }
313
314                 public void OpenWriteAsync (Uri address, string method, object userToken)
315                 {
316                         if (address == null)
317                                 throw new ArgumentNullException ("address");
318
319                         lock (locker) {
320                                 SetBusy ();
321
322                                 try {
323                                         request = SetupRequest (address, method);
324                                         request.BeginGetRequestStream (new AsyncCallback (OpenWriteAsyncCallback), userToken);
325                                 }
326                                 catch (Exception e) {
327                                         WebException wex = new WebException ("Could not start operation.", e);
328                                         OnOpenWriteCompleted (
329                                                 new OpenWriteCompletedEventArgs (null, wex, false, userToken));
330                                 }
331                         }
332                 }
333
334                 private void OpenWriteAsyncCallback (IAsyncResult result)
335                 {
336                         Stream stream = null;
337                         Exception ex = null;
338                         bool cancel = false;
339                         try {
340                                 stream = request.EndGetRequestStream (result);
341                         }
342                         catch (WebException web) {
343                                 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
344                                 ex = web;
345                         }
346                         catch (Exception e) {
347                                 ex = e;
348                         }
349                         finally {
350                                 OnOpenWriteCompleted (
351                                         new OpenWriteCompletedEventArgs (stream, ex, cancel, result.AsyncState));
352                         }
353                 }
354
355                 //    UploadStringAsync
356
357                 public void UploadStringAsync (Uri address, string data)
358                 {
359                         UploadStringAsync (address, null, data);
360                 }
361
362                 public void UploadStringAsync (Uri address, string method, string data)
363                 {
364                         UploadStringAsync (address, method, data, null);
365                 }
366
367                 public void UploadStringAsync (Uri address, string method, string data, object userToken)
368                 {
369                         if (address == null)
370                                 throw new ArgumentNullException ("address");
371                         if (data == null)
372                                 throw new ArgumentNullException ("data");
373
374                         lock (locker) {
375                                 SetBusy ();
376
377                                 try {
378                                         request = SetupRequest (address, method);
379                                         object[] bag = new object [] { encoding.GetBytes (data), userToken };
380
381                                         request.BeginGetRequestStream (new AsyncCallback (UploadStringRequestAsyncCallback), bag);
382                                 }
383                                 catch (Exception e) {
384                                         WebException wex = new WebException ("Could not start operation.", e);
385                                         OnUploadStringCompleted (
386                                                 new UploadStringCompletedEventArgs (null, wex, false, userToken));
387                                 }
388                         }
389                 }
390
391                 private void UploadStringRequestAsyncCallback (IAsyncResult result)
392                 {
393                         try {
394                                 object[] bag = (result.AsyncState as object[]);
395                                 byte[] data = (bag [0] as byte[]);
396                                 Stream stream = request.EndGetRequestStream (result);
397                                 stream.Write (data, 0, data.Length);
398                                 request.BeginGetResponse (new AsyncCallback (UploadStringResponseAsyncCallback), bag [1]);
399                         }
400                         catch {
401                                 request.Abort ();
402                                 throw;
403                         }
404                 }
405
406                 private void UploadStringResponseAsyncCallback (IAsyncResult result)
407                 {
408                         string data = null;
409                         Exception ex = null;
410                         bool cancel = false;
411                         try {
412                                 WebResponse response = request.EndGetResponse (result);
413                                 Stream stream = ProcessResponse (response);
414
415                                 using (StreamReader sr = new StreamReader (stream, encoding, true)) {
416                                         data = sr.ReadToEnd ();
417                                 }
418                         }
419                         catch (WebException web) {
420                                 cancel = (web.Status == WebExceptionStatus.RequestCanceled);
421                                 ex = web;
422                         }
423                         catch (Exception e) {
424                                 ex = e;
425                         }
426                         finally {
427                                 OnUploadStringCompleted (
428                                         new UploadStringCompletedEventArgs (data, ex, cancel, result.AsyncState));
429                         }
430                 }
431
432                 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
433                 {
434                         if (DownloadProgressChanged != null) {
435                                 DownloadProgressChanged (this, e);
436                         }
437                 }
438                 
439                 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
440                 {
441                         CompleteAsync ();
442                         if (OpenReadCompleted != null) {
443                                 OpenReadCompleted (this, args);
444                         }
445                 }
446
447                 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
448                 {
449                         CompleteAsync ();
450                         if (DownloadStringCompleted != null) {
451                                 DownloadStringCompleted (this, args);
452                         }
453                 }
454
455                 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
456                 {
457                         CompleteAsync ();
458                         if (OpenWriteCompleted != null)
459                                 OpenWriteCompleted (this, args);
460                 }
461
462                 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
463                 {
464                         if (UploadProgressChanged != null)
465                                 UploadProgressChanged (this, e);
466                 }
467
468                 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
469                 {
470                         CompleteAsync ();
471                         if (UploadStringCompleted != null)
472                                 UploadStringCompleted (this, args);
473                 }
474
475                 protected virtual void OnWriteStreamClosed (WriteStreamClosedEventArgs e)
476                 {
477                         throw new NotImplementedException ();
478                 }
479
480                 protected virtual WebRequest GetWebRequest (Uri address)
481                 {
482                         if (address == null)
483                                 throw new ArgumentNullException ("address");
484
485                         // if the URI is relative then we use our base address URI to make an absolute one
486                         Uri uri = address.IsAbsoluteUri ? address : new Uri (baseAddress, address);
487
488                         WebRequest request = WebRequest.Create (uri);
489
490                         request.SetupProgressDelegate ((ProgressChangedDelegate) delegate (long read, long length, object state) {
491                                 OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (read, length, state));
492                         });
493                         return request;
494                 }
495
496                 protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
497                 {
498                         return request.EndGetResponse (result);
499                 }
500         }
501 }
502