[runtime] Fixed get_process_module module name.
[mono.git] / mcs / class / System.Net.Http / System.Net.Http / HttpClientHandler.cs
1 //
2 // HttpClientHandler.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.Threading.Tasks;
31 using System.Collections.Specialized;
32 using System.Net.Http.Headers;
33
34 namespace System.Net.Http
35 {
36         public class HttpClientHandler : HttpMessageHandler
37         {
38                 static long groupCounter;
39
40                 bool allowAutoRedirect;
41                 DecompressionMethods automaticDecompression;
42                 CookieContainer cookieContainer;
43                 ICredentials credentials;
44                 int maxAutomaticRedirections;
45                 long maxRequestContentBufferSize;
46                 bool preAuthenticate;
47                 IWebProxy proxy;
48                 bool useCookies;
49                 bool useDefaultCredentials;
50                 bool useProxy;
51                 ClientCertificateOption certificate;
52                 bool sentRequest;
53                 string connectionGroupName;
54                 bool disposed;
55
56                 public HttpClientHandler ()
57                 {
58                         allowAutoRedirect = true;
59                         maxAutomaticRedirections = 50;
60                         maxRequestContentBufferSize = int.MaxValue;
61                         useCookies = true;
62                         useProxy = true;
63                         connectionGroupName = "HttpClientHandler" + Interlocked.Increment (ref groupCounter);
64                 }
65
66                 internal void EnsureModifiability ()
67                 {
68                         if (sentRequest)
69                                 throw new InvalidOperationException (
70                                         "This instance has already started one or more requests. " +
71                                         "Properties can only be modified before sending the first request.");
72                 }
73
74                 public bool AllowAutoRedirect {
75                         get {
76                                 return allowAutoRedirect;
77                         }
78                         set {
79                                 EnsureModifiability ();
80                                 allowAutoRedirect = value;
81                         }
82                 }
83
84                 public DecompressionMethods AutomaticDecompression {
85                         get {
86                                 return automaticDecompression;
87                         }
88                         set {
89                                 EnsureModifiability ();
90                                 automaticDecompression = value;
91                         }
92                 }
93
94                 public ClientCertificateOption ClientCertificateOptions {
95                         get {
96                                 return certificate;
97                         }
98                         set {
99                                 EnsureModifiability ();
100                                 certificate = value;
101                         }
102                 }
103
104                 public CookieContainer CookieContainer {
105                         get {
106                                 return cookieContainer ?? (cookieContainer = new CookieContainer ());
107                         }
108                         set {
109                                 EnsureModifiability ();
110                                 cookieContainer = value;
111                         }
112                 }
113
114                 public ICredentials Credentials {
115                         get {
116                                 return credentials;
117                         }
118                         set {
119                                 EnsureModifiability ();
120                                 credentials = value;
121                         }
122                 }
123
124                 public int MaxAutomaticRedirections {
125                         get {
126                                 return maxAutomaticRedirections;
127                         }
128                         set {
129                                 EnsureModifiability ();
130                                 if (value <= 0)
131                                         throw new ArgumentOutOfRangeException ();
132
133                                 maxAutomaticRedirections = value;
134                         }
135                 }
136
137                 public long MaxRequestContentBufferSize {
138                         get {
139                                 return maxRequestContentBufferSize;
140                         }
141                         set {
142                                 EnsureModifiability ();
143                                 if (value < 0)
144                                         throw new ArgumentOutOfRangeException ();
145
146                                 maxRequestContentBufferSize = value;
147                         }
148                 }
149
150                 public bool PreAuthenticate {
151                         get {
152                                 return preAuthenticate;
153                         }
154                         set {
155                                 EnsureModifiability ();
156                                 preAuthenticate = value;
157                         }
158                 }
159
160                 public IWebProxy Proxy {
161                         get {
162                                 return proxy;
163                         }
164                         set {
165                                 EnsureModifiability ();
166                                 if (!UseProxy)
167                                         throw new InvalidOperationException ();
168
169                                 proxy = value;
170                         }
171                 }
172
173                 public virtual bool SupportsAutomaticDecompression {
174                         get {
175                                 return true;
176                         }
177                 }
178
179                 public virtual bool SupportsProxy {
180                         get {
181                                 return true;
182                         }
183                 }
184
185                 public virtual bool SupportsRedirectConfiguration {
186                         get {
187                                 return true;
188                         }
189                 }
190
191                 public bool UseCookies {
192                         get {
193                                 return useCookies;
194                         }
195                         set {
196                                 EnsureModifiability ();
197                                 useCookies = value;
198                         }
199                 }
200
201                 public bool UseDefaultCredentials {
202                         get {
203                                 return useDefaultCredentials;
204                         }
205                         set {
206                                 EnsureModifiability ();
207                                 useDefaultCredentials = value;
208                         }
209                 }
210
211                 public bool UseProxy {
212                         get {
213                                 return useProxy;
214                         }
215                         set {
216                                 EnsureModifiability ();
217                                 useProxy = value;
218                         }
219                 }
220
221                 protected override void Dispose (bool disposing)
222                 {
223                         if (disposing && !disposed) {
224                                 Volatile.Write (ref disposed, true);
225                                 ServicePointManager.CloseConnectionGroup (connectionGroupName);
226                         }
227
228                         base.Dispose (disposing);
229                 }
230
231                 internal virtual HttpWebRequest CreateWebRequest (HttpRequestMessage request)
232                 {
233                         var wr = new HttpWebRequest (request.RequestUri);
234                         wr.ThrowOnError = false;
235                         wr.AllowWriteStreamBuffering = false;
236
237                         wr.ConnectionGroupName = connectionGroupName;
238                         wr.Method = request.Method.Method;
239                         wr.ProtocolVersion = request.Version;
240
241                         if (wr.ProtocolVersion == HttpVersion.Version10) {
242                                 wr.KeepAlive = request.Headers.ConnectionKeepAlive;
243                         } else {
244                                 wr.KeepAlive = request.Headers.ConnectionClose != true;
245                         }
246
247                         wr.ServicePoint.Expect100Continue = request.Headers.ExpectContinue == true;
248
249                         if (allowAutoRedirect) {
250                                 wr.AllowAutoRedirect = true;
251                                 wr.MaximumAutomaticRedirections = maxAutomaticRedirections;
252                         } else {
253                                 wr.AllowAutoRedirect = false;
254                         }
255
256                         wr.AutomaticDecompression = automaticDecompression;
257                         wr.PreAuthenticate = preAuthenticate;
258
259                         if (useCookies) {
260                                 // It cannot be null or allowAutoRedirect won't work
261                                 wr.CookieContainer = CookieContainer;
262                         }
263
264                         if (useDefaultCredentials) {
265                                 wr.UseDefaultCredentials = true;
266                         } else {
267                                 wr.Credentials = credentials;
268                         }
269
270                         if (useProxy) {
271                                 wr.Proxy = proxy;
272                         }
273
274                         // Add request headers
275                         var headers = wr.Headers;
276                         foreach (var header in request.Headers) {
277                                 foreach (var value in header.Value) {
278                                         headers.AddValue (header.Key, value);
279                                 }
280                         }
281                         
282                         return wr;
283                 }
284
285                 HttpResponseMessage CreateResponseMessage (HttpWebResponse wr, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
286                 {
287                         var response = new HttpResponseMessage (wr.StatusCode);
288                         response.RequestMessage = requestMessage;
289                         response.ReasonPhrase = wr.StatusDescription;
290                         response.Content = new StreamContent (wr.GetResponseStream (), cancellationToken);
291
292                         var headers = wr.Headers;
293                         for (int i = 0; i < headers.Count; ++i) {
294                                 var key = headers.GetKey(i);
295                                 var value = headers.GetValues (i);
296
297                                 HttpHeaders item_headers;
298                                 if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content)
299                                         item_headers = response.Content.Headers;
300                                 else
301                                         item_headers = response.Headers;
302                                         
303                                 item_headers.TryAddWithoutValidation (key, value);
304                         }
305
306                         requestMessage.RequestUri = wr.ResponseUri;
307
308                         return response;
309                 }
310
311                 protected async internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
312                 {
313                         if (disposed)
314                                 throw new ObjectDisposedException (GetType ().ToString ());
315
316                         Volatile.Write (ref sentRequest, true);
317                         var wrequest = CreateWebRequest (request);
318                         HttpWebResponse wresponse = null;
319
320                         try {
321                                 using (cancellationToken.Register (l => ((HttpWebRequest)l).Abort (), wrequest)) {
322                                         var content = request.Content;
323                                         if (content != null) {
324                                                 var headers = wrequest.Headers;
325
326                                                 foreach (var header in content.Headers) {
327                                                         foreach (var value in header.Value) {
328                                                                 headers.AddValue (header.Key, value);
329                                                         }
330                                                 }
331
332                                                 //
333                                                 // Content length has to be set because HttpWebRequest is running without buffering
334                                                 //
335                                                 var contentLength = content.Headers.ContentLength;
336                                                 if (contentLength != null) {
337                                                         wrequest.ContentLength = contentLength.Value;
338                                                 } else {
339                                                         await content.LoadIntoBufferAsync (MaxRequestContentBufferSize).ConfigureAwait (false);
340                                                         wrequest.ContentLength = content.Headers.ContentLength.Value;
341                                                 }
342
343                                                 var stream = await wrequest.GetRequestStreamAsync ().ConfigureAwait (false);
344                                                 await request.Content.CopyToAsync (stream).ConfigureAwait (false);
345                                         } else if (HttpMethod.Post.Equals (request.Method) || HttpMethod.Put.Equals (request.Method) || HttpMethod.Delete.Equals (request.Method)) {
346                                                 // Explicitly set this to make sure we're sending a "Content-Length: 0" header.
347                                                 // This fixes the issue that's been reported on the forums:
348                                                 // http://forums.xamarin.com/discussion/17770/length-required-error-in-http-post-since-latest-release
349                                                 wrequest.ContentLength = 0;
350                                         }
351
352                                         wresponse = (HttpWebResponse)await wrequest.GetResponseAsync ().ConfigureAwait (false);
353                                 }
354                         } catch (WebException we) {
355                                 if (we.Status != WebExceptionStatus.RequestCanceled)
356                                         throw;
357                         }
358
359                         if (cancellationToken.IsCancellationRequested) {
360                                 var cancelled = new TaskCompletionSource<HttpResponseMessage> ();
361                                 cancelled.SetCanceled ();
362                                 return await cancelled.Task;
363                         }
364                         
365                         return CreateResponseMessage (wresponse, request, cancellationToken);
366                 }
367         }
368 }