Merge pull request #3330 from madewokherd/compactframework
[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                 class CustomStream : Stream
87                 {
88                         public override void Flush ()
89                         {
90                                 throw new NotImplementedException ();
91                         }
92
93                         int pos;
94
95                         public override int Read (byte[] buffer, int offset, int count)
96                         {
97                                 ++pos;
98                                 if (pos > 4)
99                                         return 0;
100
101                                 return 11;
102                         }
103
104                         public override long Seek (long offset, SeekOrigin origin)
105                         {
106                                 throw new NotImplementedException ();
107                         }
108
109                         public override void SetLength (long value)
110                         {
111                                 throw new NotImplementedException ();
112                         }
113
114                         public override void Write (byte[] buffer, int offset, int count)
115                         {
116                                 throw new NotImplementedException ();
117                         }
118
119                         public override bool CanRead {
120                                 get {
121                                         return true;
122                                 }
123                         }
124
125                         public override bool CanSeek {
126                                 get {
127                                         return false;
128                                 }
129                         }
130
131                         public override bool CanWrite {
132                                 get {
133                                         throw new NotImplementedException ();
134                                 }
135                         }
136
137                         public override long Length {
138                                 get {
139                                         throw new NotImplementedException ();
140                                 }
141                         }
142
143                         public override long Position {
144                                 get {
145                                         throw new NotImplementedException ();
146                                 }
147                                 set {
148                                         throw new NotImplementedException ();
149                                 }
150                         }
151                 }
152
153                 class ThrowOnlyProxy : IWebProxy
154                 {
155                         public ICredentials Credentials {
156                                 get {
157                                         throw new NotImplementedException ();
158                                 }
159
160                                 set {
161                                         throw new NotImplementedException ();
162                                 }
163                         }
164
165                         public Uri GetProxy (Uri destination)
166                         {
167                                 throw new NotImplementedException ();
168                         }
169
170                         public bool IsBypassed (Uri host)
171                         {
172                                 throw new NotImplementedException ();
173                         }
174                 }
175
176                 const int WaitTimeout = 5000;
177
178                 string TestHost, LocalServer;
179                 int port;
180
181                 [SetUp]
182                 public void SetupFixture ()
183                 {
184                         if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
185                                 port = 810;
186                         } else {
187                                 port = 8810;
188                         }
189
190                         TestHost = "localhost:" + port;
191                         LocalServer = string.Format ("http://{0}/", TestHost);
192                 }
193
194                 [Test]
195                 public void Ctor_Default ()
196                 {
197                         var client = new HttpClient ();
198                         Assert.IsNull (client.BaseAddress, "#1");
199                         Assert.IsNotNull (client.DefaultRequestHeaders, "#2");  // TODO: full check
200                         Assert.AreEqual (int.MaxValue, client.MaxResponseContentBufferSize, "#3");
201                         Assert.AreEqual (TimeSpan.FromSeconds (100), client.Timeout, "#4");
202                 }
203
204                 [Test]
205                 public void CancelPendingRequests ()
206                 {
207                         var mh = new HttpMessageHandlerMock ();
208
209                         var client = new HttpClient (mh);
210                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
211                         var mre = new ManualResetEvent (false);
212
213                         mh.OnSendFull = (l, c) => {
214                                 mre.Set ();
215                                 Assert.IsTrue (c.WaitHandle.WaitOne (1000), "#20");
216                                 Assert.IsTrue (c.IsCancellationRequested, "#21");
217                                 mre.Set ();
218                                 return Task.FromResult (new HttpResponseMessage ());
219                         };
220
221                         var t = Task.Factory.StartNew (() => {
222                                 client.SendAsync (request).Wait (WaitTimeout);
223                         });
224
225                         Assert.IsTrue (mre.WaitOne (500), "#1");
226                         mre.Reset ();
227                         client.CancelPendingRequests ();
228                         Assert.IsTrue (t.Wait (500), "#2");
229
230                         request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
231                         mh.OnSendFull = (l, c) => {
232                                 Assert.IsFalse (c.IsCancellationRequested, "#30");
233                                 return Task.FromResult (new HttpResponseMessage ());
234                         };
235
236                         client.SendAsync (request).Wait (WaitTimeout);
237                 }
238
239                 [Test]
240                 public void CancelPendingRequests_BeforeSend ()
241                 {
242                         var ct = new CancellationTokenSource ();
243                         ct.Cancel ();
244                         var rr = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken (), ct.Token);
245
246
247                         var mh = new HttpMessageHandlerMock ();
248
249                         var client = new HttpClient (mh);
250                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
251                         client.CancelPendingRequests ();
252
253                         mh.OnSendFull = (l, c) => {
254                                 Assert.IsFalse (c.IsCancellationRequested, "#30");
255                                 return Task.FromResult (new HttpResponseMessage ());
256                         };
257
258                         client.SendAsync (request).Wait (WaitTimeout);
259
260                         request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
261                         client.SendAsync (request).Wait (WaitTimeout);
262                 }
263
264
265                 [Test]
266                 public void CancelRequestViaProxy ()
267                 {
268                         var handler = new HttpClientHandler {
269                                 Proxy = new WebProxy ("192.168.10.25:8888/"), // proxy that doesn't exist
270                                 UseProxy = true,
271                                 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
272                         };
273
274                         var httpClient = new HttpClient (handler) {
275                                 BaseAddress = new Uri ("https://google.com"),
276                                 Timeout = TimeSpan.FromMilliseconds (1)
277                         };
278
279                         try {
280                                 var restRequest = new HttpRequestMessage {
281                                         Method = HttpMethod.Post,
282                                         RequestUri = new Uri("foo", UriKind.Relative),
283                                         Content = new StringContent("", null, "application/json")
284                                 };
285
286                                 httpClient.PostAsync (restRequest.RequestUri, restRequest.Content).Wait (WaitTimeout);
287                                 Assert.Fail ("#1");
288                         } catch (AggregateException e) {
289                                 Assert.IsTrue (e.InnerException is TaskCanceledException, "#2");
290                         }
291                 }
292
293                 [Test]
294                 public void Properties ()
295                 {
296                         var client = new HttpClient ();
297                         client.BaseAddress = null;
298                         client.MaxResponseContentBufferSize = int.MaxValue;
299                         client.Timeout = Timeout.InfiniteTimeSpan;
300
301                         Assert.IsNull (client.BaseAddress, "#1");
302                         Assert.AreEqual (int.MaxValue, client.MaxResponseContentBufferSize, "#2");
303                         Assert.AreEqual (Timeout.InfiniteTimeSpan, client.Timeout, "#3");
304                 }
305
306                 [Test]
307                 public void Properties_Invalid ()
308                 {
309                         var client = new HttpClient ();
310                         try {
311                                 client.MaxResponseContentBufferSize = 0;
312                                 Assert.Fail ("#1");
313                         } catch (ArgumentOutOfRangeException) {
314                         }
315
316                         try {
317                                 client.Timeout = TimeSpan.MinValue;
318                                 Assert.Fail ("#2");
319                         } catch (ArgumentOutOfRangeException) {
320                         }
321                 }
322
323                 [Test]
324                 [Category ("RequiresBSDSockets")]
325                 public void Proxy_Disabled ()
326                 {
327                         var pp = WebRequest.DefaultWebProxy;
328
329                         try {
330                                 WebRequest.DefaultWebProxy = new ThrowOnlyProxy ();
331
332                                 var request = new HttpClientHandler {
333                                         UseProxy = false
334                                 };
335
336                                 var client = new HttpClient (request);
337                                 Assert.IsTrue (client.GetAsync ("http://google.com").Wait (5000), "needs internet access");
338                         } finally {
339                                 WebRequest.DefaultWebProxy = pp;
340                         }
341                 }
342
343                 [Test]
344                 public void Send ()
345                 {
346                         var mh = new HttpMessageHandlerMock ();
347
348                         var client = new HttpClient (mh);
349                         client.BaseAddress = new Uri ("http://xamarin.com");
350                         var request = new HttpRequestMessage ();
351                         var response = new HttpResponseMessage ();
352
353                         mh.OnSend = l => {
354                                 Assert.AreEqual (l, request, "#2");
355                                 Assert.AreEqual (client.BaseAddress, l.RequestUri, "#2");
356                                 return Task.FromResult (response);
357                         };
358
359                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
360                 }
361
362                 [Test]
363                 public void Send_BaseAddress ()
364                 {
365                         var mh = new HttpMessageHandlerMock ();
366
367                         var client = new HttpClient (mh);
368                         client.BaseAddress = new Uri ("http://localhost/");
369                         var response = new HttpResponseMessage ();
370
371                         mh.OnSend = l => {
372                                 Assert.AreEqual ("http://localhost/relative", l.RequestUri.ToString (), "#2");
373                                 return Task.FromResult (response);
374                         };
375
376                         Assert.AreEqual (response, client.GetAsync ("relative").Result, "#1");
377                         Assert.AreEqual (response, client.GetAsync ("/relative").Result, "#2");
378                 }
379
380                 [Test]
381                 public void Send_DefaultRequestHeaders ()
382                 {
383                         var mh = new HttpMessageHandlerMock ();
384
385                         var client = new HttpClient (mh);
386                         client.DefaultRequestHeaders.Referrer = new Uri ("http://google.com");
387
388                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
389                         var response = new HttpResponseMessage ();
390
391                         mh.OnSend = l => {
392                                 Assert.AreEqual (client.DefaultRequestHeaders.Referrer, l.Headers.Referrer, "#2");
393                                 Assert.IsNotNull (l.Headers.Referrer, "#3");
394                                 return Task.FromResult (response);
395                         };
396
397                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
398                 }
399
400                 [Test]
401                 [Category ("RequiresBSDSockets")]
402                 public void Send_Complete_Default ()
403                 {
404                         bool? failed = null;
405                         var listener = CreateListener (l => {
406                                 try {
407                                         var request = l.Request;
408         
409                                         Assert.IsNull (request.AcceptTypes, "#1");
410                                         Assert.AreEqual (0, request.ContentLength64, "#2");
411                                         Assert.IsNull (request.ContentType, "#3");
412                                         Assert.AreEqual (0, request.Cookies.Count, "#4");
413                                         Assert.IsFalse (request.HasEntityBody, "#5");
414                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6b");
415                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
416                                         Assert.IsFalse (request.IsAuthenticated, "#8");
417                                         Assert.IsTrue (request.IsLocal, "#9");
418                                         Assert.IsFalse (request.IsSecureConnection, "#10");
419                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
420                                         Assert.IsTrue (request.KeepAlive, "#12");
421                                         Assert.AreEqual (HttpVersion.Version11, request.ProtocolVersion, "#13");
422                                         Assert.IsNull (request.ServiceName, "#14");
423                                         Assert.IsNull (request.UrlReferrer, "#15");
424                                         Assert.IsNull (request.UserAgent, "#16");
425                                         Assert.IsNull (request.UserLanguages, "#17");
426                                         failed = false;
427                                 } catch {
428                                         failed = true;
429                                 }
430                         });
431
432                         try {
433                                 var client = new HttpClient ();
434                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
435                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
436
437                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
438                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
439                                 Assert.AreEqual (false, failed, "#102");
440                         } finally {
441                                 listener.Close ();
442                         }
443                 }
444
445                 [Test]
446                 [Category ("RequiresBSDSockets")]
447                 public void Send_Complete_Version_1_0 ()
448                 {
449                         bool? failed = null;
450                         
451                         var listener = CreateListener (l => {
452                                 try {
453                                         var request = l.Request;
454         
455                                         Assert.IsNull (request.AcceptTypes, "#1");
456                                         Assert.AreEqual (0, request.ContentLength64, "#2");
457                                         Assert.IsNull (request.ContentType, "#3");
458                                         Assert.AreEqual (0, request.Cookies.Count, "#4");
459                                         Assert.IsFalse (request.HasEntityBody, "#5");
460                                         Assert.AreEqual (1, request.Headers.Count, "#6");
461                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
462                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
463                                         Assert.IsFalse (request.IsAuthenticated, "#8");
464                                         Assert.IsTrue (request.IsLocal, "#9");
465                                         Assert.IsFalse (request.IsSecureConnection, "#10");
466                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
467                                         Assert.IsFalse (request.KeepAlive, "#12");
468                                         Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
469                                         Assert.IsNull (request.ServiceName, "#14");
470                                         Assert.IsNull (request.UrlReferrer, "#15");
471                                         Assert.IsNull (request.UserAgent, "#16");
472                                         Assert.IsNull (request.UserLanguages, "#17");
473                                         failed = false;
474                                 } catch {
475                                         failed = true;
476                                 }
477                         });
478
479                         try {
480                                 var client = new HttpClient ();
481                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
482                                 request.Version = HttpVersion.Version10;
483                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
484
485                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
486                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
487                                 Assert.AreEqual (false, failed, "#102");
488                         } finally {
489                                 listener.Close ();
490                         }
491                 }
492
493                 [Test]
494                 [Category ("RequiresBSDSockets")]
495                 public void Send_Complete_ClientHandlerSettings ()
496                 {
497                         bool? failed = null;
498                         
499                         var listener = CreateListener (l => {
500                                 var request = l.Request;
501                                 
502                                 try {
503                                         Assert.IsNull (request.AcceptTypes, "#1");
504                                         Assert.AreEqual (0, request.ContentLength64, "#2");
505                                         Assert.IsNull (request.ContentType, "#3");
506                                         Assert.AreEqual (1, request.Cookies.Count, "#4");
507                                         Assert.AreEqual (new Cookie ("mycookie", "vv"), request.Cookies[0], "#4a");
508                                         Assert.IsFalse (request.HasEntityBody, "#5");
509                                         Assert.AreEqual (4, request.Headers.Count, "#6");
510                                         Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
511                                         Assert.AreEqual ("gzip", request.Headers["Accept-Encoding"], "#6b");
512                                         Assert.AreEqual ("mycookie=vv", request.Headers["Cookie"], "#6c");
513                                         Assert.AreEqual ("GET", request.HttpMethod, "#7");
514                                         Assert.IsFalse (request.IsAuthenticated, "#8");
515                                         Assert.IsTrue (request.IsLocal, "#9");
516                                         Assert.IsFalse (request.IsSecureConnection, "#10");
517                                         Assert.IsFalse (request.IsWebSocketRequest, "#11");
518                                         Assert.IsTrue (request.KeepAlive, "#12");
519                                         Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
520                                         Assert.IsNull (request.ServiceName, "#14");
521                                         Assert.IsNull (request.UrlReferrer, "#15");
522                                         Assert.IsNull (request.UserAgent, "#16");
523                                         Assert.IsNull (request.UserLanguages, "#17");
524                                         failed = false;
525                                 } catch {
526                                         failed = true;
527                                 }
528                         });
529
530                         try {
531                                 var chandler = new HttpClientHandler ();
532                                 chandler.AllowAutoRedirect = true;
533                                 chandler.AutomaticDecompression = DecompressionMethods.GZip;
534                                 chandler.MaxAutomaticRedirections = 33;
535                                 chandler.MaxRequestContentBufferSize = 5555;
536                                 chandler.PreAuthenticate = true;
537                                 chandler.CookieContainer.Add (new Uri (LocalServer), new Cookie ( "mycookie", "vv"));
538                                 chandler.UseCookies = true;
539                                 chandler.UseDefaultCredentials = true;
540                                 chandler.Proxy = new WebProxy ("ee");
541                                 chandler.UseProxy = true;
542
543                                 var client = new HttpClient (chandler);
544                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
545                                 request.Version = HttpVersion.Version10;
546                                 request.Headers.Add ("Keep-Alive", "false");
547                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
548
549                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
550                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
551                                 Assert.AreEqual (false, failed, "#102");
552                         } finally {
553                                 listener.Abort ();
554                                 listener.Close ();
555                         }
556                 }
557
558                 [Test]
559                 [Category ("RequiresBSDSockets")]
560                 public void Send_Complete_CustomHeaders ()
561                 {
562                         bool? failed = null;
563                         
564                         var listener = CreateListener (l => {
565                                 var request = l.Request;
566                                 try {
567                                         Assert.AreEqual ("vv", request.Headers["aa"], "#1");
568         
569                                         var response = l.Response;
570                                         response.Headers.Add ("rsp", "rrr");
571                                         response.Headers.Add ("upgrade", "vvvvaa");
572                                         response.Headers.Add ("Date", "aa");
573                                         response.Headers.Add ("cache-control", "audio");
574         
575                                         response.StatusDescription = "test description";
576                                         response.ProtocolVersion = HttpVersion.Version10;
577                                         response.SendChunked = true;
578                                         response.RedirectLocation = "w3.org";
579                                         
580                                         failed = false;
581                                 } catch {
582                                         failed = true;
583                                 }
584                         });
585
586                         try {
587                                 var client = new HttpClient ();
588                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
589                                 Assert.IsTrue (request.Headers.TryAddWithoutValidation ("aa", "vv"), "#0");
590                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
591
592                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
593                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
594                                 
595                                 IEnumerable<string> values;
596                                 Assert.IsTrue (response.Headers.TryGetValues ("rsp", out values), "#102");
597                                 Assert.AreEqual ("rrr", values.First (), "#102a");
598
599                                 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#103");
600                                 Assert.AreEqual ("chunked", values.First (), "#103a");
601                                 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#103b");
602
603                                 Assert.IsTrue (response.Headers.TryGetValues ("Date", out values), "#104");
604                                 Assert.AreEqual (1, values.Count (), "#104b");
605                                 // .NET overwrites Date, Mono does not
606                                 // Assert.IsNotNull (response.Headers.Date, "#104c");
607
608                                 Assert.AreEqual (new ProductHeaderValue ("vvvvaa"), response.Headers.Upgrade.First (), "#105");
609
610                                 Assert.AreEqual ("audio", response.Headers.CacheControl.Extensions.First ().Name, "#106");
611
612                                 Assert.AreEqual ("w3.org", response.Headers.Location.OriginalString, "#107");
613
614                                 Assert.AreEqual ("test description", response.ReasonPhrase, "#110");
615                                 Assert.AreEqual (HttpVersion.Version11, response.Version, "#111");
616                                 
617                                 Assert.AreEqual (false, failed, "#112");
618                         } finally {
619                                 listener.Close ();
620                         }
621                 }
622
623                 [Test]
624                 [Category ("RequiresBSDSockets")]
625                 public void Send_Complete_CustomHeaders_SpecialSeparators ()
626                 {
627                         bool? failed = null;
628
629                         var listener = CreateListener (l => {
630                                 var request = l.Request;
631
632                                 try {
633                                         Assert.AreEqual ("MLK Android Phone 1.1.9", request.UserAgent, "#1");
634                                         failed = false;
635                                 } catch {
636                                         failed = true;
637                                 }
638                         });
639
640                         try {
641                                 var client = new HttpClient ();
642
643                                 client.DefaultRequestHeaders.Add("User-Agent", "MLK Android Phone 1.1.9");
644
645                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
646
647                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
648
649                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
650                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
651                                 Assert.AreEqual (false, failed, "#102");
652                         } finally {
653                                 listener.Abort ();
654                                 listener.Close ();
655                         }
656                 }
657
658                 [Test]
659                 [Category ("RequiresBSDSockets")]
660                 public void Send_Complete_CustomHeaders_Host ()
661                 {
662                         bool? failed = null;
663                         var listener = CreateListener (l => {
664                                 var request = l.Request;
665
666                                 try {
667                                         Assert.AreEqual ("customhost", request.Headers["Host"], "#1");
668                                         failed = false;
669                                 } catch {
670                                         failed = true;
671                                 }
672                         });
673
674                         try {
675                                 var client = new HttpClient ();
676
677                                 client.DefaultRequestHeaders.Add("Host", "customhost");
678
679                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
680
681                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
682
683                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
684                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
685                                 Assert.AreEqual (false, failed, "#102");
686                         } finally {
687                                 listener.Abort ();
688                                 listener.Close ();
689                         }
690                 }
691
692                 [Test]
693                 [Category ("RequiresBSDSockets")]
694                 public void Send_Transfer_Encoding_Chunked ()
695                 {
696                         bool? failed = null;
697
698                         var listener = CreateListener (l => {
699                                 var request = l.Request;
700
701                                 try {
702                                         Assert.AreEqual (1, request.Headers.Count, "#1");
703                                         failed = false;
704                                 } catch {
705                                         failed = true;
706                                 }
707                         });
708
709                         try {
710                                 var client = new HttpClient ();
711                                 client.DefaultRequestHeaders.TransferEncodingChunked = true;
712
713                                 client.GetAsync (LocalServer).Wait ();
714
715                                 Assert.AreEqual (false, failed, "#102");
716                         } finally {
717                                 listener.Abort ();
718                                 listener.Close ();
719                         }
720                 }
721
722                 [Test]
723                 [Category ("RequiresBSDSockets")]
724                 public void Send_Transfer_Encoding_Custom ()
725                 {
726                         bool? failed = null;
727
728                         var listener = CreateListener (l => {
729                                 failed = true;
730                         });
731
732                         try {
733                                 var client = new HttpClient ();
734                                 client.DefaultRequestHeaders.TransferEncoding.Add (new TransferCodingHeaderValue ("chunked2"));
735
736                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
737
738                                 try {
739                                         client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Wait ();
740                                         Assert.Fail ("#1");
741                                 } catch (AggregateException e) {
742                                         Assert.AreEqual (typeof (ProtocolViolationException), e.InnerException.GetType (), "#2");
743                                 }
744                                 Assert.IsNull (failed, "#102");
745                         } finally {
746                                 listener.Abort ();
747                                 listener.Close ();
748                         }
749                 }
750
751                 [Test]
752                 [Category ("RequiresBSDSockets")]
753                 public void Send_Complete_Content ()
754                 {
755                         var listener = CreateListener (l => {
756                                 var request = l.Request;
757                                 l.Response.OutputStream.WriteByte (55);
758                                 l.Response.OutputStream.WriteByte (75);
759                         });
760
761                         try {
762                                 var client = new HttpClient ();
763                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
764                                 Assert.IsTrue (request.Headers.TryAddWithoutValidation ("aa", "vv"), "#0");
765                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
766
767                                 Assert.AreEqual ("7K", response.Content.ReadAsStringAsync ().Result, "#100");
768                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
769
770                                 IEnumerable<string> values;
771                                 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#102");
772                                 Assert.AreEqual ("chunked", values.First (), "#102a");
773                                 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#102b");
774                         } finally {
775                                 listener.Close ();
776                         }
777                 }
778
779                 [Test]
780                 [Category ("RequiresBSDSockets")]
781                 public void Send_Complete_Content_MaxResponseContentBufferSize ()
782                 {
783                         var listener = CreateListener (l => {
784                                 var request = l.Request;
785                                 var b = new byte[4000];
786                                 l.Response.OutputStream.Write (b, 0, b.Length);
787                         });
788
789                         try {
790                                 var client = new HttpClient ();
791                                 client.MaxResponseContentBufferSize = 1000;
792                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
793                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
794
795                                 Assert.AreEqual (4000, response.Content.ReadAsStringAsync ().Result.Length, "#100");
796                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
797                         } finally {
798                                 listener.Close ();
799                         }
800                 }
801
802                 [Test]
803                 [Category ("RequiresBSDSockets")]
804                 public void Send_Complete_Content_MaxResponseContentBufferSize_Error ()
805                 {
806                         var listener = CreateListener (l => {
807                                 var request = l.Request;
808                                 var b = new byte[4000];
809                                 l.Response.OutputStream.Write (b, 0, b.Length);
810                         });
811
812                         try {
813                                 var client = new HttpClient ();
814                                 client.MaxResponseContentBufferSize = 1000;
815                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
816
817                                 try {
818                                         client.SendAsync (request, HttpCompletionOption.ResponseContentRead).Wait (WaitTimeout);
819                                         Assert.Fail ("#2");
820                                 } catch (AggregateException e) {
821                                         Assert.IsTrue (e.InnerException is HttpRequestException, "#3");
822                                 }
823
824                         } finally {
825                                 listener.Close ();
826                         }
827                 }
828
829                 [Test]
830                 [Category ("RequiresBSDSockets")]
831                 public void Send_Complete_NoContent ()
832                 {
833                         foreach (var method in new HttpMethod[] { HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete }) {
834                                 bool? failed = null;
835                                 var listener = CreateListener (l => {
836                                         try {
837                                                 var request = l.Request;
838
839                                                 Assert.AreEqual (2, request.Headers.Count, "#1");
840                                                 Assert.AreEqual ("0", request.Headers ["Content-Length"], "#1b");
841                                                 Assert.AreEqual (method.Method, request.HttpMethod, "#2");
842                                                 failed = false;
843                                         } catch {
844                                                 failed = true;
845                                         }
846                                 });
847
848                                 try {
849                                         var client = new HttpClient ();
850                                         var request = new HttpRequestMessage (method, LocalServer);
851                                         var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
852
853                                         Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
854                                         Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
855                                         Assert.AreEqual (false, failed, "#102");
856                                 } finally {
857                                         listener.Close ();
858                                 }
859                         }
860                 }
861
862                 [Test]
863                 [Category ("RequiresBSDSockets")]
864                 public void Send_Complete_Error ()
865                 {
866                         var listener = CreateListener (l => {
867                                 var response = l.Response;
868                                 response.StatusCode = 500;
869                         });
870
871                         try {
872                                 var client = new HttpClient ();
873                                 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
874                                 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
875
876                                 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
877                                 Assert.AreEqual (HttpStatusCode.InternalServerError, response.StatusCode, "#101");
878                         } finally {
879                                 listener.Close ();
880                         }
881                 }
882
883                 [Test]
884                 [Category ("RequiresBSDSockets")]
885                 public void Send_Content_Get ()
886                 {
887                         var listener = CreateListener (l => {
888                                 var request = l.Request;
889                                 l.Response.OutputStream.WriteByte (72);
890                         });
891
892                         try {
893                                 var client = new HttpClient ();
894                                 var r = new HttpRequestMessage (HttpMethod.Get, LocalServer);
895                                 var response = client.SendAsync (r).Result;
896
897                                 Assert.AreEqual ("H", response.Content.ReadAsStringAsync ().Result);
898                         } finally {
899                                 listener.Close ();
900                         }
901                 }
902
903                 [Test]
904                 [Category ("RequiresBSDSockets")]
905                 public void Send_Content_BomEncoding ()
906                 {
907                         var listener = CreateListener (l => {
908                                 var request = l.Request;
909
910                                 var str = l.Response.OutputStream;
911                                 str.WriteByte (0xEF);
912                                 str.WriteByte (0xBB);
913                                 str.WriteByte (0xBF);
914                                 str.WriteByte (71);
915                         });
916
917                         try {
918                                 var client = new HttpClient ();
919                                 var r = new HttpRequestMessage (HttpMethod.Get, LocalServer);
920                                 var response = client.SendAsync (r).Result;
921
922                                 Assert.AreEqual ("G", response.Content.ReadAsStringAsync ().Result);
923                         } finally {
924                                 listener.Close ();
925                         }
926                 }
927
928                 [Test]
929                 [Category ("RequiresBSDSockets")]
930                 public void Send_Content_Put ()
931                 {
932                         bool passed = false;
933                         var listener = CreateListener (l => {
934                                 var request = l.Request;
935                                 passed = 7 == request.ContentLength64;
936                                 passed &= request.ContentType == "text/plain; charset=utf-8";
937                                 passed &= request.InputStream.ReadByte () == 'm';
938                         });
939
940                         try {
941                                 var client = new HttpClient ();
942                                 var r = new HttpRequestMessage (HttpMethod.Put, LocalServer);
943                                 r.Content = new StringContent ("my text");
944                                 var response = client.SendAsync (r).Result;
945
946                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#1");
947                                 Assert.IsTrue (passed, "#2");
948                         } finally {
949                                 listener.Abort ();
950                                 listener.Close ();
951                         }
952                 }
953
954                 [Test]
955                 [Category ("RequiresBSDSockets")]
956                 public void Send_Content_Put_CustomStream ()
957                 {
958                         bool passed = false;
959                         var listener = CreateListener (l => {
960                                 var request = l.Request;
961                                 passed = 44 == request.ContentLength64;
962                                 passed &= request.ContentType == null;
963                         });
964
965                         try {
966                                 var client = new HttpClient ();
967                                 var r = new HttpRequestMessage (HttpMethod.Put, LocalServer);
968                                 r.Content = new StreamContent (new CustomStream ());
969                                 var response = client.SendAsync (r).Result;
970
971                                 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#1");
972                                 Assert.IsTrue (passed, "#2");
973                         } finally {
974                                 listener.Abort ();
975
976                                 listener.Close ();
977                         }
978                 }
979
980                 [Test]
981                 public void Send_Timeout ()
982                 {
983                         var mh = new HttpMessageHandlerMock ();
984
985                         var client = new HttpClient (mh);
986                         client.Timeout = TimeSpan.FromMilliseconds (100);
987                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
988                         var response = new HttpResponseMessage ();
989
990                         mh.OnSendFull = (l, c) => {
991                                 Assert.IsTrue (c.WaitHandle.WaitOne (500), "#2");
992                                 return Task.FromResult (response);
993                         };
994
995                         Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
996                 }
997
998                 [Test]
999                 public void Send_Invalid ()
1000                 {
1001                         var client = new HttpClient ();
1002                         try {
1003                                 client.SendAsync (null).Wait (WaitTimeout);
1004                                 Assert.Fail ("#1");
1005                         } catch (ArgumentNullException) {
1006                         }
1007
1008                         try {
1009                                 var request = new HttpRequestMessage ();
1010                                 client.SendAsync (request).Wait (WaitTimeout);
1011                                 Assert.Fail ("#2");
1012                         } catch (InvalidOperationException) {
1013                         }
1014                 }
1015
1016                 [Test]
1017                 public void Send_InvalidHandler ()
1018                 {
1019                         var mh = new HttpMessageHandlerMock ();
1020
1021                         var client = new HttpClient (mh);
1022                         client.BaseAddress = new Uri ("http://xamarin.com");
1023                         var request = new HttpRequestMessage ();
1024
1025                         mh.OnSend = l => {
1026                                 Assert.AreEqual (l, request, "#1");
1027                                 return null;
1028                         };
1029
1030                         try {
1031                                 // Broken by design
1032                                 client.SendAsync (request).Wait (WaitTimeout);
1033                                 Assert.Fail ("#2");
1034                         } catch (Exception) {
1035                         }
1036                 }
1037
1038                 [Test]
1039                 public void Send_SameMessage ()
1040                 {
1041                         var mh = new HttpMessageHandlerMock ();
1042
1043                         var client = new HttpClient (mh);
1044                         var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
1045
1046                         mh.OnSend = l => Task.FromResult (new HttpResponseMessage ());
1047
1048                         client.SendAsync (request).Wait (WaitTimeout);
1049                         try {
1050                                 client.SendAsync (request).Wait (WaitTimeout);
1051                                 Assert.Fail ("#1");
1052                         } catch (InvalidOperationException) {
1053                         }
1054                 }
1055
1056                 [Test]
1057                 [Category ("MobileNotWorking")] // Missing encoding
1058                 [Category ("RequiresBSDSockets")]
1059                 public void GetString_Many ()
1060                 {
1061                         Action<HttpListenerContext> context = (HttpListenerContext l) => {
1062                                 var response = l.Response;
1063                                 response.StatusCode = 200;
1064                                 response.OutputStream.WriteByte (0x68);
1065                                 response.OutputStream.WriteByte (0x65);
1066                                 response.OutputStream.WriteByte (0x6c);
1067                                 response.OutputStream.WriteByte (0x6c);
1068                                 response.OutputStream.WriteByte (0x6f);
1069                         };
1070
1071                         var listener = CreateListener (context); // creates a default request handler
1072                         AddListenerContext (listener, context);  // add another request handler for the second request
1073
1074                         try {
1075                                 var client = new HttpClient ();
1076                                 var t1 = client.GetStringAsync (LocalServer);
1077                                 var t2 = client.GetStringAsync (LocalServer);
1078                                 Assert.IsTrue (Task.WaitAll (new [] { t1, t2 }, WaitTimeout));
1079                                 Assert.AreEqual ("hello", t1.Result, "#1");
1080                                 Assert.AreEqual ("hello", t2.Result, "#2");
1081                         } finally {
1082                                 listener.Abort ();
1083                                 listener.Close ();
1084                         }
1085                 }
1086
1087                 [Test]
1088                 [Category ("RequiresBSDSockets")]
1089                 public void GetByteArray_ServerError ()
1090                 {
1091                         var listener = CreateListener (l => {
1092                                 var response = l.Response;
1093                                 response.StatusCode = 500;
1094                                 l.Response.OutputStream.WriteByte (72);
1095                         });
1096
1097                         try {
1098                                 var client = new HttpClient ();
1099                                 try {
1100                                         client.GetByteArrayAsync (LocalServer).Wait (WaitTimeout);
1101                                         Assert.Fail ("#1");
1102                                 } catch (AggregateException e) {
1103                                         Assert.IsTrue (e.InnerException is HttpRequestException , "#2");
1104                                 }
1105                         } finally {
1106                                 listener.Close ();
1107                         }
1108                 }
1109
1110                 [Test]
1111                 [Category ("RequiresBSDSockets")]
1112                 public void DisallowAutoRedirect ()
1113                 {
1114                         var listener = CreateListener (l => {
1115                                 var request = l.Request;
1116                                 var response = l.Response;
1117                                 
1118                                 response.StatusCode = (int)HttpStatusCode.Moved;
1119                                 response.RedirectLocation = "http://xamarin.com/";
1120                         });
1121
1122                         try {
1123                                 var chandler = new HttpClientHandler ();
1124                                 chandler.AllowAutoRedirect = false;
1125                                 var client = new HttpClient (chandler);
1126
1127                                 try {
1128                                         client.GetStringAsync (LocalServer).Wait (WaitTimeout);
1129                                         Assert.Fail ("#1");
1130                                 } catch (AggregateException e) {
1131                                         Assert.IsTrue (e.InnerException is HttpRequestException, "#2");
1132                                 }
1133                         } finally {
1134                                 listener.Abort ();
1135                                 listener.Close ();
1136                         }
1137                 }
1138
1139                 [Test]
1140                 [Category ("RequiresBSDSockets")]
1141                 public void RequestUriAfterRedirect ()
1142                 {
1143                         var listener = CreateListener (l => {
1144                                 var request = l.Request;
1145                                 var response = l.Response;
1146
1147                                 response.StatusCode = (int)HttpStatusCode.Moved;
1148                                 response.RedirectLocation = "http://localhost:8811/";
1149                         });
1150
1151                         var listener2 = CreateListener (l => {
1152                                 var response = l.Response;
1153
1154                                 response.StatusCode = (int)HttpStatusCode.OK;
1155                                 response.OutputStream.WriteByte (0x68);
1156                                 response.OutputStream.WriteByte (0x65);
1157                                 response.OutputStream.WriteByte (0x6c);
1158                                 response.OutputStream.WriteByte (0x6c);
1159                                 response.OutputStream.WriteByte (0x6f);
1160                         }, 8811);
1161
1162                         try {
1163                                 var chandler = new HttpClientHandler ();
1164                                 chandler.AllowAutoRedirect = true;
1165                                 var client = new HttpClient (chandler);
1166
1167                                 var r = client.GetAsync (LocalServer);
1168                                 Assert.IsTrue (r.Wait (WaitTimeout), "#1");
1169                                 var resp = r.Result;
1170                                 Assert.AreEqual ("http://localhost:8811/", resp.RequestMessage.RequestUri.AbsoluteUri, "#2");
1171                                 Assert.AreEqual ("hello", resp.Content.ReadAsStringAsync ().Result, "#3");
1172                         } finally {
1173                                 listener.Abort ();
1174                                 listener.Close ();
1175                                 listener2.Abort ();
1176                                 listener2.Close ();
1177                         }
1178                 }
1179
1180                 [Test]
1181                 [Category ("RequiresBSDSockets")]
1182                 /*
1183                  * Properties may only be modified before sending the first request.
1184                  */
1185                 public void ModifyHandlerAfterFirstRequest ()
1186                 {
1187                         var chandler = new HttpClientHandler ();
1188                         chandler.AllowAutoRedirect = true;
1189                         var client = new HttpClient (chandler, true);
1190
1191                         var listener = CreateListener (l => {
1192                                 var response = l.Response;
1193                                 response.StatusCode = 200;
1194                                 response.OutputStream.WriteByte (55);
1195                         });
1196
1197                         try {
1198                                 client.GetStringAsync (LocalServer).Wait (WaitTimeout);
1199                                 try {
1200                                         chandler.AllowAutoRedirect = false;
1201                                         Assert.Fail ("#1");
1202                                 } catch (InvalidOperationException) {
1203                                         ;
1204                                 }
1205                         } finally {
1206                                 listener.Abort ();
1207                                 listener.Close ();
1208                         }
1209                 }
1210
1211                 [Test]
1212                 /*
1213                  * However, this policy is not enforced for custom handlers and there
1214                  * is also no way a derived class could tell its HttpClientHandler parent
1215                  * that it just sent a request.
1216                  * 
1217                  */
1218                 public void ModifyHandlerAfterFirstRequest_Mock ()
1219                 {
1220                         var ch = new HttpClientHandlerMock ();
1221                         ch.AllowAutoRedirect = true;
1222
1223                         var client = new HttpClient (ch);
1224
1225                         ch.OnSend = (l) => {
1226                                 return Task.FromResult (new HttpResponseMessage ());
1227                         };
1228
1229                         client.GetAsync ("http://xamarin.com").Wait (WaitTimeout);
1230                         ch.AllowAutoRedirect = false;
1231                 }
1232
1233                 HttpListener CreateListener (Action<HttpListenerContext> contextAssert)
1234                 {
1235                         return CreateListener (contextAssert, port);
1236                 }
1237
1238                 HttpListener CreateListener (Action<HttpListenerContext> contextAssert, int port)
1239                 {
1240                         var l = new HttpListener ();
1241                         l.Prefixes.Add (string.Format ("http://+:{0}/", port));
1242                         l.Start ();
1243                         AddListenerContext(l, contextAssert);
1244
1245                         return l;
1246                 }
1247
1248                 HttpListener AddListenerContext (HttpListener l, Action<HttpListenerContext> contextAssert)
1249                 {
1250                         l.BeginGetContext (ar => {
1251                                 var ctx = l.EndGetContext (ar);
1252
1253                                 try {
1254                                         if (contextAssert != null)
1255                                                 contextAssert (ctx);
1256                                 } finally {
1257                                         ctx.Response.Close ();
1258                                 }
1259                         }, null);
1260
1261                         return l;
1262                 }
1263         }
1264 }