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 static long groupCounter;
40 bool allowAutoRedirect;
41 DecompressionMethods automaticDecompression;
42 CookieContainer cookieContainer;
43 ICredentials credentials;
44 int maxAutomaticRedirections;
45 long maxRequestContentBufferSize;
49 bool useDefaultCredentials;
51 ClientCertificateOption certificate;
53 HttpWebRequest wrequest;
54 string connectionGroupName;
57 public HttpClientHandler ()
59 allowAutoRedirect = true;
60 maxAutomaticRedirections = 50;
61 maxRequestContentBufferSize = int.MaxValue;
64 connectionGroupName = "HttpClientHandler" + Interlocked.Increment (ref groupCounter);
67 internal void EnsureModifiability ()
70 throw new InvalidOperationException (
71 "This instance has already started one or more requests. " +
72 "Properties can only be modified before sending the first request.");
75 public bool AllowAutoRedirect {
77 return allowAutoRedirect;
80 EnsureModifiability ();
81 allowAutoRedirect = value;
85 public DecompressionMethods AutomaticDecompression {
87 return automaticDecompression;
90 EnsureModifiability ();
91 automaticDecompression = value;
95 public ClientCertificateOption ClientCertificateOptions {
100 EnsureModifiability ();
105 public CookieContainer CookieContainer {
107 return cookieContainer ?? (cookieContainer = new CookieContainer ());
110 EnsureModifiability ();
111 cookieContainer = value;
115 public ICredentials Credentials {
120 EnsureModifiability ();
125 public int MaxAutomaticRedirections {
127 return maxAutomaticRedirections;
130 EnsureModifiability ();
132 throw new ArgumentOutOfRangeException ();
134 maxAutomaticRedirections = value;
138 public long MaxRequestContentBufferSize {
140 return maxRequestContentBufferSize;
143 EnsureModifiability ();
145 throw new ArgumentOutOfRangeException ();
147 maxRequestContentBufferSize = value;
151 public bool PreAuthenticate {
153 return preAuthenticate;
156 EnsureModifiability ();
157 preAuthenticate = value;
161 public IWebProxy Proxy {
166 EnsureModifiability ();
168 throw new InvalidOperationException ();
174 public virtual bool SupportsAutomaticDecompression {
180 public virtual bool SupportsProxy {
186 public virtual bool SupportsRedirectConfiguration {
192 public bool UseCookies {
197 EnsureModifiability ();
202 public bool UseDefaultCredentials {
204 return useDefaultCredentials;
207 EnsureModifiability ();
208 useDefaultCredentials = value;
212 public bool UseProxy {
217 EnsureModifiability ();
222 protected override void Dispose (bool disposing)
225 if (wrequest != null) {
226 wrequest.ServicePoint.CloseConnectionGroup (wrequest.ConnectionGroupName);
227 Volatile.Write (ref wrequest, null);
229 Volatile.Write (ref disposed, true);
232 base.Dispose (disposing);
235 internal virtual HttpWebRequest CreateWebRequest (HttpRequestMessage request)
237 var wr = new HttpWebRequest (request.RequestUri);
238 wr.ThrowOnError = false;
240 wr.ConnectionGroupName = connectionGroupName;
241 wr.Method = request.Method.Method;
242 wr.ProtocolVersion = request.Version;
244 if (wr.ProtocolVersion == HttpVersion.Version10) {
245 wr.KeepAlive = request.Headers.ConnectionKeepAlive;
247 wr.KeepAlive = request.Headers.ConnectionClose != true;
250 wr.ServicePoint.Expect100Continue = request.Headers.ExpectContinue == true;
252 if (allowAutoRedirect) {
253 wr.AllowAutoRedirect = true;
254 wr.MaximumAutomaticRedirections = maxAutomaticRedirections;
256 wr.AllowAutoRedirect = false;
259 wr.AutomaticDecompression = automaticDecompression;
260 wr.PreAuthenticate = preAuthenticate;
263 // It cannot be null or allowAutoRedirect won't work
264 wr.CookieContainer = CookieContainer;
267 if (useDefaultCredentials) {
268 wr.UseDefaultCredentials = true;
270 wr.Credentials = credentials;
277 // Add request headers
278 var headers = wr.Headers;
279 foreach (var header in request.Headers) {
280 foreach (var value in header.Value) {
281 headers.AddValue (header.Key, value);
288 HttpResponseMessage CreateResponseMessage (HttpWebResponse wr, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
290 var response = new HttpResponseMessage (wr.StatusCode);
291 response.RequestMessage = requestMessage;
292 response.ReasonPhrase = wr.StatusDescription;
293 response.Content = new StreamContent (wr.GetResponseStream (), cancellationToken);
295 var headers = wr.Headers;
296 for (int i = 0; i < headers.Count; ++i) {
297 var key = headers.GetKey(i);
298 var value = headers.GetValues (i);
300 HttpHeaders item_headers;
301 if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content)
302 item_headers = response.Content.Headers;
304 item_headers = response.Headers;
306 item_headers.TryAddWithoutValidation (key, value);
312 protected async internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
315 throw new ObjectDisposedException (GetType ().ToString ());
317 Volatile.Write (ref sentRequest, true);
318 wrequest = CreateWebRequest (request);
320 if (request.Content != null) {
321 var headers = wrequest.Headers;
322 foreach (var header in request.Content.Headers) {
323 foreach (var value in header.Value) {
324 headers.AddValue (header.Key, value);
328 var stream = await wrequest.GetRequestStreamAsync ().ConfigureAwait (false);
329 await request.Content.CopyToAsync (stream).ConfigureAwait (false);
332 HttpWebResponse wresponse = null;
333 using (cancellationToken.Register (l => ((HttpWebRequest) l).Abort (), wrequest)) {
335 wresponse = (HttpWebResponse) await wrequest.GetResponseAsync ().ConfigureAwait (false);
336 } catch (WebException we) {
337 if (we.Status != WebExceptionStatus.RequestCanceled)
341 if (cancellationToken.IsCancellationRequested) {
342 var cancelled = new TaskCompletionSource<HttpResponseMessage> ();
343 cancelled.SetCanceled ();
344 return await cancelled.Task;
348 return CreateResponseMessage (wresponse, request, cancellationToken);