From eeb8f691c716711e76efb1a81d7e69731c4975a1 Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Mon, 17 Apr 2017 21:25:04 +0100 Subject: [PATCH 1/9] Very hacky proof of concept --- .../Middleware/AuthenticationMiddleware.cs | 176 +++++----- .../Cache/Middleware/OutputCacheMiddleware.cs | 2 +- .../DownstreamUrlCreatorMiddleware.cs | 31 +- src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs | 1 + src/Ocelot/Headers/AddHeadersToRequest.cs | 37 ++- src/Ocelot/Headers/IAddHeadersToRequest.cs | 7 +- .../HttpRequestHeadersBuilderMiddleware.cs | 3 +- .../Middleware/LoadBalancingMiddleware.cs | 9 +- src/Ocelot/Middleware/OcelotMiddleware.cs | 51 +-- .../Middleware/OcelotMiddlewareExtensions.cs | 3 + .../QueryStrings/AddQueriesToRequest.cs | 69 +++- .../QueryStrings/IAddQueriesToRequest.cs | 8 +- .../QueryStringBuilderMiddleware.cs | 3 +- .../Middleware/ClientRateLimitMiddleware.cs | 6 +- .../Request/Builder/HttpRequestCreator.cs | 49 +-- src/Ocelot/Request/Builder/IRequestCreator.cs | 20 +- src/Ocelot/Request/Builder/RequestBuilder.cs | 300 +++++++++--------- src/Ocelot/Request/Mapper.cs | 37 +++ .../DownstreamRequestInitialiserMiddleware.cs | 46 +++ .../HttpRequestBuilderMiddleware.cs | 21 +- .../HttpRequestBuilderMiddlewareExtensions.cs | 5 + .../Middleware/RequestIdMiddleware.cs | 26 +- .../Headers/AddHeadersToRequestTests.cs | 4 +- ...ttpRequestHeadersBuilderMiddlewareTests.cs | 19 +- .../QueryStrings/AddQueriesToRequestTests.cs | 4 +- .../QueryStringBuilderMiddlewareTests.cs | 20 +- .../HttpRequestBuilderMiddlewareTests.cs | 7 +- .../Request/RequestBuilderTests.cs | 7 +- 28 files changed, 615 insertions(+), 356 deletions(-) create mode 100644 src/Ocelot/Request/Mapper.cs create mode 100644 src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index a6eb9cc2..f29161ce 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -1,97 +1,97 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Ocelot.Authentication.Handler.Factory; -using Ocelot.Configuration; -using Ocelot.Errors; -using Ocelot.Infrastructure.Extensions; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Logging; -using Ocelot.Middleware; - -namespace Ocelot.Authentication.Middleware -{ - public class AuthenticationMiddleware : OcelotMiddleware - { - private readonly RequestDelegate _next; - private readonly IApplicationBuilder _app; - private readonly IAuthenticationHandlerFactory _authHandlerFactory; - private readonly IOcelotLogger _logger; - - public AuthenticationMiddleware(RequestDelegate next, - IApplicationBuilder app, - IRequestScopedDataRepository requestScopedDataRepository, - IAuthenticationHandlerFactory authHandlerFactory, - IOcelotLoggerFactory loggerFactory) - : base(requestScopedDataRepository) - { - _next = next; - _authHandlerFactory = authHandlerFactory; - _app = app; - _logger = loggerFactory.CreateLogger(); - } - - public async Task Invoke(HttpContext context) - { - _logger.TraceMiddlewareEntry(); - - if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) - { - _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlwareName} checking if client is authenticated"); - - var authenticationHandler = _authHandlerFactory.Get(_app, DownstreamRoute.ReRoute.AuthenticationOptions); - - if (authenticationHandler.IsError) +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Ocelot.Authentication.Handler.Factory; +using Ocelot.Configuration; +using Ocelot.Errors; +using Ocelot.Infrastructure.Extensions; +using Ocelot.Infrastructure.RequestData; +using Ocelot.Logging; +using Ocelot.Middleware; + +namespace Ocelot.Authentication.Middleware +{ + public class AuthenticationMiddleware : OcelotMiddleware + { + private readonly RequestDelegate _next; + private readonly IApplicationBuilder _app; + private readonly IAuthenticationHandlerFactory _authHandlerFactory; + private readonly IOcelotLogger _logger; + + public AuthenticationMiddleware(RequestDelegate next, + IApplicationBuilder app, + IRequestScopedDataRepository requestScopedDataRepository, + IAuthenticationHandlerFactory authHandlerFactory, + IOcelotLoggerFactory loggerFactory) + : base(requestScopedDataRepository) + { + _next = next; + _authHandlerFactory = authHandlerFactory; + _app = app; + _logger = loggerFactory.CreateLogger(); + } + + public async Task Invoke(HttpContext context) + { + _logger.TraceMiddlewareEntry(); + + if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) + { + _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlwareName} checking if client is authenticated"); + + var authenticationHandler = _authHandlerFactory.Get(_app, DownstreamRoute.ReRoute.AuthenticationOptions); + + if (authenticationHandler.IsError) { - _logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}"); - SetPipelineError(authenticationHandler.Errors); - _logger.TraceMiddlewareCompleted(); - return; + _logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}"); + SetPipelineError(authenticationHandler.Errors); + _logger.TraceMiddlewareCompleted(); + return; } - await authenticationHandler.Data.Handler.Handle(context); - - - if (context.User.Identity.IsAuthenticated) - { - _logger.LogDebug($"Client has been authenticated for {context.Request.Path}"); - - _logger.TraceInvokeNext(); - await _next.Invoke(context); - _logger.TraceInvokeNextCompleted(); - _logger.TraceMiddlewareCompleted(); - } - else - { - var error = new List - { - new UnauthenticatedError( - $"Request for authenticated route {context.Request.Path} by {context.User.Identity.Name} was unauthenticated") - }; - - _logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}"); - SetPipelineError(error); - - _logger.TraceMiddlewareCompleted(); - return; - } - } - else + await authenticationHandler.Data.Handler.Handle(context); + + + if (context.User.Identity.IsAuthenticated) + { + _logger.LogDebug($"Client has been authenticated for {context.Request.Path}"); + + _logger.TraceInvokeNext(); + await _next.Invoke(context); + _logger.TraceInvokeNextCompleted(); + _logger.TraceMiddlewareCompleted(); + } + else + { + var error = new List + { + new UnauthenticatedError( + $"Request for authenticated route {context.Request.Path} by {context.User.Identity.Name} was unauthenticated") + }; + + _logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}"); + SetPipelineError(error); + + _logger.TraceMiddlewareCompleted(); + return; + } + } + else { _logger.LogTrace($"No authentication needed for {context.Request.Path}"); _logger.TraceInvokeNext(); await _next.Invoke(context); _logger.TraceInvokeNextCompleted(); - _logger.TraceMiddlewareCompleted(); - } - } - - private static bool IsAuthenticatedRoute(ReRoute reRoute) - { - return reRoute.IsAuthenticated; - } - } -} - + _logger.TraceMiddlewareCompleted(); + } + } + + private static bool IsAuthenticatedRoute(ReRoute reRoute) + { + return reRoute.IsAuthenticated; + } + } +} + diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index 948a7397..d671f182 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -27,7 +27,7 @@ namespace Ocelot.Cache.Middleware public async Task Invoke(HttpContext context) { - var downstreamUrlKey = DownstreamUrl; + var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString; if (!DownstreamRoute.ReRoute.IsCached) { diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index 631e278a..7cabd305 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -4,6 +4,7 @@ using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; +using System; namespace Ocelot.DownstreamUrlCreator.Middleware { @@ -42,23 +43,31 @@ namespace Ocelot.DownstreamUrlCreator.Middleware return; } - var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; - - var dsHostAndPort = HostAndPort; + //var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; - var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); + //var dsHostAndPort = HostAndPort; - if (dsUrl.IsError) + //var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); + + //if (dsUrl.IsError) + //{ + // _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); + + // SetPipelineError(dsUrl.Errors); + // return; + //} + + var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri) { - _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); + Path = dsPath.Data.Value, + Scheme = DownstreamRoute.ReRoute.DownstreamScheme + }; - SetPipelineError(dsUrl.Errors); - return; - } + DownstreamRequest.RequestUri = uriBuilder.Uri; - _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", dsUrl.Data.Value); + _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri); - SetDownstreamUrlForThisRequest(dsUrl.Data.Value); + //SetDownstreamUrlForThisRequest(dsUrl.Data.Value); _logger.LogDebug("calling next middleware"); diff --git a/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs b/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs index 43a63715..eed7b8d7 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs +++ b/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs @@ -25,6 +25,7 @@ namespace Ocelot.DownstreamUrlCreator return new ErrorResponse(new List { new DownstreamHostNullOrEmptyError() }); } + var builder = new UriBuilder { Host = downstreamHostAndPort.DownstreamHost, diff --git a/src/Ocelot/Headers/AddHeadersToRequest.cs b/src/Ocelot/Headers/AddHeadersToRequest.cs index 97cd3e69..791860b5 100644 --- a/src/Ocelot/Headers/AddHeadersToRequest.cs +++ b/src/Ocelot/Headers/AddHeadersToRequest.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; using Ocelot.Configuration; using Ocelot.Infrastructure.Claims.Parser; using Ocelot.Responses; +using System.Net.Http; namespace Ocelot.Headers { @@ -17,25 +16,49 @@ namespace Ocelot.Headers _claimsParser = claimsParser; } - public Response SetHeadersOnContext(List claimsToThings, HttpContext context) + //public Response SetHeadersOnContext(List claimsToThings, HttpContext context) + //{ + // foreach (var config in claimsToThings) + // { + // var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); + + // if (value.IsError) + // { + // return new ErrorResponse(value.Errors); + // } + + // var exists = context.Request.Headers.FirstOrDefault(x => x.Key == config.ExistingKey); + + // if (!string.IsNullOrEmpty(exists.Key)) + // { + // context.Request.Headers.Remove(exists); + // } + + // context.Request.Headers.Add(config.ExistingKey, new StringValues(value.Data)); + // } + + // return new OkResponse(); + //} + + public Response SetHeadersOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest) { foreach (var config in claimsToThings) { - var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); + var value = _claimsParser.GetValue(claims, config.NewKey, config.Delimiter, config.Index); if (value.IsError) { return new ErrorResponse(value.Errors); } - var exists = context.Request.Headers.FirstOrDefault(x => x.Key == config.ExistingKey); + var exists = downstreamRequest.Headers.FirstOrDefault(x => x.Key == config.ExistingKey); if (!string.IsNullOrEmpty(exists.Key)) { - context.Request.Headers.Remove(exists); + downstreamRequest.Headers.Remove(exists.Key); } - context.Request.Headers.Add(config.ExistingKey, new StringValues(value.Data)); + downstreamRequest.Headers.Add(config.ExistingKey, value.Data); } return new OkResponse(); diff --git a/src/Ocelot/Headers/IAddHeadersToRequest.cs b/src/Ocelot/Headers/IAddHeadersToRequest.cs index 3bf786a4..a819bbf1 100644 --- a/src/Ocelot/Headers/IAddHeadersToRequest.cs +++ b/src/Ocelot/Headers/IAddHeadersToRequest.cs @@ -2,12 +2,15 @@ using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.Responses; +using System.Net.Http; namespace Ocelot.Headers { public interface IAddHeadersToRequest { - Response SetHeadersOnContext(List claimsToThings, - HttpContext context); + //Response SetHeadersOnContext(List claimsToThings, + // HttpContext context); + + Response SetHeadersOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest); } } diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs index a89d2ec2..380fcc7d 100644 --- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs @@ -32,7 +32,8 @@ namespace Ocelot.Headers.Middleware { _logger.LogDebug("this route has instructions to convert claims to headers"); - var response = _addHeadersToRequest.SetHeadersOnContext(DownstreamRoute.ReRoute.ClaimsToHeaders, context); + //var response = _addHeadersToRequest.SetHeadersOnContext(DownstreamRoute.ReRoute.ClaimsToHeaders, context); + var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest); if (response.IsError) { diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index 8e26cbf2..d74759e9 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -44,7 +44,14 @@ namespace Ocelot.LoadBalancer.Middleware return; } - SetHostAndPortForThisRequest(hostAndPort.Data); + //SetHostAndPortForThisRequest(hostAndPort.Data); + var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri); + uriBuilder.Host = hostAndPort.Data.DownstreamHost; + if (hostAndPort.Data.DownstreamPort > 0) + { + uriBuilder.Port = hostAndPort.Data.DownstreamPort; + } + DownstreamRequest.RequestUri = uriBuilder.Uri; _logger.LogDebug("calling next middleware"); diff --git a/src/Ocelot/Middleware/OcelotMiddleware.cs b/src/Ocelot/Middleware/OcelotMiddleware.cs index 2926e543..373f1472 100644 --- a/src/Ocelot/Middleware/OcelotMiddleware.cs +++ b/src/Ocelot/Middleware/OcelotMiddleware.cs @@ -46,14 +46,16 @@ namespace Ocelot.Middleware } } - public string DownstreamUrl - { - get - { - var downstreamUrl = _requestScopedDataRepository.Get("DownstreamUrl"); - return downstreamUrl.Data; - } - } + //public string DownstreamUrl + //{ + // get + // { + // var downstreamUrl = _requestScopedDataRepository.Get("DownstreamUrl"); + // return downstreamUrl.Data; + // } + //} + + public HttpRequestMessage DownstreamRequest => _requestScopedDataRepository.Get("DownstreamRequest").Data; public Request.Request Request { @@ -73,18 +75,23 @@ namespace Ocelot.Middleware } } - public HostAndPort HostAndPort - { - get - { - var hostAndPort = _requestScopedDataRepository.Get("HostAndPort"); - return hostAndPort.Data; - } - } + //public HostAndPort HostAndPort + //{ + // get + // { + // var hostAndPort = _requestScopedDataRepository.Get("HostAndPort"); + // return hostAndPort.Data; + // } + //} - public void SetHostAndPortForThisRequest(HostAndPort hostAndPort) + //public void SetHostAndPortForThisRequest(HostAndPort hostAndPort) + //{ + // _requestScopedDataRepository.Add("HostAndPort", hostAndPort); + //} + + public void SetDownstreamRequest(HttpRequestMessage request) { - _requestScopedDataRepository.Add("HostAndPort", hostAndPort); + _requestScopedDataRepository.Add("DownstreamRequest", request); } public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute) @@ -92,10 +99,10 @@ namespace Ocelot.Middleware _requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute); } - public void SetDownstreamUrlForThisRequest(string downstreamUrl) - { - _requestScopedDataRepository.Add("DownstreamUrl", downstreamUrl); - } + //public void SetDownstreamUrlForThisRequest(string downstreamUrl) + //{ + // _requestScopedDataRepository.Add("DownstreamUrl", downstreamUrl); + //} public void SetUpstreamRequestForThisRequest(Request.Request request) { diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 457c2448..105be8dd 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -53,6 +53,9 @@ namespace Ocelot.Middleware { await CreateAdministrationArea(builder); + // Initialises downstream request + builder.UseDownstreamRequestInitialiser(); + // This is registered to catch any global exceptions that are not handled builder.UseExceptionHandlerMiddleware(); diff --git a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs index 02fcb63d..ecf07715 100644 --- a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs +++ b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs @@ -4,6 +4,9 @@ using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.Infrastructure.Claims.Parser; using Ocelot.Responses; +using System.Security.Claims; +using System.Net.Http; +using System; namespace Ocelot.QueryStrings { @@ -16,13 +19,44 @@ namespace Ocelot.QueryStrings _claimsParser = claimsParser; } - public Response SetQueriesOnContext(List claimsToThings, HttpContext context) + //public Response SetQueriesOnContext(List claimsToThings, HttpContext context) + //{ + // var queryDictionary = ConvertQueryStringToDictionary(context.Request.QueryString); + + // foreach (var config in claimsToThings) + // { + // var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); + + // if (value.IsError) + // { + // return new ErrorResponse(value.Errors); + // } + + // var exists = queryDictionary.FirstOrDefault(x => x.Key == config.ExistingKey); + + // if (!string.IsNullOrEmpty(exists.Key)) + // { + // queryDictionary[exists.Key] = value.Data; + // } + // else + // { + // queryDictionary.Add(config.ExistingKey, value.Data); + // } + // } + + // context.Request.QueryString = ConvertDictionaryToQueryString(queryDictionary); + + // return new OkResponse(); + //} + + public Response SetQueriesOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest) { - var queryDictionary = ConvertQueryStringToDictionary(context); + + var queryDictionary = ConvertQueryStringToDictionary(downstreamRequest.RequestUri.Query); foreach (var config in claimsToThings) { - var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); + var value = _claimsParser.GetValue(claims, config.NewKey, config.Delimiter, config.Index); if (value.IsError) { @@ -41,22 +75,37 @@ namespace Ocelot.QueryStrings } } - context.Request.QueryString = ConvertDictionaryToQueryString(queryDictionary); + var uriBuilder = new UriBuilder(downstreamRequest.RequestUri); + uriBuilder.Query = ConvertDictionaryToQueryString(queryDictionary); + + downstreamRequest.RequestUri = uriBuilder.Uri; return new OkResponse(); } - private Dictionary ConvertQueryStringToDictionary(HttpContext context) + //private Dictionary ConvertQueryStringToDictionary(HttpContext context) + //{ + // return Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(context.Request.QueryString.Value) + // .ToDictionary(q => q.Key, q => q.Value.FirstOrDefault() ?? string.Empty); + //} + + private Dictionary ConvertQueryStringToDictionary(string queryString) { - return Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(context.Request.QueryString.Value) + return Microsoft.AspNetCore.WebUtilities.QueryHelpers + .ParseQuery(queryString) .ToDictionary(q => q.Key, q => q.Value.FirstOrDefault() ?? string.Empty); } - private Microsoft.AspNetCore.Http.QueryString ConvertDictionaryToQueryString(Dictionary queryDictionary) - { - var newQueryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString("", queryDictionary); + //private Microsoft.AspNetCore.Http.QueryString ConvertDictionaryToQueryString(Dictionary queryDictionary) + //{ + // var newQueryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString("", queryDictionary); - return new Microsoft.AspNetCore.Http.QueryString(newQueryString); + // return new Microsoft.AspNetCore.Http.QueryString(newQueryString); + //} + + private string ConvertDictionaryToQueryString(Dictionary queryDictionary) + { + return Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString("", queryDictionary); } } } \ No newline at end of file diff --git a/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs b/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs index 6fa1b8da..606732c6 100644 --- a/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs +++ b/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs @@ -2,12 +2,16 @@ using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.Responses; +using System.Net.Http; +using System.Security.Claims; namespace Ocelot.QueryStrings { public interface IAddQueriesToRequest { - Response SetQueriesOnContext(List claimsToThings, - HttpContext context); + //Response SetQueriesOnContext(List claimsToThings, + // HttpContext context); + + Response SetQueriesOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest); } } diff --git a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs index edeee51c..80db18b4 100644 --- a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs +++ b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs @@ -32,7 +32,8 @@ namespace Ocelot.QueryStrings.Middleware { _logger.LogDebug("this route has instructions to convert claims to queries"); - var response = _addQueriesToRequest.SetQueriesOnContext(DownstreamRoute.ReRoute.ClaimsToQueries, context); + //var response = _addQueriesToRequest.SetQueriesOnContext(DownstreamRoute.ReRoute.ClaimsToQueries, context); + var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest); if (response.IsError) { diff --git a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs index dffc6448..f29dcb82 100644 --- a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs +++ b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs @@ -111,11 +111,7 @@ namespace Ocelot.RateLimit.Middleware public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option) { - if (option.ClientWhitelist.Contains(requestIdentity.ClientId)) - { - return true; - } - return false; + return option.ClientWhitelist.Contains(requestIdentity.ClientId); } public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule) diff --git a/src/Ocelot/Request/Builder/HttpRequestCreator.cs b/src/Ocelot/Request/Builder/HttpRequestCreator.cs index de030f83..b4aa57aa 100644 --- a/src/Ocelot/Request/Builder/HttpRequestCreator.cs +++ b/src/Ocelot/Request/Builder/HttpRequestCreator.cs @@ -4,35 +4,44 @@ using Microsoft.AspNetCore.Http; using Ocelot.Responses; using Ocelot.Configuration; using Ocelot.Requester.QoS; +using System.Net.Http; namespace Ocelot.Request.Builder { public sealed class HttpRequestCreator : IRequestCreator { + //public async Task> Build( + // string httpMethod, + // string downstreamUrl, + // Stream content, + // IHeaderDictionary headers, + // QueryString queryString, + // string contentType, + // RequestId.RequestId requestId, + // bool isQos, + // IQoSProvider qosProvider) + //{ + // var request = await new RequestBuilder() + // .WithHttpMethod(httpMethod) + // .WithDownstreamUrl(downstreamUrl) + // .WithQueryString(queryString) + // .WithContent(content) + // .WithContentType(contentType) + // .WithHeaders(headers) + // .WithRequestId(requestId) + // .WithIsQos(isQos) + // .WithQos(qosProvider) + // .Build(); + + // return new OkResponse(request); + //} + public async Task> Build( - string httpMethod, - string downstreamUrl, - Stream content, - IHeaderDictionary headers, - QueryString queryString, - string contentType, - RequestId.RequestId requestId, + HttpRequestMessage httpRequestMessage, bool isQos, IQoSProvider qosProvider) { - var request = await new RequestBuilder() - .WithHttpMethod(httpMethod) - .WithDownstreamUrl(downstreamUrl) - .WithQueryString(queryString) - .WithContent(content) - .WithContentType(contentType) - .WithHeaders(headers) - .WithRequestId(requestId) - .WithIsQos(isQos) - .WithQos(qosProvider) - .Build(); - - return new OkResponse(request); + return new OkResponse(new Request(httpRequestMessage, isQos, qosProvider)); } } } \ No newline at end of file diff --git a/src/Ocelot/Request/Builder/IRequestCreator.cs b/src/Ocelot/Request/Builder/IRequestCreator.cs index 379f0aac..d4eae34f 100644 --- a/src/Ocelot/Request/Builder/IRequestCreator.cs +++ b/src/Ocelot/Request/Builder/IRequestCreator.cs @@ -3,18 +3,24 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Ocelot.Requester.QoS; using Ocelot.Responses; +using System.Net.Http; namespace Ocelot.Request.Builder { public interface IRequestCreator { - Task> Build(string httpMethod, - string downstreamUrl, - Stream content, - IHeaderDictionary headers, - QueryString queryString, - string contentType, - RequestId.RequestId requestId, + //Task> Build(string httpMethod, + // string downstreamUrl, + // Stream content, + // IHeaderDictionary headers, + // QueryString queryString, + // string contentType, + // RequestId.RequestId requestId, + // bool isQos, + // IQoSProvider qosProvider); + + Task> Build( + HttpRequestMessage httpRequestMessage, bool isQos, IQoSProvider qosProvider); } diff --git a/src/Ocelot/Request/Builder/RequestBuilder.cs b/src/Ocelot/Request/Builder/RequestBuilder.cs index e47eea1f..694cb71e 100644 --- a/src/Ocelot/Request/Builder/RequestBuilder.cs +++ b/src/Ocelot/Request/Builder/RequestBuilder.cs @@ -1,177 +1,177 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; -using Ocelot.Requester.QoS; +//using System; +//using System.Collections.Generic; +//using System.IO; +//using System.Linq; +//using System.Net; +//using System.Net.Http; +//using System.Net.Http.Headers; +//using System.Threading.Tasks; +//using Microsoft.AspNetCore.Http; +//using Microsoft.Extensions.Primitives; +//using Ocelot.Requester.QoS; -namespace Ocelot.Request.Builder -{ - internal sealed class RequestBuilder - { - private HttpMethod _method; - private string _downstreamUrl; - private QueryString _queryString; - private Stream _content; - private string _contentType; - private IHeaderDictionary _headers; - private RequestId.RequestId _requestId; - private readonly string[] _unsupportedHeaders = {"host"}; - private bool _isQos; - private IQoSProvider _qoSProvider; +//namespace Ocelot.Request.Builder +//{ +// internal sealed class RequestBuilder +// { +// private HttpMethod _method; +// private string _downstreamUrl; +// private QueryString _queryString; +// private Stream _content; +// private string _contentType; +// private IHeaderDictionary _headers; +// private RequestId.RequestId _requestId; +// private readonly string[] _unsupportedHeaders = {"host"}; +// private bool _isQos; +// private IQoSProvider _qoSProvider; - public RequestBuilder WithHttpMethod(string httpMethod) - { - _method = new HttpMethod(httpMethod); - return this; - } +// public RequestBuilder WithHttpMethod(string httpMethod) +// { +// _method = new HttpMethod(httpMethod); +// return this; +// } - public RequestBuilder WithDownstreamUrl(string downstreamUrl) - { - _downstreamUrl = downstreamUrl; - return this; - } +// public RequestBuilder WithDownstreamUrl(string downstreamUrl) +// { +// _downstreamUrl = downstreamUrl; +// return this; +// } - public RequestBuilder WithQueryString(QueryString queryString) - { - _queryString = queryString; - return this; - } +// public RequestBuilder WithQueryString(QueryString queryString) +// { +// _queryString = queryString; +// return this; +// } - public RequestBuilder WithContent(Stream content) - { - _content = content; - return this; - } +// public RequestBuilder WithContent(Stream content) +// { +// _content = content; +// return this; +// } - public RequestBuilder WithContentType(string contentType) - { - _contentType = contentType; - return this; - } +// public RequestBuilder WithContentType(string contentType) +// { +// _contentType = contentType; +// return this; +// } - public RequestBuilder WithHeaders(IHeaderDictionary headers) - { - _headers = headers; - return this; - } +// public RequestBuilder WithHeaders(IHeaderDictionary headers) +// { +// _headers = headers; +// return this; +// } - public RequestBuilder WithRequestId(RequestId.RequestId requestId) - { - _requestId = requestId; - return this; - } +// public RequestBuilder WithRequestId(RequestId.RequestId requestId) +// { +// _requestId = requestId; +// return this; +// } - public RequestBuilder WithIsQos(bool isqos) - { - _isQos = isqos; - return this; - } +// public RequestBuilder WithIsQos(bool isqos) +// { +// _isQos = isqos; +// return this; +// } - public RequestBuilder WithQos(IQoSProvider qoSProvider) - { - _qoSProvider = qoSProvider; - return this; - } +// public RequestBuilder WithQos(IQoSProvider qoSProvider) +// { +// _qoSProvider = qoSProvider; +// return this; +// } - public async Task Build() - { - var uri = CreateUri(); +// public async Task Build() +// { +// var uri = CreateUri(); - var httpRequestMessage = new HttpRequestMessage(_method, uri); +// var httpRequestMessage = new HttpRequestMessage(_method, uri); - await AddContentToRequest(httpRequestMessage); +// await AddContentToRequest(httpRequestMessage); - AddContentTypeToRequest(httpRequestMessage); +// AddContentTypeToRequest(httpRequestMessage); - AddHeadersToRequest(httpRequestMessage); +// AddHeadersToRequest(httpRequestMessage); - if (ShouldAddRequestId(_requestId, httpRequestMessage.Headers)) - { - AddRequestIdHeader(_requestId, httpRequestMessage); - } +// if (ShouldAddRequestId(_requestId, httpRequestMessage.Headers)) +// { +// AddRequestIdHeader(_requestId, httpRequestMessage); +// } - return new Request(httpRequestMessage,_isQos, _qoSProvider); - } +// return new Request(httpRequestMessage,_isQos, _qoSProvider); +// } - private Uri CreateUri() - { - var uri = new Uri(string.Format("{0}{1}", _downstreamUrl, _queryString.ToUriComponent())); - return uri; - } +// private Uri CreateUri() +// { +// var uri = new Uri(string.Format("{0}{1}", _downstreamUrl, _queryString.ToUriComponent())); +// return uri; +// } - private async Task AddContentToRequest(HttpRequestMessage httpRequestMessage) - { - if (_content != null) - { - httpRequestMessage.Content = new ByteArrayContent(await ToByteArray(_content)); - } - } +// private async Task AddContentToRequest(HttpRequestMessage httpRequestMessage) +// { +// if (_content != null) +// { +// httpRequestMessage.Content = new ByteArrayContent(await ToByteArray(_content)); +// } +// } - private void AddContentTypeToRequest(HttpRequestMessage httpRequestMessage) - { - if (!string.IsNullOrEmpty(_contentType)) - { - httpRequestMessage.Content.Headers.Remove("Content-Type"); - httpRequestMessage.Content.Headers.TryAddWithoutValidation("Content-Type", _contentType); - } - } +// private void AddContentTypeToRequest(HttpRequestMessage httpRequestMessage) +// { +// if (!string.IsNullOrEmpty(_contentType)) +// { +// httpRequestMessage.Content.Headers.Remove("Content-Type"); +// httpRequestMessage.Content.Headers.TryAddWithoutValidation("Content-Type", _contentType); +// } +// } - private void AddHeadersToRequest(HttpRequestMessage httpRequestMessage) - { - if (_headers != null) - { - _headers.Remove("Content-Type"); +// private void AddHeadersToRequest(HttpRequestMessage httpRequestMessage) +// { +// if (_headers != null) +// { +// _headers.Remove("Content-Type"); - foreach (var header in _headers) - { - //todo get rid of if.. - if (IsSupportedHeader(header)) - { - httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); - } - } - } - } +// foreach (var header in _headers) +// { +// //todo get rid of if.. +// if (IsSupportedHeader(header)) +// { +// httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); +// } +// } +// } +// } - private bool IsSupportedHeader(KeyValuePair header) - { - return !_unsupportedHeaders.Contains(header.Key.ToLower()); - } +// private bool IsSupportedHeader(KeyValuePair header) +// { +// return !_unsupportedHeaders.Contains(header.Key.ToLower()); +// } - private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage) - { - httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); - } +// private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage) +// { +// httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); +// } - private bool RequestIdInHeaders(RequestId.RequestId requestId, HttpRequestHeaders headers) - { - IEnumerable value; - return headers.TryGetValues(requestId.RequestIdKey, out value); - } +// private bool RequestIdInHeaders(RequestId.RequestId requestId, HttpRequestHeaders headers) +// { +// IEnumerable value; +// return headers.TryGetValues(requestId.RequestIdKey, out value); +// } - private bool ShouldAddRequestId(RequestId.RequestId requestId, HttpRequestHeaders headers) - { - return !string.IsNullOrEmpty(requestId?.RequestIdKey) - && !string.IsNullOrEmpty(requestId.RequestIdValue) - && !RequestIdInHeaders(requestId, headers); - } +// private bool ShouldAddRequestId(RequestId.RequestId requestId, HttpRequestHeaders headers) +// { +// return !string.IsNullOrEmpty(requestId?.RequestIdKey) +// && !string.IsNullOrEmpty(requestId.RequestIdValue) +// && !RequestIdInHeaders(requestId, headers); +// } - private async Task ToByteArray(Stream stream) - { - using (stream) - { - using (var memStream = new MemoryStream()) - { - await stream.CopyToAsync(memStream); - return memStream.ToArray(); - } - } - } - } -} +// private async Task ToByteArray(Stream stream) +// { +// using (stream) +// { +// using (var memStream = new MemoryStream()) +// { +// await stream.CopyToAsync(memStream); +// return memStream.ToArray(); +// } +// } +// } +// } +//} diff --git a/src/Ocelot/Request/Mapper.cs b/src/Ocelot/Request/Mapper.cs new file mode 100644 index 00000000..30ad3820 --- /dev/null +++ b/src/Ocelot/Request/Mapper.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ocelot.Request +{ + public class Mapper + { + public async Task Map(Microsoft.AspNetCore.Http.HttpRequest request) + { + var requestMessage = new HttpRequestMessage() + { + Content = new ByteArrayContent(await ToByteArray(request.Body)), + //Headers = request.Headers, + //Method = request.Method, + //Properties = request.P, + //RequestUri = request., + //Version = null + }; + + return requestMessage; + } + + private async Task ToByteArray(Stream stream) + { + using (stream) + { + using (var memStream = new MemoryStream()) + { + await stream.CopyToAsync(memStream); + return memStream.ToArray(); + } + } + } + } +} + diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs new file mode 100644 index 00000000..39fe3b9b --- /dev/null +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Ocelot.Infrastructure.RequestData; +using Ocelot.Logging; +using Ocelot.Middleware; +using Ocelot.Request.Builder; +using Ocelot.Requester.QoS; + +namespace Ocelot.Request.Middleware +{ + public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware + { + private readonly RequestDelegate _next; + private readonly IRequestCreator _requestCreator; + private readonly IOcelotLogger _logger; + private readonly IQosProviderHouse _qosProviderHouse; + + public DownstreamRequestInitialiserMiddleware(RequestDelegate next, + IOcelotLoggerFactory loggerFactory, + IRequestScopedDataRepository requestScopedDataRepository, + IRequestCreator requestCreator, + IQosProviderHouse qosProviderHouse) + :base(requestScopedDataRepository) + { + _next = next; + _requestCreator = requestCreator; + _qosProviderHouse = qosProviderHouse; + _logger = loggerFactory.CreateLogger(); + } + + public async Task Invoke(HttpContext context) + { + _logger.LogDebug("started calling request builder middleware"); + + var mapper = new Mapper(); + + SetDownstreamRequest(await mapper.Map(context.Request)); + + _logger.LogDebug("calling next middleware"); + + await _next.Invoke(context); + + _logger.LogDebug("succesfully called next middleware"); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs index 0701e089..fa92c8e1 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs @@ -43,14 +43,19 @@ namespace Ocelot.Request.Middleware return; } - var buildResult = await _requestCreator - .Build(context.Request.Method, - DownstreamUrl, - context.Request.Body, - context.Request.Headers, - context.Request.QueryString, - context.Request.ContentType, - new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier), + //var buildResult = await _requestCreator + // .Build(context.Request.Method, + // DownstreamUrl, + // context.Request.Body, + // context.Request.Headers, + // context.Request.QueryString, + // context.Request.ContentType, + // new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier), + // DownstreamRoute.ReRoute.IsQos, + // qosProvider.Data); + + var buildResult = await _requestCreator.Build( + DownstreamRequest, DownstreamRoute.ReRoute.IsQos, qosProvider.Data); diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs index 4c08afec..20bb9164 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs @@ -8,5 +8,10 @@ namespace Ocelot.Request.Middleware { return builder.UseMiddleware(); } + + public static IApplicationBuilder UseDownstreamRequestInitialiser(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } } } \ No newline at end of file diff --git a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs index 1e1a955c..168c377a 100644 --- a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs +++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs @@ -5,6 +5,9 @@ using Microsoft.Extensions.Primitives; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Collections.Generic; namespace Ocelot.RequestId.Middleware { @@ -30,8 +33,6 @@ namespace Ocelot.RequestId.Middleware SetOcelotRequestId(context); - _logger.LogDebug("set requestId"); - _logger.TraceInvokeNext(); await _next.Invoke(context); _logger.TraceInvokeNextCompleted(); @@ -46,15 +47,28 @@ namespace Ocelot.RequestId.Middleware { key = DownstreamRoute.ReRoute.RequestIdKey; } + + StringValues requestIds; - StringValues requestId; - - if (context.Request.Headers.TryGetValue(key, out requestId)) + if (context.Request.Headers.TryGetValue(key, out requestIds)) { - _requestScopedDataRepository.Add("RequestId", requestId.First()); + var requestId = requestIds.First(); + var downstreamRequestHeaders = DownstreamRequest.Headers; + + if (!string.IsNullOrEmpty(requestId) && + !HeaderExists(key, downstreamRequestHeaders)) + { + downstreamRequestHeaders.Add(key, requestId); + } context.TraceIdentifier = requestId; } } + + private bool HeaderExists(string headerKey, HttpRequestHeaders headers) + { + IEnumerable value; + return headers.TryGetValues(headerKey, out value); + } } } \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs index 47951859..950dc72d 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs @@ -129,7 +129,9 @@ namespace Ocelot.UnitTests.Headers private void WhenIAddHeadersToTheRequest() { - _result = _addHeadersToRequest.SetHeadersOnContext(_configuration, _context); + //_result = _addHeadersToRequest.SetHeadersOnContext(_configuration, _context); + //TODO: pass in DownstreamRequest + _result = _addHeadersToRequest.SetHeadersOnDownstreamRequest(_configuration, _context.User.Claims, null); } private void ThenTheResultIsSuccess() diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index 032a76e9..aff337fc 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -84,17 +84,28 @@ namespace Ocelot.UnitTests.Headers private void GivenTheAddHeadersToRequestReturns() { + //_addHeaders + // .Setup(x => x.SetHeadersOnContext(It.IsAny>(), + // It.IsAny())) + // .Returns(new OkResponse()); _addHeaders - .Setup(x => x.SetHeadersOnContext(It.IsAny>(), - It.IsAny())) + .Setup(x => x.SetHeadersOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + It.IsAny())) .Returns(new OkResponse()); } private void ThenTheAddHeadersToRequestIsCalledCorrectly() { + //_addHeaders + // .Verify(x => x.SetHeadersOnContext(It.IsAny>(), + // It.IsAny()), Times.Once); _addHeaders - .Verify(x => x.SetHeadersOnContext(It.IsAny>(), - It.IsAny()), Times.Once); + .Verify(x => x.SetHeadersOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + It.IsAny()), Times.Once); } private void WhenICallTheMiddleware() diff --git a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs index 99cf68b8..cf713017 100644 --- a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs @@ -128,7 +128,9 @@ namespace Ocelot.UnitTests.QueryStrings private void WhenIAddQueriesToTheRequest() { - _result = _addQueriesToRequest.SetQueriesOnContext(_configuration, _context); + //_result = _addQueriesToRequest.SetQueriesOnContext(_configuration, _context); + //TODO: set downstreamRequest + _result = _addQueriesToRequest.SetQueriesOnDownstreamRequest(_configuration, _context.User.Claims, null); } private void ThenTheResultIsSuccess() diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index f381ff1b..3b701554 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -20,6 +20,7 @@ using Ocelot.QueryStrings.Middleware; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; +using System.Security.Claims; namespace Ocelot.UnitTests.QueryStrings { @@ -82,17 +83,28 @@ namespace Ocelot.UnitTests.QueryStrings private void GivenTheAddHeadersToRequestReturns() { + //_addQueries + // .Setup(x => x.SetQueriesOnContext(It.IsAny>(), + // It.IsAny())) + // .Returns(new OkResponse()); _addQueries - .Setup(x => x.SetQueriesOnContext(It.IsAny>(), - It.IsAny())) + .Setup(x => x.SetQueriesOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + It.IsAny())) .Returns(new OkResponse()); } private void ThenTheAddQueriesToRequestIsCalledCorrectly() { + //_addQueries + // .Verify(x => x.SetQueriesOnContext(It.IsAny>(), + // It.IsAny()), Times.Once); _addQueries - .Verify(x => x.SetQueriesOnContext(It.IsAny>(), - It.IsAny()), Times.Once); + .Verify(x => x.SetQueriesOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + It.IsAny()), Times.Once); } private void WhenICallTheMiddleware() diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 002e4e02..4bae0151 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -103,9 +103,12 @@ namespace Ocelot.UnitTests.Request private void GivenTheRequestBuilderReturns(Ocelot.Request.Request request) { _request = new OkResponse(request); + //_requestBuilder + // .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + // It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(), It.IsAny())) + // .ReturnsAsync(_request); _requestBuilder - .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(), It.IsAny())) + .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(_request); } diff --git a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs b/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs index 667c80b0..c182e47b 100644 --- a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs +++ b/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs @@ -287,8 +287,11 @@ namespace Ocelot.UnitTests.Request private void WhenICreateARequest() { - _result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers, - _query, _contentType, _requestId,_isQos,_qoSProvider).Result; + //_result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers, + // _query, _contentType, _requestId,_isQos,_qoSProvider).Result; + + //todo: add httprequestmessage + _result = _requestCreator.Build(null, _isQos, _qoSProvider).Result; } From da922c0e04dde934cafb0d0f7b6c9e5329e7012a Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Tue, 18 Apr 2017 10:13:10 +0100 Subject: [PATCH 2/9] Initial mapping of request --- src/Ocelot/Middleware/OcelotMiddleware.cs | 86 ++---------- .../Middleware/OcelotMiddlewareExtensions.cs | 2 +- src/Ocelot/Request/Builder/RequestBuilder.cs | 128 ++++++++++-------- src/Ocelot/Request/Mapper.cs | 61 ++++++++- 4 files changed, 137 insertions(+), 140 deletions(-) diff --git a/src/Ocelot/Middleware/OcelotMiddleware.cs b/src/Ocelot/Middleware/OcelotMiddleware.cs index 373f1472..af56891e 100644 --- a/src/Ocelot/Middleware/OcelotMiddleware.cs +++ b/src/Ocelot/Middleware/OcelotMiddleware.cs @@ -3,7 +3,6 @@ using System.Net.Http; using Ocelot.DownstreamRouteFinder; using Ocelot.Errors; using Ocelot.Infrastructure.RequestData; -using Ocelot.Values; namespace Ocelot.Middleware { @@ -17,98 +16,35 @@ namespace Ocelot.Middleware MiddlwareName = this.GetType().Name; } - public string MiddlwareName { get; } + public string MiddlwareName { get; } - public bool PipelineError - { - get - { - var response = _requestScopedDataRepository.Get("OcelotMiddlewareError"); - return response.Data; - } - } + public bool PipelineError => _requestScopedDataRepository.Get("OcelotMiddlewareError").Data; - public List PipelineErrors - { - get - { - var response = _requestScopedDataRepository.Get>("OcelotMiddlewareErrors"); - return response.Data; - } - } + public List PipelineErrors => _requestScopedDataRepository.Get>("OcelotMiddlewareErrors").Data; - public DownstreamRoute DownstreamRoute - { - get - { - var downstreamRoute = _requestScopedDataRepository.Get("DownstreamRoute"); - return downstreamRoute.Data; - } - } + public DownstreamRoute DownstreamRoute => _requestScopedDataRepository.Get("DownstreamRoute").Data; - //public string DownstreamUrl - //{ - // get - // { - // var downstreamUrl = _requestScopedDataRepository.Get("DownstreamUrl"); - // return downstreamUrl.Data; - // } - //} + public Request.Request Request => _requestScopedDataRepository.Get("Request").Data; public HttpRequestMessage DownstreamRequest => _requestScopedDataRepository.Get("DownstreamRequest").Data; - public Request.Request Request - { - get - { - var request = _requestScopedDataRepository.Get("Request"); - return request.Data; - } - } - - public HttpResponseMessage HttpResponseMessage - { - get - { - var request = _requestScopedDataRepository.Get("HttpResponseMessage"); - return request.Data; - } - } - - //public HostAndPort HostAndPort - //{ - // get - // { - // var hostAndPort = _requestScopedDataRepository.Get("HostAndPort"); - // return hostAndPort.Data; - // } - //} - - //public void SetHostAndPortForThisRequest(HostAndPort hostAndPort) - //{ - // _requestScopedDataRepository.Add("HostAndPort", hostAndPort); - //} - - public void SetDownstreamRequest(HttpRequestMessage request) - { - _requestScopedDataRepository.Add("DownstreamRequest", request); - } + public HttpResponseMessage HttpResponseMessage => _requestScopedDataRepository.Get("HttpResponseMessage").Data; public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute) { _requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute); } - //public void SetDownstreamUrlForThisRequest(string downstreamUrl) - //{ - // _requestScopedDataRepository.Add("DownstreamUrl", downstreamUrl); - //} - public void SetUpstreamRequestForThisRequest(Request.Request request) { _requestScopedDataRepository.Add("Request", request); } + public void SetDownstreamRequest(HttpRequestMessage request) + { + _requestScopedDataRepository.Add("DownstreamRequest", request); + } + public void SetHttpResponseMessageThisRequest(HttpResponseMessage responseMessage) { _requestScopedDataRepository.Add("HttpResponseMessage", responseMessage); diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 105be8dd..e35c2d11 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -72,7 +72,7 @@ namespace Ocelot.Middleware builder.UseRateLimiting(); // Now we can look for the requestId - builder.UseRequestIdMiddleware(); + builder.UseRequestIdMiddleware(); //obsolete? // Allow pre authentication logic. The idea being people might want to run something custom before what is built in. builder.UseIfNotNull(middlewareConfiguration.PreAuthenticationMiddleware); diff --git a/src/Ocelot/Request/Builder/RequestBuilder.cs b/src/Ocelot/Request/Builder/RequestBuilder.cs index 694cb71e..a76391df 100644 --- a/src/Ocelot/Request/Builder/RequestBuilder.cs +++ b/src/Ocelot/Request/Builder/RequestBuilder.cs @@ -25,12 +25,16 @@ // private bool _isQos; // private IQoSProvider _qoSProvider; +// ---- sets Method + // public RequestBuilder WithHttpMethod(string httpMethod) // { // _method = new HttpMethod(httpMethod); // return this; // } +// ---- sets RequestUri + // public RequestBuilder WithDownstreamUrl(string downstreamUrl) // { // _downstreamUrl = downstreamUrl; @@ -43,6 +47,14 @@ // return this; // } +// private Uri CreateUri() +// { +// var uri = new Uri(string.Format("{0}{1}", _downstreamUrl, _queryString.ToUriComponent())); +// return uri; +// } + +// ---- Content and ContentType + // public RequestBuilder WithContent(Stream content) // { // _content = content; @@ -55,56 +67,6 @@ // return this; // } -// public RequestBuilder WithHeaders(IHeaderDictionary headers) -// { -// _headers = headers; -// return this; -// } - -// public RequestBuilder WithRequestId(RequestId.RequestId requestId) -// { -// _requestId = requestId; -// return this; -// } - -// public RequestBuilder WithIsQos(bool isqos) -// { -// _isQos = isqos; -// return this; -// } - -// public RequestBuilder WithQos(IQoSProvider qoSProvider) -// { -// _qoSProvider = qoSProvider; -// return this; -// } - -// public async Task Build() -// { -// var uri = CreateUri(); - -// var httpRequestMessage = new HttpRequestMessage(_method, uri); - -// await AddContentToRequest(httpRequestMessage); - -// AddContentTypeToRequest(httpRequestMessage); - -// AddHeadersToRequest(httpRequestMessage); - -// if (ShouldAddRequestId(_requestId, httpRequestMessage.Headers)) -// { -// AddRequestIdHeader(_requestId, httpRequestMessage); -// } - -// return new Request(httpRequestMessage,_isQos, _qoSProvider); -// } - -// private Uri CreateUri() -// { -// var uri = new Uri(string.Format("{0}{1}", _downstreamUrl, _queryString.ToUriComponent())); -// return uri; -// } - // private async Task AddContentToRequest(HttpRequestMessage httpRequestMessage) // { // if (_content != null) @@ -113,6 +75,18 @@ // } // } +// private async Task ToByteArray(Stream stream) +// { +// using (stream) +// { +// using (var memStream = new MemoryStream()) +// { +// await stream.CopyToAsync(memStream); +// return memStream.ToArray(); +// } +// } +// } + // private void AddContentTypeToRequest(HttpRequestMessage httpRequestMessage) // { // if (!string.IsNullOrEmpty(_contentType)) @@ -122,6 +96,14 @@ // } // } +// ---- Headers + +// public RequestBuilder WithHeaders(IHeaderDictionary headers) +// { +// _headers = headers; +// return this; +// } + // private void AddHeadersToRequest(HttpRequestMessage httpRequestMessage) // { // if (_headers != null) @@ -144,6 +126,14 @@ // return !_unsupportedHeaders.Contains(header.Key.ToLower()); // } +// ---- Request ID + +// public RequestBuilder WithRequestId(RequestId.RequestId requestId) +// { +// _requestId = requestId; +// return this; +// } + // private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage) // { // httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); @@ -162,16 +152,40 @@ // && !RequestIdInHeaders(requestId, headers); // } -// private async Task ToByteArray(Stream stream) +// ---- QoS + +// public RequestBuilder WithIsQos(bool isqos) // { -// using (stream) +// _isQos = isqos; +// return this; +// } + +// public RequestBuilder WithQos(IQoSProvider qoSProvider) +// { +// _qoSProvider = qoSProvider; +// return this; +// } + +// ---- + +// public async Task Build() +// { +// var uri = CreateUri(); + +// var httpRequestMessage = new HttpRequestMessage(_method, uri); + +// await AddContentToRequest(httpRequestMessage); + +// AddContentTypeToRequest(httpRequestMessage); + +// AddHeadersToRequest(httpRequestMessage); + +// if (ShouldAddRequestId(_requestId, httpRequestMessage.Headers)) // { -// using (var memStream = new MemoryStream()) -// { -// await stream.CopyToAsync(memStream); -// return memStream.ToArray(); -// } +// AddRequestIdHeader(_requestId, httpRequestMessage); // } + +// return new Request(httpRequestMessage,_isQos, _qoSProvider); // } // } //} diff --git a/src/Ocelot/Request/Mapper.cs b/src/Ocelot/Request/Mapper.cs index 30ad3820..c0cc3bba 100644 --- a/src/Ocelot/Request/Mapper.cs +++ b/src/Ocelot/Request/Mapper.cs @@ -1,4 +1,10 @@ -using System.IO; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -6,21 +12,57 @@ namespace Ocelot.Request { public class Mapper { - public async Task Map(Microsoft.AspNetCore.Http.HttpRequest request) + private readonly string[] _unsupportedHeaders = { "host" }; + + public async Task Map(HttpRequest request) { var requestMessage = new HttpRequestMessage() { - Content = new ByteArrayContent(await ToByteArray(request.Body)), - //Headers = request.Headers, - //Method = request.Method, - //Properties = request.P, - //RequestUri = request., + Content = await MapContent(request), + Method = MapMethod(request), + RequestUri = MapUri(request), + //Properties = null //Version = null }; + MapHeaders(request, requestMessage); + return requestMessage; } + private async Task MapContent(HttpRequest request) + { + if (request.Body == null) + { + return null; + } + + + return new ByteArrayContent(await ToByteArray(request.Body)); + } + + private HttpMethod MapMethod(HttpRequest request) + { + return new HttpMethod(request.Method); + } + + private Uri MapUri(HttpRequest request) + { + return new Uri(request.GetEncodedUrl()); + } + + private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) + { + foreach (var header in request.Headers) + { + //todo get rid of if.. + if (IsSupportedHeader(header)) + { + requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } + } + private async Task ToByteArray(Stream stream) { using (stream) @@ -32,6 +74,11 @@ namespace Ocelot.Request } } } + + private bool IsSupportedHeader(KeyValuePair header) + { + return !_unsupportedHeaders.Contains(header.Key.ToLower()); + } } } From 8b93f4407720551ae0b29d3db17e16fd238e3357 Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Tue, 18 Apr 2017 11:05:37 +0100 Subject: [PATCH 3/9] Fix request id middleware --- .../Middleware/OcelotMiddlewareExtensions.cs | 2 +- .../Middleware/RequestIdMiddleware.cs | 44 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index e35c2d11..105be8dd 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -72,7 +72,7 @@ namespace Ocelot.Middleware builder.UseRateLimiting(); // Now we can look for the requestId - builder.UseRequestIdMiddleware(); //obsolete? + builder.UseRequestIdMiddleware(); // Allow pre authentication logic. The idea being people might want to run something custom before what is built in. builder.UseIfNotNull(middlewareConfiguration.PreAuthenticationMiddleware); diff --git a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs index 168c377a..08222d9d 100644 --- a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs +++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs @@ -41,34 +41,40 @@ namespace Ocelot.RequestId.Middleware private void SetOcelotRequestId(HttpContext context) { - var key = DefaultRequestIdKey.Value; - - if (DownstreamRoute.ReRoute.RequestIdKey != null) - { - key = DownstreamRoute.ReRoute.RequestIdKey; - } + // if get request ID is set on upstream request then retrieve it + var key = DownstreamRoute.ReRoute.RequestIdKey ?? DefaultRequestIdKey.Value; - StringValues requestIds; - - if (context.Request.Headers.TryGetValue(key, out requestIds)) + StringValues upstreamRequestIds; + if (context.Request.Headers.TryGetValue(key, out upstreamRequestIds)) { - var requestId = requestIds.First(); - var downstreamRequestHeaders = DownstreamRequest.Headers; + context.TraceIdentifier = upstreamRequestIds.First(); + } - if (!string.IsNullOrEmpty(requestId) && - !HeaderExists(key, downstreamRequestHeaders)) - { - downstreamRequestHeaders.Add(key, requestId); - } + // set request ID on downstream request, if required + var requestId = new RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier); - context.TraceIdentifier = requestId; + if (ShouldAddRequestId(requestId, DownstreamRequest.Headers)) + { + AddRequestIdHeader(requestId, DownstreamRequest); } } - private bool HeaderExists(string headerKey, HttpRequestHeaders headers) + private bool ShouldAddRequestId(RequestId requestId, HttpRequestHeaders headers) + { + return !string.IsNullOrEmpty(requestId?.RequestIdKey) + && !string.IsNullOrEmpty(requestId.RequestIdValue) + && !RequestIdInHeaders(requestId, headers); + } + + private bool RequestIdInHeaders(RequestId requestId, HttpRequestHeaders headers) { IEnumerable value; - return headers.TryGetValues(headerKey, out value); + return headers.TryGetValues(requestId.RequestIdKey, out value); + } + + private void AddRequestIdHeader(RequestId requestId, HttpRequestMessage httpRequestMessage) + { + httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); } } } \ No newline at end of file From 7c1a2771477da2e4a7f8afd9ad40cbaaa37fc85e Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Tue, 18 Apr 2017 14:05:15 +0100 Subject: [PATCH 4/9] Fixed some unit tests --- .../Cache/Middleware/OutputCacheMiddleware.cs | 4 +- .../QueryStrings/AddQueriesToRequest.cs | 1 - .../Request/Builder/HttpRequestCreator.cs | 31 +- .../Cache/OutputCacheMiddlewareTests.cs | 7 +- .../DownstreamUrlCreatorMiddlewareTests.cs | 7 +- .../LoadBalancerMiddlewareTests.cs | 36 +- .../QueryStrings/AddQueriesToRequestTests.cs | 52 +-- .../QueryStringBuilderMiddlewareTests.cs | 22 +- .../HttpRequestBuilderMiddlewareTests.cs | 4 - .../Request/HttpRequestCreatorTests.cs | 56 ++++ .../Request/RequestBuilderTests.cs | 313 ------------------ .../RequestId/RequestIdMiddlewareTests.cs | 37 ++- 12 files changed, 145 insertions(+), 425 deletions(-) create mode 100644 test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs delete mode 100644 test/Ocelot.UnitTests/Request/RequestBuilderTests.cs diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index d671f182..85a73e70 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -27,14 +27,14 @@ namespace Ocelot.Cache.Middleware public async Task Invoke(HttpContext context) { - var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString; - if (!DownstreamRoute.ReRoute.IsCached) { await _next.Invoke(context); return; } + var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString; + _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey); var cached = _outputCache.Get(downstreamUrlKey); diff --git a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs index ecf07715..0c099271 100644 --- a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs +++ b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs @@ -51,7 +51,6 @@ namespace Ocelot.QueryStrings public Response SetQueriesOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest) { - var queryDictionary = ConvertQueryStringToDictionary(downstreamRequest.RequestUri.Query); foreach (var config in claimsToThings) diff --git a/src/Ocelot/Request/Builder/HttpRequestCreator.cs b/src/Ocelot/Request/Builder/HttpRequestCreator.cs index b4aa57aa..f95db4b9 100644 --- a/src/Ocelot/Request/Builder/HttpRequestCreator.cs +++ b/src/Ocelot/Request/Builder/HttpRequestCreator.cs @@ -1,8 +1,5 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using System.Threading.Tasks; using Ocelot.Responses; -using Ocelot.Configuration; using Ocelot.Requester.QoS; using System.Net.Http; @@ -10,32 +7,6 @@ namespace Ocelot.Request.Builder { public sealed class HttpRequestCreator : IRequestCreator { - //public async Task> Build( - // string httpMethod, - // string downstreamUrl, - // Stream content, - // IHeaderDictionary headers, - // QueryString queryString, - // string contentType, - // RequestId.RequestId requestId, - // bool isQos, - // IQoSProvider qosProvider) - //{ - // var request = await new RequestBuilder() - // .WithHttpMethod(httpMethod) - // .WithDownstreamUrl(downstreamUrl) - // .WithQueryString(queryString) - // .WithContent(content) - // .WithContentType(contentType) - // .WithHeaders(headers) - // .WithRequestId(requestId) - // .WithIsQos(isQos) - // .WithQos(qosProvider) - // .Build(); - - // return new OkResponse(request); - //} - public async Task> Build( HttpRequestMessage httpRequestMessage, bool isQos, diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 66532a18..6537c684 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Net.Http; -using CacheManager.Core; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; using Ocelot.Cache; using Ocelot.Cache.Middleware; @@ -37,7 +35,6 @@ namespace Ocelot.UnitTests.Cache _cacheManager = new Mock>(); _scopedRepo = new Mock(); - _url = "http://localhost:51879"; var builder = new WebHostBuilder() .ConfigureServices(x => @@ -57,6 +54,10 @@ namespace Ocelot.UnitTests.Cache app.UseOutputCacheMiddleware(); }); + _scopedRepo + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); + _server = new TestServer(builder); _client = _server.CreateClient(); } diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 438710f6..d511c4c1 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -5,12 +5,9 @@ using System.Net.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; -using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; -using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamUrlCreator; using Ocelot.DownstreamUrlCreator.Middleware; @@ -63,6 +60,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator app.UseDownstreamUrlCreatorMiddleware(); }); + _scopedRepository + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"))); + _server = new TestServer(builder); _client = _server.CreateClient(); } diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index e76f3c2f..c3607e82 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -16,6 +16,7 @@ using Ocelot.Responses; using Ocelot.Values; using TestStack.BDDfy; using Xunit; +using Shouldly; namespace Ocelot.UnitTests.LoadBalancer { @@ -33,6 +34,7 @@ namespace Ocelot.UnitTests.LoadBalancer private OkResponse _downstreamRoute; private ErrorResponse _getLoadBalancerHouseError; private ErrorResponse _getHostAndPortError; + private HttpRequestMessage _downstreamRequest; public LoadBalancerMiddlewareTests() { @@ -59,6 +61,10 @@ namespace Ocelot.UnitTests.LoadBalancer app.UseLoadBalancingMiddleware(); }); + _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, ""); + _scopedRepository + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(_downstreamRequest)); _server = new TestServer(builder); _client = _server.CreateClient(); } @@ -71,12 +77,12 @@ namespace Ocelot.UnitTests.LoadBalancer .WithUpstreamHttpMethod("Get") .Build()); - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) + this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturns()) .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) + .Then(x => x.ThenTheDownstreamUrlIsReplacedWith("http://127.0.0.1:80/abc?q=123")) .BDDfy(); } @@ -88,7 +94,7 @@ namespace Ocelot.UnitTests.LoadBalancer .WithUpstreamHttpMethod("Get") .Build()); - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) + this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturnsAnError()) .When(x => x.WhenICallTheMiddleware()) @@ -104,7 +110,7 @@ namespace Ocelot.UnitTests.LoadBalancer .WithUpstreamHttpMethod("Get") .Build()); - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) + this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturnsAnError()) @@ -113,6 +119,11 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } + private void GivenTheDownStreamUrlIs(string downstreamUrl) + { + _downstreamRequest.RequestUri = new System.Uri(downstreamUrl); + } + private void GivenTheLoadBalancerReturnsAnError() { _getHostAndPortError = new ErrorResponse(new List() { new ServicesAreNullError($"services were null for bah") }); @@ -157,10 +168,9 @@ namespace Ocelot.UnitTests.LoadBalancer .Returns(_getLoadBalancerHouseError); } - private void ThenTheScopedDataRepositoryIsCalledCorrectly() + private void WhenICallTheMiddleware() { - _scopedRepository - .Verify(x => x.Add("HostAndPort", _hostAndPort), Times.Once()); + _result = _client.GetAsync(_url).Result; } private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline() @@ -190,17 +200,11 @@ namespace Ocelot.UnitTests.LoadBalancer .Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once); } - private void WhenICallTheMiddleware() - { - _result = _client.GetAsync(_url).Result; - } - private void GivenTheDownStreamUrlIs(string downstreamUrl) + + private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) { - _downstreamUrl = new OkResponse(downstreamUrl); - _scopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamUrl); + _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); } public void Dispose() diff --git a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs index cf713017..b65ce531 100644 --- a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs @@ -11,15 +11,18 @@ using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; +using System.Net.Http; +using System; namespace Ocelot.UnitTests.QueryStrings { public class AddQueriesToRequestTests { private readonly AddQueriesToRequest _addQueriesToRequest; + private readonly HttpRequestMessage _downstreamRequest; private readonly Mock _parser; private List _configuration; - private HttpContext _context; + private List _claims; private Response _result; private Response _claimValue; @@ -27,17 +30,15 @@ namespace Ocelot.UnitTests.QueryStrings { _parser = new Mock(); _addQueriesToRequest = new AddQueriesToRequest(_parser.Object); + _downstreamRequest = new HttpRequestMessage(HttpMethod.Post, "http://my.url/abc?q=123"); } [Fact] - public void should_add_queries_to_context() + public void should_add_new_queries_to_downstream_request() { - var context = new DefaultHttpContext + var claims = new List { - User = new ClaimsPrincipal(new ClaimsIdentity(new List - { - new Claim("test", "data") - })) + new Claim("test", "data") }; this.Given( @@ -45,7 +46,7 @@ namespace Ocelot.UnitTests.QueryStrings { new ClaimToThing("query-key", "", "", 0) })) - .Given(x => x.GivenHttpContext(context)) + .Given(x => x.GivenClaims(claims)) .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) .When(x => x.WhenIAddQueriesToTheRequest()) .Then(x => x.ThenTheResultIsSuccess()) @@ -54,24 +55,20 @@ namespace Ocelot.UnitTests.QueryStrings } [Fact] - public void if_query_exists_should_replace_it() + public void should_replace_existing_queries_on_downstream_request() { - var context = new DefaultHttpContext + var claims = new List { - User = new ClaimsPrincipal(new ClaimsIdentity(new List - { - new Claim("test", "data") - })), + new Claim("test", "data") }; - context.Request.QueryString = context.Request.QueryString.Add("query-key", "initial"); - this.Given( x => x.GivenAClaimToThing(new List { new ClaimToThing("query-key", "", "", 0) })) - .Given(x => x.GivenHttpContext(context)) + .And(x => x.GivenClaims(claims)) + .And(x => x.GivenTheDownstreamRequestHasQueryString("query-key", "initial")) .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) .When(x => x.WhenIAddQueriesToTheRequest()) .Then(x => x.ThenTheResultIsSuccess()) @@ -87,7 +84,7 @@ namespace Ocelot.UnitTests.QueryStrings { new ClaimToThing("", "", "", 0) })) - .Given(x => x.GivenHttpContext(new DefaultHttpContext())) + .Given(x => x.GivenClaims(new List())) .And(x => x.GivenTheClaimParserReturns(new ErrorResponse(new List { new AnyError() @@ -99,7 +96,8 @@ namespace Ocelot.UnitTests.QueryStrings private void ThenTheQueryIsAdded() { - var query = _context.Request.Query.First(x => x.Key == "query-key"); + var queries = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_downstreamRequest.RequestUri.OriginalString); + var query = queries.First(x => x.Key == "query-key"); query.Value.First().ShouldBe(_claimValue.Data); } @@ -108,9 +106,17 @@ namespace Ocelot.UnitTests.QueryStrings _configuration = configuration; } - private void GivenHttpContext(HttpContext context) + private void GivenClaims(List claims) { - _context = context; + _claims = claims; + } + + private void GivenTheDownstreamRequestHasQueryString(string key, string value) + { + var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers + .AddQueryString(_downstreamRequest.RequestUri.OriginalString, key, value); + + _downstreamRequest.RequestUri = new Uri(newUri); } private void GivenTheClaimParserReturns(Response claimValue) @@ -128,9 +134,7 @@ namespace Ocelot.UnitTests.QueryStrings private void WhenIAddQueriesToTheRequest() { - //_result = _addQueriesToRequest.SetQueriesOnContext(_configuration, _context); - //TODO: set downstreamRequest - _result = _addQueriesToRequest.SetQueriesOnDownstreamRequest(_configuration, _context.User.Claims, null); + _result = _addQueriesToRequest.SetQueriesOnDownstreamRequest(_configuration, _claims, _downstreamRequest); } private void ThenTheResultIsSuccess() diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index 3b701554..898563f7 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -3,16 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Net.Http; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.Headers.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.QueryStrings; @@ -31,6 +28,7 @@ namespace Ocelot.UnitTests.QueryStrings private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; + private readonly HttpRequestMessage _downstreamRequest; private Response _downstreamRoute; private HttpResponseMessage _result; @@ -57,6 +55,11 @@ namespace Ocelot.UnitTests.QueryStrings app.UseQueryStringBuilderMiddleware(); }); + _downstreamRequest = new HttpRequestMessage(); + + _scopedRepository.Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(_downstreamRequest)); + _server = new TestServer(builder); _client = _server.CreateClient(); } @@ -75,18 +78,14 @@ namespace Ocelot.UnitTests.QueryStrings .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheAddHeadersToRequestReturns()) + .And(x => x.GivenTheAddHeadersToRequestReturnsOk()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheAddQueriesToRequestIsCalledCorrectly()) .BDDfy(); } - private void GivenTheAddHeadersToRequestReturns() + private void GivenTheAddHeadersToRequestReturnsOk() { - //_addQueries - // .Setup(x => x.SetQueriesOnContext(It.IsAny>(), - // It.IsAny())) - // .Returns(new OkResponse()); _addQueries .Setup(x => x.SetQueriesOnDownstreamRequest( It.IsAny>(), @@ -97,14 +96,11 @@ namespace Ocelot.UnitTests.QueryStrings private void ThenTheAddQueriesToRequestIsCalledCorrectly() { - //_addQueries - // .Verify(x => x.SetQueriesOnContext(It.IsAny>(), - // It.IsAny()), Times.Once); _addQueries .Verify(x => x.SetQueriesOnDownstreamRequest( It.IsAny>(), It.IsAny>(), - It.IsAny()), Times.Once); + _downstreamRequest), Times.Once); } private void WhenICallTheMiddleware() diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 4bae0151..00b2aa4c 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net; using System.Net.Http; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; @@ -19,7 +16,6 @@ using Ocelot.Request.Middleware; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; -using Ocelot.Configuration; using Ocelot.Requester.QoS; namespace Ocelot.UnitTests.Request diff --git a/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs new file mode 100644 index 00000000..d831aed9 --- /dev/null +++ b/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs @@ -0,0 +1,56 @@ +namespace Ocelot.UnitTests.Request +{ + using System.Net.Http; + + using Ocelot.Request.Builder; + using Ocelot.Requester.QoS; + using Ocelot.Responses; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class HttpRequestCreatorTests + { + private readonly IRequestCreator _requestCreator; + private readonly bool _isQos; + private readonly IQoSProvider _qoSProvider; + private readonly HttpRequestMessage _requestMessage; + private Response _response; + + public HttpRequestCreatorTests() + { + _requestCreator = new HttpRequestCreator(); + _isQos = true; + _qoSProvider = new NoQoSProvider(); + _requestMessage = new HttpRequestMessage(); + } + + [Fact] + public void ShouldBuildRequest() + { + this.When(x => x.WhenIBuildARequest()) + .Then(x => x.ThenTheRequestContainsTheRequestMessage()) + .BDDfy(); + } + + private void WhenIBuildARequest() + { + _response = _requestCreator.Build(_requestMessage, _isQos, _qoSProvider).GetAwaiter().GetResult(); + } + + private void ThenTheRequestContainsTheRequestMessage() + { + _response.Data.HttpRequestMessage.ShouldBe(_requestMessage); + } + + private void ThenTheRequestContainsTheIsQos() + { + _response.Data.IsQos.ShouldBe(_isQos); + } + + private void ThenTheRequestContainsTheQosProvider() + { + _response.Data.QosProvider.ShouldBe(_qoSProvider); + } + } +} diff --git a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs b/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs deleted file mode 100644 index c182e47b..00000000 --- a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Internal; -using Ocelot.Request.Builder; -using Ocelot.Responses; -using Shouldly; -using TestStack.BDDfy; -using Xunit; -using Ocelot.Configuration; -using Ocelot.Requester.QoS; - -namespace Ocelot.UnitTests.Request -{ - public class RequestBuilderTests - { - private string _httpMethod; - private string _downstreamUrl; - private HttpContent _content; - private IHeaderDictionary _headers; - private IRequestCookieCollection _cookies; - private QueryString _query; - private string _contentType; - private readonly IRequestCreator _requestCreator; - private Response _result; - private Ocelot.RequestId.RequestId _requestId; - private bool _isQos; - private IQoSProvider _qoSProvider; - - public RequestBuilderTests() - { - _content = new StringContent(string.Empty); - _requestCreator = new HttpRequestCreator(); - } - - [Fact] - public void should_user_downstream_url() - { - this.Given(x => x.GivenIHaveHttpMethod("GET")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x=> x.GivenTheQos(true, new NoQoSProvider())) - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectDownstreamUrlIsUsed("http://www.bbc.co.uk/")) - .BDDfy(); - } - - [Fact] - public void should_use_http_method() - { - this.Given(x => x.GivenIHaveHttpMethod("POST")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectHttpMethodIsUsed(HttpMethod.Post)) - .BDDfy(); - } - - [Fact] - public void should_use_http_content() - { - this.Given(x => x.GivenIHaveHttpMethod("POST")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) - .And(x => x.GivenTheContentTypeIs("application/json")) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectContentIsUsed(new StringContent("Hi from Tom"))) - .BDDfy(); - } - - [Fact] - public void should_use_http_content_headers() - { - this.Given(x => x.GivenIHaveHttpMethod("POST")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) - .And(x => x.GivenTheContentTypeIs("application/json")) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary - { - { - "Content-Type", "application/json" - } - })) - .BDDfy(); - } - - [Fact] - public void should_use_unvalidated_http_content_headers() - { - this.Given(x => x.GivenIHaveHttpMethod("POST")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) - .And(x => x.GivenTheContentTypeIs("application/json; charset=utf-8")) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary - { - { - "Content-Type", "application/json; charset=utf-8" - } - })) - .BDDfy(); - } - - [Fact] - public void should_use_headers() - { - this.Given(x => x.GivenIHaveHttpMethod("GET")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary - { - {"ChopSticks", "Bubbles" } - })) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary - { - {"ChopSticks", "Bubbles" } - })) - .BDDfy(); - } - - [Fact] - public void should_use_request_id() - { - var requestId = Guid.NewGuid().ToString(); - - this.Given(x => x.GivenIHaveHttpMethod("GET")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary())) - .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId))) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary - { - {"RequestId", requestId } - })) - .BDDfy(); - } - - [Fact] - public void should_not_use_request_if_if_already_in_headers() - { - this.Given(x => x.GivenIHaveHttpMethod("GET")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary - { - {"RequestId", "534534gv54gv45g" } - })) - .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString()))) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary - { - {"RequestId", "534534gv54gv45g" } - })) - .BDDfy(); - } - - [Theory] - [InlineData(null, "blahh")] - [InlineData("", "blahh")] - [InlineData("RequestId", "")] - [InlineData("RequestId", null)] - public void should_not_use_request_id(string requestIdKey, string requestIdValue) - { - this.Given(x => x.GivenIHaveHttpMethod("GET")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary())) - .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue))) - .And(x => x.GivenTheQos(true, new NoQoSProvider())) - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheRequestIdIsNotInTheHeaders()) - .BDDfy(); - } - - private void GivenTheRequestIdIs(Ocelot.RequestId.RequestId requestId) - { - _requestId = requestId; - } - - private void GivenTheQos(bool isQos, IQoSProvider qoSProvider) - { - _isQos = isQos; - _qoSProvider = qoSProvider; - } - - [Fact] - public void should_user_query_string() - { - this.Given(x => x.GivenIHaveHttpMethod("POST")) - .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheQueryStringIs(new QueryString("?jeff=1&geoff=2"))) - .When(x => x.WhenICreateARequest()) - .And(x => x.ThenTheCorrectQueryStringIsUsed("?jeff=1&geoff=2")) - .BDDfy(); - } - - private void GivenTheContentTypeIs(string contentType) - { - _contentType = contentType; - } - - private void ThenTheCorrectQueryStringIsUsed(string expected) - { - _result.Data.HttpRequestMessage.RequestUri.Query.ShouldBe(expected); - } - - private void GivenTheQueryStringIs(QueryString query) - { - _query = query; - } - - private void ThenTheCorrectCookiesAreUsed(IRequestCookieCollection expected) - { - /* var resultCookies = _result.Data.CookieContainer.GetCookies(new Uri(_downstreamUrl + _query)); - var resultDictionary = resultCookies.Cast().ToDictionary(cook => cook.Name, cook => cook.Value); - - foreach (var expectedCookie in expected) - { - var resultCookie = resultDictionary[expectedCookie.Key]; - resultCookie.ShouldBe(expectedCookie.Value); - }*/ - } - - private void GivenTheCookiesAre(IRequestCookieCollection cookies) - { - _cookies = cookies; - } - - private void ThenTheRequestIdIsNotInTheHeaders() - { - _result.Data.HttpRequestMessage.Headers.ShouldNotContain(x => x.Key == "RequestId"); - } - - private void ThenTheCorrectHeadersAreUsed(IHeaderDictionary expected) - { - var expectedHeaders = expected.Select(x => new KeyValuePair(x.Key, x.Value)); - - foreach (var expectedHeader in expectedHeaders) - { - _result.Data.HttpRequestMessage.Headers.ShouldContain(x => x.Key == expectedHeader.Key && x.Value.First() == expectedHeader.Value[0]); - } - } - - private void ThenTheCorrectContentHeadersAreUsed(IHeaderDictionary expected) - { - var expectedHeaders = expected.Select(x => new KeyValuePair(x.Key, x.Value)); - - foreach (var expectedHeader in expectedHeaders) - { - _result.Data.HttpRequestMessage.Content.Headers.ShouldContain(x => x.Key == expectedHeader.Key - && x.Value.First() == expectedHeader.Value[0] - ); - } - } - - private void GivenTheHttpHeadersAre(IHeaderDictionary headers) - { - _headers = headers; - } - - private void GivenIHaveTheHttpContent(HttpContent content) - { - _content = content; - } - - private void GivenIHaveHttpMethod(string httpMethod) - { - _httpMethod = httpMethod; - } - - private void GivenIHaveDownstreamUrl(string downstreamUrl) - { - _downstreamUrl = downstreamUrl; - } - - private void WhenICreateARequest() - { - //_result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers, - // _query, _contentType, _requestId,_isQos,_qoSProvider).Result; - - //todo: add httprequestmessage - _result = _requestCreator.Build(null, _isQos, _qoSProvider).Result; - } - - - private void ThenTheCorrectDownstreamUrlIsUsed(string expected) - { - _result.Data.HttpRequestMessage.RequestUri.AbsoluteUri.ShouldBe(expected); - } - - private void ThenTheCorrectHttpMethodIsUsed(HttpMethod expected) - { - _result.Data.HttpRequestMessage.Method.Method.ShouldBe(expected.Method); - } - - private void ThenTheCorrectContentIsUsed(HttpContent expected) - { - _result.Data.HttpRequestMessage.Content.ReadAsStringAsync().Result.ShouldBe(expected.ReadAsStringAsync().Result); - } - } -} diff --git a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs index 26450ab8..4521e315 100644 --- a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs @@ -8,14 +8,12 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; -using Ocelot.Request.Middleware; using Ocelot.RequestId.Middleware; using Ocelot.Responses; using Shouldly; @@ -27,6 +25,7 @@ namespace Ocelot.UnitTests.RequestId public class RequestIdMiddlewareTests { private readonly Mock _scopedRepository; + private readonly HttpRequestMessage _downstreamRequest; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; @@ -64,10 +63,16 @@ namespace Ocelot.UnitTests.RequestId _server = new TestServer(builder); _client = _server.CreateClient(); + + _downstreamRequest = new HttpRequestMessage(); + + _scopedRepository + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(_downstreamRequest)); } [Fact] - public void should_add_request_id_to_repository() + public void should_pass_down_request_id_from_upstream_request() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() @@ -86,7 +91,7 @@ namespace Ocelot.UnitTests.RequestId } [Fact] - public void should_add_trace_indentifier_to_repository() + public void should_add_request_id_when_not_on_upstream_request() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() @@ -101,14 +106,12 @@ namespace Ocelot.UnitTests.RequestId .BDDfy(); } - private void ThenTheTraceIdIsAnything() + private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _result.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty(); - } - - private void ThenTheTraceIdIs(string expected) - { - _result.Headers.GetValues("LSRequestId").First().ShouldBe(expected); + _downstreamRoute = new OkResponse(downstreamRoute); + _scopedRepository + .Setup(x => x.Get(It.IsAny())) + .Returns(_downstreamRoute); } private void GivenTheRequestIdIsAddedToTheRequest(string key, string value) @@ -123,12 +126,14 @@ namespace Ocelot.UnitTests.RequestId _result = _client.GetAsync(_url).Result; } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + private void ThenTheTraceIdIsAnything() { - _downstreamRoute = new OkResponse(downstreamRoute); - _scopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _result.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty(); + } + + private void ThenTheTraceIdIs(string expected) + { + _result.Headers.GetValues("LSRequestId").First().ShouldBe(expected); } public void Dispose() From a1c6ab4ce4ea8cecbb4b7bc052afe2753be7e842 Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Tue, 18 Apr 2017 15:16:27 +0100 Subject: [PATCH 5/9] Get rest of unit tests working --- .../DownstreamUrlCreatorMiddleware.cs | 16 ---- .../HttpRequestBuilderMiddleware.cs | 11 --- .../DownstreamUrlCreatorMiddlewareTests.cs | 80 ++++++++----------- .../DownstreamUrlCreator/UrlBuilderTests.cs | 2 - .../Headers/AddHeadersToRequestTests.cs | 54 ++++++------- ...ttpRequestHeadersBuilderMiddlewareTests.cs | 21 ++--- .../HttpRequestBuilderMiddlewareTests.cs | 7 ++ 7 files changed, 75 insertions(+), 116 deletions(-) diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index 7cabd305..c91e06da 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -43,20 +43,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware return; } - //var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; - - //var dsHostAndPort = HostAndPort; - - //var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); - - //if (dsUrl.IsError) - //{ - // _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); - - // SetPipelineError(dsUrl.Errors); - // return; - //} - var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri) { Path = dsPath.Data.Value, @@ -67,8 +53,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri); - //SetDownstreamUrlForThisRequest(dsUrl.Data.Value); - _logger.LogDebug("calling next middleware"); await _next.Invoke(context); diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs index fa92c8e1..c59af7a6 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs @@ -43,17 +43,6 @@ namespace Ocelot.Request.Middleware return; } - //var buildResult = await _requestCreator - // .Build(context.Request.Method, - // DownstreamUrl, - // context.Request.Body, - // context.Request.Headers, - // context.Request.QueryString, - // context.Request.ContentType, - // new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier), - // DownstreamRoute.ReRoute.IsQos, - // qosProvider.Data); - var buildResult = await _requestCreator.Build( DownstreamRequest, DownstreamRoute.ReRoute.IsQos, diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index d511c4c1..aea1780f 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -18,6 +18,7 @@ using Ocelot.Responses; using Ocelot.Values; using TestStack.BDDfy; using Xunit; +using Shouldly; namespace Ocelot.UnitTests.DownstreamUrlCreator { @@ -30,10 +31,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator private readonly TestServer _server; private readonly HttpClient _client; private Response _downstreamRoute; - private HttpResponseMessage _result; private OkResponse _downstreamPath; - private OkResponse _downstreamUrl; - private HostAndPort _hostAndPort; + private HttpRequestMessage _downstreamRequest; + private HttpResponseMessage _result; public DownstreamUrlCreatorMiddlewareTests() { @@ -60,69 +60,34 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator app.UseDownstreamUrlCreatorMiddleware(); }); + _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); + _scopedRepository .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"))); + .Returns(new OkResponse(_downstreamRequest)); _server = new TestServer(builder); _client = _server.CreateClient(); } [Fact] - public void should_call_dependencies_correctly() + public void should_replace_scheme_and_path() { - var hostAndPort = new HostAndPort("127.0.0.1", 80); - this.Given(x => x.GivenTheDownStreamRouteIs( new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithUpstreamHttpMethod("Get") + .WithDownstreamScheme("https") .Build()))) - .And(x => x.GivenTheHostAndPortIs(hostAndPort)) - .And(x => x.TheUrlReplacerReturns("/api/products/1")) - .And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1")) + .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) + .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123")) .BDDfy(); } - private void GivenTheHostAndPortIs(HostAndPort hostAndPort) - { - _hostAndPort = hostAndPort; - _scopedRepository - .Setup(x => x.Get("HostAndPort")) - .Returns(new OkResponse(_hostAndPort)); - } - - private void TheUrlBuilderReturns(string dsUrl) - { - _downstreamUrl = new OkResponse(new DownstreamUrl(dsUrl)); - _urlBuilder - .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_downstreamUrl); - } - - private void TheUrlReplacerReturns(string downstreamUrl) - { - _downstreamPath = new OkResponse(new DownstreamPath(downstreamUrl)); - _downstreamUrlTemplateVariableReplacer - .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) - .Returns(_downstreamPath); - } - - private void ThenTheScopedDataRepositoryIsCalledCorrectly() - { - _scopedRepository - .Verify(x => x.Add("DownstreamUrl", _downstreamUrl.Data.Value), Times.Once()); - } - - private void WhenICallTheMiddleware() - { - _result = _client.GetAsync(_url).Result; - } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { _downstreamRoute = new OkResponse(downstreamRoute); @@ -131,6 +96,29 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator .Returns(_downstreamRoute); } + private void GivenTheDownstreamRequestUriIs(string uri) + { + _downstreamRequest.RequestUri = new Uri(uri); + } + + private void GivenTheUrlReplacerWillReturn(string path) + { + _downstreamPath = new OkResponse(new DownstreamPath(path)); + _downstreamUrlTemplateVariableReplacer + .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) + .Returns(_downstreamPath); + } + + private void WhenICallTheMiddleware() + { + _result = _client.GetAsync(_url).Result; + } + + private void ThenTheDownstreamRequestUriIs(string expectedUri) + { + _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); + } + public void Dispose() { _client.Dispose(); diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs index 7e512798..0414bbb3 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs @@ -1,7 +1,5 @@ using System; -using Ocelot.Configuration; using Ocelot.DownstreamUrlCreator; -using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Responses; using Ocelot.Values; using Shouldly; diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs index 950dc72d..0395df91 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs @@ -12,6 +12,7 @@ using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; +using System.Net.Http; namespace Ocelot.UnitTests.Headers { @@ -19,6 +20,8 @@ namespace Ocelot.UnitTests.Headers { private readonly AddHeadersToRequest _addHeadersToRequest; private readonly Mock _parser; + private readonly HttpRequestMessage _downstreamRequest; + private List _claims; private List _configuration; private HttpContext _context; private Response _result; @@ -28,17 +31,15 @@ namespace Ocelot.UnitTests.Headers { _parser = new Mock(); _addHeadersToRequest = new AddHeadersToRequest(_parser.Object); + _downstreamRequest = new HttpRequestMessage(); } [Fact] - public void should_add_headers_to_context() + public void should_add_headers_to_downstreamRequest() { - var context = new DefaultHttpContext + var claims = new List { - User = new ClaimsPrincipal(new ClaimsIdentity(new List - { - new Claim("test", "data") - })) + new Claim("test", "data") }; this.Given( @@ -46,7 +47,7 @@ namespace Ocelot.UnitTests.Headers { new ClaimToThing("header-key", "", "", 0) })) - .Given(x => x.GivenHttpContext(context)) + .Given(x => x.GivenClaims(claims)) .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) .When(x => x.WhenIAddHeadersToTheRequest()) .Then(x => x.ThenTheResultIsSuccess()) @@ -55,25 +56,19 @@ namespace Ocelot.UnitTests.Headers } [Fact] - public void if_header_exists_should_replace_it() + public void should_replace_existing_headers_on_request() { - var context = new DefaultHttpContext - { - User = new ClaimsPrincipal(new ClaimsIdentity(new List - { - new Claim("test", "data") - })), - }; - - context.Request.Headers.Add("header-key", new StringValues("initial")); - this.Given( x => x.GivenConfigurationHeaderExtractorProperties(new List { new ClaimToThing("header-key", "", "", 0) })) - .Given(x => x.GivenHttpContext(context)) + .Given(x => x.GivenClaims(new List + { + new Claim("test", "data") + })) .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) + .And(x => x.GivenThatTheRequestContainsHeader("header-key", "initial")) .When(x => x.WhenIAddHeadersToTheRequest()) .Then(x => x.ThenTheResultIsSuccess()) .And(x => x.ThenTheHeaderIsAdded()) @@ -88,7 +83,7 @@ namespace Ocelot.UnitTests.Headers { new ClaimToThing("", "", "", 0) })) - .Given(x => x.GivenHttpContext(new DefaultHttpContext())) + .Given(x => x.GivenClaims(new List())) .And(x => x.GivenTheClaimParserReturns(new ErrorResponse(new List { new AnyError() @@ -98,10 +93,9 @@ namespace Ocelot.UnitTests.Headers .BDDfy(); } - private void ThenTheHeaderIsAdded() + private void GivenClaims(List claims) { - var header = _context.Request.Headers.First(x => x.Key == "header-key"); - header.Value.First().ShouldBe(_claimValue.Data); + _claims = claims; } private void GivenConfigurationHeaderExtractorProperties(List configuration) @@ -109,9 +103,9 @@ namespace Ocelot.UnitTests.Headers _configuration = configuration; } - private void GivenHttpContext(HttpContext context) + private void GivenThatTheRequestContainsHeader(string key, string value) { - _context = context; + _downstreamRequest.Headers.Add(key, value); } private void GivenTheClaimParserReturns(Response claimValue) @@ -129,9 +123,7 @@ namespace Ocelot.UnitTests.Headers private void WhenIAddHeadersToTheRequest() { - //_result = _addHeadersToRequest.SetHeadersOnContext(_configuration, _context); - //TODO: pass in DownstreamRequest - _result = _addHeadersToRequest.SetHeadersOnDownstreamRequest(_configuration, _context.User.Claims, null); + _result = _addHeadersToRequest.SetHeadersOnDownstreamRequest(_configuration, _claims, _downstreamRequest); } private void ThenTheResultIsSuccess() @@ -145,6 +137,12 @@ namespace Ocelot.UnitTests.Headers _result.IsError.ShouldBe(true); } + private void ThenTheHeaderIsAdded() + { + var header = _downstreamRequest.Headers.First(x => x.Key == "header-key"); + header.Value.First().ShouldBe(_claimValue.Data); + } + class AnyError : Error { public AnyError() diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index aff337fc..d6a49dc5 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -3,16 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Net.Http; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Headers; using Ocelot.Headers.Middleware; using Ocelot.Infrastructure.RequestData; @@ -27,6 +24,7 @@ namespace Ocelot.UnitTests.Headers { private readonly Mock _scopedRepository; private readonly Mock _addHeaders; + private readonly HttpRequestMessage _downstreamRequest; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; @@ -58,6 +56,10 @@ namespace Ocelot.UnitTests.Headers app.UseHttpRequestHeadersBuilderMiddleware(); }); + _scopedRepository + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(_downstreamRequest)); + _server = new TestServer(builder); _client = _server.CreateClient(); } @@ -76,18 +78,14 @@ namespace Ocelot.UnitTests.Headers .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheAddHeadersToRequestReturns()) + .And(x => x.GivenTheAddHeadersToDownstreamRequestReturnsOk()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheAddHeadersToRequestIsCalledCorrectly()) .BDDfy(); } - private void GivenTheAddHeadersToRequestReturns() + private void GivenTheAddHeadersToDownstreamRequestReturnsOk() { - //_addHeaders - // .Setup(x => x.SetHeadersOnContext(It.IsAny>(), - // It.IsAny())) - // .Returns(new OkResponse()); _addHeaders .Setup(x => x.SetHeadersOnDownstreamRequest( It.IsAny>(), @@ -98,14 +96,11 @@ namespace Ocelot.UnitTests.Headers private void ThenTheAddHeadersToRequestIsCalledCorrectly() { - //_addHeaders - // .Verify(x => x.SetHeadersOnContext(It.IsAny>(), - // It.IsAny()), Times.Once); _addHeaders .Verify(x => x.SetHeadersOnDownstreamRequest( It.IsAny>(), It.IsAny>(), - It.IsAny()), Times.Once); + _downstreamRequest), Times.Once); } private void WhenICallTheMiddleware() diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 00b2aa4c..8c2a0879 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -25,6 +25,7 @@ namespace Ocelot.UnitTests.Request private readonly Mock _requestBuilder; private readonly Mock _scopedRepository; private readonly Mock _qosProviderHouse; + private readonly HttpRequestMessage _downstreamRequest; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; @@ -58,6 +59,12 @@ namespace Ocelot.UnitTests.Request app.UseHttpRequestBuilderMiddleware(); }); + _downstreamRequest = new HttpRequestMessage(); + + _scopedRepository + .Setup(sr => sr.Get("DownstreamRequest")) + .Returns(new OkResponse(_downstreamRequest)); + _server = new TestServer(builder); _client = _server.CreateClient(); } From 878eacf1dba1fc6eb1dc5c1d634839b67aaf4721 Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Tue, 18 Apr 2017 15:35:54 +0100 Subject: [PATCH 6/9] Remove obsolete code --- src/Ocelot/Headers/AddHeadersToRequest.cs | 24 --- src/Ocelot/Headers/IAddHeadersToRequest.cs | 17 +- .../HttpRequestHeadersBuilderMiddleware.cs | 1 - .../Middleware/LoadBalancingMiddleware.cs | 1 - .../QueryStrings/AddQueriesToRequest.cs | 43 ---- .../QueryStrings/IAddQueriesToRequest.cs | 3 - .../QueryStringBuilderMiddleware.cs | 1 - src/Ocelot/Request/Builder/IRequestCreator.cs | 25 +-- src/Ocelot/Request/Builder/RequestBuilder.cs | 191 ------------------ .../Headers/AddHeadersToRequestTests.cs | 3 - ...ttpRequestHeadersBuilderMiddlewareTests.cs | 2 + .../LoadBalancerMiddlewareTests.cs | 1 - .../QueryStrings/AddQueriesToRequestTests.cs | 2 - .../HttpRequestBuilderMiddlewareTests.cs | 5 +- 14 files changed, 17 insertions(+), 302 deletions(-) delete mode 100644 src/Ocelot/Request/Builder/RequestBuilder.cs diff --git a/src/Ocelot/Headers/AddHeadersToRequest.cs b/src/Ocelot/Headers/AddHeadersToRequest.cs index 791860b5..3c818bcc 100644 --- a/src/Ocelot/Headers/AddHeadersToRequest.cs +++ b/src/Ocelot/Headers/AddHeadersToRequest.cs @@ -16,30 +16,6 @@ namespace Ocelot.Headers _claimsParser = claimsParser; } - //public Response SetHeadersOnContext(List claimsToThings, HttpContext context) - //{ - // foreach (var config in claimsToThings) - // { - // var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); - - // if (value.IsError) - // { - // return new ErrorResponse(value.Errors); - // } - - // var exists = context.Request.Headers.FirstOrDefault(x => x.Key == config.ExistingKey); - - // if (!string.IsNullOrEmpty(exists.Key)) - // { - // context.Request.Headers.Remove(exists); - // } - - // context.Request.Headers.Add(config.ExistingKey, new StringValues(value.Data)); - // } - - // return new OkResponse(); - //} - public Response SetHeadersOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest) { foreach (var config in claimsToThings) diff --git a/src/Ocelot/Headers/IAddHeadersToRequest.cs b/src/Ocelot/Headers/IAddHeadersToRequest.cs index a819bbf1..cc4e0383 100644 --- a/src/Ocelot/Headers/IAddHeadersToRequest.cs +++ b/src/Ocelot/Headers/IAddHeadersToRequest.cs @@ -1,16 +1,13 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Http; -using Ocelot.Configuration; -using Ocelot.Responses; -using System.Net.Http; - -namespace Ocelot.Headers +namespace Ocelot.Headers { + using System.Collections.Generic; + using System.Net.Http; + + using Ocelot.Configuration; + using Ocelot.Responses; + public interface IAddHeadersToRequest { - //Response SetHeadersOnContext(List claimsToThings, - // HttpContext context); - Response SetHeadersOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest); } } diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs index 380fcc7d..cb0fdc98 100644 --- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs @@ -32,7 +32,6 @@ namespace Ocelot.Headers.Middleware { _logger.LogDebug("this route has instructions to convert claims to headers"); - //var response = _addHeadersToRequest.SetHeadersOnContext(DownstreamRoute.ReRoute.ClaimsToHeaders, context); var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest); if (response.IsError) diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index d74759e9..be5a34d1 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -44,7 +44,6 @@ namespace Ocelot.LoadBalancer.Middleware return; } - //SetHostAndPortForThisRequest(hostAndPort.Data); var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri); uriBuilder.Host = hostAndPort.Data.DownstreamHost; if (hostAndPort.Data.DownstreamPort > 0) diff --git a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs index 0c099271..839da9bc 100644 --- a/src/Ocelot/QueryStrings/AddQueriesToRequest.cs +++ b/src/Ocelot/QueryStrings/AddQueriesToRequest.cs @@ -19,36 +19,6 @@ namespace Ocelot.QueryStrings _claimsParser = claimsParser; } - //public Response SetQueriesOnContext(List claimsToThings, HttpContext context) - //{ - // var queryDictionary = ConvertQueryStringToDictionary(context.Request.QueryString); - - // foreach (var config in claimsToThings) - // { - // var value = _claimsParser.GetValue(context.User.Claims, config.NewKey, config.Delimiter, config.Index); - - // if (value.IsError) - // { - // return new ErrorResponse(value.Errors); - // } - - // var exists = queryDictionary.FirstOrDefault(x => x.Key == config.ExistingKey); - - // if (!string.IsNullOrEmpty(exists.Key)) - // { - // queryDictionary[exists.Key] = value.Data; - // } - // else - // { - // queryDictionary.Add(config.ExistingKey, value.Data); - // } - // } - - // context.Request.QueryString = ConvertDictionaryToQueryString(queryDictionary); - - // return new OkResponse(); - //} - public Response SetQueriesOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest) { var queryDictionary = ConvertQueryStringToDictionary(downstreamRequest.RequestUri.Query); @@ -82,12 +52,6 @@ namespace Ocelot.QueryStrings return new OkResponse(); } - //private Dictionary ConvertQueryStringToDictionary(HttpContext context) - //{ - // return Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(context.Request.QueryString.Value) - // .ToDictionary(q => q.Key, q => q.Value.FirstOrDefault() ?? string.Empty); - //} - private Dictionary ConvertQueryStringToDictionary(string queryString) { return Microsoft.AspNetCore.WebUtilities.QueryHelpers @@ -95,13 +59,6 @@ namespace Ocelot.QueryStrings .ToDictionary(q => q.Key, q => q.Value.FirstOrDefault() ?? string.Empty); } - //private Microsoft.AspNetCore.Http.QueryString ConvertDictionaryToQueryString(Dictionary queryDictionary) - //{ - // var newQueryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString("", queryDictionary); - - // return new Microsoft.AspNetCore.Http.QueryString(newQueryString); - //} - private string ConvertDictionaryToQueryString(Dictionary queryDictionary) { return Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString("", queryDictionary); diff --git a/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs b/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs index 606732c6..762b8a00 100644 --- a/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs +++ b/src/Ocelot/QueryStrings/IAddQueriesToRequest.cs @@ -9,9 +9,6 @@ namespace Ocelot.QueryStrings { public interface IAddQueriesToRequest { - //Response SetQueriesOnContext(List claimsToThings, - // HttpContext context); - Response SetQueriesOnDownstreamRequest(List claimsToThings, IEnumerable claims, HttpRequestMessage downstreamRequest); } } diff --git a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs index 80db18b4..f3001e87 100644 --- a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs +++ b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs @@ -32,7 +32,6 @@ namespace Ocelot.QueryStrings.Middleware { _logger.LogDebug("this route has instructions to convert claims to queries"); - //var response = _addQueriesToRequest.SetQueriesOnContext(DownstreamRoute.ReRoute.ClaimsToQueries, context); var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest); if (response.IsError) diff --git a/src/Ocelot/Request/Builder/IRequestCreator.cs b/src/Ocelot/Request/Builder/IRequestCreator.cs index d4eae34f..85fbfa8d 100644 --- a/src/Ocelot/Request/Builder/IRequestCreator.cs +++ b/src/Ocelot/Request/Builder/IRequestCreator.cs @@ -1,24 +1,13 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Ocelot.Requester.QoS; -using Ocelot.Responses; -using System.Net.Http; - -namespace Ocelot.Request.Builder +namespace Ocelot.Request.Builder { + using System.Net.Http; + using System.Threading.Tasks; + + using Ocelot.Requester.QoS; + using Ocelot.Responses; + public interface IRequestCreator { - //Task> Build(string httpMethod, - // string downstreamUrl, - // Stream content, - // IHeaderDictionary headers, - // QueryString queryString, - // string contentType, - // RequestId.RequestId requestId, - // bool isQos, - // IQoSProvider qosProvider); - Task> Build( HttpRequestMessage httpRequestMessage, bool isQos, diff --git a/src/Ocelot/Request/Builder/RequestBuilder.cs b/src/Ocelot/Request/Builder/RequestBuilder.cs deleted file mode 100644 index a76391df..00000000 --- a/src/Ocelot/Request/Builder/RequestBuilder.cs +++ /dev/null @@ -1,191 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.IO; -//using System.Linq; -//using System.Net; -//using System.Net.Http; -//using System.Net.Http.Headers; -//using System.Threading.Tasks; -//using Microsoft.AspNetCore.Http; -//using Microsoft.Extensions.Primitives; -//using Ocelot.Requester.QoS; - -//namespace Ocelot.Request.Builder -//{ -// internal sealed class RequestBuilder -// { -// private HttpMethod _method; -// private string _downstreamUrl; -// private QueryString _queryString; -// private Stream _content; -// private string _contentType; -// private IHeaderDictionary _headers; -// private RequestId.RequestId _requestId; -// private readonly string[] _unsupportedHeaders = {"host"}; -// private bool _isQos; -// private IQoSProvider _qoSProvider; - -// ---- sets Method - -// public RequestBuilder WithHttpMethod(string httpMethod) -// { -// _method = new HttpMethod(httpMethod); -// return this; -// } - -// ---- sets RequestUri - -// public RequestBuilder WithDownstreamUrl(string downstreamUrl) -// { -// _downstreamUrl = downstreamUrl; -// return this; -// } - -// public RequestBuilder WithQueryString(QueryString queryString) -// { -// _queryString = queryString; -// return this; -// } - -// private Uri CreateUri() -// { -// var uri = new Uri(string.Format("{0}{1}", _downstreamUrl, _queryString.ToUriComponent())); -// return uri; -// } - -// ---- Content and ContentType - -// public RequestBuilder WithContent(Stream content) -// { -// _content = content; -// return this; -// } - -// public RequestBuilder WithContentType(string contentType) -// { -// _contentType = contentType; -// return this; -// } - -// private async Task AddContentToRequest(HttpRequestMessage httpRequestMessage) -// { -// if (_content != null) -// { -// httpRequestMessage.Content = new ByteArrayContent(await ToByteArray(_content)); -// } -// } - -// private async Task ToByteArray(Stream stream) -// { -// using (stream) -// { -// using (var memStream = new MemoryStream()) -// { -// await stream.CopyToAsync(memStream); -// return memStream.ToArray(); -// } -// } -// } - -// private void AddContentTypeToRequest(HttpRequestMessage httpRequestMessage) -// { -// if (!string.IsNullOrEmpty(_contentType)) -// { -// httpRequestMessage.Content.Headers.Remove("Content-Type"); -// httpRequestMessage.Content.Headers.TryAddWithoutValidation("Content-Type", _contentType); -// } -// } - -// ---- Headers - -// public RequestBuilder WithHeaders(IHeaderDictionary headers) -// { -// _headers = headers; -// return this; -// } - -// private void AddHeadersToRequest(HttpRequestMessage httpRequestMessage) -// { -// if (_headers != null) -// { -// _headers.Remove("Content-Type"); - -// foreach (var header in _headers) -// { -// //todo get rid of if.. -// if (IsSupportedHeader(header)) -// { -// httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); -// } -// } -// } -// } - -// private bool IsSupportedHeader(KeyValuePair header) -// { -// return !_unsupportedHeaders.Contains(header.Key.ToLower()); -// } - -// ---- Request ID - -// public RequestBuilder WithRequestId(RequestId.RequestId requestId) -// { -// _requestId = requestId; -// return this; -// } - -// private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage) -// { -// httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); -// } - -// private bool RequestIdInHeaders(RequestId.RequestId requestId, HttpRequestHeaders headers) -// { -// IEnumerable value; -// return headers.TryGetValues(requestId.RequestIdKey, out value); -// } - -// private bool ShouldAddRequestId(RequestId.RequestId requestId, HttpRequestHeaders headers) -// { -// return !string.IsNullOrEmpty(requestId?.RequestIdKey) -// && !string.IsNullOrEmpty(requestId.RequestIdValue) -// && !RequestIdInHeaders(requestId, headers); -// } - -// ---- QoS - -// public RequestBuilder WithIsQos(bool isqos) -// { -// _isQos = isqos; -// return this; -// } - -// public RequestBuilder WithQos(IQoSProvider qoSProvider) -// { -// _qoSProvider = qoSProvider; -// return this; -// } - -// ---- - -// public async Task Build() -// { -// var uri = CreateUri(); - -// var httpRequestMessage = new HttpRequestMessage(_method, uri); - -// await AddContentToRequest(httpRequestMessage); - -// AddContentTypeToRequest(httpRequestMessage); - -// AddHeadersToRequest(httpRequestMessage); - -// if (ShouldAddRequestId(_requestId, httpRequestMessage.Headers)) -// { -// AddRequestIdHeader(_requestId, httpRequestMessage); -// } - -// return new Request(httpRequestMessage,_isQos, _qoSProvider); -// } -// } -//} diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs index 0395df91..d276fd24 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestTests.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; using Moq; using Ocelot.Configuration; using Ocelot.Errors; @@ -23,7 +21,6 @@ namespace Ocelot.UnitTests.Headers private readonly HttpRequestMessage _downstreamRequest; private List _claims; private List _configuration; - private HttpContext _context; private Response _result; private Response _claimValue; diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index d6a49dc5..95989688 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -56,6 +56,8 @@ namespace Ocelot.UnitTests.Headers app.UseHttpRequestHeadersBuilderMiddleware(); }); + _downstreamRequest = new HttpRequestMessage(); + _scopedRepository .Setup(sr => sr.Get("DownstreamRequest")) .Returns(new OkResponse(_downstreamRequest)); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index c3607e82..a7dfd1ac 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -30,7 +30,6 @@ namespace Ocelot.UnitTests.LoadBalancer private readonly HttpClient _client; private HttpResponseMessage _result; private HostAndPort _hostAndPort; - private OkResponse _downstreamUrl; private OkResponse _downstreamRoute; private ErrorResponse _getLoadBalancerHouseError; private ErrorResponse _getHostAndPortError; diff --git a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs index b65ce531..ee7769a5 100644 --- a/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/AddQueriesToRequestTests.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using Microsoft.AspNetCore.Http; using Moq; using Ocelot.Configuration; using Ocelot.Errors; @@ -144,7 +143,6 @@ namespace Ocelot.UnitTests.QueryStrings private void ThenTheResultIsError() { - _result.IsError.ShouldBe(true); } diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 8c2a0879..8cec477e 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -106,10 +106,7 @@ namespace Ocelot.UnitTests.Request private void GivenTheRequestBuilderReturns(Ocelot.Request.Request request) { _request = new OkResponse(request); - //_requestBuilder - // .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - // It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(), It.IsAny())) - // .ReturnsAsync(_request); + _requestBuilder .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(_request); From 80e728aa00eba876b0ce7b13921be72f39b57a6e Mon Sep 17 00:00:00 2001 From: Philip Wood Date: Wed, 19 Apr 2017 20:12:08 +0100 Subject: [PATCH 7/9] Made changes based on PR comments. Also added lots more tests! --- .../ServiceCollectionExtensions.cs | 2 + src/Ocelot/Errors/OcelotErrorCode.cs | 3 +- .../Middleware/OcelotMiddlewareExtensions.cs | 6 +- src/Ocelot/Request/Mapper/IRequestMapper.cs | 12 + .../{Mapper.cs => Mapper/RequestMapper.cs} | 52 ++-- .../Request/Mapper/UnmappableRequestError.cs | 12 + .../DownstreamRequestInitialiserMiddleware.cs | 35 +-- ...streamRequestInitialiserMiddlewareTests.cs | 142 ++++++++++ .../Request/Mapper/RequestMapperTests.cs | 258 ++++++++++++++++++ 9 files changed, 478 insertions(+), 44 deletions(-) create mode 100644 src/Ocelot/Request/Mapper/IRequestMapper.cs rename src/Ocelot/Request/{Mapper.cs => Mapper/RequestMapper.cs} (59%) create mode 100644 src/Ocelot/Request/Mapper/UnmappableRequestError.cs create mode 100644 test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs create mode 100644 test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index e975dcb6..ef022923 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -31,6 +31,7 @@ using Ocelot.Middleware; using Ocelot.QueryStrings; using Ocelot.RateLimit; using Ocelot.Request.Builder; +using Ocelot.Request.Mapper; using Ocelot.Requester; using Ocelot.Requester.QoS; using Ocelot.Responder; @@ -160,6 +161,7 @@ namespace Ocelot.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index c1c55dbb..de7960c4 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -28,6 +28,7 @@ UnableToFindLoadBalancerError, RequestTimedOutError, UnableToFindQoSProviderError, - UnableToSetConfigInConsulError + UnableToSetConfigInConsulError, + UnmappableRequestError } } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 105be8dd..3f98f959 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -53,9 +53,6 @@ namespace Ocelot.Middleware { await CreateAdministrationArea(builder); - // Initialises downstream request - builder.UseDownstreamRequestInitialiser(); - // This is registered to catch any global exceptions that are not handled builder.UseExceptionHandlerMiddleware(); @@ -65,6 +62,9 @@ namespace Ocelot.Middleware // This is registered first so it can catch any errors and issue an appropriate response builder.UseResponderMiddleware(); + // Initialises downstream request + builder.UseDownstreamRequestInitialiser(); + // Then we get the downstream route information builder.UseDownstreamRouteFinderMiddleware(); diff --git a/src/Ocelot/Request/Mapper/IRequestMapper.cs b/src/Ocelot/Request/Mapper/IRequestMapper.cs new file mode 100644 index 00000000..941a24f7 --- /dev/null +++ b/src/Ocelot/Request/Mapper/IRequestMapper.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Request.Mapper +{ + using System.Net.Http; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Http; + using Ocelot.Responses; + + public interface IRequestMapper + { + Task> Map(HttpRequest request); + } +} diff --git a/src/Ocelot/Request/Mapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs similarity index 59% rename from src/Ocelot/Request/Mapper.cs rename to src/Ocelot/Request/Mapper/RequestMapper.cs index c0cc3bba..17e2afe5 100644 --- a/src/Ocelot/Request/Mapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -1,33 +1,40 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.Extensions.Primitives; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Ocelot.Request +namespace Ocelot.Request.Mapper { - public class Mapper + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Extensions; + using Microsoft.Extensions.Primitives; + using Ocelot.Responses; + + public class RequestMapper : IRequestMapper { private readonly string[] _unsupportedHeaders = { "host" }; - public async Task Map(HttpRequest request) + public async Task> Map(HttpRequest request) { - var requestMessage = new HttpRequestMessage() + try { - Content = await MapContent(request), - Method = MapMethod(request), - RequestUri = MapUri(request), - //Properties = null - //Version = null - }; + var requestMessage = new HttpRequestMessage() + { + Content = await MapContent(request), + Method = MapMethod(request), + RequestUri = MapUri(request) + }; - MapHeaders(request, requestMessage); + MapHeaders(request, requestMessage); - return requestMessage; + return new OkResponse(requestMessage); + } + catch (Exception ex) + { + return new ErrorResponse(new UnmappableRequestError(ex)); + } } private async Task MapContent(HttpRequest request) @@ -37,7 +44,6 @@ namespace Ocelot.Request return null; } - return new ByteArrayContent(await ToByteArray(request.Body)); } diff --git a/src/Ocelot/Request/Mapper/UnmappableRequestError.cs b/src/Ocelot/Request/Mapper/UnmappableRequestError.cs new file mode 100644 index 00000000..4a860f5b --- /dev/null +++ b/src/Ocelot/Request/Mapper/UnmappableRequestError.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Request.Mapper +{ + using Ocelot.Errors; + using System; + + public class UnmappableRequestError : Error + { + public UnmappableRequestError(Exception ex) : base($"Error when parsing incoming request, exception: {ex.Message}", OcelotErrorCode.UnmappableRequestError) + { + } + } +} diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs index 39fe3b9b..a2813c25 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -1,40 +1,41 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Logging; -using Ocelot.Middleware; -using Ocelot.Request.Builder; -using Ocelot.Requester.QoS; - namespace Ocelot.Request.Middleware { + using System.Threading.Tasks; + using Microsoft.AspNetCore.Http; + + using Ocelot.Infrastructure.RequestData; + using Ocelot.Logging; + using Ocelot.Middleware; + public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; - private readonly IRequestCreator _requestCreator; private readonly IOcelotLogger _logger; - private readonly IQosProviderHouse _qosProviderHouse; + private readonly Mapper.IRequestMapper _requestMapper; public DownstreamRequestInitialiserMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, - IRequestCreator requestCreator, - IQosProviderHouse qosProviderHouse) + IRequestScopedDataRepository requestScopedDataRepository, + Mapper.IRequestMapper requestMapper) :base(requestScopedDataRepository) { _next = next; - _requestCreator = requestCreator; - _qosProviderHouse = qosProviderHouse; _logger = loggerFactory.CreateLogger(); + _requestMapper = requestMapper; } public async Task Invoke(HttpContext context) { _logger.LogDebug("started calling request builder middleware"); - var mapper = new Mapper(); + var downstreamRequest = await _requestMapper.Map(context.Request); + if (downstreamRequest.IsError) + { + SetPipelineError(downstreamRequest.Errors); + return; + } - SetDownstreamRequest(await mapper.Map(context.Request)); + SetDownstreamRequest(downstreamRequest.Data); _logger.LogDebug("calling next middleware"); diff --git a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs new file mode 100644 index 00000000..91c1d011 --- /dev/null +++ b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs @@ -0,0 +1,142 @@ +namespace Ocelot.UnitTests.Request +{ + using System.Net.Http; + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Logging; + using Ocelot.Request.Mapper; + using Ocelot.Request.Middleware; + using Ocelot.Infrastructure.RequestData; + using TestStack.BDDfy; + using Xunit; + using Ocelot.Responses; + + public class DownstreamRequestInitialiserMiddlewareTests + { + readonly DownstreamRequestInitialiserMiddleware _middleware; + + readonly Mock _httpContext; + + readonly Mock _httpRequest; + + readonly Mock _next; + + readonly Mock _requestMapper; + + readonly Mock _repo; + + readonly Mock _loggerFactory; + + readonly Mock _logger; + + Response _mappedRequest; + + public DownstreamRequestInitialiserMiddlewareTests() + { + + _httpContext = new Mock(); + _httpRequest = new Mock(); + _requestMapper = new Mock(); + _repo = new Mock(); + _next = new Mock(); + _logger = new Mock(); + + _loggerFactory = new Mock(); + _loggerFactory + .Setup(lf => lf.CreateLogger()) + .Returns(_logger.Object); + + _middleware = new DownstreamRequestInitialiserMiddleware( + _next.Object, + _loggerFactory.Object, + _repo.Object, + _requestMapper.Object); + } + + [Fact] + public void Should_handle_valid_httpRequest() + { + this.Given(_ => GivenTheHttpContextContainsARequest()) + .And(_ => GivenTheMapperWillReturnAMappedRequest()) + .When(_ => WhenTheMiddlewareIsInvoked()) + .Then(_ => ThenTheContexRequestIsMappedToADownstreamRequest()) + .And(_ => ThenTheDownstreamRequestIsStored()) + .And(_ => ThenTheNextMiddlewareIsInvoked()) + .BDDfy(); + } + + [Fact] + public void Should_handle_mapping_failure() + { + this.Given(_ => GivenTheHttpContextContainsARequest()) + .And(_ => GivenTheMapperWillReturnAnError()) + .When(_ => WhenTheMiddlewareIsInvoked()) + .And(_ => ThenTheDownstreamRequestIsNotStored()) + .And(_ => ThenAPipelineErrorIsStored()) + .And(_ => ThenTheNextMiddlewareIsNotInvoked()) + .BDDfy(); + } + + private void GivenTheHttpContextContainsARequest() + { + _httpContext + .Setup(hc => hc.Request) + .Returns(_httpRequest.Object); + } + + private void GivenTheMapperWillReturnAMappedRequest() + { + _mappedRequest = new OkResponse(new HttpRequestMessage()); + + _requestMapper + .Setup(rm => rm.Map(It.IsAny())) + .ReturnsAsync(_mappedRequest); + } + + private void GivenTheMapperWillReturnAnError() + { + _mappedRequest = new ErrorResponse(new UnmappableRequestError(new System.Exception("boooom!"))); + + _requestMapper + .Setup(rm => rm.Map(It.IsAny())) + .ReturnsAsync(_mappedRequest); + } + + private void WhenTheMiddlewareIsInvoked() + { + _middleware.Invoke(_httpContext.Object).GetAwaiter().GetResult(); + } + + private void ThenTheContexRequestIsMappedToADownstreamRequest() + { + _requestMapper.Verify(rm => rm.Map(_httpRequest.Object), Times.Once); + } + + private void ThenTheDownstreamRequestIsStored() + { + _repo.Verify(r => r.Add("DownstreamRequest", _mappedRequest.Data), Times.Once); + } + + private void ThenTheDownstreamRequestIsNotStored() + { + _repo.Verify(r => r.Add("DownstreamRequest", It.IsAny()), Times.Never); + } + + private void ThenAPipelineErrorIsStored() + { + _repo.Verify(r => r.Add("OcelotMiddlewareError", true), Times.Once); + _repo.Verify(r => r.Add("OcelotMiddlewareErrors", _mappedRequest.Errors), Times.Once); + } + + private void ThenTheNextMiddlewareIsInvoked() + { + _next.Verify(n => n(_httpContext.Object), Times.Once); + } + + private void ThenTheNextMiddlewareIsNotInvoked() + { + _next.Verify(n => n(It.IsAny()), Times.Never); + } + + } +} diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs new file mode 100644 index 00000000..4334e017 --- /dev/null +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -0,0 +1,258 @@ +namespace Ocelot.UnitTests.Request.Mapper +{ + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Internal; + using Microsoft.Extensions.Primitives; + using Ocelot.Request.Mapper; + using Ocelot.Responses; + using TestStack.BDDfy; + using Xunit; + using Shouldly; + using System; + using System.IO; + using System.Text; + + public class RequestMapperTests + { + readonly HttpRequest _inputRequest; + + readonly RequestMapper _requestMapper; + + Response _mappedRequest; + + List> _inputHeaders = null; + + public RequestMapperTests() + { + _inputRequest = new DefaultHttpRequest(new DefaultHttpContext()); + + _requestMapper = new RequestMapper(); + } + + [Theory] + [InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")] + [InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request + [InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")] + [InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")] + [InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")] + public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasScheme(scheme)) + .And(_ => GivenTheInputRequestHasHost(host)) + .And(_ => GivenTheInputRequestHasPath(path)) + .And(_ => GivenTheInputRequestHasQueryString(queryString)) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasUri(expectedUri)) + .BDDfy(); + } + + [Theory] + [InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")] + public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasScheme(scheme)) + .And(_ => GivenTheInputRequestHasHost(host)) + .And(_ => GivenTheInputRequestHasPath(path)) + .And(_ => GivenTheInputRequestHasQueryString(queryString)) + .When(_ => WhenMapped()) + .Then(_ => ThenAnErrorIsReturned()) + .And(_ => ThenTheMappedRequestIsNull()) + .BDDfy(); + } + + [Theory] + [InlineData("GET")] + [InlineData("POST")] + [InlineData("WHATEVER")] + public void Should_map_method(string method) + { + this.Given(_ => GivenTheInputRequestHasMethod(method)) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasMethod(method)) + .BDDfy(); + } + + [Fact] + public void Should_map_all_headers() + { + this.Given(_ => GivenTheInputRequestHasHeaders()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasEachHeader()) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_headers() + { + this.Given(_ => GivenTheInputRequestHasNoHeaders()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoHeaders()) + .BDDfy(); + } + + [Fact] + public void Should_map_content() + { + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasContent("This is my content")) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_content() + { + this.Given(_ => GivenTheInputRequestHasNoContent()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoContent()) + .BDDfy(); + } + + private void GivenTheInputRequestHasMethod(string method) + { + _inputRequest.Method = method; + } + + private void GivenTheInputRequestHasScheme(string scheme) + { + _inputRequest.Scheme = scheme; + } + + private void GivenTheInputRequestHasHost(string host) + { + _inputRequest.Host = new HostString(host); + } + + private void GivenTheInputRequestHasPath(string path) + { + if (path != null) + { + _inputRequest.Path = path; + } + } + + private void GivenTheInputRequestHasQueryString(string querystring) + { + if (querystring != null) + { + _inputRequest.QueryString = new QueryString(querystring); + } + } + + private void GivenTheInputRequestHasAValidUri() + { + GivenTheInputRequestHasScheme("http"); + GivenTheInputRequestHasHost("www.google.com"); + } + + private void GivenTheInputRequestHasHeaders() + { + _inputHeaders = new List>() + { + new KeyValuePair("abc", new StringValues(new string[]{"123","456" })), + new KeyValuePair("def", new StringValues(new string[]{"789","012" })), + }; + + foreach (var inputHeader in _inputHeaders) + { + _inputRequest.Headers.Add(inputHeader); + } + } + + private void GivenTheInputRequestHasNoHeaders() + { + _inputRequest.Headers.Clear(); + } + + private void GivenTheInputRequestHasContent(string content) + { + _inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content)); + } + + private void GivenTheInputRequestHasNoContent() + { + _inputRequest.Body = null; + } + + private void WhenMapped() + { + _mappedRequest = _requestMapper.Map(_inputRequest).GetAwaiter().GetResult(); + } + + private void ThenNoErrorIsReturned() + { + _mappedRequest.IsError.ShouldBeFalse(); + } + + private void ThenAnErrorIsReturned() + { + _mappedRequest.IsError.ShouldBeTrue(); + } + + private void ThenTheMappedRequestHasUri(string expectedUri) + { + _mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri); + } + + private void ThenTheMappedRequestHasMethod(string expectedMethod) + { + _mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod); + } + + private void ThenTheMappedRequestHasEachHeader() + { + _mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count); + foreach(var header in _mappedRequest.Data.Headers) + { + var inputHeader = _inputHeaders.First(h => h.Key == header.Key); + inputHeader.ShouldNotBeNull(); + inputHeader.Value.Count().ShouldBe(header.Value.Count()); + foreach(var inputHeaderValue in inputHeader.Value) + { + header.Value.Any(v => v == inputHeaderValue); + } + } + } + + private void ThenTheMappedRequestHasNoHeaders() + { + _mappedRequest.Data.Headers.Count().ShouldBe(0); + } + + private void ThenTheMappedRequestHasContent(string expectedContent) + { + _mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent); + } + + private void ThenTheMappedRequestHasNoContent() + { + _mappedRequest.Data.Content.ShouldBeNull(); + } + + private void ThenTheMappedRequestIsNull() + { + _mappedRequest.Data.ShouldBeNull(); + } + } +} From b8ee43c737141850e0a8b653e8033bb76aae07d1 Mon Sep 17 00:00:00 2001 From: Juan Carlos Santana Herrera Date: Thu, 20 Apr 2017 11:05:46 +0100 Subject: [PATCH 8/9] Adds the posibility of use final paths in the reroutes of configuration.json file. Adds a unit test method to test the functionality. --- .../UrlPathPlaceholderNameAndValueFinder.cs | 10 +++++----- .../TemplateVariableNameAndValueFinderTests.cs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs b/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs index 249b153e..0859f4c0 100644 --- a/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs @@ -18,8 +18,8 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate])) { var variableName = GetPlaceholderVariableName(upstreamUrlPathTemplate, counterForTemplate); - - var variableValue = GetPlaceholderVariableValue(upstreamUrlPath, counterForUrl); + + var variableValue = GetPlaceholderVariableValue(upstreamUrlPathTemplate, variableName, upstreamUrlPath, counterForUrl); var templateVariableNameAndValue = new UrlPathPlaceholderNameAndValue(variableName, variableValue); @@ -40,15 +40,15 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher return new OkResponse>(templateKeysAndValues); } - private string GetPlaceholderVariableValue(string urlPath, int counterForUrl) + private string GetPlaceholderVariableValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl) { var positionOfNextSlash = urlPath.IndexOf('/', counterForUrl); - if(positionOfNextSlash == -1) + if (positionOfNextSlash == -1 || urlPathTemplate.Trim('/').EndsWith(variableName)) { positionOfNextSlash = urlPath.Length; } - + var variableValue = urlPath.Substring(counterForUrl, positionOfNextSlash - counterForUrl); return variableValue; diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/TemplateVariableNameAndValueFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/TemplateVariableNameAndValueFinderTests.cs index cf71c809..7c73483a 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/TemplateVariableNameAndValueFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/TemplateVariableNameAndValueFinderTests.cs @@ -140,6 +140,21 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher .BDDfy(); } + [Fact] + public void can_match_down_stream_url_with_downstream_template_with_place_holder_to_final_url_path() + { + var expectedTemplates = new List + { + new UrlPathPlaceholderNameAndValue("{finalUrlPath}", "product/products/categories/"), + }; + + this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/categories/")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplate("api/{finalUrlPath}/")) + .When(x => x.WhenIFindTheUrlVariableNamesAndValues()) + .And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates)) + .BDDfy(); + } + private void ThenTheTemplatesVariablesAre(List expectedResults) { foreach (var expectedResult in expectedResults) From 6510f173b9ec263dffa075b3b92374d977f5148a Mon Sep 17 00:00:00 2001 From: Juan Carlos Santana Herrera Date: Sat, 22 Apr 2017 22:06:00 +0100 Subject: [PATCH 9/9] Adds an acceptance test to check the use of placeholder for a final url path. --- test/Ocelot.AcceptanceTests/RoutingTests.cs | 29 ++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index c1a6716c..99c11877 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -46,7 +46,6 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", - } } }; @@ -292,6 +291,34 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_return_response_200_with_placeholder_for_final_url_path() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/{urlPath}", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", + UpstreamHttpMethod = "Get", + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/myApp1Name/api/products/1", 200, "Some Product")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Some Product")) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) { _builder = new WebHostBuilder()