Skip to content

Commit

Permalink
Rewrite System.Net.Http.Json functional tests to use a custom HttpMes…
Browse files Browse the repository at this point in the history
…sageHandler (#38733)

All of the tests start a socket-based loopback server which doesn't work on WebAssembly.
We can do the tests using a custom HttpMessageHandler instead of the loopback server as well.
  • Loading branch information
akoeplinger authored Jul 7, 2020
1 parent 87919a0 commit 4c1b842
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,36 @@ public HttpRequestData()
Headers = new List<HttpHeaderData>();
}

public static async Task<HttpRequestData> FromHttpRequestMessageAsync(System.Net.Http.HttpRequestMessage request)
{
var result = new HttpRequestData();
result.Method = request.Method.ToString();
result.Path = request.RequestUri?.AbsolutePath;

foreach (var header in request.Headers)
{
foreach (var value in header.Value)
{
result.Headers.Add(new HttpHeaderData(header.Key, value));
}
}

if (request.Content != null)
{
result.Body = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

foreach (var header in request.Content.Headers)
{
foreach (var value in header.Value)
{
result.Headers.Add(new HttpHeaderData(header.Key, value));
}
}
}

return result;
}

public string[] GetHeaderValues(string headerName)
{
return Headers.Where(h => h.Name.Equals(headerName, StringComparison.OrdinalIgnoreCase))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Test.Common
{
public class HttpMessageHandlerLoopbackServer : GenericLoopbackServer
{
HttpRequestMessage _request;
public HttpStatusCode ResponseStatusCode;
public IList<HttpHeaderData> ResponseHeaders;
public string ReponseContentString;
public byte[] ReponseContentBytes;

private HttpMessageHandlerLoopbackServer(HttpRequestMessage request)
{
_request = request;
}

public static async Task CreateClientAndServerAsync(Func<HttpMessageHandler, Uri, Task> clientFunc, Func<HttpMessageHandlerLoopbackServer, Task> serverFunc)
{
await clientFunc(new LoopbackServerHttpMessageHandler(serverFunc), new Uri("http://example.com")).ConfigureAwait(false);
}

public async override Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
ResponseStatusCode = statusCode;
ResponseHeaders = headers;
ReponseContentString = content;
return await HttpRequestData.FromHttpRequestMessageAsync(_request).ConfigureAwait(false);
}

public async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode, IList<HttpHeaderData> headers, byte[] bytes)
{
ResponseStatusCode = statusCode;
ResponseHeaders = headers;
ReponseContentBytes = bytes;
return await HttpRequestData.FromHttpRequestMessageAsync(_request).ConfigureAwait(false);
}

public override Task AcceptConnectionAsync(Func<GenericLoopbackConnection, Task> funcAsync) => throw new NotImplementedException();

public override Task<GenericLoopbackConnection> EstablishGenericConnectionAsync() => throw new NotImplementedException();

public override void Dispose() { }

class LoopbackServerHttpMessageHandler : HttpMessageHandler
{
Func<HttpMessageHandlerLoopbackServer, Task> _serverFunc;

public LoopbackServerHttpMessageHandler(Func<HttpMessageHandlerLoopbackServer, Task> serverFunc)
{
_serverFunc = serverFunc;
}

#if NETCOREAPP
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
return SendAsync(request, cancellationToken).GetAwaiter().GetResult();
}
#endif

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var server = new HttpMessageHandlerLoopbackServer(request);
await _serverFunc(server).ConfigureAwait(false);

var response = new HttpResponseMessage(server.ResponseStatusCode);
if (server.ReponseContentString != null)
{
response.Content = new StringContent(server.ReponseContentString);
}
else
{
response.Content = new ByteArrayContent(server.ReponseContentBytes);
}

foreach (var header in server.ResponseHeaders ?? Array.Empty<HttpHeaderData>())
{
if (String.Equals(header.Name, "Content-Type", StringComparison.InvariantCultureIgnoreCase))
{
response.Content.Headers.Remove("Content-Type");
response.Content.Headers.TryAddWithoutValidation("Content-Type", header.Value);
}
else
{
response.Headers.Add(header.Name, header.Value);
}
}

return response;
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@ private static readonly JsonSerializerOptions s_defaultSerializerOptions
public async Task TestGetFromJsonAsync()
{
const string json = @"{""Name"":""David"",""Age"":24}";
const int NumRequests = 4;
HttpHeaderData header = new HttpHeaderData("Content-Type", "application/json");
List<HttpHeaderData> headers = new List<HttpHeaderData> { header };

await LoopbackServer.CreateClientAndServerAsync(
async uri =>
await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
async (handler, uri) =>
{
using (HttpClient client = new HttpClient())
using (HttpClient client = new HttpClient(handler))
{
Person per = (Person)await client.GetFromJsonAsync(uri, typeof(Person));
per.Validate();
Expand All @@ -47,45 +46,31 @@ await LoopbackServer.CreateClientAndServerAsync(
per.Validate();
}
},
async server =>
{
for (int i = 0; i < NumRequests; i++)
{
await server.HandleRequestAsync(content: json, headers: headers);
}
});
server => server.HandleRequestAsync(content: json, headers: headers));
}

[Fact]
public async Task TestGetFromJsonAsyncUnsuccessfulResponseAsync()
{
const int NumRequests = 2;
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
async (handler, uri) =>
{
using (HttpClient client = new HttpClient())
using (HttpClient client = new HttpClient(handler))
{
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync(uri, typeof(Person)));
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync<Person>(uri));
}
},
async server =>
{
for (int i = 0; i < NumRequests; i++)
{
await server.HandleRequestAsync(statusCode: HttpStatusCode.InternalServerError);
}
});
server => server.HandleRequestAsync(statusCode: HttpStatusCode.InternalServerError));
}

[Fact]
public async Task TestPostAsJsonAsync()
{
const int NumRequests = 4;
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
async (handler, uri) =>
{
using (HttpClient client = new HttpClient())
using (HttpClient client = new HttpClient(handler))
{
Person person = Person.Create();

Expand All @@ -103,24 +88,20 @@ await LoopbackServer.CreateClientAndServerAsync(
}
},
async server => {
for (int i = 0; i < NumRequests; i++)
{
HttpRequestData request = await server.HandleRequestAsync();
ValidateRequest(request);
Person per = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
per.Validate();
}
HttpRequestData request = await server.HandleRequestAsync();
ValidateRequest(request);
Person per = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
per.Validate();
});
}

[Fact]
public async Task TestPutAsJsonAsync()
{
const int NumRequests = 4;
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
async (handler, uri) =>
{
using (HttpClient client = new HttpClient())
using (HttpClient client = new HttpClient(handler))
{
Person person = Person.Create();
Type typePerson = typeof(Person);
Expand All @@ -139,13 +120,10 @@ await LoopbackServer.CreateClientAndServerAsync(
}
},
async server => {
for (int i = 0; i < NumRequests; i++)
{
HttpRequestData request = await server.HandleRequestAsync();
ValidateRequest(request);
Person obj = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
obj.Validate();
}
HttpRequestData request = await server.HandleRequestAsync();
ValidateRequest(request);
Person obj = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
obj.Validate();
});
}

Expand Down Expand Up @@ -177,11 +155,10 @@ private void ValidateRequest(HttpRequestData requestData)
[Fact]
public async Task AllowNullRequesturlAsync()
{
const int NumRequests = 4;
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
async (handler, uri) =>
{
using (HttpClient client = new HttpClient())
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = uri;

Expand All @@ -196,10 +173,7 @@ await LoopbackServer.CreateClientAndServerAsync(
List<HttpHeaderData> headers = new List<HttpHeaderData> { new HttpHeaderData("Content-Type", "application/json") };
string json = Person.Create().Serialize();

for (int i = 0; i < NumRequests; i++)
{
await server.HandleRequestAsync(content: json, headers: headers);
}
await server.HandleRequestAsync(content: json, headers: headers);
});
}
}
Expand Down
Loading

0 comments on commit 4c1b842

Please sign in to comment.