5 // Marek Safar <marek.safar@gmail.com>
7 // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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;
41 namespace MonoTests.System.Net.Http
44 public class HttpClientTest
46 class HttpMessageHandlerMock : HttpMessageHandler
48 public Func<HttpRequestMessage, Task<HttpResponseMessage>> OnSend;
49 public Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> OnSendFull;
51 public HttpMessageHandlerMock ()
55 protected override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
58 return OnSend (request);
60 if (OnSendFull != null)
61 return OnSendFull (request, cancellationToken);
68 string port, TestHost, LocalServer;
71 public void SetupFixture ()
73 if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
79 TestHost = "localhost:" + port;
80 LocalServer = string.Format ("http://{0}/", TestHost);
84 public void Ctor_Default ()
86 var client = new HttpClient ();
87 Assert.IsNull (client.BaseAddress, "#1");
88 Assert.IsNotNull (client.DefaultRequestHeaders, "#2"); // TODO: full check
89 Assert.AreEqual (0x10000, client.MaxResponseContentBufferSize, "#3");
90 Assert.AreEqual (TimeSpan.FromSeconds (100), client.Timeout, "#4");
94 public void CancelPendingRequests ()
96 var mh = new HttpMessageHandlerMock ();
98 var client = new HttpClient (mh);
99 var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
100 var mre = new ManualResetEvent (false);
102 mh.OnSendFull = (l, c) => {
104 Assert.IsTrue (c.WaitHandle.WaitOne (1000), "#20");
105 Assert.IsTrue (c.IsCancellationRequested, "#21");
107 return Task.FromResult (new HttpResponseMessage ());
110 var t = Task.Factory.StartNew (() => {
111 client.SendAsync (request).Wait ();
114 Assert.IsTrue (mre.WaitOne (500), "#1");
116 client.CancelPendingRequests ();
117 Assert.IsTrue (t.Wait (500), "#2");
119 request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
120 mh.OnSendFull = (l, c) => {
121 Assert.IsFalse (c.IsCancellationRequested, "#30");
122 return Task.FromResult (new HttpResponseMessage ());
125 client.SendAsync (request).Wait ();
129 public void CancelPendingRequests_BeforeSend ()
131 var ct = new CancellationTokenSource ();
133 var rr = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken (), ct.Token);
136 var mh = new HttpMessageHandlerMock ();
138 var client = new HttpClient (mh);
139 var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
140 client.CancelPendingRequests ();
142 mh.OnSendFull = (l, c) => {
143 Assert.IsFalse (c.IsCancellationRequested, "#30");
144 return Task.FromResult (new HttpResponseMessage ());
147 client.SendAsync (request).Wait ();
149 request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
150 client.SendAsync (request).Wait ();
154 public void Properties ()
156 var client = new HttpClient ();
157 client.BaseAddress = null;
158 client.MaxResponseContentBufferSize = int.MaxValue;
159 client.Timeout = Timeout.InfiniteTimeSpan;
161 Assert.IsNull (client.BaseAddress, "#1");
162 Assert.AreEqual (int.MaxValue, client.MaxResponseContentBufferSize, "#2");
163 Assert.AreEqual (Timeout.InfiniteTimeSpan, client.Timeout, "#3");
167 public void Properties_Invalid ()
169 var client = new HttpClient ();
171 client.MaxResponseContentBufferSize = 0;
173 } catch (ArgumentOutOfRangeException) {
177 client.Timeout = TimeSpan.MinValue;
179 } catch (ArgumentOutOfRangeException) {
186 var mh = new HttpMessageHandlerMock ();
188 var client = new HttpClient (mh);
189 client.BaseAddress = new Uri ("http://xamarin.com");
190 var request = new HttpRequestMessage ();
191 var response = new HttpResponseMessage ();
194 Assert.AreEqual (l, request, "#2");
195 Assert.AreEqual (client.BaseAddress, l.RequestUri, "#2");
196 return Task.FromResult (response);
199 Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
203 public void Send_DefaultRequestHeaders ()
205 var mh = new HttpMessageHandlerMock ();
207 var client = new HttpClient (mh);
208 client.DefaultRequestHeaders.Referrer = new Uri ("http://google.com");
210 var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
211 var response = new HttpResponseMessage ();
214 Assert.AreEqual (client.DefaultRequestHeaders.Referrer, l.Headers.Referrer, "#2");
215 Assert.IsNotNull (l.Headers.Referrer, "#3");
216 return Task.FromResult (response);
219 Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
223 public void Send_Complete_Default ()
226 var listener = CreateListener (l => {
228 var request = l.Request;
230 Assert.IsNull (request.AcceptTypes, "#1");
231 Assert.AreEqual (0, request.ContentLength64, "#2");
232 Assert.IsNull (request.ContentType, "#3");
233 Assert.AreEqual (0, request.Cookies.Count, "#4");
234 Assert.IsFalse (request.HasEntityBody, "#5");
235 Assert.AreEqual (TestHost, request.Headers["Host"], "#6b");
236 Assert.AreEqual ("GET", request.HttpMethod, "#7");
237 Assert.IsFalse (request.IsAuthenticated, "#8");
238 Assert.IsTrue (request.IsLocal, "#9");
239 Assert.IsFalse (request.IsSecureConnection, "#10");
240 Assert.IsFalse (request.IsWebSocketRequest, "#11");
241 Assert.IsTrue (request.KeepAlive, "#12");
242 Assert.AreEqual (HttpVersion.Version11, request.ProtocolVersion, "#13");
243 Assert.IsNull (request.ServiceName, "#14");
244 Assert.IsNull (request.UrlReferrer, "#15");
245 Assert.IsNull (request.UserAgent, "#16");
246 Assert.IsNull (request.UserLanguages, "#17");
254 var client = new HttpClient ();
255 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
256 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
258 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
259 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
260 Assert.AreEqual (false, failed, "#102");
267 public void Send_Complete_Version_1_0 ()
271 var listener = CreateListener (l => {
273 var request = l.Request;
275 Assert.IsNull (request.AcceptTypes, "#1");
276 Assert.AreEqual (0, request.ContentLength64, "#2");
277 Assert.IsNull (request.ContentType, "#3");
278 Assert.AreEqual (0, request.Cookies.Count, "#4");
279 Assert.IsFalse (request.HasEntityBody, "#5");
280 Assert.AreEqual (1, request.Headers.Count, "#6");
281 Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
282 Assert.AreEqual ("GET", request.HttpMethod, "#7");
283 Assert.IsFalse (request.IsAuthenticated, "#8");
284 Assert.IsTrue (request.IsLocal, "#9");
285 Assert.IsFalse (request.IsSecureConnection, "#10");
286 Assert.IsFalse (request.IsWebSocketRequest, "#11");
287 Assert.IsFalse (request.KeepAlive, "#12");
288 Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
289 Assert.IsNull (request.ServiceName, "#14");
290 Assert.IsNull (request.UrlReferrer, "#15");
291 Assert.IsNull (request.UserAgent, "#16");
292 Assert.IsNull (request.UserLanguages, "#17");
300 var client = new HttpClient ();
301 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
302 request.Version = HttpVersion.Version10;
303 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
305 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
306 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
307 Assert.AreEqual (false, failed, "#102");
314 public void Send_Complete_ClientHandlerSettings ()
318 var listener = CreateListener (l => {
319 var request = l.Request;
322 Assert.IsNull (request.AcceptTypes, "#1");
323 Assert.AreEqual (0, request.ContentLength64, "#2");
324 Assert.IsNull (request.ContentType, "#3");
325 Assert.AreEqual (1, request.Cookies.Count, "#4");
326 Assert.AreEqual (new Cookie ("mycookie", "vv"), request.Cookies[0], "#4a");
327 Assert.IsFalse (request.HasEntityBody, "#5");
328 Assert.AreEqual (4, request.Headers.Count, "#6");
329 Assert.AreEqual (TestHost, request.Headers["Host"], "#6a");
330 Assert.AreEqual ("gzip", request.Headers["Accept-Encoding"], "#6b");
331 Assert.AreEqual ("mycookie=vv", request.Headers["Cookie"], "#6c");
332 Assert.AreEqual ("GET", request.HttpMethod, "#7");
333 Assert.IsFalse (request.IsAuthenticated, "#8");
334 Assert.IsTrue (request.IsLocal, "#9");
335 Assert.IsFalse (request.IsSecureConnection, "#10");
336 Assert.IsFalse (request.IsWebSocketRequest, "#11");
337 Assert.IsTrue (request.KeepAlive, "#12");
338 Assert.AreEqual (HttpVersion.Version10, request.ProtocolVersion, "#13");
339 Assert.IsNull (request.ServiceName, "#14");
340 Assert.IsNull (request.UrlReferrer, "#15");
341 Assert.IsNull (request.UserAgent, "#16");
342 Assert.IsNull (request.UserLanguages, "#17");
350 var chandler = new HttpClientHandler ();
351 chandler.AllowAutoRedirect = true;
352 chandler.AutomaticDecompression = DecompressionMethods.GZip;
353 chandler.MaxAutomaticRedirections = 33;
354 chandler.MaxRequestContentBufferSize = 5555;
355 chandler.PreAuthenticate = true;
356 chandler.CookieContainer.Add (new Uri (LocalServer), new Cookie ( "mycookie", "vv"));
357 chandler.UseCookies = true;
358 chandler.UseDefaultCredentials = true;
359 chandler.Proxy = new WebProxy ("ee");
360 chandler.UseProxy = true;
362 var client = new HttpClient (chandler);
363 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
364 request.Version = HttpVersion.Version10;
365 request.Headers.Add ("Keep-Alive", "false");
366 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
368 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
369 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
370 Assert.AreEqual (false, failed, "#102");
378 public void Send_Complete_CustomHeaders ()
382 var listener = CreateListener (l => {
383 var request = l.Request;
385 Assert.AreEqual ("vv", request.Headers["aa"], "#1");
387 var response = l.Response;
388 response.Headers.Add ("rsp", "rrr");
389 response.Headers.Add ("upgrade", "vvvvaa");
390 response.Headers.Add ("Date", "aa");
391 response.Headers.Add ("cache-control", "audio");
393 response.StatusDescription = "test description";
394 response.ProtocolVersion = HttpVersion.Version10;
395 response.SendChunked = true;
396 response.RedirectLocation = "w3.org";
405 var client = new HttpClient ();
406 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
407 request.Headers.AddWithoutValidation ("aa", "vv");
408 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
410 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
411 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
413 IEnumerable<string> values;
414 Assert.IsTrue (response.Headers.TryGetValues ("rsp", out values), "#102");
415 Assert.AreEqual ("rrr", values.First (), "#102a");
417 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#103");
418 Assert.AreEqual ("chunked", values.First (), "#103a");
419 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#103b");
421 Assert.IsTrue (response.Headers.TryGetValues ("Date", out values), "#104");
422 Assert.AreEqual (1, values.Count (), "#104b");
423 // .NET overwrites Date, Mono does not
424 // Assert.IsNotNull (response.Headers.Date, "#104c");
426 Assert.AreEqual (new ProductHeaderValue ("vvvvaa"), response.Headers.Upgrade.First (), "#105");
428 Assert.AreEqual ("audio", response.Headers.CacheControl.Extensions.First ().Name, "#106");
430 Assert.AreEqual ("w3.org", response.Headers.Location.OriginalString, "#107");
432 Assert.AreEqual ("test description", response.ReasonPhrase, "#110");
433 Assert.AreEqual (HttpVersion.Version11, response.Version, "#111");
435 Assert.AreEqual (false, failed, "#112");
442 public void Send_Complete_Content ()
444 var listener = CreateListener (l => {
445 var request = l.Request;
446 l.Response.OutputStream.WriteByte (55);
447 l.Response.OutputStream.WriteByte (75);
451 var client = new HttpClient ();
452 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
453 request.Headers.AddWithoutValidation ("aa", "vv");
454 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
456 Assert.AreEqual ("7K", response.Content.ReadAsStringAsync ().Result, "#100");
457 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
459 IEnumerable<string> values;
460 Assert.IsTrue (response.Headers.TryGetValues ("Transfer-Encoding", out values), "#102");
461 Assert.AreEqual ("chunked", values.First (), "#102a");
462 Assert.AreEqual (true, response.Headers.TransferEncodingChunked, "#102b");
469 public void Send_Complete_Content_MaxResponseContentBufferSize ()
471 var listener = CreateListener (l => {
472 var request = l.Request;
473 var b = new byte[4000];
474 l.Response.OutputStream.Write (b, 0, b.Length);
478 var client = new HttpClient ();
479 client.MaxResponseContentBufferSize = 1000;
480 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
481 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
483 Assert.AreEqual (4000, response.Content.ReadAsStringAsync ().Result.Length, "#100");
484 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#101");
491 public void Send_Complete_Content_MaxResponseContentBufferSize_Error ()
493 var listener = CreateListener (l => {
494 var request = l.Request;
495 var b = new byte[4000];
496 l.Response.OutputStream.Write (b, 0, b.Length);
500 var client = new HttpClient ();
501 client.MaxResponseContentBufferSize = 1000;
502 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
505 client.SendAsync (request, HttpCompletionOption.ResponseContentRead).Wait ();
507 } catch (AggregateException e) {
508 Assert.IsInstanceOfType (typeof (HttpRequestException), e.InnerException, "#3");
517 public void Send_Complete_Error ()
519 var listener = CreateListener (l => {
520 var response = l.Response;
521 response.StatusCode = 500;
525 var client = new HttpClient ();
526 var request = new HttpRequestMessage (HttpMethod.Get, LocalServer);
527 var response = client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead).Result;
529 Assert.AreEqual ("", response.Content.ReadAsStringAsync ().Result, "#100");
530 Assert.AreEqual (HttpStatusCode.InternalServerError, response.StatusCode, "#101");
537 public void Send_Content_Get ()
539 var listener = CreateListener (l => {
540 var request = l.Request;
541 l.Response.OutputStream.WriteByte (72);
545 var client = new HttpClient ();
546 var r = new HttpRequestMessage (HttpMethod.Get, LocalServer);
547 var response = client.SendAsync (r).Result;
549 Assert.AreEqual ("H", response.Content.ReadAsStringAsync ().Result);
556 public void Send_Content_Put ()
559 var listener = CreateListener (l => {
560 var request = l.Request;
561 passed = 7 == request.ContentLength64;
562 passed &= request.ContentType == "text/plain; charset=utf-8";
563 passed &= request.InputStream.ReadByte () == 'm';
567 var client = new HttpClient ();
568 var r = new HttpRequestMessage (HttpMethod.Put, LocalServer);
569 r.Content = new StringContent ("my text");
570 var response = client.SendAsync (r).Result;
572 Assert.AreEqual (HttpStatusCode.OK, response.StatusCode, "#1");
573 Assert.IsTrue (passed, "#2");
581 public void Send_Timeout ()
583 var mh = new HttpMessageHandlerMock ();
585 var client = new HttpClient (mh);
586 client.Timeout = TimeSpan.FromMilliseconds (100);
587 var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
588 var response = new HttpResponseMessage ();
590 mh.OnSendFull = (l, c) => {
591 Assert.IsTrue (c.WaitHandle.WaitOne (500), "#2");
592 return Task.FromResult (response);
595 Assert.AreEqual (response, client.SendAsync (request).Result, "#1");
599 public void Send_Invalid ()
601 var client = new HttpClient ();
603 client.SendAsync (null).Wait ();
605 } catch (ArgumentNullException) {
609 var request = new HttpRequestMessage ();
610 client.SendAsync (request).Wait ();
612 } catch (InvalidOperationException) {
617 public void Send_InvalidHandler ()
619 var mh = new HttpMessageHandlerMock ();
621 var client = new HttpClient (mh);
622 client.BaseAddress = new Uri ("http://xamarin.com");
623 var request = new HttpRequestMessage ();
626 Assert.AreEqual (l, request, "#1");
632 client.SendAsync (request).Wait ();
634 } catch (Exception) {
639 public void Send_SameMessage ()
641 var mh = new HttpMessageHandlerMock ();
643 var client = new HttpClient (mh);
644 var request = new HttpRequestMessage (HttpMethod.Get, "http://xamarin.com");
646 mh.OnSend = l => Task.FromResult (new HttpResponseMessage ());
648 client.SendAsync (request).Wait ();
650 client.SendAsync (request).Wait ();
652 } catch (InvalidOperationException) {
656 HttpListener CreateListener (Action<HttpListenerContext> contextAssert)
658 var l = new HttpListener ();
659 l.Prefixes.Add (string.Format ("http://+:{0}/", port));
661 l.BeginGetContext (ar => {
662 var ctx = l.EndGetContext (ar);
665 if (contextAssert != null)
668 ctx.Response.Close ();