Merge pull request #1155 from steffen-kiess/json-string
[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 cts;
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                         cts = new CancellationTokenSource ();
63                 }
64
65                 public Uri BaseAddress {
66                         get {
67                                 return base_address;
68                         }
69                         set {
70                                 base_address = value;
71                         }
72                 }
73
74                 public HttpRequestHeaders DefaultRequestHeaders {
75                         get {
76                                 return headers ?? (headers = new HttpRequestHeaders ());
77                         }
78                 }
79
80                 public long MaxResponseContentBufferSize {
81                         get {
82                                 return buffer_size;
83                         }
84                         set {
85                                 if (value <= 0)
86                                         throw new ArgumentOutOfRangeException ();
87
88                                 buffer_size = value;
89                         }
90                 }
91
92                 public TimeSpan Timeout {
93                         get {
94                                 return timeout;
95                         }
96                         set {
97                                 if (value != System.Threading.Timeout.InfiniteTimeSpan && value < TimeSpan.Zero)
98                                         throw new ArgumentOutOfRangeException ();
99
100                                 timeout = value;
101                         }
102                 }
103
104                 public void CancelPendingRequests ()
105                 {
106                         // Cancel only any already running requests not any new request after this cancellation
107                         using (var c = Interlocked.Exchange (ref cts, new CancellationTokenSource ()))
108                                 c.Cancel ();
109                 }
110
111                 protected override void Dispose (bool disposing)
112                 {
113                         if (disposing && !disposed) {
114                                 disposed = true;
115
116                                 cts.Dispose ();
117                         }
118                         
119                         base.Dispose (disposing);
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), cancellationToken);
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 override 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                         var uri = request.RequestUri;
246                         if (uri == 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 (!uri.IsAbsoluteUri || uri.Scheme == Uri.UriSchemeFile && uri.OriginalString.StartsWith ("/", StringComparison.Ordinal)) {
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, uri);
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                         using (var lcts = CancellationTokenSource.CreateLinkedTokenSource (cts.Token, cancellationToken)) {
268                                 lcts.CancelAfter (timeout);
269
270                                 var task = base.SendAsync (request, lcts.Token);
271                                 if (task == null)
272                                         throw new InvalidOperationException ("Handler failed to return a value");
273                                         
274                                 var response = await task.ConfigureAwait (false);
275                                 if (response == null)
276                                         throw new InvalidOperationException ("Handler failed to return a response");
277
278                                 //
279                                 // Read the content when default HttpCompletionOption.ResponseContentRead is set
280                                 //
281                                 if (response.Content != null && (completionOption & HttpCompletionOption.ResponseHeadersRead) == 0) {
282                                         await response.Content.LoadIntoBufferAsync (MaxResponseContentBufferSize).ConfigureAwait (false);
283                                 }
284                                         
285                                 return response;
286                         }
287                 }
288
289                 public async Task<byte[]> GetByteArrayAsync (string requestUri)
290                 {
291                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
292                                 resp.EnsureSuccessStatusCode ();
293                                 return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
294                         }
295                 }
296
297                 public async Task<byte[]> GetByteArrayAsync (Uri 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<Stream> GetStreamAsync (string requestUri)
306                 {
307                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait (false);
308                         resp.EnsureSuccessStatusCode ();
309                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
310                 }
311
312                 public async Task<Stream> GetStreamAsync (Uri requestUri)
313                 {
314                         var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait (false);
315                         resp.EnsureSuccessStatusCode ();
316                         return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
317                 }
318
319                 public async Task<string> GetStringAsync (string requestUri)
320                 {
321                         using (var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false)) {
322                                 resp.EnsureSuccessStatusCode ();
323                                 return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
324                         }
325                 }
326
327                 public async Task<string> GetStringAsync (Uri 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 }