Feature/#274 (#281)

* #274 added acceptance tests, need to work out failing unit tests but probably going to be a redesign where we hold a reference to the cookie container and empty it if needed

* #274 updated code coverage value

* #274 offloaded cache logic to builder in preparation for adding state

* #274 hacked something together but this is not right approach

* #274 updated defaults and docs

* #274 updated code coverage
This commit is contained in:
Tom Pallister
2018-03-17 11:35:16 +00:00
committed by GitHub
parent c1b315173f
commit ed11f3024c
15 changed files with 372 additions and 85 deletions

View File

@ -16,6 +16,8 @@ namespace Ocelot.AcceptanceTests
public class HeaderTests : IDisposable
{
private IWebHost _builder;
private string _cookieValue;
private int _count;
private readonly Steps _steps;
public HeaderTests()
@ -184,6 +186,125 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
[Fact]
public void request_should_reuse_cookies_with_cookie_container()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6774,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" },
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseCookieContainer = true
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6774", "/sso/test", 200))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
.And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void request_should_have_own_cookies_no_cookie_container()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6775,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" },
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseCookieContainer = false
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6775", "/sso/test", 200))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
.And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(_count == 0)
{
context.Response.Cookies.Append("test", "0");
_count++;
context.Response.StatusCode = statusCode;
return;
}
if(context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
{
if(cookieValue == "0" || headerValue == "test=1; path=/")
{
context.Response.StatusCode = statusCode;
return;
}
}
context.Response.StatusCode = 500;
});
})
.Build();
_builder.Start();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{
_builder = new WebHostBuilder()

View File

@ -274,6 +274,11 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient();
}
internal void GivenIAddCookieToMyRequest(string cookie)
{
_ocelotClient.DefaultRequestHeaders.Add("Set-Cookie", cookie);
}
/// <summary>
/// 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.
/// </summary>

View File

@ -19,10 +19,10 @@ namespace Ocelot.UnitTests.Configuration
}
[Fact]
public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default()
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{
var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(true, true, false);
var expectedOptions = new HttpHandlerOptions(false, false, false);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -61,12 +61,12 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute);
}
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions options)
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected)
{
_httpHandlerOptions.ShouldNotBeNull();
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(options.UseTracing);
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
}
}
}

View File

@ -1,9 +1,19 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Requester;
using Ocelot.Responses;
using Shouldly;
@ -12,25 +22,37 @@ using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class HttpClientBuilderTests
public class HttpClientBuilderTests : IDisposable
{
private readonly HttpClientBuilder _builder;
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
private IHttpClient _httpClient;
private HttpResponseMessage _response;
private DownstreamReRoute _request;
private DownstreamContext _context;
private readonly Mock<IHttpClientCache> _cacheHandlers;
private Mock<IOcelotLogger> _logger;
private int _count;
private IWebHost _host;
public HttpClientBuilderTests()
{
_cacheHandlers = new Mock<IHttpClientCache>();
_logger = new Mock<IOcelotLogger>();
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
_builder = new HttpClientBuilder(_factory.Object);
_builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object);
}
[Fact]
public void should_build_http_client()
{
var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFactoryReturns())
.And(x => GivenARequest())
.And(x => GivenARequest(reRoute))
.When(x => WhenIBuild())
.Then(x => ThenTheHttpClientShouldNotBeNull())
.BDDfy();
@ -39,6 +61,12 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_call_delegating_handlers_in_order()
{
var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("")
.Build();
var fakeOne = new FakeDelegatingHandler();
var fakeTwo = new FakeDelegatingHandler();
@ -49,7 +77,7 @@ namespace Ocelot.UnitTests.Requester
};
this.Given(x => GivenTheFactoryReturns(handlers))
.And(x => GivenARequest())
.And(x => GivenARequest(reRoute))
.And(x => WhenIBuild())
.When(x => WhenICallTheClient())
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
@ -57,12 +85,95 @@ namespace Ocelot.UnitTests.Requester
.BDDfy();
}
private void GivenARequest()
[Fact]
public void should_re_use_cookies_from_container()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(false)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)).WithReRouteKey("").Build();
var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false))
.WithReRouteKey("")
.Build();
_request = reRoute;
this.Given(_ => GivenADownstreamService())
.And(_ => GivenARequest(reRoute))
.And(_ => GivenTheFactoryReturnsNothing())
.And(_ => WhenIBuild())
.And(_ => WhenICallTheClient("http://localhost:5003"))
.And(_ => ThenTheCookieIsSet())
.And(_ => GivenTheClientIsCached())
.And(_ => WhenIBuild())
.When(_ => WhenICallTheClient("http://localhost:5003"))
.Then(_ => ThenTheResponseIsOk())
.BDDfy();
}
private void GivenTheClientIsCached()
{
_cacheHandlers.Setup(x => x.Get(It.IsAny<string>())).Returns(_httpClient);
}
private void ThenTheCookieIsSet()
{
_response.Headers.TryGetValues("Set-Cookie", out var test).ShouldBeTrue();
}
private void WhenICallTheClient(string url)
{
_response = _httpClient
.SendAsync(new HttpRequestMessage(HttpMethod.Get, url))
.GetAwaiter()
.GetResult();
}
private void ThenTheResponseIsOk()
{
_response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
private void GivenADownstreamService()
{
_host = new WebHostBuilder()
.UseUrls("http://localhost:5003")
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.Run(async context =>
{
if (_count == 0)
{
context.Response.Cookies.Append("test", "0");
context.Response.StatusCode = 200;
_count++;
return;
}
if (_count == 1)
{
if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
{
context.Response.StatusCode = 200;
return;
}
context.Response.StatusCode = 500;
}
});
})
.Build();
_host.Start();
}
private void GivenARequest(DownstreamReRoute downstream)
{
var context = new DownstreamContext(new DefaultHttpContext())
{
DownstreamReRoute = downstream,
DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:5003") },
};
_context = context;
}
private void ThenSomethingIsReturned()
@ -88,6 +199,14 @@ namespace Ocelot.UnitTests.Requester
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
}
private void GivenTheFactoryReturnsNothing()
{
var handlers = new List<Func<DelegatingHandler>>();
_factory
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
}
private void GivenTheFactoryReturns(List<Func<DelegatingHandler>> handlers)
{
@ -98,12 +217,18 @@ namespace Ocelot.UnitTests.Requester
private void WhenIBuild()
{
_httpClient = _builder.Create(_request);
_httpClient = _builder.Create(_context);
}
private void ThenTheHttpClientShouldNotBeNull()
{
_httpClient.ShouldNotBeNull();
}
public void Dispose()
{
_response?.Dispose();
_host?.Dispose();
}
}
}

View File

@ -35,7 +35,10 @@ namespace Ocelot.UnitTests.Requester
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
.Returns(_logger.Object);
_cacheHandlers = new Mock<IHttpClientCache>();
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _house.Object);
_httpClientRequester = new HttpClientHttpRequester(
_loggerFactory.Object,
_cacheHandlers.Object,
_house.Object);
}
[Fact]