diff --git a/src/Ocelot/Cache/CachedResponse.cs b/src/Ocelot/Cache/CachedResponse.cs index e4360e0d..538a4c35 100644 --- a/src/Ocelot/Cache/CachedResponse.cs +++ b/src/Ocelot/Cache/CachedResponse.cs @@ -1,30 +1,33 @@ -using System.Collections.Generic; -using System.Net; - -namespace Ocelot.Cache -{ - public class CachedResponse - { - public CachedResponse( - HttpStatusCode statusCode, - Dictionary> headers, - string body, - Dictionary> contentHeaders - - ) - { - StatusCode = statusCode; - Headers = headers ?? new Dictionary>(); - ContentHeaders = contentHeaders ?? new Dictionary>(); - Body = body ?? ""; - } - - public HttpStatusCode StatusCode { get; private set; } - - public Dictionary> Headers { get; private set; } - - public Dictionary> ContentHeaders { get; private set; } - - public string Body { get; private set; } - } -} +using System.Collections.Generic; +using System.Net; + +namespace Ocelot.Cache +{ + public class CachedResponse + { + public CachedResponse( + HttpStatusCode statusCode, + Dictionary> headers, + string body, + Dictionary> contentHeaders, + string reasonPhrase + ) + { + StatusCode = statusCode; + Headers = headers ?? new Dictionary>(); + ContentHeaders = contentHeaders ?? new Dictionary>(); + Body = body ?? ""; + ReasonPhrase = reasonPhrase; + } + + public HttpStatusCode StatusCode { get; private set; } + + public Dictionary> Headers { get; private set; } + + public Dictionary> ContentHeaders { get; private set; } + + public string Body { get; private set; } + + public string ReasonPhrase { get; private set; } + } +} diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index 00c2e322..5b96e79c 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -87,7 +87,7 @@ streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value); } - return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList()); + return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase); } internal async Task CreateCachedResponse(DownstreamResponse response) @@ -109,7 +109,7 @@ var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value); - var cached = new CachedResponse(statusCode, headers, body, contentHeaders); + var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase); return cached; } } diff --git a/src/Ocelot/Middleware/DownstreamResponse.cs b/src/Ocelot/Middleware/DownstreamResponse.cs index 19345735..c58a5198 100644 --- a/src/Ocelot/Middleware/DownstreamResponse.cs +++ b/src/Ocelot/Middleware/DownstreamResponse.cs @@ -7,25 +7,27 @@ namespace Ocelot.Middleware { public class DownstreamResponse { - public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List
headers) + public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List
headers, string reasonPhrase) { Content = content; StatusCode = statusCode; Headers = headers ?? new List
(); + ReasonPhrase = reasonPhrase; } public DownstreamResponse(HttpResponseMessage response) - :this(response.Content, response.StatusCode, response.Headers.Select(x => new Header(x.Key, x.Value)).ToList()) + :this(response.Content, response.StatusCode, response.Headers.Select(x => new Header(x.Key, x.Value)).ToList(), response.ReasonPhrase) { } - public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, IEnumerable>> headers) - :this(content, statusCode, headers.Select(x => new Header(x.Key, x.Value)).ToList()) + public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, IEnumerable>> headers, string reasonPhrase) + :this(content, statusCode, headers.Select(x => new Header(x.Key, x.Value)).ToList(), reasonPhrase) { } public HttpContent Content { get; } public HttpStatusCode StatusCode { get; } public List
Headers { get; } + public string ReasonPhrase {get;} } } diff --git a/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs index bfc1351f..ef2a00f5 100644 --- a/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs +++ b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs @@ -46,7 +46,7 @@ namespace Ocelot.Middleware.Multiplexer Headers = {ContentType = new MediaTypeHeaderValue("application/json")} }; - originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List>>()); + originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List>>(), "cannot return from aggregate..which reason phrase would you use?"); } private static void MapAggregateError(DownstreamContext originalContext, List downstreamContexts, int i) diff --git a/src/Ocelot/Responder/HttpContextResponder.cs b/src/Ocelot/Responder/HttpContextResponder.cs index cb677c8d..1b20992d 100644 --- a/src/Ocelot/Responder/HttpContextResponder.cs +++ b/src/Ocelot/Responder/HttpContextResponder.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; using Ocelot.Headers; using Ocelot.Middleware; @@ -45,6 +46,8 @@ namespace Ocelot.Responder context.Response.StatusCode = (int)response.StatusCode; + context.Response.HttpContext.Features.Get().ReasonPhrase = response.ReasonPhrase; + using(content) { if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0) diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index 7f3bc902..a98fd3ef 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -559,7 +559,7 @@ namespace Ocelot.AcceptanceTests var merge = $"{one}, {two}"; merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", ""); var headers = responses.SelectMany(x => x.Headers).ToList(); - return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers); + return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason"); } } } diff --git a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs new file mode 100644 index 00000000..14742b86 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs @@ -0,0 +1,76 @@ +namespace Ocelot.AcceptanceTests +{ + using System; + using System.Collections.Generic; + using System.Net; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Features; + using Ocelot.Configuration.File; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class ReasonPhraseTests : IDisposable + { + private readonly Steps _steps; + private string _contentType; + private long? _contentLength; + private bool _contentTypeHeaderExists; + private readonly ServiceHandler _serviceHandler; + + public ReasonPhraseTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_reason_phrase() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51339, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", "some reason")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(_ => _steps.ThenTheReasonPhraseIs("some reason")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string reasonPhrase) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + context.Response.HttpContext.Features.Get().ReasonPhrase = reasonPhrase; + + await context.Response.WriteAsync("YOYO!"); + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 274f1554..8472b474 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -452,6 +452,11 @@ header.First().ShouldBe(value); } + public void ThenTheReasonPhraseIs(string expected) + { + _response.ReasonPhrase.ShouldBe(expected); + } + /// /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. /// diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index d13b22f2..6f32739b 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -53,7 +53,7 @@ { "content-type", new List { "application/json" } } }; - var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders); + var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders, "some reason"); this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) .And(x => x.GivenTheDownstreamRouteIs()) .When(x => x.WhenICallTheMiddleware()) @@ -69,7 +69,7 @@ { "Expires", new List { "-1" } } }; - var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary>(), "", contentHeaders); + var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary>(), "", contentHeaders, "some reason"); this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) .And(x => x.GivenTheDownstreamRouteIs()) .When(x => x.WhenICallTheMiddleware()) diff --git a/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs b/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs index 3e7bc077..a630c2d8 100644 --- a/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs @@ -1,283 +1,283 @@ -using Xunit; -using Shouldly; -using TestStack.BDDfy; -using System.Net.Http; -using Ocelot.Headers; -using Ocelot.Configuration; -using System.Collections.Generic; -using Ocelot.Responses; -using System.Linq; -using System.Net; -using Moq; -using Ocelot.Infrastructure; -using Ocelot.Middleware; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Middleware.Multiplexer; -using Ocelot.Request.Middleware; - -namespace Ocelot.UnitTests.Headers -{ - public class HttpResponseHeaderReplacerTests - { - private DownstreamResponse _response; - private Placeholders _placeholders; - private readonly HttpResponseHeaderReplacer _replacer; - private List _headerFindAndReplaces; - private Response _result; - private DownstreamRequest _request; - private Mock _finder; - private Mock _repo; - - public HttpResponseHeaderReplacerTests() - { - _repo = new Mock(); - _finder = new Mock(); - _placeholders = new Placeholders(_finder.Object, _repo.Object); - _replacer = new HttpResponseHeaderReplacer(_placeholders); - } - - [Fact] - public void should_replace_headers() - { - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("test", new List {"test"}) - }); - - var fAndRs = new List {new HeaderFindAndReplace("test", "test", "chiken", 0)}; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeadersAreReplaced()) - .BDDfy(); - } - - [Fact] - public void should_not_replace_headers() - { - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("test", new List {"test"}) - }); - - var fAndRs = new List(); - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeadersAreNotReplaced()) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_with_ocelot_base_url() - { - const string downstreamUrl = "http://downstream.com/"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/")) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_with_ocelot_base_url_with_port() - { - const string downstreamUrl = "http://downstream.com/"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/")) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_with_ocelot_base_url_and_path() - { - const string downstreamUrl = "http://downstream.com/test/product"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product")) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_with_ocelot_base_url_with_path_and_port() - { - const string downstreamUrl = "http://downstream.com/test/product"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/test/product")) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_and_port_with_ocelot_base_url() - { - const string downstreamUrl = "http://downstream.com:123/test/product"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product")) - .BDDfy(); - } - - [Fact] - public void should_replace_downstream_base_url_and_port_with_ocelot_base_url_and_port() - { - const string downstreamUrl = "http://downstream.com:123/test/product"; - - var request = - new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; - - var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, - new List>>() - { - new KeyValuePair>("Location", new List {downstreamUrl}) - }); - - var fAndRs = new List - { - new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:321/", 0) - }; - - this.Given(x => GivenTheHttpResponse(response)) - .And(x => GivenTheRequestIs(request)) - .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) - .When(x => WhenICallTheReplacer()) - .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:321/test/product")) - .BDDfy(); - } - - private void GivenTheRequestIs(HttpRequestMessage request) - { - _request = new DownstreamRequest(request); - } - - private void ThenTheHeadersAreNotReplaced() - { - _result.ShouldBeOfType(); - foreach (var f in _headerFindAndReplaces) - { - var values = _response.Headers.First(x => x.Key == f.Key); - values.Values.ToList()[f.Index].ShouldBe("test"); - } - } - - private void GivenTheFollowingHeaderReplacements(List fAndRs) - { - _headerFindAndReplaces = fAndRs; - } - - private void GivenTheHttpResponse(DownstreamResponse response) - { - _response = response; - } - - private void WhenICallTheReplacer() - { - _result = _replacer.Replace(_response, _headerFindAndReplaces, _request); - } - - private void ThenTheHeaderShouldBe(string key, string value) - { - var test = _response.Headers.First(x => x.Key == key); - test.Values.First().ShouldBe(value); - } - - private void ThenTheHeadersAreReplaced() - { - _result.ShouldBeOfType(); - foreach (var f in _headerFindAndReplaces) - { - var values = _response.Headers.First(x => x.Key == f.Key); - values.Values.ToList()[f.Index].ShouldBe(f.Replace); - } - } - } -} +using Xunit; +using Shouldly; +using TestStack.BDDfy; +using System.Net.Http; +using Ocelot.Headers; +using Ocelot.Configuration; +using System.Collections.Generic; +using Ocelot.Responses; +using System.Linq; +using System.Net; +using Moq; +using Ocelot.Infrastructure; +using Ocelot.Middleware; +using Ocelot.Infrastructure.RequestData; +using Ocelot.Middleware.Multiplexer; +using Ocelot.Request.Middleware; + +namespace Ocelot.UnitTests.Headers +{ + public class HttpResponseHeaderReplacerTests + { + private DownstreamResponse _response; + private Placeholders _placeholders; + private readonly HttpResponseHeaderReplacer _replacer; + private List _headerFindAndReplaces; + private Response _result; + private DownstreamRequest _request; + private Mock _finder; + private Mock _repo; + + public HttpResponseHeaderReplacerTests() + { + _repo = new Mock(); + _finder = new Mock(); + _placeholders = new Placeholders(_finder.Object, _repo.Object); + _replacer = new HttpResponseHeaderReplacer(_placeholders); + } + + [Fact] + public void should_replace_headers() + { + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("test", new List {"test"}) + }, ""); + + var fAndRs = new List {new HeaderFindAndReplace("test", "test", "chiken", 0)}; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeadersAreReplaced()) + .BDDfy(); + } + + [Fact] + public void should_not_replace_headers() + { + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("test", new List {"test"}) + }, ""); + + var fAndRs = new List(); + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeadersAreNotReplaced()) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_with_ocelot_base_url() + { + const string downstreamUrl = "http://downstream.com/"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/")) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_with_ocelot_base_url_with_port() + { + const string downstreamUrl = "http://downstream.com/"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/")) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_with_ocelot_base_url_and_path() + { + const string downstreamUrl = "http://downstream.com/test/product"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product")) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_with_ocelot_base_url_with_path_and_port() + { + const string downstreamUrl = "http://downstream.com/test/product"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/test/product")) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_and_port_with_ocelot_base_url() + { + const string downstreamUrl = "http://downstream.com:123/test/product"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product")) + .BDDfy(); + } + + [Fact] + public void should_replace_downstream_base_url_and_port_with_ocelot_base_url_and_port() + { + const string downstreamUrl = "http://downstream.com:123/test/product"; + + var request = + new HttpRequestMessage(HttpMethod.Get, "http://test.com") {RequestUri = new System.Uri(downstreamUrl)}; + + var response = new DownstreamResponse(new StringContent(string.Empty), HttpStatusCode.Accepted, + new List>>() + { + new KeyValuePair>("Location", new List {downstreamUrl}) + }, ""); + + var fAndRs = new List + { + new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:321/", 0) + }; + + this.Given(x => GivenTheHttpResponse(response)) + .And(x => GivenTheRequestIs(request)) + .And(x => GivenTheFollowingHeaderReplacements(fAndRs)) + .When(x => WhenICallTheReplacer()) + .Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:321/test/product")) + .BDDfy(); + } + + private void GivenTheRequestIs(HttpRequestMessage request) + { + _request = new DownstreamRequest(request); + } + + private void ThenTheHeadersAreNotReplaced() + { + _result.ShouldBeOfType(); + foreach (var f in _headerFindAndReplaces) + { + var values = _response.Headers.First(x => x.Key == f.Key); + values.Values.ToList()[f.Index].ShouldBe("test"); + } + } + + private void GivenTheFollowingHeaderReplacements(List fAndRs) + { + _headerFindAndReplaces = fAndRs; + } + + private void GivenTheHttpResponse(DownstreamResponse response) + { + _response = response; + } + + private void WhenICallTheReplacer() + { + _result = _replacer.Replace(_response, _headerFindAndReplaces, _request); + } + + private void ThenTheHeaderShouldBe(string key, string value) + { + var test = _response.Headers.First(x => x.Key == key); + test.Values.First().ShouldBe(value); + } + + private void ThenTheHeadersAreReplaced() + { + _result.ShouldBeOfType(); + foreach (var f in _headerFindAndReplaces) + { + var values = _response.Headers.First(x => x.Key == f.Key); + values.Values.ToList()[f.Index].ShouldBe(f.Replace); + } + } + } +} diff --git a/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs index fb2a1c98..7d2e5edd 100644 --- a/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs +++ b/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs @@ -49,13 +49,13 @@ namespace Ocelot.UnitTests.Middleware var billDownstreamContext = new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList>>()), + DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList>>(), "some reason"), DownstreamReRoute = billDownstreamReRoute }; var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List>>()), + DownstreamResponse = new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List>>(), "some reason"), DownstreamReRoute = georgeDownstreamReRoute }; @@ -69,6 +69,7 @@ namespace Ocelot.UnitTests.Middleware .When(x => WhenIAggregate()) .Then(x => ThenTheContentIs(expected)) .And(x => ThenTheContentTypeIs("application/json")) + .And(x => ThenTheReasonPhraseIs("cannot return from aggregate..which reason phrase would you use?")) .BDDfy(); } @@ -91,13 +92,13 @@ namespace Ocelot.UnitTests.Middleware var billDownstreamContext = new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List>>()), + DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List>>(), "some reason"), DownstreamReRoute = billDownstreamReRoute }; var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List>>()), + DownstreamResponse = new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List>>(), "some reason"), DownstreamReRoute = georgeDownstreamReRoute, }; @@ -116,6 +117,11 @@ namespace Ocelot.UnitTests.Middleware .BDDfy(); } + private void ThenTheReasonPhraseIs(string expected) + { + _upstreamContext.DownstreamResponse.ReasonPhrase.ShouldBe(expected); + } + private void ThenTheErrorIsMapped() { _upstreamContext.Errors.ShouldBe(_downstreamContexts[1].Errors); diff --git a/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs index 2b5bd552..daef2a2c 100644 --- a/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs +++ b/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs @@ -43,11 +43,11 @@ namespace Ocelot.UnitTests.Middleware { new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List>>()) + DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List>>(), "some reason") }, new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List>>()) + DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List>>(), "some reason") } }; @@ -72,11 +72,11 @@ namespace Ocelot.UnitTests.Middleware { new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List>>()) + DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List>>(), "some reason") }, new DownstreamContext(new DefaultHttpContext()) { - DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List>>()) + DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List>>(), "some reason") } }; @@ -146,7 +146,7 @@ namespace Ocelot.UnitTests.Middleware var laura = await responses[1].Content.ReadAsStringAsync(); var content = $"{tom}, {laura}"; var headers = responses.SelectMany(x => x.Headers).ToList(); - return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers); + return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers, "some reason"); } } } diff --git a/test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs b/test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs index 6ff956d2..a5716908 100644 --- a/test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs +++ b/test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Http; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Ocelot.Headers; using Ocelot.Middleware; using Ocelot.Middleware.Multiplexer; @@ -31,7 +32,7 @@ namespace Ocelot.UnitTests.Responder new List>> { new KeyValuePair>("Transfer-Encoding", new List {"woop"}) - }); + }, "some reason"); _responder.SetResponseOnHttpContext(httpContext, response).GetAwaiter().GetResult(); var header = httpContext.Response.Headers["Transfer-Encoding"]; @@ -43,7 +44,7 @@ namespace Ocelot.UnitTests.Responder { var httpContext = new DefaultHttpContext(); var response = new DownstreamResponse(new StringContent("test"), HttpStatusCode.OK, - new List>>()); + new List>>(), "some reason"); _responder.SetResponseOnHttpContext(httpContext, response).GetAwaiter().GetResult(); var header = httpContext.Response.Headers["Content-Length"]; @@ -58,13 +59,28 @@ namespace Ocelot.UnitTests.Responder new List>> { new KeyValuePair>("test", new List {"test"}) - }); + }, "some reason"); _responder.SetResponseOnHttpContext(httpContext, response).GetAwaiter().GetResult(); var header = httpContext.Response.Headers["test"]; header.First().ShouldBe("test"); } + + [Fact] + public void should_add_reason_phrase() + { + var httpContext = new DefaultHttpContext(); + var response = new DownstreamResponse(new StringContent(""), HttpStatusCode.OK, + new List>> + { + new KeyValuePair>("test", new List {"test"}) + }, "some reason"); + + _responder.SetResponseOnHttpContext(httpContext, response).GetAwaiter().GetResult(); + httpContext.Response.HttpContext.Features.Get().ReasonPhrase.ShouldBe(response.ReasonPhrase); + } + [Fact] public void should_call_without_exception() {