2
// AsyncTests.HttpClientTests.Test.HttpClientTests
5
// Martin Baulig (martin.baulig@gmail.com)
7
// Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
10
// Permission is hereby granted, free of charge, to any person obtaining
11
// a copy of this software and associated documentation files (the
12
// "Software"), to deal in the Software without restriction, including
13
// without limitation the rights to use, copy, modify, merge, publish,
14
// distribute, sublicense, and/or sell copies of the Software, and to
15
// permit persons to whom the Software is furnished to do so, subject to
16
// the following conditions:
18
// The above copyright notice and this permission notice shall be
19
// included in all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
using System.Collections.Generic;
34
using System.Threading;
35
using System.Threading.Tasks;
36
using System.Net.Http;
37
using System.Net.Http.Headers;
38
using NUnit.Framework;
39
using NUnit.Framework.Constraints;
40
using NUnit.Framework.SyntaxHelpers;
43
* Copied from System.Net.Http.Tests/HttpClientTests.cs
46
namespace AsyncTests.HttpClientTests.Test {
50
[HttpClientTestFixture (ReuseContext = false)]
51
public class HttpClientTests {
52
#region Default Settings
55
public static void DefaultHandler (ServerContext ctx)
57
var request = ctx.Request;
59
ctx.Expect (request.AcceptTypes, Is.Null, "#1");
60
ctx.Expect (request.ContentLength64, Is.EqualTo (0), "#2");
61
ctx.Expect (request.ContentType, Is.Null, "#3");
62
ctx.Expect (request.Cookies.Count, Is.EqualTo (0), "#4");
63
ctx.Expect (request.HasEntityBody, Is.False, "#5");
64
ctx.Expect (request.Headers ["Host"], Is.EqualTo (Server.Host), "#6b");
65
ctx.Expect (request.HttpMethod, Is.EqualTo ("GET"), "#7");
66
ctx.Expect (request.IsAuthenticated, Is.False, "#8");
67
ctx.Expect (request.IsLocal, Is.EqualTo (Server.IsLocal), "#9");
68
ctx.Expect (request.IsSecureConnection, Is.False, "#10");
69
ctx.Expect (request.IsWebSocketRequest, Is.False, "#11");
70
ctx.Expect (request.KeepAlive, Is.True, "#12");
71
ctx.Expect (request.ProtocolVersion, Is.EqualTo (HttpVersion.Version11), "#13");
72
ctx.Expect (request.ServiceName, Is.Null, "#14");
73
ctx.Expect (request.UrlReferrer, Is.Null, "#15");
74
// FIXME: CFNetwork seems to set the UserAgent automatically.
75
// context.Expect (request.UserAgent, Is.Null, "#16");
76
ctx.Expect (request.UserLanguages, Is.Null, "#17");
78
ctx.Response.StatusCode = 200;
82
public async Task Default (HttpClientTestContext ctx,
83
CancellationToken cancellationToken)
85
var uri = Server.GetUri (DefaultHandler);
87
var request = new HttpRequestMessage (HttpMethod.Get, uri);
88
var response = await ctx.Client.SendAsync (
89
request, HttpCompletionOption.ResponseHeadersRead,
90
cancellationToken).ConfigureAwait (false);
92
ctx.AutoDispose (response);
93
Server.CheckException (response);
95
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#100");
96
ctx.Assert (await response.Content.ReadAsStringAsync (), Is.Empty, "#101");
100
public static void DefaultHandler_Version_1_0 (ServerContext ctx)
102
var request = ctx.Request;
104
ctx.Expect (request.AcceptTypes, Is.Null, "#1");
105
ctx.Expect (request.ContentLength64, Is.EqualTo (0), "#2");
106
ctx.Expect (request.ContentType, Is.Null, "#3");
107
ctx.Expect (request.Cookies.Count, Is.EqualTo (0), "#4");
108
ctx.Expect (request.HasEntityBody, Is.False, "#5");
111
* CFNetwork seems to always send 'Connection' and 'User-Agent'.
113
foreach (var obj in request.Headers) {
114
if (obj.Equals ("Host"))
116
else if (obj.Equals ("Connection"))
118
else if (obj.Equals ("User-Agent"))
120
ctx.Expect (false, "#6");
122
ctx.Expect (request.Headers ["Host"], Is.EqualTo (Server.Host), "#6a");
123
ctx.Expect (request.HttpMethod, Is.EqualTo ("GET"), "#7");
124
ctx.Expect (request.IsAuthenticated, Is.False, "#8");
125
ctx.Expect (request.IsLocal, Is.EqualTo (Server.IsLocal), "#9");
126
ctx.Expect (request.IsSecureConnection, Is.False, "#10");
127
ctx.Expect (request.IsWebSocketRequest, Is.False, "#11");
128
ctx.Expect (request.KeepAlive, Is.False, "#12");
129
ctx.Expect (request.ProtocolVersion, Is.EqualTo (HttpVersion.Version10), "#13");
130
ctx.Expect (request.ServiceName, Is.Null, "#14");
131
ctx.Expect (request.UrlReferrer, Is.Null, "#15");
132
// FIXME: CFNetwork seems to set the UserAgent automatically.
133
// Assert.IsNull (request.UserAgent, "#16");
134
ctx.Expect (request.UserLanguages, Is.Null, "#17");
136
ctx.Response.StatusCode = 200;
140
public async Task Default_Version_1_0 (HttpClientTestContext ctx,
141
CancellationToken cancellationToken)
143
var uri = Server.GetUri (DefaultHandler_Version_1_0);
145
var request = new HttpRequestMessage (HttpMethod.Get, uri);
146
request.Version = HttpVersion.Version10;
148
var response = await ctx.Client.SendAsync (
149
request, HttpCompletionOption.ResponseHeadersRead,
150
cancellationToken).ConfigureAwait (false);
152
ctx.AutoDispose (response);
153
Server.CheckException (response);
155
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#100");
156
ctx.Assert (await response.Content.ReadAsStringAsync (), Is.Empty, "#101");
164
public static void KeepAlive_Version1_0_Handler (ServerContext ctx)
166
var request = ctx.Request;
168
var keepalive = request.Headers ["Keep-Alive"];
169
var connection = request.Headers ["Connection"];
171
ctx.Expect (keepalive, Is.Not.Null, "#1");
172
ctx.Expect ((connection == null) ||
173
connection.Equals ("keep-alive", StringComparison.OrdinalIgnoreCase), "#2");
174
ctx.Expect (request.KeepAlive, Is.True, "#3");
178
public async Task KeepAlive_Version1_0 (HttpClientTestContext ctx,
179
CancellationToken cancellationToken)
181
var uri = Server.GetUri (KeepAlive_Version1_0_Handler);
183
var req = new HttpRequestMessage (HttpMethod.Get, uri);
184
req.Version = HttpVersion.Version10;
185
req.Headers.Add ("Keep-Alive", "false");
187
var response = await ctx.Client.SendAsync (
188
req, HttpCompletionOption.ResponseHeadersRead,
189
cancellationToken).ConfigureAwait (false);
191
ctx.AutoDispose (response);
192
Server.CheckException (response);
199
public static void ClientHandlerSettings_Handler (ServerContext ctx)
201
var request = ctx.Request;
203
ctx.Expect (request.AcceptTypes, Is.Null, "#1");
204
ctx.Expect (request.ContentLength64, Is.EqualTo (0), "#2");
205
ctx.Expect (request.ContentType, Is.Null, "#3");
206
if (ctx.Expect (request.Cookies.Count, Is.EqualTo (1), "#4"))
207
ctx.Expect (request.Cookies [0], Is.EqualTo (new Cookie ("mycookie", "vv")), "#4a");
208
ctx.Expect (request.HasEntityBody, Is.False, "#5");
211
* CFNetwork seems to always send 'Connection' and 'User-Agent'.
213
var allowed = new string[] {
214
"Host", "Connection", "User-Agent", "Keep-Alive", "Cookie",
218
foreach (var obj in request.Headers) {
219
var header = (string)obj;
220
if (allowed.Contains (header))
222
ctx.Expect (false, "#6: unexpected header: {0}", header);
225
ctx.Expect (request.Headers ["Host"], Is.EqualTo (Server.Host), "#6a");
226
ctx.Expect (request.Headers ["Accept-Encoding"], Is.EqualTo ("gzip"), "#6b");
227
ctx.Expect (request.Headers ["Cookie"], Is.EqualTo ("mycookie=vv"), "#6c");
228
ctx.Expect (request.HttpMethod, Is.EqualTo ("GET"), "#7");
229
ctx.Expect (request.IsAuthenticated, Is.False, "#8");
230
ctx.Expect (Server.IsLocal, request.IsLocal, Is.EqualTo (Server.IsLocal), "#9");
231
ctx.Expect (request.IsSecureConnection, Is.False, "#10");
232
ctx.Expect (request.IsWebSocketRequest, Is.False, "#11");
233
ctx.Expect (request.KeepAlive, Is.True, "#12");
234
ctx.Expect (request.ProtocolVersion, Is.EqualTo (HttpVersion.Version10), "#13");
235
ctx.Expect (request.ServiceName, Is.Null, "#14");
236
ctx.Expect (request.UrlReferrer, Is.Null, "#15");
237
// ctx.Expect (request.UserAgent, Is.Null, "#16");
238
ctx.Expect (request.UserLanguages, Is.Null, "#17");
240
ctx.Response.StatusCode = 200;
244
public async Task ClientHandlerSettings (HttpClientTestContext ctx,
245
CancellationToken cancellationToken)
247
var uri = Server.GetUri (ClientHandlerSettings_Handler);
249
ctx.Handler.AllowAutoRedirect = true;
250
ctx.Handler.AutomaticDecompression = DecompressionMethods.GZip;
251
ctx.Handler.MaxAutomaticRedirections = 33;
252
ctx.Handler.MaxRequestContentBufferSize = 5555;
253
ctx.Handler.PreAuthenticate = true;
254
ctx.Handler.CookieContainer.Add (uri, new Cookie ("mycookie", "vv"));
255
ctx.Handler.UseCookies = true;
256
ctx.Handler.UseDefaultCredentials = true;
258
if (Server.IsLocal) {
259
ctx.Handler.Proxy = new WebProxy ("ee");
260
ctx.Handler.UseProxy = true;
263
var request = new HttpRequestMessage (HttpMethod.Get, uri);
264
request.Version = HttpVersion.Version10;
265
request.Headers.Add ("Keep-Alive", "false");
267
var response = await ctx.Client.SendAsync (
268
request, HttpCompletionOption.ResponseHeadersRead,
269
cancellationToken).ConfigureAwait (false);
271
ctx.AutoDispose (response);
272
Server.CheckException (response);
274
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#100");
275
ctx.Assert (await response.Content.ReadAsStringAsync (), Is.Empty, "#101");
279
public static void CustomHeaders_Handler (ServerContext ctx)
281
var request = ctx.Request;
282
ctx.Expect (request.Headers ["aa"], Is.EqualTo ("vv"), "#1");
284
var response = ctx.Response;
285
response.Headers.Add ("rsp", "rrr");
286
response.Headers.Add ("upgrade", "vvvvaa");
287
response.Headers.Add ("Date", "aa");
288
response.Headers.Add ("cache-control", "audio");
290
response.StatusDescription = "test description";
291
response.ProtocolVersion = HttpVersion.Version10;
292
response.SendChunked = true;
293
response.RedirectLocation = "w3.org";
297
public async Task CustomHeaders (HttpClientTestContext ctx,
298
CancellationToken cancellationToken)
300
var uri = Server.GetUri (CustomHeaders_Handler);
302
var request = new HttpRequestMessage (HttpMethod.Get, uri);
303
ctx.Expect (request.Headers.TryAddWithoutValidation ("aa", "vv"), Is.True, "#0");
305
var response = await ctx.Client.SendAsync (
306
request, HttpCompletionOption.ResponseHeadersRead,
307
cancellationToken).ConfigureAwait (false);
309
ctx.AutoDispose (response);
310
Server.CheckException (response);
312
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#101");
313
ctx.Assert (await response.Content.ReadAsStringAsync (), Is.Empty, "#100");
315
var headers = response.Headers;
317
IEnumerable<string> values;
318
if (ctx.Expect (headers.TryGetValues ("rsp", out values), Is.True, "#102"))
319
ctx.Expect (values.First (), Is.EqualTo ("rrr"), "#102a");
321
if (ctx.Expect (headers.TryGetValues ("Transfer-Encoding", out values), Is.True, "#103")) {
322
// CFNetwork automatically combines all chunks.
323
if (ctx.IsDefaultHandler) {
324
ctx.Expect (values.First (), Is.EqualTo ("chunked"), "#103a");
325
ctx.Expect (headers.TransferEncodingChunked, Is.True, "#103b");
327
ctx.Expect (values.First (), Is.EqualTo ("Identity"), "#103c");
328
ctx.Expect (headers.TransferEncodingChunked, Is.Null, "#103d");
332
if (ctx.Expect (headers.TryGetValues ("Date", out values), Is.True, "#104")) {
333
ctx.Expect (values.Count (), Is.EqualTo (1), "#104b");
334
// .NET overwrites Date, Mono does not
335
if (!ctx.IsDefaultHandler)
336
ctx.Expect (headers.Date, Is.Not.Null, "#104c");
339
ctx.Expect (headers.Upgrade.First (), Is.EqualTo (new ProductHeaderValue ("vvvvaa")), "#105");
341
ctx.Expect (headers.CacheControl.Extensions.First ().Name, Is.EqualTo ("audio"), "#106");
343
ctx.Expect (headers.Location.OriginalString, Is.EqualTo ("w3.org"), "#107");
344
ctx.Expect (response.ReasonPhrase, Is.EqualTo ("test description"), "#110");
345
// FIXME: .NET also returns 1.1 - bug ?
346
if (ctx.IsDefaultHandler)
347
ctx.Expect (response.Version, Is.EqualTo (HttpVersion.Version11), "#111");
351
public static void ContentHandler (ServerContext ctx)
353
ctx.Response.OutputStream.WriteByte (55);
354
ctx.Response.OutputStream.WriteByte (75);
358
public async Task Content (HttpClientTestContext ctx,
359
CancellationToken cancellationToken)
361
var uri = Server.GetUri (ContentHandler);
362
var request = new HttpRequestMessage (HttpMethod.Get, uri);
363
ctx.Assert (request.Headers.TryAddWithoutValidation ("aa", "vv"), Is.True, "#0");
365
var response = await ctx.Client.SendAsync (
366
request, HttpCompletionOption.ResponseHeadersRead,
367
cancellationToken).ConfigureAwait (false);
369
ctx.AutoDispose (response);
370
Server.CheckException (response);
372
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#101");
373
ctx.Expect (await response.Content.ReadAsStringAsync (), Is.EqualTo ("7K"), "#100");
375
var headers = response.Headers;
377
IEnumerable<string> values;
378
if (ctx.Expect (headers.TryGetValues ("Transfer-Encoding", out values), Is.True, "#102")) {
379
// CFNetwork automatically combines all chunks.
380
if (ctx.IsDefaultHandler) {
381
ctx.Expect (values.First (), Is.EqualTo ("chunked"), "#102a");
382
ctx.Expect (headers.TransferEncodingChunked, Is.True, "#102b");
384
ctx.Expect (values.First (), Is.EqualTo ("Identity"), "#102c");
385
ctx.Expect (headers.TransferEncodingChunked, Is.Null, "#102d");
390
const int MaxResponseContentBufferSize_size = 8000;
393
public static void MaxResponseContentBufferSize_Handler (ServerContext ctx)
395
var b = new byte[MaxResponseContentBufferSize_size];
396
ctx.Response.OutputStream.Write (b, 0, b.Length);
400
public async Task MaxResponseContentBufferSize (HttpClientTestContext ctx,
401
CancellationToken cancellationToken)
403
var uri = Server.GetUri (MaxResponseContentBufferSize_Handler);
404
ctx.Client.MaxResponseContentBufferSize = 1000;
405
var request = new HttpRequestMessage (HttpMethod.Get, uri);
407
var response = await ctx.Client.SendAsync (
408
request, HttpCompletionOption.ResponseHeadersRead,
409
cancellationToken).ConfigureAwait (false);
411
ctx.AutoDispose (response);
412
Server.CheckException (response);
414
ctx.Assert (response.StatusCode, Is.EqualTo (HttpStatusCode.OK), "#101");
415
var result = await response.Content.ReadAsStringAsync ();
416
ctx.Expect (result.Length, Is.EqualTo (MaxResponseContentBufferSize_size), "#100");
420
public async Task MaxResponseContentBufferSize_Error (HttpClientTestContext ctx,
421
CancellationToken cancellationToken)
423
var uri = Server.GetUri (MaxResponseContentBufferSize_Handler);
424
ctx.Client.MaxResponseContentBufferSize = 1000;
425
var request = new HttpRequestMessage (HttpMethod.Get, uri);
428
var response = await ctx.Client.SendAsync (
429
request, HttpCompletionOption.ResponseContentRead,
430
cancellationToken).ConfigureAwait (false);
431
ctx.AutoDispose (response);
432
ctx.Assert (false, "#2");
433
} catch (AggregateException e) {
434
ctx.Assert (e.InnerException, Is.InstanceOfType (typeof(HttpRequestException)), "#3");
435
} catch (HttpRequestException) {