Skip to content

Commit

Permalink
Response test
Browse files Browse the repository at this point in the history
  • Loading branch information
ManickaP committed Feb 18, 2021
1 parent f3a11b5 commit 6670731
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 25 deletions.
45 changes: 26 additions & 19 deletions test/ReverseProxy.FunctionalTests/Common/TestEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,55 +66,62 @@ public async Task Invoke(Func<string, Task> clientFunc, CancellationToken cancel
using var destination = CreateHost(HttpProtocols.Http1AndHttp2, _useHttpsOnDestination, _headerEncoding, _configureDestinationServices, _configureDestinationApp);
await destination.StartAsync(cancellationToken);

using var proxy = CreateHost(_proxyProtocol, false, _headerEncoding,
using var proxy = CreateProxy(_proxyProtocol, _useHttpsOnDestination, _headerEncoding, ClusterId, destination.GetAddress(), _configureProxy, _configureProxyApp);
await proxy.StartAsync(cancellationToken);

try
{
await clientFunc(proxy.GetAddress());
}
finally
{
await proxy.StopAsync(cancellationToken);
await destination.StopAsync(cancellationToken);
}
}

public static IHost CreateProxy(HttpProtocols protocols, bool useHttps, Encoding headerEncoding, string clusterId, string destinationAddress,
Action<IReverseProxyBuilder> configureProxy, Action<IApplicationBuilder> configureProxyApp)
{
return CreateHost(protocols, false, headerEncoding,
services =>
{
var proxyRoute = new ProxyRoute
{
RouteId = "route1",
ClusterId = ClusterId,
ClusterId = clusterId,
Match = new ProxyMatch { Path = "/{**catchall}" }
};
var cluster = new Cluster
{
Id = ClusterId,
Id = clusterId,
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new Destination() { Address = destination.GetAddress() } }
{ "destination1", new Destination() { Address = destinationAddress } }
},
HttpClient = new ProxyHttpClientOptions
{
DangerousAcceptAnyServerCertificate = _useHttpsOnDestination
DangerousAcceptAnyServerCertificate = useHttps
}
};
var proxyBuilder = services.AddReverseProxy().LoadFromMemory(new[] { proxyRoute }, new[] { cluster });
_configureProxy(proxyBuilder);
configureProxy(proxyBuilder);
},
app =>
{
_configureProxyApp(app);
configureProxyApp(app);
app.UseRouting();
app.UseEndpoints(builder =>
{
builder.MapReverseProxy();
});
});
await proxy.StartAsync(cancellationToken);

try
{
await clientFunc(proxy.GetAddress());
}
finally
{
await proxy.StopAsync(cancellationToken);
await destination.StopAsync(cancellationToken);
}
}

private static IHost CreateHost(HttpProtocols protocols, bool useHttps, Encoding headerEncoding, Action<IServiceCollection> configureServices, Action<IApplicationBuilder> configureApp)
private static IHost CreateHost(HttpProtocols protocols, bool useHttps, Encoding headerEncoding,
Action<IServiceCollection> configureServices, Action<IApplicationBuilder> configureApp)
{
return new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
Expand Down
117 changes: 111 additions & 6 deletions test/ReverseProxy.FunctionalTests/HeaderEncodingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -29,6 +30,8 @@ public async Task ProxyAsync_RequestWithEncodedHeaderValue(string headerValue, s
var tcs = new TaskCompletionSource<StringValues>(TaskCreationOptions.RunContinuationsAsynchronously);

IProxyErrorFeature proxyError = null;
IExceptionHandlerFeature kestrelError = null;

var test = new TestEnvironment(
context =>
{
Expand All @@ -48,7 +51,15 @@ public async Task ProxyAsync_RequestWithEncodedHeaderValue(string headerValue, s
},
proxyApp =>
{
proxyApp.UseMiddleware<CheckHeaderMiddleware>(headerValue, encoding);
proxyApp.UseExceptionHandler(new ExceptionHandlerOptions
{
ExceptionHandler = context =>
{
kestrelError = context.Features.Get<IExceptionHandlerFeature>();
return Task.CompletedTask;
}
});
proxyApp.UseMiddleware<CheckHeaderMiddleware>(headerValue);
proxyApp.Use(async (context, next) =>
{
await next();
Expand Down Expand Up @@ -85,27 +96,120 @@ await test.Invoke(async proxyUri =>
var response = responseBuilder.ToString();
Assert.Null(proxyError);
Assert.Null(kestrelError);
Assert.StartsWith("HTTP/1.1 200 OK", response);
Assert.True(tcs.Task.IsCompleted);
var refererHeader = await tcs.Task;
var referer = Assert.Single(refererHeader);
//Assert.Equal(Utf8HeaderValue, referer);
Assert.Equal(headerValue, referer);
});
}

[Theory]
[InlineData("http://www.ěščřžýáíé.com", "utf-8")]
[InlineData("http://www.çáéôîèñøæ.com", "iso-8859-1")]
public async Task ProxyAsync_ResponseWithEncodedHeaderValue(string headerValue, string encodingName)
{
var encoding = Encoding.GetEncoding(encodingName);

var tcpListener = new TcpListener(IPAddress.Loopback, 0);
tcpListener.Start();
var destinationTask = Task.Run(async () =>
{
using var tcpClient = await tcpListener.AcceptTcpClientAsync();
await using var stream = tcpClient.GetStream();
var buffer = new byte[4096];
var requestBuilder = new StringBuilder();
while (true)
{
var count = await stream.ReadAsync(buffer);
if (count == 0)
{
break;
}
requestBuilder.Append(encoding.GetString(buffer, 0, count));
// End of the request
if (requestBuilder.Length >= 4 &&
requestBuilder[^4] == '\r' && requestBuilder[^3] == '\n' &&
requestBuilder[^2] == '\r' && requestBuilder[^1] == '\n')
{
break;
}
}
await stream.WriteAsync(Encoding.ASCII.GetBytes($"HTTP/1.1 200 OK\r\n"));
await stream.WriteAsync(Encoding.ASCII.GetBytes($"Content-Length: 0\r\n"));
await stream.WriteAsync(Encoding.ASCII.GetBytes($"Connection: close\r\n"));
await stream.WriteAsync(Encoding.ASCII.GetBytes($"Referer: "));
await stream.WriteAsync(encoding.GetBytes(headerValue));
await stream.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
await stream.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
});

IProxyErrorFeature proxyError = null;
IExceptionHandlerFeature kestrelError = null;

using var proxy = TestEnvironment.CreateProxy(HttpProtocols.Http1, false, encoding, "cluster1", $"http://{tcpListener.LocalEndpoint}",
proxyBuilder =>
{
proxyBuilder.Services.AddSingleton<IProxyHttpClientFactory>(new HeaderEncodingClientFactory(encoding));
},
proxyApp =>
{
proxyApp.UseExceptionHandler(new ExceptionHandlerOptions
{
ExceptionHandler = context =>
{
kestrelError = context.Features.Get<IExceptionHandlerFeature>();
return Task.CompletedTask;
}
});
proxyApp.Use(async (context, next) =>
{
await next();
proxyError = context.Features.Get<IProxyErrorFeature>();
});
});

await proxy.StartAsync();

try
{
using var httpClient = new HttpClient();
using var response = await httpClient.GetAsync(proxy.GetAddress());

Assert.Null(proxyError);
Assert.Null(kestrelError);

response.EnsureSuccessStatusCode();

Assert.True(response.Headers.TryGetValues(HeaderNames.Referer, out var refererHeader));
var referer = Assert.Single(refererHeader);
Assert.Equal(headerValue, referer);

Assert.True(destinationTask.IsCompleted);
await destinationTask;
}
finally
{
await proxy.StopAsync();
tcpListener.Stop();
}
}

private class CheckHeaderMiddleware
{
private readonly RequestDelegate _next;
private readonly string _headerValue;
private readonly Encoding _encoding;

public CheckHeaderMiddleware(RequestDelegate next, string headerValue, Encoding encoding)
public CheckHeaderMiddleware(RequestDelegate next, string headerValue)
{
_next = next;
_headerValue = headerValue;
_encoding = encoding;
}

public async Task Invoke(HttpContext context)
Expand Down Expand Up @@ -143,7 +247,8 @@ public HttpMessageInvoker CreateClient(ProxyHttpClientContext context)
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
UseCookies = false,
RequestHeaderEncodingSelector = (_, _) => _encoding
RequestHeaderEncodingSelector = (_, _) => _encoding,
ResponseHeaderEncodingSelector = (_, _) => _encoding

// NOTE: MaxResponseHeadersLength = 64, which means up to 64 KB of headers are allowed by default as of .NET Core 3.1.
};
Expand Down

0 comments on commit 6670731

Please sign in to comment.