Merge pull request #347 from JamesB7/master
[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                 bool allowAutoRedirect;
39                 DecompressionMethods automaticDecompression;
40                 CookieContainer cookieContainer;
41                 ICredentials credentials;
42                 int maxAutomaticRedirections;
43                 long maxRequestContentBufferSize;
44                 bool preAuthenticate;
45                 IWebProxy proxy;
46                 bool useCookies;
47                 bool useDefaultCredentials;
48                 bool useProxy;
49                 ClientCertificateOption certificate;
50
51                 public HttpClientHandler ()
52                 {
53                         allowAutoRedirect = true;
54                         maxAutomaticRedirections = 50;
55                         maxRequestContentBufferSize = int.MaxValue;
56                         useCookies = true;
57                         useProxy = true;
58                 }
59
60                 public bool AllowAutoRedirect {
61                         get {
62                                 return allowAutoRedirect;
63                         }
64                         set {
65                                 allowAutoRedirect = value;
66                         }
67                 }
68
69                 public DecompressionMethods AutomaticDecompression {
70                         get {
71                                 return automaticDecompression;
72                         }
73                         set {
74                                 automaticDecompression = value;
75                         }
76                 }
77
78                 public ClientCertificateOption ClientCertificateOptions {
79                         get {
80                                 return certificate;
81                         }
82                         set {
83                                 certificate = value;
84                         }
85                 }
86
87                 public CookieContainer CookieContainer {
88                         get {
89                                 return cookieContainer ?? (cookieContainer = new CookieContainer ());
90                         }
91                         set {
92                                 cookieContainer = value;
93                         }
94                 }
95
96                 public ICredentials Credentials {
97                         get {
98                                 return credentials;
99                         }
100                         set {
101                                 credentials = value;
102                         }
103                 }
104
105                 public int MaxAutomaticRedirections {
106                         get {
107                                 return maxAutomaticRedirections;
108                         }
109                         set {
110                                 if (value <= 0)
111                                         throw new ArgumentOutOfRangeException ();
112
113                                 maxAutomaticRedirections = value;
114                         }
115                 }
116
117                 public long MaxRequestContentBufferSize {
118                         get {
119                                 return maxRequestContentBufferSize;
120                         }
121                         set {
122                                 if (value < 0)
123                                         throw new ArgumentOutOfRangeException ();
124
125                                 maxRequestContentBufferSize = value;
126                         }
127                 }
128
129                 public bool PreAuthenticate {
130                         get {
131                                 return preAuthenticate;
132                         }
133                         set {
134                                 preAuthenticate = value;
135                         }
136                 }
137
138                 public IWebProxy Proxy {
139                         get {
140                                 return proxy;
141                         }
142                         set {
143                                 if (!UseProxy)
144                                         throw new InvalidOperationException ();
145
146                                 proxy = value;
147                         }
148                 }
149
150                 public virtual bool SupportsAutomaticDecompression {
151                         get {
152                                 return true;
153                         }
154                 }
155
156                 public virtual bool SupportsProxy {
157                         get {
158                                 return true;
159                         }
160                 }
161
162                 public virtual bool SupportsRedirectConfiguration {
163                         get {
164                                 return true;
165                         }
166                 }
167
168                 public bool UseCookies {
169                         get {
170                                 return useCookies;
171                         }
172                         set {
173                                 useCookies = value;
174                         }
175                 }
176
177                 public bool UseDefaultCredentials {
178                         get {
179                                 return useDefaultCredentials;
180                         }
181                         set {
182                                 useDefaultCredentials = value;
183                         }
184                 }
185
186                 public bool UseProxy {
187                         get {
188                                 return useProxy;
189                         }
190                         set {
191                                 useProxy = value;
192                         }
193                 }
194
195                 protected override void Dispose (bool disposing)
196                 {
197                         // TODO: ?
198                         base.Dispose (disposing);
199                 }
200
201                 HttpWebRequest CreateWebRequest (HttpRequestMessage request)
202                 {
203                         var wr = new HttpWebRequest (request.RequestUri);
204                         wr.ThrowOnError = false;
205
206                         wr.ConnectionGroupName = "HttpClientHandler";
207                         wr.Method = request.Method.Method;
208                         wr.ProtocolVersion = request.Version;
209
210                         if (wr.ProtocolVersion == HttpVersion.Version10) {
211                                 wr.KeepAlive = request.Headers.ConnectionKeepAlive;
212                         } else {
213                                 wr.KeepAlive = request.Headers.ConnectionClose != true;
214                         }
215
216                         wr.ServicePoint.Expect100Continue = request.Headers.ExpectContinue == true;
217
218                         if (allowAutoRedirect) {
219                                 wr.AllowAutoRedirect = true;
220                                 wr.MaximumAutomaticRedirections = maxAutomaticRedirections;
221                         }
222
223                         wr.AutomaticDecompression = automaticDecompression;
224                         wr.PreAuthenticate = preAuthenticate;
225
226                         if (useCookies) {
227                                 wr.CookieContainer = cookieContainer;
228                         }
229
230                         if (useDefaultCredentials) {
231                                 wr.UseDefaultCredentials = true;
232                         } else {
233                                 wr.Credentials = credentials;
234                         }
235
236                         if (useProxy) {
237                                 wr.Proxy = proxy;
238                         }
239
240                         // Add request headers
241                         var headers = wr.Headers;
242                         foreach (var header in request.Headers) {
243                                 foreach (var value in header.Value) {
244                                         headers.AddValue (header.Key, value);
245                                 }
246                         }
247                         
248                         return wr;
249                 }
250
251                 HttpResponseMessage CreateResponseMessage (HttpWebResponse wr, HttpRequestMessage requestMessage)
252                 {
253                         var response = new HttpResponseMessage (wr.StatusCode);
254                         response.RequestMessage = requestMessage;
255                         response.ReasonPhrase = wr.StatusDescription;
256                         response.Content = new StreamContent (wr.GetResponseStream ());
257
258                         var headers = wr.Headers;
259                         for (int i = 0; i < headers.Count; ++i) {
260                                 var key = headers.GetKey(i);
261                                 var value = headers.GetValues (i);
262
263                                 HttpHeaders item_headers;
264                                 if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content)
265                                         item_headers = response.Content.Headers;
266                                 else
267                                         item_headers = response.Headers;
268                                         
269                                 item_headers.TryAddWithoutValidation (key, value);
270                         }
271
272                         return response;
273                 }
274
275                 protected async internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
276                 {
277                         var wrequest = CreateWebRequest (request);
278
279                         if (request.Content != null) {
280                                 var headers = wrequest.Headers;
281                                 foreach (var header in request.Content.Headers) {
282                                         foreach (var value in header.Value) {
283                                                 headers.AddValue (header.Key, value);
284                                         }
285                                 }
286                                 
287                                 var stream = wrequest.GetRequestStream ();
288                                 await request.Content.CopyToAsync (stream);
289                         }
290
291                         // FIXME: GetResponseAsync does not accept cancellationToken
292                         var wresponse = (HttpWebResponse) await wrequest.GetResponseAsync ().ConfigureAwait (false);
293                         return CreateResponseMessage (wresponse, request);
294                 }
295         }
296 }