Now supports the same upstream url by difrenciating by http method. Also broke up the proxy middleware into three seperate pieces that do their thing and stick something into the OWIN context

This commit is contained in:
TomPallister 2016-10-04 21:30:16 +01:00
parent da1957311b
commit ab8407e7dc
17 changed files with 227 additions and 82 deletions

View File

@ -4,5 +4,6 @@
{ {
public string DownstreamTemplate { get; set; } public string DownstreamTemplate { get; set; }
public string UpstreamTemplate { get; set; } public string UpstreamTemplate { get; set; }
public string UpstreamHttpMethod { get; set; }
} }
} }

View File

@ -1,4 +1,6 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.UrlMatcher; using Ocelot.Library.Infrastructure.UrlMatcher;
@ -16,9 +18,9 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder
_urlMatcher = urlMatcher; _urlMatcher = urlMatcher;
} }
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath) public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
{ {
foreach (var template in _configuration.Value.ReRoutes) foreach (var template in _configuration.Value.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
{ {
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate); var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate);

View File

@ -4,6 +4,6 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder
{ {
public interface IDownstreamRouteFinder public interface IDownstreamRouteFinder
{ {
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath); Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
} }
} }

View File

@ -0,0 +1,40 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Responder;
namespace Ocelot.Library.Middleware
{
public class DownstreamRouteFinderMiddleware
{
private readonly RequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IHttpResponder _responder;
public DownstreamRouteFinderMiddleware(RequestDelegate next,
IDownstreamRouteFinder downstreamRouteFinder,
IHttpResponder responder)
{
_next = next;
_downstreamRouteFinder = downstreamRouteFinder;
_responder = responder;
}
public async Task Invoke(HttpContext context)
{
var upstreamUrlPath = context.Request.Path.ToString();
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
if (downstreamRoute.IsError)
{
await _responder.CreateNotFoundResponse(context);
return;
}
context.Items.Add("DownstreamRoute", downstreamRoute.Data);
await _next.Invoke(context);
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
namespace Ocelot.Library.Middleware
{
public static class DownstreamRouteFinderMiddlewareExtensions
{
public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
}
}
}

View File

@ -0,0 +1,42 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.UrlTemplateReplacer;
namespace Ocelot.Library.Middleware
{
public class DownstreamUrlCreatorMiddleware
{
private readonly RequestDelegate _next;
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
public DownstreamUrlCreatorMiddleware(RequestDelegate next,
IDownstreamUrlTemplateVariableReplacer urlReplacer)
{
_next = next;
_urlReplacer = urlReplacer;
}
public async Task Invoke(HttpContext context)
{
var downstreamRoute = GetDownstreamRouteFromOwinItems(context);
var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute);
context.Items.Add("DownstreamUrl", downstreamUrl);
await _next.Invoke(context);
}
private DownstreamRoute GetDownstreamRouteFromOwinItems(HttpContext context)
{
object obj;
DownstreamRoute downstreamRoute = null;
if (context.Items.TryGetValue("DownstreamRoute", out obj))
{
downstreamRoute = (DownstreamRoute) obj;
}
return downstreamRoute;
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
namespace Ocelot.Library.Middleware
{
public static class DownstreamUrlCreatorMiddlewareExtensions
{
public static IApplicationBuilder UserDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
}
}
}

View File

@ -0,0 +1,47 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.Requester;
using Ocelot.Library.Infrastructure.Responder;
namespace Ocelot.Library.Middleware
{
public class HttpRequesterMiddleware
{
private readonly RequestDelegate _next;
private readonly IHttpRequester _requester;
private readonly IHttpResponder _responder;
public HttpRequesterMiddleware(RequestDelegate next,
IHttpRequester requester,
IHttpResponder responder)
{
_next = next;
_requester = requester;
_responder = responder;
}
public async Task Invoke(HttpContext context)
{
var downstreamUrl = GetDownstreamUrlFromOwinItems(context);
var response = await _requester
.GetResponse(context.Request.Method, downstreamUrl, context.Request.Body,
context.Request.Headers, context.Request.Cookies, context.Request.Query, context.Request.ContentType);
await _responder.CreateResponse(context, response);
await _next.Invoke(context);
}
private string GetDownstreamUrlFromOwinItems(HttpContext context)
{
object obj;
string downstreamUrl = null;
if (context.Items.TryGetValue("DownstreamUrl", out obj))
{
downstreamUrl = (string) obj;
}
return downstreamUrl;
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
namespace Ocelot.Library.Middleware
{
public static class HttpRequesterMiddlewareExtensions
{
public static IApplicationBuilder UseHttpRequesterMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HttpRequesterMiddleware>();
}
}
}

View File

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Builder;
namespace Ocelot.Library.Middleware
{
public static class ProxyExtensions
{
public static IApplicationBuilder UseProxy(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ProxyMiddleware>();
}
}
}

View File

@ -1,59 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Requester;
using Ocelot.Library.Infrastructure.Responder;
using Ocelot.Library.Infrastructure.UrlTemplateReplacer;
namespace Ocelot.Library.Middleware
{
public class ProxyMiddleware
{
private readonly RequestDelegate _next;
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
private readonly IOptions<Configuration> _configuration;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IHttpRequester _requester;
private readonly IHttpResponder _responder;
public ProxyMiddleware(RequestDelegate next,
IDownstreamUrlTemplateVariableReplacer urlReplacer,
IOptions<Configuration> configuration,
IDownstreamRouteFinder downstreamRouteFinder,
IHttpRequester requester,
IHttpResponder responder)
{
_next = next;
_urlReplacer = urlReplacer;
_configuration = configuration;
_downstreamRouteFinder = downstreamRouteFinder;
_requester = requester;
_responder = responder;
}
public async Task Invoke(HttpContext context)
{
var upstreamUrlPath = context.Request.Path.ToString();
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath);
if (downstreamRoute.IsError)
{
await _responder.CreateNotFoundResponse(context);
return;
}
var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute.Data);
var response = await _requester
.GetResponse(context.Request.Method, downstreamUrl, context.Request.Body,
context.Request.Headers, context.Request.Cookies, context.Request.Query, context.Request.ContentType);
await _responder.CreateResponse(context, response);
await _next.Invoke(context);
}
}
}

View File

@ -51,7 +51,11 @@ namespace Ocelot
loggerFactory.AddDebug(); loggerFactory.AddDebug();
app.UseProxy(); app.UseDownstreamRouteFinderMiddleware();
app.UserDownstreamUrlCreatorMiddleware();
app.UseHttpRequesterMiddleware();
} }
} }
} }

View File

@ -50,7 +50,8 @@ namespace Ocelot.AcceptanceTests
new ReRoute new ReRoute
{ {
DownstreamTemplate = "http://localhost:51879/", DownstreamTemplate = "http://localhost:51879/",
UpstreamTemplate = "/" UpstreamTemplate = "/",
UpstreamHttpMethod = "Get"
} }
} }
})) }))
@ -72,7 +73,8 @@ namespace Ocelot.AcceptanceTests
new ReRoute new ReRoute
{ {
DownstreamTemplate = "http://localhost:51879/", DownstreamTemplate = "http://localhost:51879/",
UpstreamTemplate = "/" UpstreamTemplate = "/",
UpstreamHttpMethod = "Post"
} }
} }
})) }))

View File

@ -1,3 +1,4 @@
ReRoutes: ReRoutes:
- DownstreamTemplate: http://localhost:51879/ - DownstreamTemplate: http://localhost:51879/
UpstreamTemplate: / UpstreamTemplate: /
HttpMethod: Get

View File

@ -21,6 +21,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private Response<DownstreamRoute> _response; private Response<DownstreamRoute> _response;
private Library.Infrastructure.Configuration.Configuration _configuration; private Library.Infrastructure.Configuration.Configuration _configuration;
private UrlMatch _match; private UrlMatch _match;
private string _upstreamHttpMethod;
public DownstreamRouteFinderTests() public DownstreamRouteFinderTests()
{ {
@ -39,11 +40,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
new ReRoute() new ReRoute()
{ {
UpstreamTemplate = "someUpstreamPath", UpstreamTemplate = "someUpstreamPath",
DownstreamTemplate = "someDownstreamPath" DownstreamTemplate = "someDownstreamPath",
UpstreamHttpMethod = "Get"
} }
} }
})) }))
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPath"))) .And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.Then( .Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPath"))) x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
@ -51,6 +54,36 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_return_correct_route_for_http_verb()
{
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(x => x.GivenTheConfigurationIs(new Library.Infrastructure.Configuration.Configuration
{
ReRoutes = new List<ReRoute>
{
new ReRoute()
{
UpstreamTemplate = "someUpstreamPath",
DownstreamTemplate = "someDownstreamPath",
UpstreamHttpMethod = "Get"
},
new ReRoute()
{
UpstreamTemplate = "someUpstreamPath",
DownstreamTemplate = "someDownstreamPathForAPost",
UpstreamHttpMethod = "Post"
}
}
}))
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPathForAPost")))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
.When(x => x.WhenICallTheFinder())
.Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPathForAPost")))
.BDDfy();
}
[Fact] [Fact]
public void should_not_return_route() public void should_not_return_route()
{ {
@ -62,11 +95,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
new ReRoute() new ReRoute()
{ {
UpstreamTemplate = "somePath", UpstreamTemplate = "somePath",
DownstreamTemplate = "somPath" DownstreamTemplate = "somPath",
UpstreamHttpMethod = "Get"
} }
} }
})) }))
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(false, new List<TemplateVariableNameAndValue>(), null))) .And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(false, new List<TemplateVariableNameAndValue>(), null)))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.Then( .Then(
x => x.ThenAnErrorResponseIsReturned()) x => x.ThenAnErrorResponseIsReturned())
@ -74,6 +109,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod)
{
_upstreamHttpMethod = upstreamHttpMethod;
}
private void ThenAnErrorResponseIsReturned() private void ThenAnErrorResponseIsReturned()
{ {
_result.IsError.ShouldBeTrue(); _result.IsError.ShouldBeTrue();
@ -108,7 +148,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void WhenICallTheFinder() private void WhenICallTheFinder()
{ {
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath); _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod);
} }
private void ThenTheFollowingIsReturned(DownstreamRoute expected) private void ThenTheFollowingIsReturned(DownstreamRoute expected)

View File

@ -25,7 +25,8 @@
"Shouldly": "2.8.0", "Shouldly": "2.8.0",
"TestStack.BDDfy": "4.3.1", "TestStack.BDDfy": "4.3.1",
"YamlDotNet": "3.9.0", "YamlDotNet": "3.9.0",
"Moq": "4.6.38-alpha" "Moq": "4.6.38-alpha",
"Microsoft.AspNetCore.TestHost": "1.0.0"
}, },
"frameworks": { "frameworks": {