2 // HttpClientHandler.cs
5 // Marek Safar <marek.safar@gmail.com>
7 // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Threading;
30 using System.Threading.Tasks;
31 using System.Collections.Specialized;
32 using System.Net.Http.Headers;
34 namespace System.Net.Http
36 public class HttpClientHandler : HttpMessageHandler
38 bool allowAutoRedirect;
39 DecompressionMethods automaticDecompression;
40 CookieContainer cookieContainer;
41 ICredentials credentials;
42 int maxAutomaticRedirections;
43 long maxRequestContentBufferSize;
47 bool useDefaultCredentials;
49 ClientCertificateOption certificate;
52 public HttpClientHandler ()
54 allowAutoRedirect = true;
55 maxAutomaticRedirections = 50;
56 maxRequestContentBufferSize = int.MaxValue;
61 internal void EnsureModifiability ()
64 throw new InvalidOperationException (
65 "This instance has already started one or more requests. " +
66 "Properties can only be modified before sending the first request.");
69 public bool AllowAutoRedirect {
71 return allowAutoRedirect;
74 EnsureModifiability ();
75 allowAutoRedirect = value;
79 public DecompressionMethods AutomaticDecompression {
81 return automaticDecompression;
84 EnsureModifiability ();
85 automaticDecompression = value;
89 public ClientCertificateOption ClientCertificateOptions {
94 EnsureModifiability ();
99 public CookieContainer CookieContainer {
101 return cookieContainer ?? (cookieContainer = new CookieContainer ());
104 EnsureModifiability ();
105 cookieContainer = value;
109 public ICredentials Credentials {
114 EnsureModifiability ();
119 public int MaxAutomaticRedirections {
121 return maxAutomaticRedirections;
124 EnsureModifiability ();
126 throw new ArgumentOutOfRangeException ();
128 maxAutomaticRedirections = value;
132 public long MaxRequestContentBufferSize {
134 return maxRequestContentBufferSize;
137 EnsureModifiability ();
139 throw new ArgumentOutOfRangeException ();
141 maxRequestContentBufferSize = value;
145 public bool PreAuthenticate {
147 return preAuthenticate;
150 EnsureModifiability ();
151 preAuthenticate = value;
155 public IWebProxy Proxy {
160 EnsureModifiability ();
162 throw new InvalidOperationException ();
168 public virtual bool SupportsAutomaticDecompression {
174 public virtual bool SupportsProxy {
180 public virtual bool SupportsRedirectConfiguration {
186 public bool UseCookies {
191 EnsureModifiability ();
196 public bool UseDefaultCredentials {
198 return useDefaultCredentials;
201 EnsureModifiability ();
202 useDefaultCredentials = value;
206 public bool UseProxy {
211 EnsureModifiability ();
216 protected override void Dispose (bool disposing)
219 base.Dispose (disposing);
222 internal virtual HttpWebRequest CreateWebRequest (HttpRequestMessage request)
224 var wr = new HttpWebRequest (request.RequestUri);
225 wr.ThrowOnError = false;
227 wr.ConnectionGroupName = "HttpClientHandler";
228 wr.Method = request.Method.Method;
229 wr.ProtocolVersion = request.Version;
231 if (wr.ProtocolVersion == HttpVersion.Version10) {
232 wr.KeepAlive = request.Headers.ConnectionKeepAlive;
234 wr.KeepAlive = request.Headers.ConnectionClose != true;
237 wr.ServicePoint.Expect100Continue = request.Headers.ExpectContinue == true;
239 if (allowAutoRedirect) {
240 wr.AllowAutoRedirect = true;
241 wr.MaximumAutomaticRedirections = maxAutomaticRedirections;
243 wr.AllowAutoRedirect = false;
246 wr.AutomaticDecompression = automaticDecompression;
247 wr.PreAuthenticate = preAuthenticate;
250 // It cannot be null or allowAutoRedirect won't work
251 wr.CookieContainer = CookieContainer;
254 if (useDefaultCredentials) {
255 wr.UseDefaultCredentials = true;
257 wr.Credentials = credentials;
264 // Add request headers
265 var headers = wr.Headers;
266 foreach (var header in request.Headers) {
267 foreach (var value in header.Value) {
268 headers.AddValue (header.Key, value);
275 HttpResponseMessage CreateResponseMessage (HttpWebResponse wr, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
277 var response = new HttpResponseMessage (wr.StatusCode);
278 response.RequestMessage = requestMessage;
279 response.ReasonPhrase = wr.StatusDescription;
280 response.Content = new StreamContent (wr.GetResponseStream (), cancellationToken);
282 var headers = wr.Headers;
283 for (int i = 0; i < headers.Count; ++i) {
284 var key = headers.GetKey(i);
285 var value = headers.GetValues (i);
287 HttpHeaders item_headers;
288 if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content)
289 item_headers = response.Content.Headers;
291 item_headers = response.Headers;
293 item_headers.TryAddWithoutValidation (key, value);
299 protected async internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
302 var wrequest = CreateWebRequest (request);
304 if (request.Content != null) {
305 var headers = wrequest.Headers;
306 foreach (var header in request.Content.Headers) {
307 foreach (var value in header.Value) {
308 headers.AddValue (header.Key, value);
312 var stream = await wrequest.GetRequestStreamAsync ().ConfigureAwait (false);
313 await request.Content.CopyToAsync (stream).ConfigureAwait (false);
316 HttpWebResponse wresponse = null;
317 using (cancellationToken.Register (l => ((HttpWebRequest) l).Abort (), wrequest)) {
319 wresponse = (HttpWebResponse) await wrequest.GetResponseAsync ().ConfigureAwait (false);
320 } catch (WebException we) {
321 if (we.Status != WebExceptionStatus.RequestCanceled)
325 if (cancellationToken.IsCancellationRequested) {
326 var cancelled = new TaskCompletionSource<HttpResponseMessage> ();
327 cancelled.SetCanceled ();
328 return await cancelled.Task;
332 return CreateResponseMessage (wresponse, request, cancellationToken);