Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / class / System.Net.Http / Test / System.Net.Http / HttpClientTest.cs
1 //
2 // HttpClientTest.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;
30 using System.Collections;
31 using System.Collections.Generic;
32 using NUnit.Framework;
33 using System.Net.Http;
34 using System.Net.Http.Headers;
35 using System.Threading;
36 using System.Threading.Tasks;
37 using System.Net;
38 using System.Linq;
39 using System.IO;
40
41 namespace MonoTests.System.Net.Http
42 {
43         [TestFixture]
44         public class HttpClientTest
45         {
46                 class HttpMessageHandlerMock : HttpMessageHandler
47                 {
48                         public Func<HttpRequestMessage, Task<HttpResponseMessage>> OnSend;
49                         public Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> OnSendFull;
50
51                         public HttpMessageHandlerMock ()
52                         {
53                         }
54
55                         protected override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
56                         {
57                                 if (OnSend != null)
58                                         return OnSend (request);
59
60                                 if (OnSendFull != null)
61                                         return OnSendFull (request, cancellationToken);
62
63                                 Assert.Fail ("Send");
64                                 return null;
65                         }
66                 }
67
68                 class HttpClientHandlerMock : HttpClientHandler
69                 {
70                         public Func<HttpRequestMessage, Task<HttpResponseMessage>> OnSend;
71                         public Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> OnSendFull;
72
73                         protected override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
74                         {
75                                 if (OnSend != null)
76                                         return OnSend (request);
77
78                                 if (OnSendFull != null)
79                                         return OnSendFull (request, cancellationToken);
80
81                                 Assert.Fail ("Send");
82                                 return null;
83                         }
84                 }
85
86                 const int WaitTimeout = 5000;
87
88                 string port, TestHost, LocalServer;
89
90                 [SetUp]
91                 public void SetupFixture ()
92                 {
93                         if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
94                                 port = "810";
95                         } else {
96                                 port = "8810";
97                         }
98
99                         TestHost = "localhost:" + port;
100                         LocalServer = string.Format ("http://{0}/", TestHost);
101                 }
102
103                 [Test]
104                 public void Ctor_Default ()
105                 {
106                         var client = new HttpClient ();
107                         Assert.IsNull (client.BaseAddress, "#1");
108                         Assert.IsNotNull (client.DefaultRequestHeaders, "#2");  // TODO: full check
109                         Assert.AreEqual (int.MaxValue, client.MaxResponseContentBufferSize, "#3");
110                         Assert.AreEqual (TimeSpan.FromSeconds (100), client.Timeout, "#4");
111                 }
112
113                 [Test]
114                 public void CancelPendingRequests ()
115                 {
116                         var mh = new HttpMessageHandlerMock ();
117
118                         var client = new HttpClient (mh);
119                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
120                         var mre = new ManualResetEvent (false);
121
122                         mh.OnSendFull = (l, c) => {
123                                 mre.Set ();
124                                 Assert.IsTrue (c.WaitHandle.WaitOne (1000), "#20");
125                                 Assert.IsTrue (c.IsCancellationRequested, "#21");
126                                 mre.Set ();
127                                 return Task.FromResult (new HttpResponseMessage ());
128                         };
129
130                         var t = Task.Factory.StartNew (() => {
131                                 client.SendAsync (request).Wait (WaitTimeout);
132                         });
133
134                         Assert.IsTrue (mre.WaitOne (500), "#1");
135                         mre.Reset ();
136                         client.CancelPendingRequests ();
137                         Assert.IsTrue (t.Wait (500), "#2");
138
139                         request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
140                         mh.OnSendFull = (l, c) => {
141                                 Assert.IsFalse (c.IsCancellationRequested, "#30");
142                                 return Task.FromResult (new HttpResponseMessage ());
143                         };
144
145                         client.SendAsync (request).Wait (WaitTimeout);
146                 }
147
148                 [Test]
149                 public void CancelPendingRequests_BeforeSend ()
150                 {
151                         var ct = new CancellationTokenSource ();
152                         ct.Cancel ();
153                         var rr = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken (), ct.Token);
154
155
156                         var mh = new HttpMessageHandlerMock ();
157
158                         var client = new HttpClient (mh);
159                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
160                         client.CancelPendingRequests ();
161
162                         mh.OnSendFull = (l, c) => {
163                                 Assert.IsFalse (c.IsCancellationRequested, "#30");
164                                 return Task.FromResult (new HttpResponseMessage ());
165                         };
166
167                         client.SendAsync (request).Wait (WaitTimeout);
168
169                         request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
170                         client.SendAsync (request).Wait (WaitTimeout);
171                 }
172
173
174                 [Test]
175                 public void CancelRequestViaProxy ()
176                 {
177                         var handler = new HttpClientHandler {
178                                 Proxy = new WebProxy ("192.168.10.25:8888/"), // proxy that doesn't exist
179                                 UseProxy = true,
180                                 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
181                         };
182
183                         var httpClient = new HttpClient (handler) {
184                                 BaseAddress = new Uri ("https://google.com"),
185                                 Timeout = TimeSpan.FromSeconds (1)
186                         };
187
188                         try {
189                                 var restRequest = new HttpRequestMessage {
190                                         Method = HttpMethod.Post,
191                                         RequestUri = new Uri("foo", UriKind.Relative),
192                                         Content = new StringContent("", null, "application/json")
193                                 };
194
195                                 httpClient.PostAsync (restRequest.RequestUri, restRequest.Content).Wait (WaitTimeout);
196                                 Assert.Fail ("#1");
197                         } catch (AggregateException e) {
198                                 Assert.IsTrue (e.InnerException is TaskCanceledException, "#2");
199                         }
200                 }
201
202                 [Test]
203                 public void Properties ()
204                 {
205                         var client = new HttpClient ();
206                         client.BaseAddress = null;
207                         client.MaxResponseContentBufferSize = int.MaxValue;
208                         client.Timeout = Timeout.InfiniteTimeSpan;
209
210                         Assert.IsNull (client.BaseAddress, "#1");
211                         Assert.AreEqual (int.MaxValue, client.MaxResponseContentBufferSize, "#2");
212                         Assert.AreEqual (Timeout.InfiniteTimeSpan, client.Timeout, "#3");
213                 }
214
215                 [Test]
216                 public void Properties_Invalid ()
217                 {
218                         var client = new HttpClient ();
219                         try {
220                                 client.MaxResponseContentBufferSize = 0;
221                                 Assert.Fail ("#1");
222                         } catch (ArgumentOutOfRangeException) {
223                         }
224
225                         try {
226                                 client.Timeout = TimeSpan.MinValue;
227                                 Assert.Fail ("#2");
228                         } catch (ArgumentOutOfRangeException) {
229                         }
230                 }
231
232                 [Test]
233                 public void Send ()
234                 {
235                         var mh = new HttpMessageHandlerMock ();
236
237                         var client = new HttpClient (mh);
238                         client.BaseAddress = new Uri ("http://xamarin.com");
239                         var request = new HttpRequestMessage ();
240                         var response = new HttpResponseMessage ();
241
242                         mh.OnSend = l => {
243                                 Assert.AreEqual (l, request, "#2");
244                                 Assert.AreEqual (client.BaseAddress, l.RequestUri, "#2");
245                                 return Task.FromResult (response);
246                         };
247
248                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
249                 }
250
251                 [Test]
252                 public void Send_DefaultRequestHeaders ()
253                 {
254                         var mh = new HttpMessageHandlerMock ();
255
256                         var client = new HttpClient (mh);
257                         client.DefaultRequestHeaders.Referrer = new Uri ("http://google.com");
258
259                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
260                         var response = new HttpResponseMessage ();
261
262                         mh.OnSend = l => {
263                                 Assert.AreEqual (client.DefaultRequestHeaders.Referrer, l.Headers.Referrer, "#2");
264                                 Assert.IsNotNull (l.Headers.Referrer, "#3");
265                                 return Task.FromResult (response);
266                         };
267
268                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
269                 }
270
271                 [Test]
272                 public void Send_Complete_Default ()
273                 {
274                         bool? failed = null;
275                         var listener = CreateListener (l => {
276                                 try {
277                                         var request = l.Request;
278         
279                                         Assert.IsNull (request.AcceptTypes, "#1");
280                                         Assert.AreEqual (0, request.ContentLength64, "#2");
281                                         Assert.IsNull (request.ContentType, "#3");
282                                         Assert.AreEqual (0, request.Cookies.Count, "#4");
283                                         Assert.IsFalse (request.HasEntityBody, "#5");
284                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6b");
285                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
286                                         Assert.IsFalse (request.IsAuthenticated, "#8");
287                                         Assert.IsTrue (request.IsLocal, "#9");
288                                         Assert.IsFalse (request.IsSecureConnection, "#10");
289                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
290                                         Assert.IsTrue (request.KeepAlive, "#12");
291                                         Assert.AreEqual (HttpVersion.Version11, request.ProtocolVersion, "#13");
292                                         Assert.IsNull (request.ServiceName, "#14");
293                                         Assert.IsNull (request.UrlReferrer, "#15");
294                                         Assert.IsNull (request.UserAgent, "#16");
295                                         Assert.IsNull (request.UserLanguages, "#17");
296                                         failed = false;
297                                 } catch {
298                                         failed = true;
299                                 }
300                         });
301
302                         try {
303                                 var client = new HttpClient ();
304                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
305                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
306
307                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
308                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
309                                 Assert.AreEqual (false, failed, "#102");
310                         } finally {
311                                 listener.Close ();
312                         }
313                 }
314
315                 [Test]
316                 public void Send_Complete_Version_1_0 ()
317                 {
318                         bool? failed = null;
319                         
320                         var listener = CreateListener (l => {
321                                 try {
322                                         var request = l.Request;
323         
324                                         Assert.IsNull (request.AcceptTypes, "#1");
325                                         Assert.AreEqual (0, request.ContentLength64, "#2");
326                                         Assert.IsNull (request.ContentType, "#3");
327                                         Assert.AreEqual (0, request.Cookies.Count, "#4");
328                                         Assert.IsFalse (request.HasEntityBody, "#5");
329                                         Assert.AreEqual (1, request.Headers.Count, "#6");
330                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
331                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
332                                         Assert.IsFalse (request.IsAuthenticated, "#8");
333                                         Assert.IsTrue (request.IsLocal, "#9");
334                                         Assert.IsFalse (request.IsSecureConnection, "#10");
335                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
336                                         Assert.IsFalse (request.KeepAlive, "#12");
337                                         Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
338                                         Assert.IsNull (request.ServiceName, "#14");
339                                         Assert.IsNull (request.UrlReferrer, "#15");
340                                         Assert.IsNull (request.UserAgent, "#16");
341                                         Assert.IsNull (request.UserLanguages, "#17");
342                                         failed = false;
343                                 } catch {
344                                         failed = true;
345                                 }
346                         });
347
348                         try {
349                                 var client = new HttpClient ();
350                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
351                                 request.Version = HttpVersion.Version10;
352                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
353
354                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
355                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
356                                 Assert.AreEqual (false, failed, "#102");
357                         } finally {
358                                 listener.Close ();
359                         }
360                 }
361
362                 [Test]
363                 public void Send_Complete_ClientHandlerSettings ()
364                 {
365                         bool? failed = null;
366                         
367                         var listener = CreateListener (l => {
368                                 var request = l.Request;
369                                 
370                                 try {
371                                         Assert.IsNull (request.AcceptTypes, "#1");
372                                         Assert.AreEqual (0, request.ContentLength64, "#2");
373                                         Assert.IsNull (request.ContentType, "#3");
374                                         Assert.AreEqual (1, request.Cookies.Count, "#4");
375                                         Assert.AreEqual (new Cookie ("mycookie", "vv"), request.Cookies[0], "#4a");
376                                         Assert.IsFalse (request.HasEntityBody, "#5");
377                                         Assert.AreEqual (4, request.Headers.Count, "#6");
378                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
379                                         Assert.AreEqual ("gzip", request.Headers["Accept-Encoding"], "#6b");
380                                         Assert.AreEqual ("mycookie=vv", request.Headers["Cookie"], "#6c");
381                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
382                                         Assert.IsFalse (request.IsAuthenticated, "#8");
383                                         Assert.IsTrue (request.IsLocal, "#9");
384                                         Assert.IsFalse (request.IsSecureConnection, "#10");
385                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
386                                         Assert.IsTrue (request.KeepAlive, "#12");
387                                         Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
388                                         Assert.IsNull (request.ServiceName, "#14");
389                                         Assert.IsNull (request.UrlReferrer, "#15");
390                                         Assert.IsNull (request.UserAgent, "#16");
391                                         Assert.IsNull (request.UserLanguages, "#17");
392                                         failed = false;
393                                 } catch {
394                                         failed = true;
395                                 }
396                         });
397
398                         try {
399                                 var chandler = new HttpClientHandler ();
400                                 chandler.AllowAutoRedirect = true;
401                                 chandler.AutomaticDecompression = DecompressionMethods.GZip;
402                                 chandler.MaxAutomaticRedirections = 33;
403                                 chandler.MaxRequestContentBufferSize = 5555;
404                                 chandler.PreAuthenticate = true;
405                                 chandler.CookieContainer.Add (new Uri (LocalServer), new Cookie ( "mycookie", "vv"));
406                                 chandler.UseCookies = true;
407                                 chandler.UseDefaultCredentials = true;
408                                 chandler.Proxy = new WebProxy ("ee");
409                                 chandler.UseProxy = true;
410
411                                 var client = new HttpClient (chandler);
412                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
413                                 request.Version = HttpVersion.Version10;
414                                 request.Headers.Add ("Keep-Alive", "false");
415                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
416
417                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
418                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
419                                 Assert.AreEqual (false, failed, "#102");
420                         } finally {
421                                 listener.Abort ();
422                                 listener.Close ();
423                         }
424                 }
425
426                 [Test]
427                 public void Send_Complete_CustomHeaders ()
428                 {
429                         bool? failed = null;
430                         
431                         var listener = CreateListener (l => {
432                                 var request = l.Request;
433                                 try {
434                                         Assert.AreEqual ("vv", request.Headers["aa"], "#1");
435         
436                                         var response = l.Response;
437                                         response.Headers.Add ("rsp", "rrr");
438                                         response.Headers.Add ("upgrade", "vvvvaa");
439                                         response.Headers.Add ("Date", "aa");
440                                         response.Headers.Add ("cache-control", "audio");
441         
442                                         response.StatusDescription = "test description";
443                                         response.ProtocolVersion = HttpVersion.Version10;
444                                         response.SendChunked = true;
445                                         response.RedirectLocation = "w3.org";
446                                         
447                                         failed = false;
448                                 } catch {
449                                         failed = true;
450                                 }
451                         });
452
453                         try {
454                                 var client = new HttpClient ();
455                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
456                                 Assert.IsTrue (request.Headers.TryAddWithoutValidation ("aa", "vv"), "#0");
457                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
458
459                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
460                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
461                                 
462                                 IEnumerable<string> values;
463                                 Assert.IsTrue (response.Headers.TryGetValues ("rsp", out values), "#102");
464                                 Assert.AreEqual ("rrr", values.First (), "#102a");
465
466                                 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#103");
467                                 Assert.AreEqual ("chunked", values.First (), "#103a");
468                                 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#103b");
469
470                                 Assert.IsTrue (response.Headers.TryGetValues ("Date", out values), "#104");
471                                 Assert.AreEqual (1, values.Count (), "#104b");
472                                 // .NET overwrites Date, Mono does not
473                                 // Assert.IsNotNull (response.Headers.Date, "#104c");
474
475                                 Assert.AreEqual (new ProductHeaderValue ("vvvvaa"), response.Headers.Upgrade.First (), "#105");
476
477                                 Assert.AreEqual ("audio", response.Headers.CacheControl.Extensions.First ().Name, "#106");
478
479                                 Assert.AreEqual ("w3.org", response.Headers.Location.OriginalString, "#107");
480
481                                 Assert.AreEqual ("test description", response.ReasonPhrase, "#110");
482                                 Assert.AreEqual (HttpVersion.Version11, response.Version, "#111");
483                                 
484                                 Assert.AreEqual (false, failed, "#112");
485                         } finally {
486                                 listener.Close ();
487                         }
488                 }
489
490                 [Test]
491                 public void Send_Complete_Content ()
492                 {
493                         var listener = CreateListener (l => {
494                                 var request = l.Request;
495                                 l.Response.OutputStream.WriteByte (55);
496                                 l.Response.OutputStream.WriteByte (75);
497                         });
498
499                         try {
500                                 var client = new HttpClient ();
501                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
502                                 Assert.IsTrue (request.Headers.TryAddWithoutValidation ("aa", "vv"), "#0");
503                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
504
505                                 Assert.AreEqual ("7K", response.Content.ReadAsStringAsync ().Result, "#100");
506                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
507
508                                 IEnumerable<string> values;
509                                 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#102");
510                                 Assert.AreEqual ("chunked", values.First (), "#102a");
511                                 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#102b");
512                         } finally {
513                                 listener.Close ();
514                         }
515                 }
516
517                 [Test]
518                 public void Send_Complete_Content_MaxResponseContentBufferSize ()
519                 {
520                         var listener = CreateListener (l => {
521                                 var request = l.Request;
522                                 var b = new byte[4000];
523                                 l.Response.OutputStream.Write (b, 0, b.Length);
524                         });
525
526                         try {
527                                 var client = new HttpClient ();
528                                 client.MaxResponseContentBufferSize = 1000;
529                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
530                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
531
532                                 Assert.AreEqual (4000, response.Content.ReadAsStringAsync ().Result.Length, "#100");
533                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
534                         } finally {
535                                 listener.Close ();
536                         }
537                 }
538
539                 [Test]
540                 public void Send_Complete_Content_MaxResponseContentBufferSize_Error ()
541                 {
542                         var listener = CreateListener (l => {
543                                 var request = l.Request;
544                                 var b = new byte[4000];
545                                 l.Response.OutputStream.Write (b, 0, b.Length);
546                         });
547
548                         try {
549                                 var client = new HttpClient ();
550                                 client.MaxResponseContentBufferSize = 1000;
551                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
552
553                                 try {
554                                         client.SendAsync (request, HttpCompletionOption.ResponseContentRead).Wait (WaitTimeout);
555                                         Assert.Fail ("#2");
556                                 } catch (AggregateException e) {
557                                         Assert.IsTrue (e.InnerException is HttpRequestException, "#3");
558                                 }
559
560                         } finally {
561                                 listener.Close ();
562                         }
563                 }
564
565                 [Test]
566                 public void Send_Complete_NoContent ()
567                 {
568                         foreach (var method in new HttpMethod[] { HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete }) {
569                                 bool? failed = null;
570                                 var listener = CreateListener (l => {
571                                         try {
572                                                 var request = l.Request;
573
574                                                 Assert.AreEqual (2, request.Headers.Count, "#1");
575                                                 Assert.AreEqual ("0", request.Headers ["Content-Length"], "#1b");
576                                                 Assert.AreEqual (method.Method, request.HttpMethod, "#2");
577                                                 failed = false;
578                                         } catch {
579                                                 failed = true;
580                                         }
581                                 });
582
583                                 try {
584                                         var client = new HttpClient ();
585                                         var request = new HttpRequestMessage (method, LocalServer);
586                                         var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
587
588                                         Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
589                                         Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
590                                         Assert.AreEqual (false, failed, "#102");
591                                 } finally {
592                                         listener.Close ();
593                                 }
594                         }
595                 }
596
597                 [Test]
598                 public void Send_Complete_Error ()
599                 {
600                         var listener = CreateListener (l => {
601                                 var response = l.Response;
602                                 response.StatusCode = 500;
603                         });
604
605                         try {
606                                 var client = new HttpClient ();
607                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
608                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
609
610                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
611                                 Assert.AreEqual (HttpStatusCode.InternalServerError, response.StatusCode, "#101");
612                         } finally {
613                                 listener.Close ();
614                         }
615                 }
616
617                 [Test]
618                 public void Send_Content_Get ()
619                 {
620                         var listener = CreateListener (l => {
621                                 var request = l.Request;
622                                 l.Response.OutputStream.WriteByte (72);
623                         });
624
625                         try {
626                                 var client = new HttpClient ();
627                                 var r = new HttpRequestMessage (HttpMethod.Get, LocalServer);
628                                 var response = client.SendAsync (r).Result;
629
630                                 Assert.AreEqual ("H", response.Content.ReadAsStringAsync ().Result);
631                         } finally {
632                                 listener.Close ();
633                         }
634                 }
635
636                 [Test]
637                 public void Send_Content_BomEncoding ()
638                 {
639                         var listener = CreateListener (l => {
640                                 var request = l.Request;
641
642                                 var str = l.Response.OutputStream;
643                                 str.WriteByte (0xEF);
644                                 str.WriteByte (0xBB);
645                                 str.WriteByte (0xBF);
646                                 str.WriteByte (71);
647                         });
648
649                         try {
650                                 var client = new HttpClient ();
651                                 var r = new HttpRequestMessage (HttpMethod.Get, LocalServer);
652                                 var response = client.SendAsync (r).Result;
653
654                                 Assert.AreEqual ("G", response.Content.ReadAsStringAsync ().Result);
655                         } finally {
656                                 listener.Close ();
657                         }
658                 }
659
660                 [Test]
661                 public void Send_Content_Put ()
662                 {
663                         bool passed = false;
664                         var listener = CreateListener (l => {
665                                 var request = l.Request;
666                                 passed = 7 == request.ContentLength64;
667                                 passed &= request.ContentType == "text/plain; charset=utf-8";
668                                 passed &= request.InputStream.ReadByte () == 'm';
669                         });
670
671                         try {
672                                 var client = new HttpClient ();
673                                 var r = new HttpRequestMessage (HttpMethod.Put, LocalServer);
674                                 r.Content = new StringContent ("my text");
675                                 var response = client.SendAsync (r).Result;
676
677                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#1");
678                                 Assert.IsTrue (passed, "#2");
679                         } finally {
680                                 listener.Abort ();
681                                 listener.Close ();
682                         }
683                 }
684
685                 [Test]
686                 public void Send_Timeout ()
687                 {
688                         var mh = new HttpMessageHandlerMock ();
689
690                         var client = new HttpClient (mh);
691                         client.Timeout = TimeSpan.FromMilliseconds (100);
692                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
693                         var response = new HttpResponseMessage ();
694
695                         mh.OnSendFull = (l, c) => {
696                                 Assert.IsTrue (c.WaitHandle.WaitOne (500), "#2");
697                                 return Task.FromResult (response);
698                         };
699
700                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
701                 }
702
703                 [Test]
704                 public void Send_Invalid ()
705                 {
706                         var client = new HttpClient ();
707                         try {
708                                 client.SendAsync (null).Wait (WaitTimeout);
709                                 Assert.Fail ("#1");
710                         } catch (ArgumentNullException) {
711                         }
712
713                         try {
714                                 var request = new HttpRequestMessage ();
715                                 client.SendAsync (request).Wait (WaitTimeout);
716                                 Assert.Fail ("#2");
717                         } catch (InvalidOperationException) {
718                         }
719                 }
720
721                 [Test]
722                 public void Send_InvalidHandler ()
723                 {
724                         var mh = new HttpMessageHandlerMock ();
725
726                         var client = new HttpClient (mh);
727                         client.BaseAddress = new Uri ("http://xamarin.com");
728                         var request = new HttpRequestMessage ();
729
730                         mh.OnSend = l => {
731                                 Assert.AreEqual (l, request, "#1");
732                                 return null;
733                         };
734
735                         try {
736                                 // Broken by design
737                                 client.SendAsync (request).Wait (WaitTimeout);
738                                 Assert.Fail ("#2");
739                         } catch (Exception) {
740                         }
741                 }
742
743                 [Test]
744                 public void Send_SameMessage ()
745                 {
746                         var mh = new HttpMessageHandlerMock ();
747
748                         var client = new HttpClient (mh);
749                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
750
751                         mh.OnSend = l => Task.FromResult (new HttpResponseMessage ());
752
753                         client.SendAsync (request).Wait (WaitTimeout);
754                         try {
755                                 client.SendAsync (request).Wait (WaitTimeout);
756                                 Assert.Fail ("#1");
757                         } catch (InvalidOperationException) {
758                         }
759                 }
760
761                 [Test]
762                 public void GetString_RelativeUri ()
763                 {
764                         var client = new HttpClient ();
765                         client.BaseAddress = new Uri ("http://en.wikipedia.org/wiki/");
766                         var uri = new Uri ("Computer", UriKind.Relative);
767
768                         Assert.That (client.GetStringAsync (uri).Result != null);
769                         Assert.That (client.GetStringAsync ("Computer").Result != null);
770                 }
771
772                 [Test]
773                 [Category ("MobileNotWorking")] // Missing encoding
774                 public void GetString_Many ()
775                 {
776                         var client = new HttpClient ();
777                         var t1 = client.GetStringAsync ("http://www.google.com");
778                         var t2 = client.GetStringAsync ("http://www.google.com");
779                         Assert.IsTrue (Task.WaitAll (new [] { t1, t2 }, WaitTimeout));          
780                 }
781
782                 [Test]
783                 public void GetByteArray_ServerError ()
784                 {
785                         var listener = CreateListener (l => {
786                                 var response = l.Response;
787                                 response.StatusCode = 500;
788                                 l.Response.OutputStream.WriteByte (72);
789                         });
790
791                         try {
792                                 var client = new HttpClient ();
793                                 try {
794                                         client.GetByteArrayAsync (LocalServer).Wait (WaitTimeout);
795                                         Assert.Fail ("#1");
796                                 } catch (AggregateException e) {
797                                         Assert.IsTrue (e.InnerException is HttpRequestException , "#2");
798                                 }
799                         } finally {
800                                 listener.Close ();
801                         }
802                 }
803
804                 [Test]
805                 public void DisallowAutoRedirect ()
806                 {
807                         var listener = CreateListener (l => {
808                                 var request = l.Request;
809                                 var response = l.Response;
810                                 
811                                 response.StatusCode = (int)HttpStatusCode.Moved;
812                                 response.RedirectLocation = "http://xamarin.com/";
813                         });
814
815                         try {
816                                 var chandler = new HttpClientHandler ();
817                                 chandler.AllowAutoRedirect = false;
818                                 var client = new HttpClient (chandler);
819
820                                 try {
821                                         client.GetStringAsync (LocalServer).Wait (WaitTimeout);
822                                         Assert.Fail ("#1");
823                                 } catch (AggregateException e) {
824                                         Assert.IsTrue (e.InnerException is HttpRequestException, "#2");
825                                 }
826                         } finally {
827                                 listener.Abort ();
828                                 listener.Close ();
829                         }
830                 }
831
832                 [Test]
833                 public void RequestUriAfterRedirect ()
834                 {
835                         var listener = CreateListener (l => {
836                                 var request = l.Request;
837                                 var response = l.Response;
838
839                                 response.StatusCode = (int)HttpStatusCode.Moved;
840                                 response.RedirectLocation = "http://xamarin.com/";
841                         });
842
843                         try {
844                                 var chandler = new HttpClientHandler ();
845                                 chandler.AllowAutoRedirect = true;
846                                 var client = new HttpClient (chandler);
847
848                                 var resp = client.GetAsync (LocalServer).Result;
849                                 Assert.AreEqual ("http://xamarin.com/", resp.RequestMessage.RequestUri.AbsoluteUri, "#1");
850                         } finally {
851                                 listener.Abort ();
852                                 listener.Close ();
853                         }
854                 }
855
856                 [Test]
857                 /*
858                  * Properties may only be modified before sending the first request.
859                  */
860                 public void ModifyHandlerAfterFirstRequest ()
861                 {
862                         var chandler = new HttpClientHandler ();
863                         chandler.AllowAutoRedirect = true;
864                         var client = new HttpClient (chandler, true);
865
866                         var listener = CreateListener (l => {
867                                 var response = l.Response;
868                                 response.StatusCode = 200;
869                                 response.OutputStream.WriteByte (55);
870                         });
871
872                         try {
873                                 client.GetStringAsync (LocalServer).Wait (WaitTimeout);
874                                 try {
875                                         chandler.AllowAutoRedirect = false;
876                                         Assert.Fail ("#1");
877                                 } catch (InvalidOperationException) {
878                                         ;
879                                 }
880                         } finally {
881                                 listener.Abort ();
882                                 listener.Close ();
883                         }
884                 }
885
886                 [Test]
887                 /*
888                  * However, this policy is not enforced for custom handlers and there
889                  * is also no way a derived class could tell its HttpClientHandler parent
890                  * that it just sent a request.
891                  * 
892                  */
893                 public void ModifyHandlerAfterFirstRequest_Mock ()
894                 {
895                         var ch = new HttpClientHandlerMock ();
896                         ch.AllowAutoRedirect = true;
897
898                         var client = new HttpClient (ch);
899
900                         ch.OnSend = (l) => {
901                                 return Task.FromResult (new HttpResponseMessage ());
902                         };
903
904                         client.GetAsync ("http://xamarin.com").Wait (WaitTimeout);
905                         ch.AllowAutoRedirect = false;
906                 }
907
908                 HttpListener CreateListener (Action<HttpListenerContext> contextAssert)
909                 {
910                         var l = new HttpListener ();
911                         l.Prefixes.Add (string.Format ("http://+:{0}/", port));
912                         l.Start ();
913                         l.BeginGetContext (ar => {
914                                 var ctx = l.EndGetContext (ar);
915
916                                 try {
917                                         if (contextAssert != null)
918                                                 contextAssert (ctx);
919                                 } finally {
920                                         ctx.Response.Close ();
921                                 }
922                         }, null);
923
924                         return l;
925                 }
926         }
927 }