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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 372 additions and 85 deletions

View File

@ -17,7 +17,7 @@ var artifactsDir = Directory("artifacts");
// unit testing // unit testing
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
var minCodeCoverage = 76.4d; var minCodeCoverage = 82d;
var coverallsRepoToken = "coveralls-repo-token-ocelot"; var coverallsRepoToken = "coveralls-repo-token-ocelot";
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot"; var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";

View File

@ -73,10 +73,17 @@ Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
- _AllowAutoRedirect_ is a value that indicates whether the request should follow redirection responses.
Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is true. 1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests. follow redirection responses from the Downstream resource; otherwise false. The default value is false.
The default value is true. 2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer
property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
requests. This would also mean that subsequent requests dont use the cookies from the previous response! All in all not a great situation. I would avoid setting
UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
Multiple environments Multiple environments
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^

View File

@ -4,8 +4,8 @@
{ {
public FileHttpHandlerOptions() public FileHttpHandlerOptions()
{ {
AllowAutoRedirect = true; AllowAutoRedirect = false;
UseCookieContainer = true; UseCookieContainer = false;
} }
public bool AllowAutoRedirect { get; set; } public bool AllowAutoRedirect { get; set; }

View File

@ -0,0 +1,14 @@
using System.Net.Http;
namespace Ocelot.Requester
{
public class GlobalDelegatingHandler
{
public GlobalDelegatingHandler(DelegatingHandler delegatingHandler)
{
DelegatingHandler = delegatingHandler;
}
public DelegatingHandler DelegatingHandler { get; private set; }
}
}

View File

@ -1,25 +1,61 @@
using System.Linq; using System;
using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Middleware;
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
public class HttpClientBuilder : IHttpClientBuilder public class HttpClientBuilder : IHttpClientBuilder
{ {
private readonly IDelegatingHandlerHandlerFactory _factory; private readonly IDelegatingHandlerHandlerFactory _factory;
private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger;
private string _cacheKey;
private HttpClient _httpClient;
private IHttpClient _client;
private HttpClientHandler _httpclientHandler;
public HttpClientBuilder(IDelegatingHandlerHandlerFactory house) public HttpClientBuilder(
IDelegatingHandlerHandlerFactory factory,
IHttpClientCache cacheHandlers,
IOcelotLogger logger)
{ {
_factory = house; _factory = factory;
_cacheHandlers = cacheHandlers;
_logger = logger;
} }
public IHttpClient Create(DownstreamReRoute request) public IHttpClient Create(DownstreamContext request)
{ {
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.HttpHandlerOptions.AllowAutoRedirect, UseCookies = request.HttpHandlerOptions.UseCookieContainer}; _cacheKey = GetCacheKey(request);
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request)); var httpClient = _cacheHandlers.Get(_cacheKey);
return new HttpClientWrapper(client); if (httpClient != null)
{
return httpClient;
}
_httpclientHandler = new HttpClientHandler
{
AllowAutoRedirect = request.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = request.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
CookieContainer = new CookieContainer()
};
_httpClient = new HttpClient(CreateHttpMessageHandler(_httpclientHandler, request.DownstreamReRoute));
_client = new HttpClientWrapper(_httpClient);
return _client;
}
public void Save()
{
_cacheHandlers.Set(_cacheKey, _client, TimeSpan.FromHours(24));
} }
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request) private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request)
@ -39,5 +75,12 @@ namespace Ocelot.Requester
}); });
return httpMessageHandler; return httpMessageHandler;
} }
private string GetCacheKey(DownstreamContext request)
{
var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}{request.DownstreamRequest.RequestUri.AbsolutePath}";
return baseUrl;
}
} }
} }

View File

@ -24,17 +24,15 @@ namespace Ocelot.Requester
_factory = house; _factory = house;
} }
public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request) public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext context)
{ {
var builder = new HttpClientBuilder(_factory); var builder = new HttpClientBuilder(_factory, _cacheHandlers, _logger);
var cacheKey = GetCacheKey(request); var httpClient = builder.Create(context);
var httpClient = GetHttpClient(cacheKey, builder, request);
try try
{ {
var response = await httpClient.SendAsync(request.DownstreamRequest); var response = await httpClient.SendAsync(context.DownstreamRequest);
return new OkResponse<HttpResponseMessage>(response); return new OkResponse<HttpResponseMessage>(response);
} }
catch (TimeoutRejectedException exception) catch (TimeoutRejectedException exception)
@ -51,43 +49,8 @@ namespace Ocelot.Requester
} }
finally finally
{ {
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24)); builder.Save();
} }
} }
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, DownstreamContext request)
{
var httpClient = _cacheHandlers.Get(cacheKey);
if (httpClient == null)
{
httpClient = builder.Create(request.DownstreamReRoute);
}
return httpClient;
}
private string GetCacheKey(DownstreamContext request)
{
var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}";
return baseUrl;
}
}
public class ReRouteDelegatingHandler<T>
where T : DelegatingHandler
{
public T DelegatingHandler { get; private set; }
}
public class GlobalDelegatingHandler
{
public GlobalDelegatingHandler(DelegatingHandler delegatingHandler)
{
DelegatingHandler = delegatingHandler;
}
public DelegatingHandler DelegatingHandler { get; private set; }
} }
} }

View File

@ -6,7 +6,7 @@ namespace Ocelot.Requester
/// <summary> /// <summary>
/// This class was made to make unit testing easier when HttpClient is used. /// This class was made to make unit testing easier when HttpClient is used.
/// </summary> /// </summary>
internal class HttpClientWrapper : IHttpClient public class HttpClientWrapper : IHttpClient
{ {
public HttpClient Client { get; } public HttpClient Client { get; }

View File

@ -1,14 +1,10 @@
using System.Net.Http; using Ocelot.Middleware;
using Ocelot.Configuration;
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
public interface IHttpClientBuilder public interface IHttpClientBuilder
{ {
/// <summary> IHttpClient Create(DownstreamContext request);
/// Creates the <see cref="HttpClient"/> void Save();
/// </summary>
/// <param name="request"></param>
IHttpClient Create(DownstreamReRoute request);
} }
} }

View File

@ -7,6 +7,6 @@ namespace Ocelot.Requester
{ {
public interface IHttpRequester public interface IHttpRequester
{ {
Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request); Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext context);
} }
} }

View File

@ -0,0 +1,10 @@
using System.Net.Http;
namespace Ocelot.Requester
{
public class ReRouteDelegatingHandler<T>
where T : DelegatingHandler
{
public T DelegatingHandler { get; private set; }
}
}

View File

@ -16,6 +16,8 @@ namespace Ocelot.AcceptanceTests
public class HeaderTests : IDisposable public class HeaderTests : IDisposable
{ {
private IWebHost _builder; private IWebHost _builder;
private string _cookieValue;
private int _count;
private readonly Steps _steps; private readonly Steps _steps;
public HeaderTests() public HeaderTests()
@ -184,6 +186,125 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .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) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()

View File

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

View File

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

View File

@ -1,9 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http; 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 Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Requester; using Ocelot.Requester;
using Ocelot.Responses; using Ocelot.Responses;
using Shouldly; using Shouldly;
@ -12,25 +22,37 @@ using Xunit;
namespace Ocelot.UnitTests.Requester namespace Ocelot.UnitTests.Requester
{ {
public class HttpClientBuilderTests public class HttpClientBuilderTests : IDisposable
{ {
private readonly HttpClientBuilder _builder; private readonly HttpClientBuilder _builder;
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory; private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
private IHttpClient _httpClient; private IHttpClient _httpClient;
private HttpResponseMessage _response; 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() public HttpClientBuilderTests()
{ {
_cacheHandlers = new Mock<IHttpClientCache>();
_logger = new Mock<IOcelotLogger>();
_factory = new Mock<IDelegatingHandlerHandlerFactory>(); _factory = new Mock<IDelegatingHandlerHandlerFactory>();
_builder = new HttpClientBuilder(_factory.Object); _builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object);
} }
[Fact] [Fact]
public void should_build_http_client() public void should_build_http_client()
{ {
var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFactoryReturns()) this.Given(x => GivenTheFactoryReturns())
.And(x => GivenARequest()) .And(x => GivenARequest(reRoute))
.When(x => WhenIBuild()) .When(x => WhenIBuild())
.Then(x => ThenTheHttpClientShouldNotBeNull()) .Then(x => ThenTheHttpClientShouldNotBeNull())
.BDDfy(); .BDDfy();
@ -39,6 +61,12 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_call_delegating_handlers_in_order() 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 fakeOne = new FakeDelegatingHandler();
var fakeTwo = new FakeDelegatingHandler(); var fakeTwo = new FakeDelegatingHandler();
@ -49,7 +77,7 @@ namespace Ocelot.UnitTests.Requester
}; };
this.Given(x => GivenTheFactoryReturns(handlers)) this.Given(x => GivenTheFactoryReturns(handlers))
.And(x => GivenARequest()) .And(x => GivenARequest(reRoute))
.And(x => WhenIBuild()) .And(x => WhenIBuild())
.When(x => WhenICallTheClient()) .When(x => WhenICallTheClient())
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo)) .Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
@ -57,12 +85,95 @@ namespace Ocelot.UnitTests.Requester
.BDDfy(); .BDDfy();
} }
private void GivenARequest() [Fact]
public void should_re_use_cookies_from_container()
{ {
var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) var reRoute = new DownstreamReRouteBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)).WithReRouteKey("").Build(); .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() private void ThenSomethingIsReturned()
@ -88,6 +199,14 @@ namespace Ocelot.UnitTests.Requester
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())) .Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers)); .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) private void GivenTheFactoryReturns(List<Func<DelegatingHandler>> handlers)
{ {
@ -98,12 +217,18 @@ namespace Ocelot.UnitTests.Requester
private void WhenIBuild() private void WhenIBuild()
{ {
_httpClient = _builder.Create(_request); _httpClient = _builder.Create(_context);
} }
private void ThenTheHttpClientShouldNotBeNull() private void ThenTheHttpClientShouldNotBeNull()
{ {
_httpClient.ShouldNotBeNull(); _httpClient.ShouldNotBeNull();
} }
public void Dispose()
{
_response?.Dispose();
_host?.Dispose();
}
} }
} }

View File

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