mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 15:58:16 +08:00
Remove Ocelot specific Middleware to make Ocelot more compatible with kestrel middleware and get ready for YARP
This commit is contained in:
@ -1,449 +1,446 @@
|
||||
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.Request.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientBuilderTests : IDisposable
|
||||
{
|
||||
private HttpClientBuilder _builder;
|
||||
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
|
||||
private IHttpClient _httpClient;
|
||||
private HttpResponseMessage _response;
|
||||
private DownstreamContext _context;
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
private int _count;
|
||||
private IWebHost _host;
|
||||
private IHttpClient _againHttpClient;
|
||||
private IHttpClient _firstHttpClient;
|
||||
private MemoryHttpClientCache _realCache;
|
||||
|
||||
public HttpClientBuilderTests()
|
||||
{
|
||||
_cacheHandlers = new Mock<IHttpClientCache>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
|
||||
_builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_http_client()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.When(x => WhenIBuild())
|
||||
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_from_cache()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_from_cache_with_different_query_string()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=1"))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=2"))
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_get_from_cache_with_different_query_string()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRouteA = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var reRouteB = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRouteA, "http://wwww.someawesomewebsite.com/woot?badman=1"))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => GivenARequest(reRouteB, "http://wwww.someawesomewebsite.com/woot?badman=2"))
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsNotFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_if_ignoring_ssl_errors()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(true)
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.When(x => WhenIBuild())
|
||||
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||
.Then(x => ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_delegating_handlers_in_order()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var fakeOne = new FakeDelegatingHandler();
|
||||
var fakeTwo = new FakeDelegatingHandler();
|
||||
|
||||
var handlers = new List<Func<DelegatingHandler>>()
|
||||
{
|
||||
() => fakeOne,
|
||||
() => fakeTwo
|
||||
};
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns(handlers))
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.And(x => WhenIBuild())
|
||||
.When(x => WhenICallTheClient())
|
||||
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
|
||||
.And(x => ThenSomethingIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_re_use_cookies_from_container()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET")]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
[InlineData("DELETE")]
|
||||
[InlineData("PATCH")]
|
||||
public void should_add_verb_to_cache_key(string verb)
|
||||
{
|
||||
var downstreamUrl = "http://localhost:5012/";
|
||||
|
||||
var method = new HttpMethod(verb);
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(_ => GivenADownstreamService())
|
||||
.And(_ => GivenARequestWithAUrlAndMethod(reRoute, downstreamUrl, method))
|
||||
.And(_ => GivenTheFactoryReturnsNothing())
|
||||
.And(_ => WhenIBuild())
|
||||
.And(_ => GivenCacheIsCalledWithExpectedKey($"{method.ToString()}:{downstreamUrl}"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenARealCache()
|
||||
{
|
||||
_realCache = new MemoryHttpClientCache();
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientIsFromTheCache()
|
||||
{
|
||||
_againHttpClient.ShouldBe(_firstHttpClient);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientIsNotFromTheCache()
|
||||
{
|
||||
_againHttpClient.ShouldNotBe(_firstHttpClient);
|
||||
}
|
||||
|
||||
private void WhenISave()
|
||||
{
|
||||
_builder.Save();
|
||||
}
|
||||
|
||||
private void GivenCacheIsCalledWithExpectedKey(string expectedKey)
|
||||
{
|
||||
_cacheHandlers.Verify(x => x.Get(It.IsAny<DownstreamReRoute>()), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged()
|
||||
{
|
||||
_logger.Verify(x => x.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {_context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {_context.DownstreamReRoute.DownstreamPathTemplate}"), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheClientIsCached()
|
||||
{
|
||||
_cacheHandlers.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).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(context =>
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
context.Response.Cookies.Append("test", "0");
|
||||
context.Response.StatusCode = 200;
|
||||
_count++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
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 Task.CompletedTask;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 500;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_host.Start();
|
||||
}
|
||||
|
||||
private void GivenARequest(DownstreamReRoute downstream)
|
||||
{
|
||||
GivenARequest(downstream, "http://localhost:5003");
|
||||
}
|
||||
|
||||
private void GivenARequest(DownstreamReRoute downstream, string downstreamUrl)
|
||||
{
|
||||
GivenARequestWithAUrlAndMethod(downstream, downstreamUrl, HttpMethod.Get);
|
||||
}
|
||||
|
||||
private void GivenARequestWithAUrlAndMethod(DownstreamReRoute downstream, string url, HttpMethod method)
|
||||
{
|
||||
var context = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamReRoute = downstream,
|
||||
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri(url), Method = method }),
|
||||
};
|
||||
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void ThenSomethingIsReturned()
|
||||
{
|
||||
_response.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void WhenICallTheClient()
|
||||
{
|
||||
_response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo)
|
||||
{
|
||||
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
|
||||
}
|
||||
|
||||
private void GivenTheFactoryReturns()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>() { () => new FakeDelegatingHandler() };
|
||||
|
||||
_factory
|
||||
.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)
|
||||
{
|
||||
_factory
|
||||
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
|
||||
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
}
|
||||
|
||||
private void WhenIBuild()
|
||||
{
|
||||
_httpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void WhenIBuildTheFirstTime()
|
||||
{
|
||||
_firstHttpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void WhenIBuildAgain()
|
||||
{
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
_againHttpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientShouldNotBeNull()
|
||||
{
|
||||
_httpClient.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_response?.Dispose();
|
||||
_host?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientBuilderTests : IDisposable
|
||||
{
|
||||
private HttpClientBuilder _builder;
|
||||
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
|
||||
private IHttpClient _httpClient;
|
||||
private HttpResponseMessage _response;
|
||||
private HttpContext _context;
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
private int _count;
|
||||
private IWebHost _host;
|
||||
private IHttpClient _againHttpClient;
|
||||
private IHttpClient _firstHttpClient;
|
||||
private MemoryHttpClientCache _realCache;
|
||||
|
||||
public HttpClientBuilderTests()
|
||||
{
|
||||
_cacheHandlers = new Mock<IHttpClientCache>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
|
||||
_builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_http_client()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.When(x => WhenIBuild())
|
||||
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_from_cache()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_from_cache_with_different_query_string()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=1"))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=2"))
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_get_from_cache_with_different_query_string()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRouteA = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var reRouteB = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRouteA, "http://wwww.someawesomewebsite.com/woot?badman=1"))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => GivenARequest(reRouteB, "http://wwww.someawesomewebsite.com/woot?badman=2"))
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsNotFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_if_ignoring_ssl_errors()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(true)
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.When(x => WhenIBuild())
|
||||
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||
.Then(x => ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_delegating_handlers_in_order()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var fakeOne = new FakeDelegatingHandler();
|
||||
var fakeTwo = new FakeDelegatingHandler();
|
||||
|
||||
var handlers = new List<Func<DelegatingHandler>>()
|
||||
{
|
||||
() => fakeOne,
|
||||
() => fakeTwo
|
||||
};
|
||||
|
||||
this.Given(x => GivenTheFactoryReturns(handlers))
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.And(x => WhenIBuild())
|
||||
.When(x => WhenICallTheClient())
|
||||
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
|
||||
.And(x => ThenSomethingIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_re_use_cookies_from_container()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET")]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
[InlineData("DELETE")]
|
||||
[InlineData("PATCH")]
|
||||
public void should_add_verb_to_cache_key(string verb)
|
||||
{
|
||||
var downstreamUrl = "http://localhost:5012/";
|
||||
|
||||
var method = new HttpMethod(verb);
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(_ => GivenADownstreamService())
|
||||
.And(_ => GivenARequestWithAUrlAndMethod(reRoute, downstreamUrl, method))
|
||||
.And(_ => GivenTheFactoryReturnsNothing())
|
||||
.And(_ => WhenIBuild())
|
||||
.And(_ => GivenCacheIsCalledWithExpectedKey($"{method.ToString()}:{downstreamUrl}"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenARealCache()
|
||||
{
|
||||
_realCache = new MemoryHttpClientCache();
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientIsFromTheCache()
|
||||
{
|
||||
_againHttpClient.ShouldBe(_firstHttpClient);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientIsNotFromTheCache()
|
||||
{
|
||||
_againHttpClient.ShouldNotBe(_firstHttpClient);
|
||||
}
|
||||
|
||||
private void WhenISave()
|
||||
{
|
||||
_builder.Save();
|
||||
}
|
||||
|
||||
private void GivenCacheIsCalledWithExpectedKey(string expectedKey)
|
||||
{
|
||||
_cacheHandlers.Verify(x => x.Get(It.IsAny<DownstreamReRoute>()), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged()
|
||||
{
|
||||
_logger.Verify(x => x.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {_context.Items.DownstreamReRoute().UpstreamPathTemplate}, DownstreamPathTemplate: {_context.Items.DownstreamReRoute().DownstreamPathTemplate}"), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheClientIsCached()
|
||||
{
|
||||
_cacheHandlers.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).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(context =>
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
context.Response.Cookies.Append("test", "0");
|
||||
context.Response.StatusCode = 200;
|
||||
_count++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
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 Task.CompletedTask;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 500;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_host.Start();
|
||||
}
|
||||
|
||||
private void GivenARequest(DownstreamReRoute downstream)
|
||||
{
|
||||
GivenARequest(downstream, "http://localhost:5003");
|
||||
}
|
||||
|
||||
private void GivenARequest(DownstreamReRoute downstream, string downstreamUrl)
|
||||
{
|
||||
GivenARequestWithAUrlAndMethod(downstream, downstreamUrl, HttpMethod.Get);
|
||||
}
|
||||
|
||||
private void GivenARequestWithAUrlAndMethod(DownstreamReRoute downstream, string url, HttpMethod method)
|
||||
{
|
||||
_context = new DefaultHttpContext();
|
||||
_context.Items.UpsertDownstreamReRoute(downstream);
|
||||
_context.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri(url), Method = method }));
|
||||
}
|
||||
|
||||
private void ThenSomethingIsReturned()
|
||||
{
|
||||
_response.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void WhenICallTheClient()
|
||||
{
|
||||
_response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo)
|
||||
{
|
||||
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
|
||||
}
|
||||
|
||||
private void GivenTheFactoryReturns()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>() { () => new FakeDelegatingHandler() };
|
||||
|
||||
_factory
|
||||
.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)
|
||||
{
|
||||
_factory
|
||||
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
|
||||
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
}
|
||||
|
||||
private void WhenIBuild()
|
||||
{
|
||||
_httpClient = _builder.Create(_context.Items.DownstreamReRoute());
|
||||
}
|
||||
|
||||
private void WhenIBuildTheFirstTime()
|
||||
{
|
||||
_firstHttpClient = _builder.Create(_context.Items.DownstreamReRoute());
|
||||
}
|
||||
|
||||
private void WhenIBuildAgain()
|
||||
{
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
_againHttpClient = _builder.Create(_context.Items.DownstreamReRoute());
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientShouldNotBeNull()
|
||||
{
|
||||
_httpClient.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_response?.Dispose();
|
||||
_host?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,202 +1,198 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientHttpRequesterTest
|
||||
{
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
|
||||
private Response<HttpResponseMessage> _response;
|
||||
private readonly HttpClientHttpRequester _httpClientRequester;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private Mock<IExceptionToErrorMapper> _mapper;
|
||||
private HttpContext _httpContext;
|
||||
|
||||
public HttpClientHttpRequesterTest()
|
||||
{
|
||||
_httpContext = new DefaultHttpContext();
|
||||
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_loggerFactory
|
||||
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
|
||||
.Returns(_logger.Object);
|
||||
_cacheHandlers = new Mock<IHttpClientCache>();
|
||||
_mapper = new Mock<IExceptionToErrorMapper>();
|
||||
_httpClientRequester = new HttpClientHttpRequester(
|
||||
_loggerFactory.Object,
|
||||
_cacheHandlers.Object,
|
||||
_factory.Object,
|
||||
_mapper.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_request_correctly()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientHttpRequesterTest
|
||||
{
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
|
||||
private Response<HttpResponseMessage> _response;
|
||||
private readonly HttpClientHttpRequester _httpClientRequester;
|
||||
private DownstreamContext _request;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private Mock<IExceptionToErrorMapper> _mapper;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Items.UpsertDownstreamReRoute(reRoute);
|
||||
httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }));
|
||||
|
||||
public HttpClientHttpRequesterTest()
|
||||
{
|
||||
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_loggerFactory
|
||||
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
|
||||
.Returns(_logger.Object);
|
||||
_cacheHandlers = new Mock<IHttpClientCache>();
|
||||
_mapper = new Mock<IExceptionToErrorMapper>();
|
||||
_httpClientRequester = new HttpClientHttpRequester(
|
||||
_loggerFactory.Object,
|
||||
_cacheHandlers.Object,
|
||||
_factory.Object,
|
||||
_mapper.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_request_correctly()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
this.Given(x => x.GivenTheRequestIs(httpContext))
|
||||
.And(x => GivenTheHouseReturnsOkHandler())
|
||||
.When(x => x.WhenIGetResponse())
|
||||
.Then(x => x.ThenTheResponseIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_request_unable_to_complete_request()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Items.UpsertDownstreamReRoute(reRoute);
|
||||
httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }));
|
||||
|
||||
this.Given(x => x.GivenTheRequestIs(httpContext))
|
||||
.When(x => x.WhenIGetResponse())
|
||||
.Then(x => x.ThenTheResponseIsCalledError())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void http_client_request_times_out()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())
|
||||
.Build();
|
||||
|
||||
var context = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamReRoute = reRoute,
|
||||
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }),
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheRequestIs(context))
|
||||
.And(x => GivenTheHouseReturnsOkHandler())
|
||||
.When(x => x.WhenIGetResponse())
|
||||
.Then(x => x.ThenTheResponseIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_request_unable_to_complete_request()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
var context = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamReRoute = reRoute,
|
||||
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }),
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheRequestIs(context))
|
||||
.When(x => x.WhenIGetResponse())
|
||||
.Then(x => x.ThenTheResponseIsCalledError())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void http_client_request_times_out()
|
||||
{
|
||||
var upstreamTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue("").Build();
|
||||
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithUpstreamPathTemplate(upstreamTemplate)
|
||||
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())
|
||||
.Build();
|
||||
|
||||
var context = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamReRoute = reRoute,
|
||||
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }),
|
||||
};
|
||||
|
||||
this.Given(_ => GivenTheRequestIs(context))
|
||||
.And(_ => GivenTheHouseReturnsTimeoutHandler())
|
||||
.When(_ => WhenIGetResponse())
|
||||
.Then(_ => ThenTheResponseIsCalledError())
|
||||
.And(_ => ThenTheErrorIsTimeout())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs(DownstreamContext request)
|
||||
{
|
||||
_request = request;
|
||||
}
|
||||
|
||||
private void WhenIGetResponse()
|
||||
{
|
||||
_response = _httpClientRequester.GetResponse(_request).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheResponseIsCalledCorrectly()
|
||||
{
|
||||
_response.IsError.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ThenTheResponseIsCalledError()
|
||||
{
|
||||
_response.IsError.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void ThenTheErrorIsTimeout()
|
||||
{
|
||||
_mapper.Verify(x => x.Map(It.IsAny<Exception>()), Times.Once);
|
||||
_response.Errors[0].ShouldBeOfType<UnableToCompleteRequestError>();
|
||||
}
|
||||
|
||||
private void GivenTheHouseReturnsOkHandler()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>
|
||||
{
|
||||
() => new OkDelegatingHandler()
|
||||
};
|
||||
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
}
|
||||
|
||||
private void GivenTheHouseReturnsTimeoutHandler()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>
|
||||
{
|
||||
() => new TimeoutDelegatingHandler()
|
||||
};
|
||||
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
|
||||
_mapper.Setup(x => x.Map(It.IsAny<Exception>())).Returns(new UnableToCompleteRequestError(new Exception()));
|
||||
}
|
||||
|
||||
private class OkDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private class TimeoutDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(100000, cancellationToken);
|
||||
return new HttpResponseMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Items.UpsertDownstreamReRoute(reRoute);
|
||||
httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }));
|
||||
|
||||
this.Given(_ => GivenTheRequestIs(httpContext))
|
||||
.And(_ => GivenTheHouseReturnsTimeoutHandler())
|
||||
.When(_ => WhenIGetResponse())
|
||||
.Then(_ => ThenTheResponseIsCalledError())
|
||||
.And(_ => ThenTheErrorIsTimeout())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs(HttpContext httpContext)
|
||||
{
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
private void WhenIGetResponse()
|
||||
{
|
||||
_response = _httpClientRequester.GetResponse(_httpContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheResponseIsCalledCorrectly()
|
||||
{
|
||||
_response.IsError.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ThenTheResponseIsCalledError()
|
||||
{
|
||||
_response.IsError.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void ThenTheErrorIsTimeout()
|
||||
{
|
||||
_mapper.Verify(x => x.Map(It.IsAny<Exception>()), Times.Once);
|
||||
_response.Errors[0].ShouldBeOfType<UnableToCompleteRequestError>();
|
||||
}
|
||||
|
||||
private void GivenTheHouseReturnsOkHandler()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>
|
||||
{
|
||||
() => new OkDelegatingHandler()
|
||||
};
|
||||
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
}
|
||||
|
||||
private void GivenTheHouseReturnsTimeoutHandler()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>
|
||||
{
|
||||
() => new TimeoutDelegatingHandler()
|
||||
};
|
||||
|
||||
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
|
||||
|
||||
_mapper.Setup(x => x.Map(It.IsAny<Exception>())).Returns(new UnableToCompleteRequestError(new Exception()));
|
||||
}
|
||||
|
||||
private class OkDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private class TimeoutDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(100000, cancellationToken);
|
||||
return new HttpResponseMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,132 +1,132 @@
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.UnitTests.Responder;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using TestStack.BDDfy;
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.UnitTests.Responder;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class HttpRequesterMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IHttpRequester> _requester;
|
||||
private Response<HttpResponseMessage> _response;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private readonly HttpRequesterMiddleware _middleware;
|
||||
private DownstreamContext _downstreamContext;
|
||||
private OcelotRequestDelegate _next;
|
||||
|
||||
public HttpRequesterMiddlewareTests()
|
||||
public class HttpRequesterMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IHttpRequester> _requester;
|
||||
private Response<HttpResponseMessage> _response;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private readonly HttpRequesterMiddleware _middleware;
|
||||
private RequestDelegate _next;
|
||||
private HttpContext _httpContext;
|
||||
|
||||
public HttpRequesterMiddlewareTests()
|
||||
{
|
||||
_httpContext = new DefaultHttpContext();
|
||||
_requester = new Mock<IHttpRequester>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<HttpRequesterMiddleware>()).Returns(_logger.Object);
|
||||
_next = context => Task.CompletedTask;
|
||||
_middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_services_correctly()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(new OkResponse<HttpResponseMessage>(new HttpResponseMessage(System.Net.HttpStatusCode.OK))))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamResponseIsSet())
|
||||
.Then(x => InformationIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(new ErrorResponse<HttpResponseMessage>(new AnyError())))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheErrorIsSet())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_downstream_internal_server_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(
|
||||
new OkResponse<HttpResponseMessage>(new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError))))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.WarningIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheErrorIsSet()
|
||||
{
|
||||
_httpContext.Items.Errors().Count.ShouldBeGreaterThan(0);
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_middleware.Invoke(_httpContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs()
|
||||
{
|
||||
_requester = new Mock<IHttpRequester>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<HttpRequesterMiddleware>()).Returns(_logger.Object);
|
||||
_next = context => Task.CompletedTask;
|
||||
_middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_services_correctly()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(new OkResponse<HttpResponseMessage>(new HttpResponseMessage(System.Net.HttpStatusCode.OK))))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamResponseIsSet())
|
||||
.Then(x => InformationIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(new ErrorResponse<HttpResponseMessage>(new AnyError())))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheErrorIsSet())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_downstream_internal_server_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs())
|
||||
.And(x => x.GivenTheRequesterReturns(
|
||||
new OkResponse<HttpResponseMessage>(new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError))))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.WarningIsLogged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheErrorIsSet()
|
||||
{
|
||||
_downstreamContext.IsError.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs()
|
||||
{
|
||||
_downstreamContext =
|
||||
new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamReRoute = new DownstreamReRouteBuilder().Build()
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenTheRequesterReturns(Response<HttpResponseMessage> response)
|
||||
{
|
||||
_response = response;
|
||||
|
||||
_requester
|
||||
.Setup(x => x.GetResponse(It.IsAny<DownstreamContext>()))
|
||||
.ReturnsAsync(_response);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamResponseIsSet()
|
||||
{
|
||||
foreach (var httpResponseHeader in _response.Data.Headers)
|
||||
{
|
||||
if (_downstreamContext.DownstreamResponse.Headers.Any(x => x.Key == httpResponseHeader.Key))
|
||||
{
|
||||
throw new Exception("Header in response not in downstreamresponse headers");
|
||||
}
|
||||
}
|
||||
|
||||
_downstreamContext.DownstreamResponse.Content.ShouldBe(_response.Data.Content);
|
||||
_downstreamContext.DownstreamResponse.StatusCode.ShouldBe(_response.Data.StatusCode);
|
||||
}
|
||||
|
||||
private void WarningIsLogged()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogWarning(
|
||||
It.IsAny<string>()
|
||||
),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private void InformationIsLogged()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogInformation(
|
||||
It.IsAny<string>()
|
||||
),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
_httpContext.Items.UpsertDownstreamReRoute(new DownstreamReRouteBuilder().Build());
|
||||
}
|
||||
|
||||
private void GivenTheRequesterReturns(Response<HttpResponseMessage> response)
|
||||
{
|
||||
_response = response;
|
||||
|
||||
_requester
|
||||
.Setup(x => x.GetResponse(It.IsAny<HttpContext>()))
|
||||
.ReturnsAsync(_response);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamResponseIsSet()
|
||||
{
|
||||
foreach (var httpResponseHeader in _response.Data.Headers)
|
||||
{
|
||||
if (_httpContext.Items.DownstreamResponse().Headers.Any(x => x.Key == httpResponseHeader.Key))
|
||||
{
|
||||
throw new Exception("Header in response not in downstreamresponse headers");
|
||||
}
|
||||
}
|
||||
|
||||
_httpContext.Items.DownstreamResponse().Content.ShouldBe(_response.Data.Content);
|
||||
_httpContext.Items.DownstreamResponse().StatusCode.ShouldBe(_response.Data.StatusCode);
|
||||
}
|
||||
|
||||
private void WarningIsLogged()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogWarning(
|
||||
It.IsAny<string>()
|
||||
),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private void InformationIsLogged()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogInformation(
|
||||
It.IsAny<string>()
|
||||
),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user