messing around with the proxy mdh the proxy middleware

This commit is contained in:
Tom Gardham-Pallister 2016-07-21 20:28:22 +01:00
parent 5e8719cde4
commit dbff2b9530
15 changed files with 213 additions and 35 deletions

View File

@ -2,13 +2,13 @@ namespace Ocelot.Library.Infrastructure.HostUrlRepository
{ {
public class HostUrlMap public class HostUrlMap
{ {
public HostUrlMap(string downstreamHostUrl, string upstreamHostUrl) public HostUrlMap(string urlPathTemplate, string upstreamHostUrl)
{ {
DownstreamHostUrl = downstreamHostUrl; UrlPathTemplate = urlPathTemplate;
UpstreamHostUrl = upstreamHostUrl; UpstreamHostUrl = upstreamHostUrl;
} }
public string DownstreamHostUrl {get;private set;} public string UrlPathTemplate {get;private set;}
public string UpstreamHostUrl {get;private set;} public string UpstreamHostUrl {get;private set;}
} }
} }

View File

@ -5,6 +5,6 @@ namespace Ocelot.Library.Infrastructure.HostUrlRepository
public interface IHostUrlMapRepository public interface IHostUrlMapRepository
{ {
Response AddBaseUrlMap(HostUrlMap baseUrlMap); Response AddBaseUrlMap(HostUrlMap baseUrlMap);
Response<HostUrlMap> GetBaseUrlMap(string downstreamUrl); Response<HostUrlMap> GetBaseUrlMap(string urlPathTemplate);
} }
} }

View File

@ -12,23 +12,23 @@ namespace Ocelot.Library.Infrastructure.HostUrlRepository
} }
public Response AddBaseUrlMap(HostUrlMap baseUrlMap) public Response AddBaseUrlMap(HostUrlMap baseUrlMap)
{ {
if(_routes.ContainsKey(baseUrlMap.DownstreamHostUrl)) if(_routes.ContainsKey(baseUrlMap.UrlPathTemplate))
{ {
return new ErrorResponse(new List<Error>(){new HostUrlMapKeyAlreadyExists()}); return new ErrorResponse(new List<Error>(){new HostUrlMapKeyAlreadyExists()});
} }
_routes.Add(baseUrlMap.DownstreamHostUrl, baseUrlMap.UpstreamHostUrl); _routes.Add(baseUrlMap.UrlPathTemplate, baseUrlMap.UpstreamHostUrl);
return new OkResponse(); return new OkResponse();
} }
public Response<HostUrlMap> GetBaseUrlMap(string downstreamUrl) public Response<HostUrlMap> GetBaseUrlMap(string urlPathTemplate)
{ {
string upstreamUrl = null; string upstreamUrl = null;
if(_routes.TryGetValue(downstreamUrl, out upstreamUrl)) if(_routes.TryGetValue(urlPathTemplate, out upstreamUrl))
{ {
return new OkResponse<HostUrlMap>(new HostUrlMap(downstreamUrl, upstreamUrl)); return new OkResponse<HostUrlMap>(new HostUrlMap(urlPathTemplate, upstreamUrl));
} }
return new ErrorResponse<HostUrlMap>(new List<Error>(){new HostUrlMapKeyDoesNotExist()}); return new ErrorResponse<HostUrlMap>(new List<Error>(){new HostUrlMapKeyDoesNotExist()});

View File

@ -4,12 +4,15 @@ namespace Ocelot.Library.Infrastructure.UrlPathMatcher
{ {
public class UrlPathMatch public class UrlPathMatch
{ {
public UrlPathMatch(bool match, List<TemplateVariableNameAndValue> templateVariableNameAndValues) public UrlPathMatch(bool match, List<TemplateVariableNameAndValue> templateVariableNameAndValues, string urlPathTemplate)
{ {
Match = match; Match = match;
TemplateVariableNameAndValues = templateVariableNameAndValues; TemplateVariableNameAndValues = templateVariableNameAndValues;
UrlPathTemplate = urlPathTemplate;
} }
public bool Match {get;private set;} public bool Match {get;private set;}
public List<TemplateVariableNameAndValue> TemplateVariableNameAndValues {get;private set;} public List<TemplateVariableNameAndValue> TemplateVariableNameAndValues {get;private set;}
public string UrlPathTemplate {get;private set;}
} }
} }

View File

@ -7,6 +7,8 @@ namespace Ocelot.Library.Infrastructure.UrlPathMatcher
{ {
public UrlPathMatch Match(string urlPath, string urlPathTemplate) public UrlPathMatch Match(string urlPath, string urlPathTemplate)
{ {
var urlPathTemplateCopy = urlPathTemplate;
var templateKeysAndValues = new List<TemplateVariableNameAndValue>(); var templateKeysAndValues = new List<TemplateVariableNameAndValue>();
urlPath = urlPath.ToLower(); urlPath = urlPath.ToLower();
@ -37,12 +39,12 @@ namespace Ocelot.Library.Infrastructure.UrlPathMatcher
} }
else else
{ {
return new UrlPathMatch(false, templateKeysAndValues); return new UrlPathMatch(false, templateKeysAndValues, string.Empty);
} }
} }
counterForUrl++; counterForUrl++;
} }
return new UrlPathMatch(true, templateKeysAndValues); return new UrlPathMatch(true, templateKeysAndValues, urlPathTemplateCopy);
} }
private string GetPlaceholderVariableValue(string urlPath, int counterForUrl) private string GetPlaceholderVariableValue(string urlPath, int counterForUrl)

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository
@ -6,5 +7,6 @@ namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository
{ {
Response AddUrlPathTemplateMap(UrlPathTemplateMap urlPathMap); Response AddUrlPathTemplateMap(UrlPathTemplateMap urlPathMap);
Response<UrlPathTemplateMap> GetUrlPathTemplateMap(string downstreamUrlPathTemplate); Response<UrlPathTemplateMap> GetUrlPathTemplateMap(string downstreamUrlPathTemplate);
Response<List<UrlPathTemplateMap>> All { get; }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository
@ -11,6 +12,18 @@ namespace Ocelot.Library.Infrastructure.UrlPathTemplateRepository
{ {
_routes = new Dictionary<string,string>(); _routes = new Dictionary<string,string>();
} }
public Response<List<UrlPathTemplateMap>> All
{
get
{
var routes = _routes
.Select(r => new UrlPathTemplateMap(r.Key, r.Value))
.ToList();
return new OkResponse<List<UrlPathTemplateMap>>(routes);
}
}
public Response AddUrlPathTemplateMap(UrlPathTemplateMap urlPathMap) public Response AddUrlPathTemplateMap(UrlPathTemplateMap urlPathMap)
{ {
if(_routes.ContainsKey(urlPathMap.DownstreamUrlPathTemplate)) if(_routes.ContainsKey(urlPathMap.DownstreamUrlPathTemplate))

View File

@ -1,26 +1,61 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.HostUrlRepository;
using Ocelot.Library.Infrastructure.UrlPathMatcher;
using Ocelot.Library.Infrastructure.UrlPathTemplateRepository;
namespace Ocelot.Library.Middleware namespace Ocelot.Library.Middleware
{ {
public class ProxyMiddleware public class ProxyMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IUrlPathToUrlPathTemplateMatcher _urlMatcher;
public ProxyMiddleware(RequestDelegate next) private readonly IUrlPathTemplateMapRepository _urlPathRepository;
private readonly IHostUrlMapRepository _hostUrlRepository;
public ProxyMiddleware(RequestDelegate next,
IUrlPathToUrlPathTemplateMatcher urlMatcher,
IUrlPathTemplateMapRepository urlPathRepository,
IHostUrlMapRepository hostUrlRepository)
{ {
_next = next; _next = next;
_urlMatcher = urlMatcher;
_urlPathRepository = urlPathRepository;
_hostUrlRepository = hostUrlRepository;
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
//get the downstream host from the request context
//get the upstream host from the host repository var path = context.Request.Path.ToString();
//if no upstream host fail this request
//get the downstream path from the request context var templates = _urlPathRepository.All;
//get the downstream path template from the path template finder
//todo think about variables.. UrlPathMatch urlPathMatch = null;
//add any query string.. string upstreamPathUrl = string.Empty;
foreach (var template in templates.Data)
{
urlPathMatch = _urlMatcher.Match(path, template.DownstreamUrlPathTemplate);
if (urlPathMatch.Match)
{
upstreamPathUrl = template.UpstreamUrlPathTemplate;
break;
}
}
if (!urlPathMatch.Match)
{
throw new Exception("BOOOM TING! no match");
}
var upstreamHostUrl = _hostUrlRepository.GetBaseUrlMap(urlPathMatch.UrlPathTemplate);
//now map the variables from the url path to the upstream url path
await _next.Invoke(context); await _next.Invoke(context);
} }
} }

View File

@ -37,11 +37,11 @@ namespace Ocelot
loggerFactory.AddDebug(); loggerFactory.AddDebug();
app.UseProxy(); app.UseProxy();
//app.Run()
app.Run(async context => // app.Run(async context =>
{ // {
await context.Response.WriteAsync("Hello from Tom"); // await context.Response.WriteAsync("Hello from Tom");
}); // });
} }
} }
} }

View File

@ -0,0 +1,34 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace Ocelot.AcceptanceTests.Fake
{
public class FakeService
{
private Task _handler;
private IWebHost _webHostBuilder;
public void Start(string url)
{
_webHostBuilder = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.UseStartup<FakeStartup>()
.Build();
_handler = Task.Run(() => _webHostBuilder.Run());
}
public void Stop()
{
if(_webHostBuilder != null)
{
_webHostBuilder.Dispose();
_handler.Wait();
}
}
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Ocelot.AcceptanceTests.Fake
{
public class FakeStartup
{
public FakeStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from Laura");
});
}
}
}

View File

@ -4,38 +4,51 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Xunit; using Xunit;
using Ocelot.AcceptanceTests.Fake;
using Shouldly;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
public class RouterTests : IDisposable public class RouterTests : IDisposable
{ {
private FakeService _fakeService;
private readonly TestServer _server; private readonly TestServer _server;
private readonly HttpClient _client; private readonly HttpClient _client;
public RouterTests() public RouterTests()
{ {
// Arrange
_server = new TestServer(new WebHostBuilder() _server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()); .UseStartup<Startup>());
_client = _server.CreateClient(); _client = _server.CreateClient();
_fakeService = new FakeService();
} }
[Fact] [Fact]
public async Task ReturnHelloWorld() public void hello_world()
{ {
var response = _client.GetAsync("/").Result;
response.EnsureSuccessStatusCode();
var responseString = response.Content.ReadAsStringAsync().Result;
responseString.ShouldBe("Hello from Tom");
}
[Fact]
public async Task can_route_request()
{
_fakeService.Start("http://localhost:5001");
// Act // Act
var response = await _client.GetAsync("/"); var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync(); var responseString = await response.Content.ReadAsStringAsync();
responseString.ShouldBe("Hello from Laura");
// Assert
Assert.Equal("Hello from Tom",
responseString);
} }
public void Dispose() public void Dispose()
{ {
_fakeService.Stop();
_client.Dispose(); _client.Dispose();
_server.Dispose(); _server.Dispose();
} }

View File

@ -77,7 +77,7 @@ namespace Ocelot.UnitTests
private void ThenTheRouteIsReturned() private void ThenTheRouteIsReturned()
{ {
_getRouteResponse.Data.DownstreamHostUrl.ShouldBe(_downstreamBaseUrl); _getRouteResponse.Data.UrlPathTemplate.ShouldBe(_downstreamBaseUrl);
_getRouteResponse.Data.UpstreamHostUrl.ShouldBe(_upstreamBaseUrl); _getRouteResponse.Data.UpstreamHostUrl.ShouldBe(_upstreamBaseUrl);
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.UrlPathTemplateRepository; using Ocelot.Library.Infrastructure.UrlPathTemplateRepository;
using Shouldly; using Shouldly;
@ -12,6 +13,7 @@ namespace Ocelot.UnitTests
private IUrlPathTemplateMapRepository _repository; private IUrlPathTemplateMapRepository _repository;
private Response _response; private Response _response;
private Response<UrlPathTemplateMap> _getResponse; private Response<UrlPathTemplateMap> _getResponse;
private Response<List<UrlPathTemplateMap>> _listResponse;
public UrlPathTemplateMapRepositoryTests() public UrlPathTemplateMapRepositoryTests()
{ {
@ -35,6 +37,14 @@ namespace Ocelot.UnitTests
ThenTheUrlPathIsReturned(); ThenTheUrlPathIsReturned();
} }
[Fact]
public void can_get_all_urls()
{
GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath("/api2", "http://www.someapi.com/api2");
WhenIRetrieveTheUrls();
ThenTheUrlsAreReturned();
}
[Fact] [Fact]
public void should_return_error_response_when_url_path_already_used() public void should_return_error_response_when_url_path_already_used()
{ {
@ -75,12 +85,22 @@ namespace Ocelot.UnitTests
_getResponse = _repository.GetUrlPathTemplateMap(_downstreamUrlPath); _getResponse = _repository.GetUrlPathTemplateMap(_downstreamUrlPath);
} }
private void WhenIRetrieveTheUrls()
{
_listResponse = _repository.All;
}
private void ThenTheUrlPathIsReturned() private void ThenTheUrlPathIsReturned()
{ {
_getResponse.Data.DownstreamUrlPathTemplate.ShouldBe(_downstreamUrlPath); _getResponse.Data.DownstreamUrlPathTemplate.ShouldBe(_downstreamUrlPath);
_getResponse.Data.UpstreamUrlPathTemplate.ShouldBe(_upstreamUrlPath); _getResponse.Data.UpstreamUrlPathTemplate.ShouldBe(_upstreamUrlPath);
} }
private void ThenTheUrlsAreReturned()
{
_listResponse.Data.Count.ShouldBeGreaterThan(0);
}
private void GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath(string downstream, string upstreamApiUrl) private void GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath(string downstream, string upstreamApiUrl)
{ {
GivenIHaveAnUpstreamUrlPath(upstreamApiUrl); GivenIHaveAnUpstreamUrlPath(upstreamApiUrl);

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ocelot.Library.Infrastructure.UrlPathMatcher; using Ocelot.Library.Infrastructure.UrlPathMatcher;
@ -25,7 +26,7 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(new List<TemplateVariableNameAndValue>()); ThenTheTemplatesDictionaryIs(new List<TemplateVariableNameAndValue>());
ThenTheUrlPathTemplateIs("api/product/products/");
} }
[Fact] [Fact]
@ -41,6 +42,7 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}/variants/");
} }
[Fact] [Fact]
@ -56,6 +58,8 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}");
} }
[Fact] [Fact]
@ -72,6 +76,8 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}/{categoryId}");
} }
[Fact] [Fact]
@ -88,6 +94,8 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}/categories/{categoryId}");
} }
[Fact] [Fact]
@ -105,6 +113,8 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}/categories/{categoryId}/variant/{variantId}");
} }
[Fact] [Fact]
@ -121,6 +131,8 @@ namespace Ocelot.UnitTests
WhenIMatchThePaths(); WhenIMatchThePaths();
ThenTheResultIsTrue(); ThenTheResultIsTrue();
ThenTheTemplatesDictionaryIs(expectedTemplates); ThenTheTemplatesDictionaryIs(expectedTemplates);
ThenTheUrlPathTemplateIs("api/product/products/{productId}/categories/{categoryId}/variant/");
} }
private void ThenTheTemplatesDictionaryIs(List<TemplateVariableNameAndValue> expectedResults) private void ThenTheTemplatesDictionaryIs(List<TemplateVariableNameAndValue> expectedResults)
@ -133,6 +145,10 @@ namespace Ocelot.UnitTests
} }
} }
private void ThenTheUrlPathTemplateIs(string expectedUrlPathTemplate)
{
_result.UrlPathTemplate.ShouldBe(expectedUrlPathTemplate);
}
private void GivenIHaveADownstreamPath(string downstreamPath) private void GivenIHaveADownstreamPath(string downstreamPath)
{ {
_downstreamPath = downstreamPath; _downstreamPath = downstreamPath;