Merge pull request #392 from baulig/master
[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 : HttpMessageInvoker
37         {
38                 static readonly TimeSpan TimeoutDefault = TimeSpan.FromSeconds (100);
39
40                 Uri base_address;
41                 CancellationTokenSource cancellation_token;
42                 bool disposed;
43                 HttpRequestHeaders headers;
44                 long buffer_size;
45                 TimeSpan timeout;
46
47                 public HttpClient ()
48                         : this (new HttpClientHandler (), true)
49                 {
50                 }
51                 
52                 public HttpClient (HttpMessageHandler handler)
53                         : this (handler, true)
54                 {
55                 }
56
57                 public HttpClient (HttpMessageHandler handler, bool disposeHandler)
58                         : base (handler, disposeHandler)
59                 {
60                         buffer_size = int.MaxValue;
61                         timeout = TimeoutDefault;
62                 }
63
64                 public Uri BaseAddress {
65                         get {
66                                 return base_address;
67                         }
68                         set {
69                                 base_address = value;
70                         }
71                 }
72
73                 public HttpRequestHeaders DefaultRequestHeaders {
74                         get {
75                                 return headers ?? (headers = new HttpRequestHeaders ());
76                         }
77                 }
78
79                 public long MaxResponseContentBufferSize {
80                         get {
81                                 return buffer_size;
82                         }
83                         set {
84                                 if (value <= 0)
85                                         throw new ArgumentOutOfRangeException ();
86
87                                 buffer_size = value;
88                         }
89                 }
90
91                 public TimeSpan Timeout {
92                         get {
93                                 return timeout;
94                         }
95                         set {
96                                 if (value != System.Threading.Timeout.InfiniteTimeSpan && value < TimeSpan.Zero)
97                                         throw new ArgumentOutOfRangeException ();
98
99                                 timeout = value;
100                         }
101                 }
102
103                 public void CancelPendingRequests ()
104                 {
105                         if (cancellation_token != null)
106                                 cancellation_token.Cancel ();
107
108                         cancellation_token = new CancellationTokenSource ();
109                 }
110
111                 protected override void Dispose (bool disposing)
112                 {
113                         if (disposing && !disposed) {
114                                 disposed = true;
115
116                                 if (cancellation_token != null)
117                                         cancellation_token.Dispose ();
118                         }
119                         
120                         base.Dispose (disposing);
121                 }
122
123                 public Task<HttpResponseMessage> DeleteAsync (string requestUri)
124                 {
125                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri));
126                 }
127
128                 public Task<HttpResponseMessage> DeleteAsync (string requestUri, CancellationToken cancellationToken)
129                 {
130                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri), cancellationToken);
131                 }
132
133                 public Task<HttpResponseMessage> DeleteAsync (Uri requestUri)
134                 {
135                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri));
136                 }
137
138                 public Task<HttpResponseMessage> DeleteAsync (Uri requestUri, CancellationToken cancellationToken)
139                 {
140                         return SendAsync (new HttpRequestMessage (HttpMethod.Delete, requestUri), cancellationToken);
141                 }
142
143                 public Task<HttpResponseMessage> GetAsync (string requestUri)
144                 {
145                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
146                 }
147
148                 public Task<HttpResponseMessage> GetAsync (string requestUri, CancellationToken cancellationToken)
149                 {
150                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
151                 }
152
153                 public Task<HttpResponseMessage> GetAsync (string requestUri, HttpCompletionOption completionOption)
154                 {
155                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption);
156                 }
157
158                 public Task<HttpResponseMessage> GetAsync (string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
159                 {
160                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption, cancellationToken);
161                 }
162
163                 public Task<HttpResponseMessage> GetAsync (Uri requestUri)
164                 {
165                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri));
166                 }
167
168                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, CancellationToken cancellationToken)
169                 {
170                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), cancellationToken);
171                 }
172
173                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, HttpCompletionOption completionOption)
174                 {
175                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption);
176                 }
177
178                 public Task<HttpResponseMessage> GetAsync (Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
179                 {
180                         return SendAsync (new HttpRequestMessage (HttpMethod.Get, requestUri), completionOption, cancellationToken);
181                 }
182
183                 public Task<HttpResponseMessage> PostAsync (string requestUri, HttpContent content)
184                 {
185                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content });
186                 }
187
188                 public Task<HttpResponseMessage> PostAsync (string requestUri, HttpContent content, CancellationToken cancellationToken)
189                 {
190                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content }, cancellationToken);
191                 }
192
193                 public Task<HttpResponseMessage> PostAsync (Uri requestUri, HttpContent content)
194                 {
195                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content });
196                 }
197
198                 public Task<HttpResponseMessage> PostAsync (Uri requestUri, HttpContent content, CancellationToken cancellationToken)
199                 {
200                         return SendAsync (new HttpRequestMessage (HttpMethod.Post, requestUri) { Content = content }, cancellationToken);
201                 }
202
203                 public Task<HttpResponseMessage> PutAsync (Uri requestUri, HttpContent content)
204                 {
205                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content });
206                 }
207
208                 public Task<HttpResponseMessage> PutAsync (Uri requestUri, HttpContent content, CancellationToken cancellationToken)
209                 {
210                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content }, cancellationToken);
211                 }
212
213                 public Task<HttpResponseMessage> PutAsync (string requestUri, HttpContent content)
214                 {
215                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content });
216                 }
217
218                 public Task<HttpResponseMessage> PutAsync (string requestUri, HttpContent content, CancellationToken cancellationToken)
219                 {
220                         return SendAsync (new HttpRequestMessage (HttpMethod.Put, requestUri) { Content = content }, cancellationToken);
221                 }
222
223                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request)
224                 {
225                         return SendAsync (request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
226                 }
227
228                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, HttpCompletionOption completionOption)
229                 {
230                         return SendAsync (request, completionOption, CancellationToken.None);
231                 }
232
233                 public override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
234                 {
235                         return SendAsync (request, HttpCompletionOption.ResponseContentRead, cancellationToken);
236                 }
237
238                 public Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
239                 {
240                         if (request == null)
241                                 throw new ArgumentNullException ("request");
242
243                         if (request.SetIsUsed ())
244                                 throw new InvalidOperationException ("Cannot send the same request message multiple times");
245
246                         if (request.RequestUri == null) {
247                                 if (base_address == null)
248                                         throw new InvalidOperationException ("The request URI must either be an absolute URI or BaseAddress must be set");
249
250                                 request.RequestUri = base_address;
251                         } else if (!request.RequestUri.IsAbsoluteUri) {
252                                 if (base_address == null)
253                                         throw new InvalidOperationException ("The request URI must either be an absolute URI or BaseAddress must be set");
254
255                                 request.RequestUri = new Uri (base_address, request.RequestUri);
256                         }
257
258                         if (headers != null) {
259                                 request.Headers.AddHeaders (headers);
260                         }
261
262                         return SendAsyncWorker (request, completionOption, cancellationToken);
263                 }
264
265                 async Task<HttpResponseMessage> SendAsyncWorker (HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
266                 {
267                         try {
268                                 if (cancellation_token == null)
269                                         cancellation_token = new CancellationTokenSource ();
270
271                                 using (var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellation_token.Token, cancellationToken)) {
272                                         cts.CancelAfter (timeout);
273
274                                         var task = base.SendAsync (request, cts.Token);
275                                         if (task == null)
276                                                 throw new InvalidOperationException ("Handler failed to return a value");
277                                         
278                                         var response = await task.ConfigureAwait (false);
279                                         if (response == null)
280                                                 throw new InvalidOperationException ("Handler failed to return a response");
281
282                                         //
283                                         // Read the content when default HttpCompletionOption.ResponseContentRead is set
284                                         //
285                                         if (response.Content != null && (completionOption & HttpCompletionOption.ResponseHeadersRead) == 0) {
286                                                 await response.Content.LoadIntoBufferAsync (MaxResponseContentBufferSize).ConfigureAwait (false);
287                                         }
288                                         
289                                         return response;
290                                 }
291                         } finally {
292                                 cancellation_token.Dispose ();
293                                 cancellation_token = null;
294                         }
295                 }
296
297                 public async Task<byte[]> GetByteArrayAsync (string requestUri)
298                 {
299                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
300                                 resp.EnsureSuccessStatusCode ();
301                                 return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
302                         }
303                 }
304
305                 public async Task<byte[]> GetByteArrayAsync (Uri requestUri)
306                 {
307                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
308                                 resp.EnsureSuccessStatusCode ();
309                                 return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
310                         }
311                 }
312
313                 public async Task<Stream> GetStreamAsync (string requestUri)
314                 {
315                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
316                         resp.EnsureSuccessStatusCode ();
317                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
318                 }
319
320                 public async Task<Stream> GetStreamAsync (Uri requestUri)
321                 {
322                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
323                         resp.EnsureSuccessStatusCode ();
324                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
325                 }
326
327                 public async Task<string> GetStringAsync (string requestUri)
328                 {
329                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
330                                 resp.EnsureSuccessStatusCode ();
331                                 return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
332                         }
333                 }
334
335                 public async Task<string> GetStringAsync (Uri requestUri)
336                 {
337                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
338                                 resp.EnsureSuccessStatusCode ();
339                                 return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
340                         }
341                 }
342         }
343 }