From ab8407e7dc8459571f5f76349c2428f0cb81c687 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 4 Oct 2016 21:30:16 +0100 Subject: [PATCH] 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 --- .../Infrastructure/Configuration/ReRoute.cs | 1 + .../DownstreamRouteFinder.cs | 8 ++- .../IDownstreamRouteFinder.cs | 2 +- .../DownstreamRouteFinderMiddleware.cs | 40 +++++++++++++ ...wnstreamRouteFinderMiddlewareExtensions.cs | 12 ++++ .../DownstreamUrlCreatorMiddleware.cs | 42 +++++++++++++ ...ownstreamUrlCreatorMiddlewareExtensions.cs | 12 ++++ .../Middleware/HttpRequesterMiddleware.cs | 47 +++++++++++++++ .../HttpRequesterMiddlewareExtensions.cs | 12 ++++ .../Middleware/ProxyExtensions.cs | 12 ---- .../Middleware/ProxyMiddleware.cs | 59 ------------------- src/Ocelot/Startup.cs | 6 +- test/Ocelot.AcceptanceTests/OcelotTests.cs | 6 +- .../Ocelot.AcceptanceTests/configuration.yaml | 1 + .../Requester/RequesterTests.cs | 0 .../DownstreamRouteFinderTests.cs | 46 ++++++++++++++- test/Ocelot.UnitTests/project.json | 3 +- 17 files changed, 227 insertions(+), 82 deletions(-) create mode 100644 src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddleware.cs create mode 100644 src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs create mode 100644 src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddleware.cs create mode 100644 src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs create mode 100644 src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs create mode 100644 src/Ocelot.Library/Middleware/HttpRequesterMiddlewareExtensions.cs delete mode 100644 src/Ocelot.Library/Middleware/ProxyExtensions.cs delete mode 100644 src/Ocelot.Library/Middleware/ProxyMiddleware.cs rename test/Ocelot.UnitTests/{ => Configuration}/Requester/RequesterTests.cs (100%) diff --git a/src/Ocelot.Library/Infrastructure/Configuration/ReRoute.cs b/src/Ocelot.Library/Infrastructure/Configuration/ReRoute.cs index 4c27ec37..12698ed4 100644 --- a/src/Ocelot.Library/Infrastructure/Configuration/ReRoute.cs +++ b/src/Ocelot.Library/Infrastructure/Configuration/ReRoute.cs @@ -4,5 +4,6 @@ { public string DownstreamTemplate { get; set; } public string UpstreamTemplate { get; set; } + public string UpstreamHttpMethod { get; set; } } } diff --git a/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/DownstreamRouteFinder.cs b/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/DownstreamRouteFinder.cs index 0cc2f18e..f02000c3 100644 --- a/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/DownstreamRouteFinder.cs +++ b/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/DownstreamRouteFinder.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.Options; using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.UrlMatcher; @@ -16,9 +18,9 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder _urlMatcher = urlMatcher; } - public Response FindDownstreamRoute(string upstreamUrlPath) + public Response 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); diff --git a/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/IDownstreamRouteFinder.cs b/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/IDownstreamRouteFinder.cs index 1394b3af..4b8f7105 100644 --- a/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/IDownstreamRouteFinder.cs +++ b/src/Ocelot.Library/Infrastructure/DownstreamRouteFinder/IDownstreamRouteFinder.cs @@ -4,6 +4,6 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder { public interface IDownstreamRouteFinder { - Response FindDownstreamRoute(string upstreamUrlPath); + Response FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod); } } diff --git a/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddleware.cs new file mode 100644 index 00000000..a4c4738a --- /dev/null +++ b/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddleware.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs b/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs new file mode 100644 index 00000000..ec454bf4 --- /dev/null +++ b/src/Ocelot.Library/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddleware.cs new file mode 100644 index 00000000..30e41802 --- /dev/null +++ b/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs b/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs new file mode 100644 index 00000000..0ba3e58c --- /dev/null +++ b/src/Ocelot.Library/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs new file mode 100644 index 00000000..d2b4b1e1 --- /dev/null +++ b/src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/HttpRequesterMiddlewareExtensions.cs b/src/Ocelot.Library/Middleware/HttpRequesterMiddlewareExtensions.cs new file mode 100644 index 00000000..8aead7bc --- /dev/null +++ b/src/Ocelot.Library/Middleware/HttpRequesterMiddlewareExtensions.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/ProxyExtensions.cs b/src/Ocelot.Library/Middleware/ProxyExtensions.cs deleted file mode 100644 index 83777448..00000000 --- a/src/Ocelot.Library/Middleware/ProxyExtensions.cs +++ /dev/null @@ -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(); - } - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Middleware/ProxyMiddleware.cs b/src/Ocelot.Library/Middleware/ProxyMiddleware.cs deleted file mode 100644 index 4540b25b..00000000 --- a/src/Ocelot.Library/Middleware/ProxyMiddleware.cs +++ /dev/null @@ -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; - private readonly IDownstreamRouteFinder _downstreamRouteFinder; - private readonly IHttpRequester _requester; - private readonly IHttpResponder _responder; - - public ProxyMiddleware(RequestDelegate next, - IDownstreamUrlTemplateVariableReplacer urlReplacer, - IOptions 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); - } - } -} \ No newline at end of file diff --git a/src/Ocelot/Startup.cs b/src/Ocelot/Startup.cs index 5b1fff8e..1142231f 100644 --- a/src/Ocelot/Startup.cs +++ b/src/Ocelot/Startup.cs @@ -51,7 +51,11 @@ namespace Ocelot loggerFactory.AddDebug(); - app.UseProxy(); + app.UseDownstreamRouteFinderMiddleware(); + + app.UserDownstreamUrlCreatorMiddleware(); + + app.UseHttpRequesterMiddleware(); } } } diff --git a/test/Ocelot.AcceptanceTests/OcelotTests.cs b/test/Ocelot.AcceptanceTests/OcelotTests.cs index 2d2a348b..4d57adf2 100644 --- a/test/Ocelot.AcceptanceTests/OcelotTests.cs +++ b/test/Ocelot.AcceptanceTests/OcelotTests.cs @@ -50,7 +50,8 @@ namespace Ocelot.AcceptanceTests new ReRoute { DownstreamTemplate = "http://localhost:51879/", - UpstreamTemplate = "/" + UpstreamTemplate = "/", + UpstreamHttpMethod = "Get" } } })) @@ -72,7 +73,8 @@ namespace Ocelot.AcceptanceTests new ReRoute { DownstreamTemplate = "http://localhost:51879/", - UpstreamTemplate = "/" + UpstreamTemplate = "/", + UpstreamHttpMethod = "Post" } } })) diff --git a/test/Ocelot.AcceptanceTests/configuration.yaml b/test/Ocelot.AcceptanceTests/configuration.yaml index 97b35e41..8dda884b 100644 --- a/test/Ocelot.AcceptanceTests/configuration.yaml +++ b/test/Ocelot.AcceptanceTests/configuration.yaml @@ -1,3 +1,4 @@ ReRoutes: - DownstreamTemplate: http://localhost:51879/ UpstreamTemplate: / + HttpMethod: Get diff --git a/test/Ocelot.UnitTests/Requester/RequesterTests.cs b/test/Ocelot.UnitTests/Configuration/Requester/RequesterTests.cs similarity index 100% rename from test/Ocelot.UnitTests/Requester/RequesterTests.cs rename to test/Ocelot.UnitTests/Configuration/Requester/RequesterTests.cs diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 6d115a66..a1c23bee 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -21,6 +21,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private Response _response; private Library.Infrastructure.Configuration.Configuration _configuration; private UrlMatch _match; + private string _upstreamHttpMethod; public DownstreamRouteFinderTests() { @@ -39,11 +40,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRoute() { UpstreamTemplate = "someUpstreamPath", - DownstreamTemplate = "someDownstreamPath" + DownstreamTemplate = "someDownstreamPath", + UpstreamHttpMethod = "Get" } } })) .And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List(), "someDownstreamPath"))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), "someDownstreamPath"))) @@ -51,6 +54,36 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .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 + { + 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(), "someDownstreamPathForAPost"))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), "someDownstreamPathForAPost"))) + .BDDfy(); + } + [Fact] public void should_not_return_route() { @@ -62,11 +95,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRoute() { UpstreamTemplate = "somePath", - DownstreamTemplate = "somPath" + DownstreamTemplate = "somPath", + UpstreamHttpMethod = "Get" } } })) .And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(false, new List(), null))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenAnErrorResponseIsReturned()) @@ -74,6 +109,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .BDDfy(); } + private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) + { + _upstreamHttpMethod = upstreamHttpMethod; + } + private void ThenAnErrorResponseIsReturned() { _result.IsError.ShouldBeTrue(); @@ -108,7 +148,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void WhenICallTheFinder() { - _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath); + _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod); } private void ThenTheFollowingIsReturned(DownstreamRoute expected) diff --git a/test/Ocelot.UnitTests/project.json b/test/Ocelot.UnitTests/project.json index 7b0acc6e..6e5a6811 100644 --- a/test/Ocelot.UnitTests/project.json +++ b/test/Ocelot.UnitTests/project.json @@ -25,7 +25,8 @@ "Shouldly": "2.8.0", "TestStack.BDDfy": "4.3.1", "YamlDotNet": "3.9.0", - "Moq": "4.6.38-alpha" + "Moq": "4.6.38-alpha", + "Microsoft.AspNetCore.TestHost": "1.0.0" }, "frameworks": {