Merge pull request #271 from pruiz/xamarin-bug-4108
[mono.git] / mcs / class / System.Net.Http / System.Net.Http / HttpClient.cs
1 //
2 // HttpClient.cs
3 //
4 // Authors:
5 //      Marek Safar  <marek.safar@gmail.com>
6 //
7 // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Threading;
30 using System.Net.Http.Headers;
31 using System.Threading.Tasks;
32 using System.IO;
33
34 namespace System.Net.Http
35 {
36         public class HttpClient : IDisposable
37         {
38                 static readonly TimeSpan TimeoutDefault = TimeSpan.FromSeconds (100);
39
40                 Uri base_address;
41                 CancellationTokenSource cancellation_token;
42                 bool disposed;
43                 readonly HttpMessageHandler handler;
44                 HttpRequestHeaders headers;
45                 int buffer_size;
46                 TimeSpan timeout;
47
48                 public HttpClient ()
49                         : this (null)
50                 {
51                 }
52
53                 public HttpClient (HttpMessageHandler handler)
54                 {
55                         this.handler = handler ?? new HttpClientHandler ();
56                         buffer_size = 0x10000;
57                         timeout = TimeoutDefault;
58                 }
59
60                 public Uri BaseAddress {
61                         get {
62                                 return base_address;
63                         }
64                         set {
65                                 base_address = value;
66                         }
67                 }
68
69                 public HttpRequestHeaders DefaultRequestHeaders {
70                         get {
71                                 return headers ?? (headers = new HttpRequestHeaders ());
72                         }
73                 }
74
75                 public int MaxResponseContentBufferSize {
76                         get {
77                                 return buffer_size;
78                         }
79                         set {
80                                 if (value <= 0)
81                                         throw new ArgumentOutOfRangeException ();
82
83                                 buffer_size = value;
84                         }
85                 }
86
87                 public TimeSpan Timeout {
88                         get {
89                                 return timeout;
90                         }
91                         set {
92                                 if (value != System.Threading.Timeout.InfiniteTimeSpan && value < TimeSpan.Zero)
93                                         throw new ArgumentOutOfRangeException ();
94
95                                 timeout = value;
96                         }
97                 }
98
99                 public void CancelPendingRequests ()
100                 {
101                         if (cancellation_token != null)
102                                 cancellation_token.Cancel ();
103
104                         cancellation_token = new CancellationTokenSource ();
105                 }
106  
107                 public void Dispose ()
108                 {
109                         Dispose (true);
110                 }
111                 
112                 protected virtual void Dispose (bool disposing)
113                 {
114                         if (disposing && !disposed) {
115                                 disposed = true;
116
117                                 if (cancellation_token != null)
118                                         cancellation_token.Dispose ();
119                         }
120                 }
121
122                 public Task<HttpResponseMessage> DeleteAsync (string requestUri)
123                 {
124                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri));
125                 }
126
127                 public Task<HttpResponseMessage> DeleteAsync (string requestUri, CancellationToken cancellationToken)
128                 {
129                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri), cancellationToken);
130                 }
131
132                 public Task<HttpResponseMessage> DeleteAsync (Uri requestUri)
133                 {
134                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri));
135                 }
136
137                 public Task<HttpResponseMessage> DeleteAsync (Uri requestUri, CancellationToken cancellationToken)
138                 {
139                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri), cancellationToken);
140                 }
141
142                 public Task<HttpResponseMessage> GetAsync (string requestUri)
143                 {
144                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
145                 }
146
147                 public Task<HttpResponseMessage> GetAsync (string requestUri, CancellationToken cancellationToken)
148                 {
149                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
150                 }
151
152                 public Task<HttpResponseMessage> GetAsync (string requestUri, HttpCompletionOption completionOption)
153                 {
154                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption);
155                 }
156
157                 public Task<HttpResponseMessage> GetAsync (string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
158                 {
159                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption, cancellationToken);
160                 }
161
162                 public Task<HttpResponseMessage> GetAsync (Uri requestUri)
163                 {
164                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
165                 }
166
167                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, CancellationToken cancellationToken)
168                 {
169                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), cancellationToken);
170                 }
171
172                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, HttpCompletionOption completionOption)
173                 {
174                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption);
175                 }
176
177                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
178                 {
179                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption, cancellationToken);
180                 }
181
182                 public Task<HttpResponseMessage> PostAsync (string requestUri, HttpContent content)
183                 {
184                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content });
185                 }
186
187                 public Task<HttpResponseMessage> PostAsync (string requestUri, HttpContent content, CancellationToken cancellationToken)
188                 {
189                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content }, cancellationToken);
190                 }
191
192                 public Task<HttpResponseMessage> PostAsync (Uri requestUri, HttpContent content)
193                 {
194                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content });
195                 }
196
197                 public Task<HttpResponseMessage> PostAsync (Uri requestUri, HttpContent content, CancellationToken cancellationToken)
198                 {
199                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content }, cancellationToken);
200                 }
201
202                 public Task<HttpResponseMessage> PutAsync (Uri requestUri, HttpContent content)
203                 {
204                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content });
205                 }
206
207                 public Task<HttpResponseMessage> PutAsync (Uri requestUri, HttpContent content, CancellationToken cancellationToken)
208                 {
209                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content }, cancellationToken);
210                 }
211
212                 public Task<HttpResponseMessage> PutAsync (string requestUri, HttpContent content)
213                 {
214                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content });
215                 }
216
217                 public Task<HttpResponseMessage> PutAsync (string requestUri, HttpContent content, CancellationToken cancellationToken)
218                 {
219                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content }, cancellationToken);
220                 }
221
222                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request)
223                 {
224                         return SendAsync (request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
225                 }
226
227                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, HttpCompletionOption completionOption)
228                 {
229                         return SendAsync (request, completionOption, CancellationToken.None);
230                 }
231
232                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
233                 {
234                         return SendAsync (request, HttpCompletionOption.ResponseContentRead, cancellationToken);
235                 }
236
237                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
238                 {
239                         if (request == null)
240                                 throw new ArgumentNullException ("request");
241
242                         if (request.SetIsUsed ())
243                                 throw new InvalidOperationException ("Cannot send the same request message multiple times");
244
245                         if (request.RequestUri == null) {
246                                 if (base_address == null)
247                                         throw new InvalidOperationException ("The request URI must either be an absolute URI or BaseAddress must be set");
248
249                                 request.RequestUri = base_address;
250                         }
251
252                         if (headers != null) {
253                                 request.Headers.AddHeaders (headers);
254                         }
255
256                         return SendAsyncWorker (request, completionOption, cancellationToken);
257                 }
258
259                 async Task<HttpResponseMessage> SendAsyncWorker (HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
260                 {
261                         try {
262                                 if (cancellation_token == null)
263                                         cancellation_token = new CancellationTokenSource ();
264
265                                 using (var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellation_token.Token, cancellationToken)) {
266                                         cts.CancelAfter (timeout);
267
268                                         var task = handler.SendAsync (request, cts.Token);
269                                         if (task == null)
270                                                 throw new InvalidOperationException ("Handler failed to return a value");
271                                         
272                                         var response = await task.ConfigureAwait (false);
273                                         if (response == null)
274                                                 throw new InvalidOperationException ("Handler failed to return a response");
275
276                                         //
277                                         // Read the content when default HttpCompletionOption.ResponseContentRead is set
278                                         //
279                                         if (response.Content != null && (completionOption & HttpCompletionOption.ResponseHeadersRead) == 0) {
280                                                 await response.Content.LoadIntoBufferAsync (MaxResponseContentBufferSize).ConfigureAwait (false);
281                                         }
282                                         
283                                         return response;
284                                 }
285                         } finally {
286                                 cancellation_token.Dispose ();
287                                 cancellation_token = null;
288                         }
289                 }
290
291                 public async Task<byte[]> GetByteArrayAsync (string requestUri)
292                 {
293                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
294                         return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
295                 }
296
297                 public async Task<byte[]> GetByteArrayAsync (Uri requestUri)
298                 {
299                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
300                         return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
301                 }
302
303                 public async Task<Stream> GetStreamAsync (string requestUri)
304                 {
305                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
306                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
307                 }
308
309                 public async Task<Stream> GetStreamAsync (Uri requestUri)
310                 {
311                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
312                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
313                 }
314
315                 public async Task<string> GetStringAsync (string requestUri)
316                 {
317                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
318                         return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
319                 }
320
321                 public async Task<string> GetStringAsync (Uri requestUri)
322                 {
323                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
324                         return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
325                 }
326         }
327 }