public async Task ProxyAsync_RequetsWithBodies_HasHttpContent(string method, string protocol, string headers) { var httpContext = new DefaultHttpContext(); httpContext.Request.Method = method; httpContext.Request.Protocol = protocol; foreach (var header in headers.Split(';', StringSplitOptions.RemoveEmptyEntries)) { var parts = header.Split(':'); var key = parts[0]; var value = parts[1]; httpContext.Request.Headers[key] = value; } var destinationPrefix = "https://localhost/"; var sut = Create <HttpProxy>(); var client = MockHttpHandler.CreateClient( async(HttpRequestMessage request, CancellationToken cancellationToken) => { Assert.Equal(new Version(2, 0), request.Version); Assert.Equal(method, request.Method.Method, StringComparer.OrdinalIgnoreCase); Assert.NotNull(request.Content); // Must consume the body await request.Content.CopyToAsync(Stream.Null); return(new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(Array.Empty <byte>()) }); }); var factoryMock = new Mock <IProxyHttpClientFactory>(); factoryMock.Setup(f => f.CreateClient()).Returns(client); var proxyTelemetryContext = new ProxyTelemetryContext( clusterId: "be1", routeId: "rt1", destinationId: "d1"); await sut.ProxyAsync(httpContext, destinationPrefix, Transforms.Empty, factoryMock.Object, proxyTelemetryContext, CancellationToken.None, CancellationToken.None); Assert.Equal(StatusCodes.Status200OK, httpContext.Response.StatusCode); }
public async Task ProxyAsync_NormalRequest_Works() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "POST"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); httpContext.Request.Headers.Add(":host", "example.com"); httpContext.Request.Headers.Add("x-ms-request-test", "request"); httpContext.Request.Headers.Add("Content-Language", "requestLanguage"); httpContext.Request.Body = StringToStream("request content"); var proxyResponseStream = new MemoryStream(); httpContext.Response.Body = proxyResponseStream; var targetUri = new Uri("https://localhost:123/a/b/api/test"); var sut = Create <HttpProxy>(); var client = MockHttpHandler.CreateClient( async(HttpRequestMessage request, CancellationToken cancellationToken) => { await Task.Yield(); request.Version.Should().BeEquivalentTo(new Version(2, 0)); request.Method.Should().Be(HttpMethod.Post); request.RequestUri.Should().Be(targetUri); request.Headers.GetValues("x-ms-request-test").Should().BeEquivalentTo("request"); request.Content.Should().NotBeNull(); request.Content.Headers.GetValues("Content-Language").Should().BeEquivalentTo("requestLanguage"); var capturedRequestContent = new MemoryStream(); // Use CopyToAsync as this is what HttpClient and friends use internally await request.Content.CopyToAsync(capturedRequestContent); capturedRequestContent.Position = 0; var capturedContentText = StreamToString(capturedRequestContent); capturedContentText.Should().Be("request content"); var response = new HttpResponseMessage((HttpStatusCode)234); response.ReasonPhrase = "Test Reason Phrase"; response.Headers.TryAddWithoutValidation("x-ms-response-test", "response"); response.Content = new StreamContent(StringToStream("response content")); response.Content.Headers.TryAddWithoutValidation("Content-Language", "responseLanguage"); return(response); }); var factoryMock = new Mock <IProxyHttpClientFactory>(); factoryMock.Setup(f => f.CreateNormalClient()).Returns(client); var proxyTelemetryContext = new ProxyTelemetryContext( backendId: "be1", routeId: "rt1", endpointId: "ep1"); // Act await sut.ProxyAsync(httpContext, targetUri, factoryMock.Object, proxyTelemetryContext, CancellationToken.None, CancellationToken.None); // Assert httpContext.Response.StatusCode.Should().Be(234); var reasonPhrase = httpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase; reasonPhrase.Should().Be("Test Reason Phrase"); httpContext.Response.Headers["x-ms-response-test"].Should().BeEquivalentTo("response"); httpContext.Response.Headers["Content-Language"].Should().BeEquivalentTo("responseLanguage"); proxyResponseStream.Position = 0; var proxyResponseText = StreamToString(proxyResponseStream); proxyResponseText.Should().Be("response content"); }
public async Task ProxyAsync_UpgradableRequestFailsToUpgrade_ProxiesResponse() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); httpContext.Request.Headers.Add(":host", "example.com"); httpContext.Request.Headers.Add("x-ms-request-test", "request"); var proxyResponseStream = new MemoryStream(); httpContext.Response.Body = proxyResponseStream; var upgradeFeatureMock = new Mock <IHttpUpgradeFeature>(MockBehavior.Strict); upgradeFeatureMock.SetupGet(u => u.IsUpgradableRequest).Returns(true); httpContext.Features.Set(upgradeFeatureMock.Object); var targetUri = new Uri("https://localhost:123/a/b/api/test?a=b&c=d"); var sut = Create <HttpProxy>(); var client = MockHttpHandler.CreateClient( async(HttpRequestMessage request, CancellationToken cancellationToken) => { await Task.Yield(); request.Version.Should().BeEquivalentTo(new Version(1, 1)); request.Method.Should().Be(HttpMethod.Get); request.RequestUri.Should().Be(targetUri); request.Headers.GetValues("x-ms-request-test").Should().BeEquivalentTo("request"); request.Content.Should().BeNull(); var response = new HttpResponseMessage((HttpStatusCode)234); response.ReasonPhrase = "Test Reason Phrase"; response.Headers.TryAddWithoutValidation("x-ms-response-test", "response"); response.Content = new StreamContent(StringToStream("response content")); response.Content.Headers.TryAddWithoutValidation("Content-Language", "responseLanguage"); return(response); }); var factoryMock = new Mock <IProxyHttpClientFactory>(); factoryMock.Setup(f => f.CreateUpgradableClient()).Returns(client); var proxyTelemetryContext = new ProxyTelemetryContext( backendId: "be1", routeId: "rt1", endpointId: "ep1"); // Act await sut.ProxyAsync(httpContext, targetUri, factoryMock.Object, proxyTelemetryContext, CancellationToken.None, CancellationToken.None); // Assert httpContext.Response.StatusCode.Should().Be(234); var reasonPhrase = httpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase; reasonPhrase.Should().Be("Test Reason Phrase"); httpContext.Response.Headers["x-ms-response-test"].Should().BeEquivalentTo("response"); httpContext.Response.Headers["Content-Language"].Should().BeEquivalentTo("responseLanguage"); proxyResponseStream.Position = 0; var proxyResponseText = StreamToString(proxyResponseStream); proxyResponseText.Should().Be("response content"); }
public async Task ProxyAsync_UpgradableRequest_Works() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); httpContext.Request.Headers.Add(":host", "example.com"); httpContext.Request.Headers.Add("x-ms-request-test", "request"); var downstreamStream = new DuplexStream( readStream: StringToStream("request content"), writeStream: new MemoryStream()); DuplexStream upstreamStream = null; var upgradeFeatureMock = new Mock <IHttpUpgradeFeature>(); upgradeFeatureMock.SetupGet(u => u.IsUpgradableRequest).Returns(true); upgradeFeatureMock.Setup(u => u.UpgradeAsync()).ReturnsAsync(downstreamStream); httpContext.Features.Set(upgradeFeatureMock.Object); var targetUri = new Uri("https://localhost:123/a/b/api/test?a=b&c=d"); var sut = Create <HttpProxy>(); var client = MockHttpHandler.CreateClient( async(HttpRequestMessage request, CancellationToken cancellationToken) => { await Task.Yield(); request.Version.Should().BeEquivalentTo(new Version(1, 1)); request.Method.Should().Be(HttpMethod.Get); request.RequestUri.Should().Be(targetUri); request.Headers.GetValues("x-ms-request-test").Should().BeEquivalentTo("request"); request.Content.Should().BeNull(); var response = new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); response.Headers.TryAddWithoutValidation("x-ms-response-test", "response"); upstreamStream = new DuplexStream( readStream: StringToStream("response content"), writeStream: new MemoryStream()); response.Content = new RawStreamContent(upstreamStream); return(response); }); var factoryMock = new Mock <IProxyHttpClientFactory>(); factoryMock.Setup(f => f.CreateUpgradableClient()).Returns(client); var proxyTelemetryContext = new ProxyTelemetryContext( backendId: "be1", routeId: "rt1", endpointId: "ep1"); // Act await sut.ProxyAsync(httpContext, targetUri, factoryMock.Object, proxyTelemetryContext, CancellationToken.None, CancellationToken.None); // Assert httpContext.Response.StatusCode.Should().Be(StatusCodes.Status101SwitchingProtocols); httpContext.Response.Headers["x-ms-response-test"].Should().BeEquivalentTo("response"); downstreamStream.WriteStream.Position = 0; var returnedToDownstream = StreamToString(downstreamStream.WriteStream); returnedToDownstream.Should().Be("response content"); upstreamStream.Should().NotBeNull(); upstreamStream.WriteStream.Position = 0; var sentToUpstream = StreamToString(upstreamStream.WriteStream); sentToUpstream.Should().Be("request content"); }
public async Task ProxyAsync_UpgradableRequestFailsToUpgrade_ProxiesResponse() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); httpContext.Request.Headers.Add(":host", "example.com"); httpContext.Request.Headers.Add("x-ms-request-test", "request"); // TODO: https://github.com/microsoft/reverse-proxy/issues/255 httpContext.Request.Headers.Add("Upgrade", "WebSocket"); var proxyResponseStream = new MemoryStream(); httpContext.Response.Body = proxyResponseStream; var upgradeFeatureMock = new Mock <IHttpUpgradeFeature>(MockBehavior.Strict); upgradeFeatureMock.SetupGet(u => u.IsUpgradableRequest).Returns(true); httpContext.Features.Set(upgradeFeatureMock.Object); var destinationPrefix = "https://localhost:123/a/b/"; var targetUri = "https://localhost:123/a/b/api/test?a=b&c=d"; var sut = Create <HttpProxy>(); var client = MockHttpHandler.CreateClient( async(HttpRequestMessage request, CancellationToken cancellationToken) => { await Task.Yield(); Assert.Equal(new Version(1, 1), request.Version); Assert.Equal(HttpMethod.Get, request.Method); Assert.Equal(targetUri, request.RequestUri.AbsoluteUri); Assert.Contains("request", request.Headers.GetValues("x-ms-request-test")); Assert.Null(request.Content); var response = new HttpResponseMessage((HttpStatusCode)234); response.ReasonPhrase = "Test Reason Phrase"; response.Headers.TryAddWithoutValidation("x-ms-response-test", "response"); response.Content = new StreamContent(StringToStream("response content")); response.Content.Headers.TryAddWithoutValidation("Content-Language", "responseLanguage"); return(response); }); var factoryMock = new Mock <IProxyHttpClientFactory>(); factoryMock.Setup(f => f.CreateClient()).Returns(client); var proxyTelemetryContext = new ProxyTelemetryContext( clusterId: "be1", routeId: "rt1", destinationId: "d1"); // Act await sut.ProxyAsync(httpContext, destinationPrefix, Transforms.Empty, factoryMock.Object, proxyTelemetryContext, CancellationToken.None, CancellationToken.None); // Assert Assert.Equal(234, httpContext.Response.StatusCode); var reasonPhrase = httpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase; Assert.Equal("Test Reason Phrase", reasonPhrase); Assert.Contains("response", httpContext.Response.Headers["x-ms-response-test"].ToArray()); Assert.Contains("responseLanguage", httpContext.Response.Headers["Content-Language"].ToArray()); proxyResponseStream.Position = 0; var proxyResponseText = StreamToString(proxyResponseStream); Assert.Equal("response content", proxyResponseText); }