diff --git a/Ocelot.sln b/Ocelot.sln index fb7008d3..6aa429fe 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2024 +VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}" EndProject diff --git a/docs/features/requestaggregation.rst b/docs/features/requestaggregation.rst new file mode 100644 index 00000000..b359eb72 --- /dev/null +++ b/docs/features/requestaggregation.rst @@ -0,0 +1,95 @@ +Request Aggregation +=================== + +Ocelot allow's you to specify Aggregate ReRoutes that compose multiple normal ReRoutes and map their responses into one object. This is usual where you have +a client that is making multiple requests to a server where it could just be one. This feature allows you to start implementing back end for a front end type +architecture with Ocelot. + +This feature was requested as part of `Issue 79 `_ . + +In order to set this up you must do something like the following in your configuration.json. Here we have specified two normal ReRoutes and each one has a Key property. +We then specify an Aggregate that composes the two ReRoutes using their keys in the ReRouteKeys list and says then we have the UpstreamPathTemplate which works like a normal ReRoute. +Obviously you cannot have duplicate UpstreamPathTemplates between ReRoutes and Aggregates. You can use all of Ocelot's normal ReRoute options apart from RequestIdKey (explained in gotchas below). + +.. code-block:: json + + { + "ReRoutes": [ + { + "DownstreamPathTemplate": "/", + "UpstreamPathTemplate": "/laura", + "UpstreamHttpMethod": [ + "Get" + ], + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 51881 + } + ], + "Key": "Laura" + }, + { + "DownstreamPathTemplate": "/", + "UpstreamPathTemplate": "/tom", + "UpstreamHttpMethod": [ + "Get" + ], + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 51882 + } + ], + "Key": "Tom" + } + ], + "Aggregates": [ + { + "ReRouteKeys": [ + "Tom", + "Laura" + ], + "UpstreamPathTemplate": "/" + } + ] + } + +You can also set UpstreamHost and ReRouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other ReRoutes. + +If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows. + +.. code-block:: json + + {"Tom":{"Age": 19},"Laura":{"Age": 25}} + +Gotcha's / Further info +^^^^^^^^^^^^^^^^^^^^^^^ + +At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary +as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just +JSON without any pretty spaces etc. + +All headers will be lost from the downstream services response. + +Ocelot will always return content type application/json with an aggregate request. + +You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track. + +Aggregation only supports the GET HTTP Verb. + +If you downstream services return a 404 the aggregate will just return nothing for that downstream service. +It will not change the aggregate response into a 404 even if all the downstreams return a 404. + +Future +^^^^^^ + +There are loads of cool ways to enchance this such as.. + +What happens when downstream goes slow..should we timeout? +Can we do something like GraphQL where the user chooses what fields are returned? +Can we handle 404 better etc? +Can we make this not just support a JSON dictionary response? + diff --git a/docs/index.rst b/docs/index.rst index c98c2174..3ae28d2e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,13 +18,13 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n :hidden: :caption: Features - features/routing features/configuration + features/routing + features/requestaggregation features/servicediscovery features/authentication features/authorisation features/administration - features/raft features/caching features/qualityofservice features/headerstransformation @@ -35,7 +35,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n features/middlewareinjection features/loadbalancer features/delegatinghandlers - + features/raft .. toctree:: :maxdepth: 2 diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index 1d820600..ffdd1ac6 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -1,12 +1,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.Errors; using Ocelot.Infrastructure.Extensions; -using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; @@ -14,35 +11,29 @@ namespace Ocelot.Authentication.Middleware { public class AuthenticationMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; - private readonly IApplicationBuilder _app; - private readonly IAuthenticationSchemeProvider _authSchemeProvider; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; - public AuthenticationMiddleware(RequestDelegate next, - IApplicationBuilder app, - IRequestScopedDataRepository requestScopedDataRepository, + public AuthenticationMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory) - : base(requestScopedDataRepository) { _next = next; - _app = app; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) + if (IsAuthenticatedRoute(context.DownstreamReRoute)) { - _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); + _logger.LogDebug($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); - var result = await context.AuthenticateAsync(DownstreamRoute.ReRoute.AuthenticationOptions.AuthenticationProviderKey); + var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey); - context.User = result.Principal; + context.HttpContext.User = result.Principal; - if (context.User.Identity.IsAuthenticated) + if (context.HttpContext.User.Identity.IsAuthenticated) { - _logger.LogDebug($"Client has been authenticated for {context.Request.Path}"); + _logger.LogDebug($"Client has been authenticated for {context.HttpContext.Request.Path}"); await _next.Invoke(context); } else @@ -50,22 +41,23 @@ namespace Ocelot.Authentication.Middleware var error = new List { new UnauthenticatedError( - $"Request for authenticated route {context.Request.Path} by {context.User.Identity.Name} was unauthenticated") + $"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.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.LogError($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error.ToErrorString()}"); + + SetPipelineError(context, error); } } else { - _logger.LogTrace($"No authentication needed for {context.Request.Path}"); + _logger.LogTrace($"No authentication needed for {context.HttpContext.Request.Path}"); await _next.Invoke(context); } } - private static bool IsAuthenticatedRoute(ReRoute reRoute) + private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) { return reRoute.IsAuthenticated; } diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs index 4539ba25..868b668b 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Authentication.Middleware { public static class AuthenticationMiddlewareMiddlewareExtensions { - public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseAuthenticationMiddleware(this IOcelotPipelineBuilder builder) { - return builder.UseMiddleware(builder); + return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index f3f55e35..f6c19522 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -9,21 +9,20 @@ namespace Ocelot.Authorisation.Middleware using System.Threading.Tasks; using Errors; using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Middleware; public class AuthorisationMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IClaimsAuthoriser _claimsAuthoriser; private readonly IScopesAuthoriser _scopesAuthoriser; private readonly IOcelotLogger _logger; - public AuthorisationMiddleware(RequestDelegate next, - IRequestScopedDataRepository requestScopedDataRepository, + public AuthorisationMiddleware(OcelotRequestDelegate next, IClaimsAuthoriser claimsAuthoriser, IScopesAuthoriser scopesAuthoriser, IOcelotLoggerFactory loggerFactory) - : base(requestScopedDataRepository) { _next = next; _claimsAuthoriser = claimsAuthoriser; @@ -31,19 +30,19 @@ namespace Ocelot.Authorisation.Middleware _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) + if (IsAuthenticatedRoute(context.DownstreamReRoute)) { _logger.LogDebug("route is authenticated scopes must be checked"); - var authorised = _scopesAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.AuthenticationOptions.AllowedScopes); + var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes); if (authorised.IsError) { _logger.LogDebug("error authorising user scopes"); - SetPipelineError(authorised.Errors); + SetPipelineError(context, authorised.Errors); return; } @@ -55,46 +54,46 @@ namespace Ocelot.Authorisation.Middleware { _logger.LogDebug("user scopes is not authorised setting pipeline error"); - SetPipelineError(new List + SetPipelineError(context, new List { new UnauthorisedError( - $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}") + $"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}") }); } } - if (IsAuthorisedRoute(DownstreamRoute.ReRoute)) + if (IsAuthorisedRoute(context.DownstreamReRoute)) { _logger.LogDebug("route is authorised"); - var authorised = _claimsAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.RouteClaimsRequirement); + var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement); if (authorised.IsError) { - _logger.LogDebug($"Error whilst authorising {context.User.Identity.Name} for {context.User.Identity.Name}. Setting pipeline error"); + _logger.LogDebug($"Error whilst authorising {context.HttpContext.User.Identity.Name} for {context.HttpContext.User.Identity.Name}. Setting pipeline error"); - SetPipelineError(authorised.Errors); + SetPipelineError(context, authorised.Errors); return; } if (IsAuthorised(authorised)) { - _logger.LogDebug($"{context.User.Identity.Name} has succesfully been authorised for {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Calling next middleware"); + _logger.LogDebug($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Calling next middleware"); await _next.Invoke(context); } else { - _logger.LogDebug($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Setting pipeline error"); + _logger.LogDebug($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Setting pipeline error"); - SetPipelineError(new List + SetPipelineError(context, new List { - new UnauthorisedError($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}") + new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}") }); } } else { - _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); + _logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); await _next.Invoke(context); } } @@ -104,12 +103,12 @@ namespace Ocelot.Authorisation.Middleware return authorised.Data; } - private static bool IsAuthenticatedRoute(ReRoute reRoute) + private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) { return reRoute.IsAuthenticated; } - private static bool IsAuthorisedRoute(ReRoute reRoute) + private static bool IsAuthorisedRoute(DownstreamReRoute reRoute) { return reRoute.IsAuthorised; } diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs index ca1fc945..ffc30177 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs @@ -1,12 +1,14 @@ +using Ocelot.Middleware.Pipeline; + namespace Ocelot.Authorisation.Middleware { using Microsoft.AspNetCore.Builder; public static class AuthorisationMiddlewareMiddlewareExtensions { - public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index 07f7445e..873c4cf7 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -7,22 +7,21 @@ using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; using System.IO; +using Ocelot.DownstreamRouteFinder.Middleware; namespace Ocelot.Cache.Middleware { public class OutputCacheMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly IOcelotCache _outputCache; private readonly IRegionCreator _regionCreator; - public OutputCacheMiddleware(RequestDelegate next, + public OutputCacheMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository scopedDataRepository, IOcelotCache outputCache, IRegionCreator regionCreator) - : base(scopedDataRepository) { _next = next; _outputCache = outputCache; @@ -30,26 +29,26 @@ namespace Ocelot.Cache.Middleware _regionCreator = regionCreator; } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (!DownstreamRoute.ReRoute.IsCached) + if (!context.DownstreamReRoute.IsCached) { await _next.Invoke(context); return; } - var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}"; + var downstreamUrlKey = $"{context.DownstreamRequest.Method.Method}-{context.DownstreamRequest.RequestUri.OriginalString}"; _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey); - var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region); + var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region); if (cached != null) { _logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey); var response = CreateHttpResponseMessage(cached); - SetHttpResponseMessageThisRequest(response); + SetHttpResponseMessageThisRequest(context, response); _logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey); @@ -60,20 +59,25 @@ namespace Ocelot.Cache.Middleware await _next.Invoke(context); - if (PipelineError) + if (context.IsError) { _logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey); return; } - cached = await CreateCachedResponse(HttpResponseMessage); + cached = await CreateCachedResponse(context.DownstreamResponse); - _outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region); + _outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region); _logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey); } + private void SetHttpResponseMessageThisRequest(DownstreamContext context, HttpResponseMessage response) + { + context.DownstreamResponse = response; + } + internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached) { if (cached == null) diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs index 89e343a0..e28df6f0 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs @@ -1,10 +1,11 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Cache.Middleware { public static class OutputCacheMiddlewareExtensions { - public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseOutputCacheMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } diff --git a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs index afd2fae2..5161b247 100644 --- a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs +++ b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; @@ -9,34 +10,32 @@ namespace Ocelot.Claims.Middleware { public class ClaimsBuilderMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IAddClaimsToRequest _addClaimsToRequest; private readonly IOcelotLogger _logger; - public ClaimsBuilderMiddleware(RequestDelegate next, - IRequestScopedDataRepository requestScopedDataRepository, + public ClaimsBuilderMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, IAddClaimsToRequest addClaimsToRequest) - : base(requestScopedDataRepository) { _next = next; _addClaimsToRequest = addClaimsToRequest; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (DownstreamRoute.ReRoute.ClaimsToClaims.Any()) + if (context.DownstreamReRoute.ClaimsToClaims.Any()) { _logger.LogDebug("this route has instructions to convert claims to other claims"); - var result = _addClaimsToRequest.SetClaimsOnContext(DownstreamRoute.ReRoute.ClaimsToClaims, context); + var result = _addClaimsToRequest.SetClaimsOnContext(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext); if (result.IsError) { _logger.LogDebug("error converting claims to other claims, setting pipeline error"); - SetPipelineError(result.Errors); + SetPipelineError(context, result.Errors); return; } } diff --git a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs index 099a0add..0790ec7c 100644 --- a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Claims.Middleware { public static class ClaimsBuilderMiddlewareExtensions { - public static IApplicationBuilder UseClaimsBuilderMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseClaimsBuilderMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs new file mode 100644 index 00000000..bc860424 --- /dev/null +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -0,0 +1,251 @@ +using System.Collections.Generic; +using System.Net.Http; +using Ocelot.Values; +using System.Linq; + +namespace Ocelot.Configuration.Builder +{ + public class DownstreamReRouteBuilder + { + private AuthenticationOptions _authenticationOptions; + private string _reRouteKey; + private string _downstreamPathTemplate; + private string _upstreamTemplate; + private UpstreamPathTemplate _upstreamTemplatePattern; + private List _upstreamHttpMethod; + private bool _isAuthenticated; + private List _claimsToHeaders; + private List _claimToClaims; + private Dictionary _routeClaimRequirement; + private bool _isAuthorised; + private List _claimToQueries; + private string _requestIdHeaderKey; + private bool _isCached; + private CacheOptions _fileCacheOptions; + private string _downstreamScheme; + private string _loadBalancer; + private bool _useQos; + private QoSOptions _qosOptions; + private HttpHandlerOptions _httpHandlerOptions; + private bool _enableRateLimiting; + private RateLimitOptions _rateLimitOptions; + private bool _useServiceDiscovery; + private string _serviceName; + private List _upstreamHeaderFindAndReplace; + private List _downstreamHeaderFindAndReplace; + private readonly List _downstreamAddresses; + private string _upstreamHost; + private string _key; + + public DownstreamReRouteBuilder() + { + _downstreamAddresses = new List(); + } + + public DownstreamReRouteBuilder WithDownstreamAddresses(List downstreamAddresses) + { + _downstreamAddresses.AddRange(downstreamAddresses); + return this; + } + + public DownstreamReRouteBuilder WithUpstreamHost(string upstreamAddresses) + { + _upstreamHost = upstreamAddresses; + return this; + } + + public DownstreamReRouteBuilder WithLoadBalancer(string loadBalancer) + { + _loadBalancer = loadBalancer; + return this; + } + + public DownstreamReRouteBuilder WithDownstreamScheme(string downstreamScheme) + { + _downstreamScheme = downstreamScheme; + return this; + } + + public DownstreamReRouteBuilder WithDownstreamPathTemplate(string input) + { + _downstreamPathTemplate = input; + return this; + } + + public DownstreamReRouteBuilder WithUpstreamPathTemplate(string input) + { + _upstreamTemplate = input; + return this; + } + + public DownstreamReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input) + { + _upstreamTemplatePattern = input; + return this; + } + + public DownstreamReRouteBuilder WithUpstreamHttpMethod(List input) + { + _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); + return this; + } + + public DownstreamReRouteBuilder WithIsAuthenticated(bool input) + { + _isAuthenticated = input; + return this; + } + + public DownstreamReRouteBuilder WithIsAuthorised(bool input) + { + _isAuthorised = input; + return this; + } + + public DownstreamReRouteBuilder WithRequestIdKey(string input) + { + _requestIdHeaderKey = input; + return this; + } + + public DownstreamReRouteBuilder WithClaimsToHeaders(List input) + { + _claimsToHeaders = input; + return this; + } + + public DownstreamReRouteBuilder WithClaimsToClaims(List input) + { + _claimToClaims = input; + return this; + } + + public DownstreamReRouteBuilder WithRouteClaimsRequirement(Dictionary input) + { + _routeClaimRequirement = input; + return this; + } + + public DownstreamReRouteBuilder WithClaimsToQueries(List input) + { + _claimToQueries = input; + return this; + } + + public DownstreamReRouteBuilder WithIsCached(bool input) + { + _isCached = input; + return this; + } + + public DownstreamReRouteBuilder WithCacheOptions(CacheOptions input) + { + _fileCacheOptions = input; + return this; + } + + public DownstreamReRouteBuilder WithIsQos(bool input) + { + _useQos = input; + return this; + } + + public DownstreamReRouteBuilder WithQosOptions(QoSOptions input) + { + _qosOptions = input; + return this; + } + + public DownstreamReRouteBuilder WithReRouteKey(string reRouteKey) + { + _reRouteKey = reRouteKey; + return this; + } + + public DownstreamReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions) + { + _authenticationOptions = authenticationOptions; + return this; + } + + public DownstreamReRouteBuilder WithEnableRateLimiting(bool input) + { + _enableRateLimiting = input; + return this; + } + + public DownstreamReRouteBuilder WithRateLimitOptions(RateLimitOptions input) + { + _rateLimitOptions = input; + return this; + } + + public DownstreamReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) + { + _httpHandlerOptions = input; + return this; + } + + public DownstreamReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) + { + _useServiceDiscovery = useServiceDiscovery; + return this; + } + + public DownstreamReRouteBuilder WithServiceName(string serviceName) + { + _serviceName = serviceName; + return this; + } + + public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List upstreamHeaderFindAndReplace) + { + _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace; + return this; + } + + public DownstreamReRouteBuilder WithDownstreamHeaderFindAndReplace(List downstreamHeaderFindAndReplace) + { + _downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace; + return this; + } + + public DownstreamReRouteBuilder WithKey(string key) + { + _key = key; + return this; + } + + + public DownstreamReRoute Build() + { + return new DownstreamReRoute( + _key, + new PathTemplate(_upstreamTemplate), + _upstreamHeaderFindAndReplace, + _downstreamHeaderFindAndReplace, + _downstreamAddresses, + _serviceName, + _httpHandlerOptions, + _useServiceDiscovery, + _enableRateLimiting, + _useQos, + _qosOptions, + _downstreamScheme, + _requestIdHeaderKey, + _isCached, + _fileCacheOptions, + _loadBalancer, + _rateLimitOptions, + _routeClaimRequirement, + _claimToQueries, + _claimsToHeaders, + _claimToClaims, + _isAuthenticated, + _isAuthorised, + _authenticationOptions, + new PathTemplate(_downstreamPathTemplate), + _reRouteKey); + } + } +} diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index fffd8daf..0d059932 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -9,43 +9,26 @@ namespace Ocelot.Configuration.Builder { public class ReRouteBuilder { - private AuthenticationOptions _authenticationOptions; - private string _reRouteKey; - private string _downstreamPathTemplate; private string _upstreamTemplate; private UpstreamPathTemplate _upstreamTemplatePattern; private List _upstreamHttpMethod; - private bool _isAuthenticated; - private List _configHeaderExtractorProperties; - private List _claimToClaims; - private Dictionary _routeClaimRequirement; - private bool _isAuthorised; - private List _claimToQueries; - private string _requestIdHeaderKey; - private bool _isCached; - private CacheOptions _fileCacheOptions; - private string _downstreamScheme; - private string _loadBalancer; - private bool _useQos; - private QoSOptions _qosOptions; - private HttpHandlerOptions _httpHandlerOptions; - private bool _enableRateLimiting; - private RateLimitOptions _rateLimitOptions; - private bool _useServiceDiscovery; - private string _serviceName; - private List _upstreamHeaderFindAndReplace; - private List _downstreamHeaderFindAndReplace; - private readonly List _downstreamAddresses; private string _upstreamHost; + private List _downstreamReRoutes; public ReRouteBuilder() { - _downstreamAddresses = new List(); + _downstreamReRoutes = new List(); } - public ReRouteBuilder WithDownstreamAddresses(List downstreamAddresses) + public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value) { - _downstreamAddresses.AddRange(downstreamAddresses); + _downstreamReRoutes.Add(value); + return this; + } + + public ReRouteBuilder WithDownstreamReRoutes(List value) + { + _downstreamReRoutes = value; return this; } @@ -55,24 +38,6 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithLoadBalancer(string loadBalancer) - { - _loadBalancer = loadBalancer; - return this; - } - - public ReRouteBuilder WithDownstreamScheme(string downstreamScheme) - { - _downstreamScheme = downstreamScheme; - return this; - } - - public ReRouteBuilder WithDownstreamPathTemplate(string input) - { - _downstreamPathTemplate = input; - return this; - } - public ReRouteBuilder WithUpstreamPathTemplate(string input) { _upstreamTemplate = input; @@ -91,158 +56,15 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithIsAuthenticated(bool input) - { - _isAuthenticated = input; - return this; - } - - public ReRouteBuilder WithIsAuthorised(bool input) - { - _isAuthorised = input; - return this; - } - - public ReRouteBuilder WithRequestIdKey(string input) - { - _requestIdHeaderKey = input; - return this; - } - - public ReRouteBuilder WithClaimsToHeaders(List input) - { - _configHeaderExtractorProperties = input; - return this; - } - - public ReRouteBuilder WithClaimsToClaims(List input) - { - _claimToClaims = input; - return this; - } - - public ReRouteBuilder WithRouteClaimsRequirement(Dictionary input) - { - _routeClaimRequirement = input; - return this; - } - - public ReRouteBuilder WithClaimsToQueries(List input) - { - _claimToQueries = input; - return this; - } - - public ReRouteBuilder WithIsCached(bool input) - { - _isCached = input; - return this; - } - - public ReRouteBuilder WithCacheOptions(CacheOptions input) - { - _fileCacheOptions = input; - return this; - } - - public ReRouteBuilder WithIsQos(bool input) - { - _useQos = input; - return this; - } - - public ReRouteBuilder WithQosOptions(QoSOptions input) - { - _qosOptions = input; - return this; - } - - public ReRouteBuilder WithReRouteKey(string reRouteKey) - { - _reRouteKey = reRouteKey; - return this; - } - - public ReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions) - { - _authenticationOptions = authenticationOptions; - return this; - } - - public ReRouteBuilder WithEnableRateLimiting(bool input) - { - _enableRateLimiting = input; - return this; - } - - public ReRouteBuilder WithRateLimitOptions(RateLimitOptions input) - { - _rateLimitOptions = input; - return this; - } - - public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) - { - _httpHandlerOptions = input; - return this; - } - - public ReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) - { - _useServiceDiscovery = useServiceDiscovery; - return this; - } - - public ReRouteBuilder WithServiceName(string serviceName) - { - _serviceName = serviceName; - return this; - } - - public ReRouteBuilder WithUpstreamHeaderFindAndReplace(List upstreamHeaderFindAndReplace) - { - _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace; - return this; - } - - public ReRouteBuilder WithDownstreamHeaderFindAndReplace(List downstreamHeaderFindAndReplace) - { - _downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace; - return this; - } - - public ReRoute Build() { return new ReRoute( - new PathTemplate(_downstreamPathTemplate), + _downstreamReRoutes, new PathTemplate(_upstreamTemplate), _upstreamHttpMethod, _upstreamTemplatePattern, - _isAuthenticated, - _authenticationOptions, - _configHeaderExtractorProperties, - _claimToClaims, - _routeClaimRequirement, - _isAuthorised, - _claimToQueries, - _requestIdHeaderKey, - _isCached, - _fileCacheOptions, - _downstreamScheme, - _loadBalancer, - _reRouteKey, - _useQos, - _qosOptions, - _enableRateLimiting, - _rateLimitOptions, - _httpHandlerOptions, - _useServiceDiscovery, - _serviceName, - _upstreamHeaderFindAndReplace, - _downstreamHeaderFindAndReplace, - _downstreamAddresses, - _upstreamHost); + _upstreamHost + ); } } } diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index a0d5a1fe..fbc58020 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -1,19 +1,14 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Ocelot.Cache; using Ocelot.Configuration.Builder; using Ocelot.Configuration.File; -using Ocelot.Configuration.Parser; using Ocelot.Configuration.Validator; using Ocelot.DependencyInjection; -using Ocelot.LoadBalancer; -using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Logging; -using Ocelot.Requester.QoS; using Ocelot.Responses; namespace Ocelot.Configuration.Creator @@ -97,7 +92,16 @@ namespace Ocelot.Configuration.Creator foreach (var reRoute in fileConfiguration.ReRoutes) { - var ocelotReRoute = SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration); + var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration); + + var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute); + + reRoutes.Add(ocelotReRoute); + } + + foreach (var aggregate in fileConfiguration.Aggregates) + { + var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration); reRoutes.Add(ocelotReRoute); } @@ -108,7 +112,48 @@ namespace Ocelot.Configuration.Creator return new OkResponse(config); } - private ReRoute SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + public ReRoute SetUpAggregateReRoute(List reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration) + { + var applicableReRoutes = reRoutes + .SelectMany(x => x.DownstreamReRoute) + .Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key)) + .ToList(); + + if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count) + { + //todo - log or throw or return error whatever? + } + + //make another re route out of these + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute); + + var reRoute = new ReRouteBuilder() + .WithUpstreamPathTemplate(aggregateReRoute.UpstreamPathTemplate) + .WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod) + .WithUpstreamTemplatePattern(upstreamTemplatePattern) + .WithDownstreamReRoutes(applicableReRoutes) + .WithUpstreamHost(aggregateReRoute.UpstreamHost) + .Build(); + + return reRoute; + } + + private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes) + { + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); + + var reRoute = new ReRouteBuilder() + .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamTemplatePattern(upstreamTemplatePattern) + .WithDownstreamReRoute(downstreamReRoutes) + .WithUpstreamHost(fileReRoute.UpstreamHost) + .Build(); + + return reRoute; + } + + private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) { var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); @@ -138,7 +183,8 @@ namespace Ocelot.Configuration.Creator var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() + .WithKey(fileReRoute.Key) .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) diff --git a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs index b40e5e3e..b8303189 100644 --- a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs @@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator { public interface IUpstreamTemplatePatternCreator { - UpstreamPathTemplate Create(FileReRoute reRoute); + UpstreamPathTemplate Create(IReRoute reRoute); } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs index cc6ebdc7..1700472a 100644 --- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs @@ -12,7 +12,7 @@ namespace Ocelot.Configuration.Creator private const string RegExForwardSlashOnly = "^/$"; private const string RegExForwardSlashAndOnePlaceHolder = "^/.*"; - public UpstreamPathTemplate Create(FileReRoute reRoute) + public UpstreamPathTemplate Create(IReRoute reRoute) { var upstreamTemplate = reRoute.UpstreamPathTemplate; @@ -73,4 +73,4 @@ namespace Ocelot.Configuration.Creator return upstreamTemplate[i] == '{'; } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs new file mode 100644 index 00000000..9322d5cf --- /dev/null +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using Ocelot.Values; + +namespace Ocelot.Configuration +{ + public class DownstreamReRoute + { + public DownstreamReRoute( + string key, + PathTemplate upstreamPathTemplate, + List upstreamHeadersFindAndReplace, + List downstreamHeadersFindAndReplace, + List downstreamAddresses, + string serviceName, + HttpHandlerOptions httpHandlerOptions, + bool useServiceDiscovery, + bool enableEndpointEndpointRateLimiting, + bool isQos, + QoSOptions qosOptionsOptions, + string downstreamScheme, + string requestIdKey, + bool isCached, + CacheOptions cacheOptions, + string loadBalancer, + RateLimitOptions rateLimitOptions, + Dictionary routeClaimsRequirement, + List claimsToQueries, + List claimsToHeaders, + List claimsToClaims, + bool isAuthenticated, + bool isAuthorised, + AuthenticationOptions authenticationOptions, + PathTemplate downstreamPathTemplate, + string reRouteKey) + { + Key = key; + UpstreamPathTemplate = upstreamPathTemplate; + UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List(); + DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List(); + DownstreamAddresses = downstreamAddresses ?? new List(); + ServiceName = serviceName; + HttpHandlerOptions = httpHandlerOptions; + UseServiceDiscovery = useServiceDiscovery; + EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting; + IsQos = isQos; + QosOptionsOptions = qosOptionsOptions; + DownstreamScheme = downstreamScheme; + RequestIdKey = requestIdKey; + IsCached = isCached; + CacheOptions = cacheOptions; + LoadBalancer = loadBalancer; + RateLimitOptions = rateLimitOptions; + RouteClaimsRequirement = routeClaimsRequirement; + ClaimsToQueries = claimsToQueries ?? new List(); + ClaimsToHeaders = claimsToHeaders ?? new List(); + ClaimsToClaims = claimsToClaims ?? new List(); + IsAuthenticated = isAuthenticated; + IsAuthorised = isAuthorised; + AuthenticationOptions = authenticationOptions; + DownstreamPathTemplate = downstreamPathTemplate; + ReRouteKey = reRouteKey; + } + + public string Key { get; private set; } + public PathTemplate UpstreamPathTemplate { get;private set; } + public List UpstreamHeadersFindAndReplace {get;private set;} + public List DownstreamHeadersFindAndReplace { get; private set; } + public List DownstreamAddresses { get; private set; } + public string ServiceName { get; private set; } + public HttpHandlerOptions HttpHandlerOptions { get; private set; } + public bool UseServiceDiscovery { get; private set; } + public bool EnableEndpointEndpointRateLimiting { get; private set; } + public bool IsQos { get; private set; } + public QoSOptions QosOptionsOptions { get; private set; } + public string DownstreamScheme { get; private set; } + public string RequestIdKey { get; private set; } + public bool IsCached { get; private set; } + public CacheOptions CacheOptions { get; private set; } + public string LoadBalancer { get; private set; } + public RateLimitOptions RateLimitOptions { get; private set; } + public Dictionary RouteClaimsRequirement { get; private set; } + public List ClaimsToQueries { get; private set; } + public List ClaimsToHeaders { get; private set; } + public List ClaimsToClaims { get; private set; } + public bool IsAuthenticated { get; private set; } + public bool IsAuthorised { get; private set; } + public AuthenticationOptions AuthenticationOptions { get; private set; } + public PathTemplate DownstreamPathTemplate { get; private set; } + public string ReRouteKey { get; private set; } + } +} diff --git a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs new file mode 100644 index 00000000..8c9eabba --- /dev/null +++ b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Ocelot.Configuration.File +{ + public class FileAggregateReRoute : IReRoute + { + public List ReRouteKeys { get;set; } + public string UpstreamPathTemplate { get;set; } + public string UpstreamHost { get; set; } + public bool ReRouteIsCaseSensitive { get; set; } + + // Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :) + public List UpstreamHttpMethod + { + get { return new List {"Get"}; } + } + } +} diff --git a/src/Ocelot/Configuration/File/FileConfiguration.cs b/src/Ocelot/Configuration/File/FileConfiguration.cs index 2c4120aa..6d73ce3f 100644 --- a/src/Ocelot/Configuration/File/FileConfiguration.cs +++ b/src/Ocelot/Configuration/File/FileConfiguration.cs @@ -8,9 +8,12 @@ namespace Ocelot.Configuration.File { ReRoutes = new List(); GlobalConfiguration = new FileGlobalConfiguration(); + Aggregates = new List(); } public List ReRoutes { get; set; } + // Seperate field for aggregates because this let's you re-use ReRoutes in multiple Aggregates + public List Aggregates { get;set; } public FileGlobalConfiguration GlobalConfiguration { get; set; } } } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 9ca6c7c9..7169e62c 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -2,7 +2,7 @@ namespace Ocelot.Configuration.File { - public class FileReRoute + public class FileReRoute : IReRoute { public FileReRoute() { @@ -36,12 +36,13 @@ namespace Ocelot.Configuration.File public string ServiceName { get; set; } public string DownstreamScheme {get;set;} public FileQoSOptions QoSOptions { get; set; } - public string LoadBalancer {get;set;} + public string LoadBalancer { get;set; } public FileRateLimitRule RateLimitOptions { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; } public FileHttpHandlerOptions HttpHandlerOptions { get; set; } - public bool UseServiceDiscovery {get;set;} + public bool UseServiceDiscovery { get;set; } public List DownstreamHostAndPorts {get;set;} public string UpstreamHost { get; set; } + public string Key { get;set; } } } diff --git a/src/Ocelot/Configuration/File/IReRoute.cs b/src/Ocelot/Configuration/File/IReRoute.cs new file mode 100644 index 00000000..69128d3a --- /dev/null +++ b/src/Ocelot/Configuration/File/IReRoute.cs @@ -0,0 +1,8 @@ +namespace Ocelot.Configuration.File +{ + public interface IReRoute + { + string UpstreamPathTemplate { get; set; } + bool ReRouteIsCaseSensitive { get; set; } + } +} diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 607e971c..47f26291 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -1,98 +1,30 @@ using System.Collections.Generic; using System.Net.Http; using Ocelot.Configuration.Creator; +using Ocelot.Requester.QoS; using Ocelot.Values; namespace Ocelot.Configuration { public class ReRoute { - public ReRoute(PathTemplate downstreamPathTemplate, + public ReRoute(List downstreamReRoute, PathTemplate upstreamPathTemplate, List upstreamHttpMethod, UpstreamPathTemplate upstreamTemplatePattern, - bool isAuthenticated, - AuthenticationOptions authenticationOptions, - List claimsToHeaders, - List claimsToClaims, - Dictionary routeClaimsRequirement, - bool isAuthorised, - List claimsToQueries, - string requestIdKey, - bool isCached, - CacheOptions cacheOptions, - string downstreamScheme, - string loadBalancer, - string reRouteKey, - bool isQos, - QoSOptions qosOptions, - bool enableEndpointRateLimiting, - RateLimitOptions ratelimitOptions, - HttpHandlerOptions httpHandlerOptions, - bool useServiceDiscovery, - string serviceName, - List upstreamHeadersFindAndReplace, - List downstreamHeadersFindAndReplace, - List downstreamAddresses, string upstreamHost) { UpstreamHost = upstreamHost; - DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List(); - UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List(); - ServiceName = serviceName; - UseServiceDiscovery = useServiceDiscovery; - ReRouteKey = reRouteKey; - LoadBalancer = loadBalancer; - DownstreamAddresses = downstreamAddresses ?? new List(); - DownstreamPathTemplate = downstreamPathTemplate; + DownstreamReRoute = downstreamReRoute; UpstreamPathTemplate = upstreamPathTemplate; UpstreamHttpMethod = upstreamHttpMethod; UpstreamTemplatePattern = upstreamTemplatePattern; - IsAuthenticated = isAuthenticated; - AuthenticationOptions = authenticationOptions; - RouteClaimsRequirement = routeClaimsRequirement; - IsAuthorised = isAuthorised; - RequestIdKey = requestIdKey; - IsCached = isCached; - CacheOptions = cacheOptions; - ClaimsToQueries = claimsToQueries ?? new List(); - ClaimsToClaims = claimsToClaims ?? new List(); - ClaimsToHeaders = claimsToHeaders ?? new List(); - DownstreamScheme = downstreamScheme; - IsQos = isQos; - QosOptionsOptions = qosOptions; - EnableEndpointEndpointRateLimiting = enableEndpointRateLimiting; - RateLimitOptions = ratelimitOptions; - HttpHandlerOptions = httpHandlerOptions; } - public string ReRouteKey {get;private set;} - public PathTemplate DownstreamPathTemplate { get; private set; } public PathTemplate UpstreamPathTemplate { get; private set; } public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; } public List UpstreamHttpMethod { get; private set; } - public bool IsAuthenticated { get; private set; } - public bool IsAuthorised { get; private set; } - public AuthenticationOptions AuthenticationOptions { get; private set; } - public List ClaimsToQueries { get; private set; } - public List ClaimsToHeaders { get; private set; } - public List ClaimsToClaims { get; private set; } - public Dictionary RouteClaimsRequirement { get; private set; } - public string RequestIdKey { get; private set; } - public bool IsCached { get; private set; } - public CacheOptions CacheOptions { get; private set; } - public string DownstreamScheme {get;private set;} - public bool IsQos { get; private set; } - public QoSOptions QosOptionsOptions { get; private set; } - public string LoadBalancer {get;private set;} - public bool EnableEndpointEndpointRateLimiting { get; private set; } - public RateLimitOptions RateLimitOptions { get; private set; } - public HttpHandlerOptions HttpHandlerOptions { get; private set; } - public bool UseServiceDiscovery {get;private set;} - public string ServiceName {get;private set;} - public List UpstreamHeadersFindAndReplace {get;private set;} - public List DownstreamHeadersFindAndReplace {get;private set;} - public List DownstreamAddresses {get;private set;} public string UpstreamHost { get; private set; } + public List DownstreamReRoute { get; private set; } } } diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs index 0a150a02..58da7c4f 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs @@ -19,6 +19,29 @@ namespace Ocelot.Configuration.Validator RuleForEach(configuration => configuration.ReRoutes) .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes)) .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate"); + + RuleForEach(configuration => configuration.ReRoutes) + .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates)) + .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates)) + .WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes)) + .WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct Key property"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes)) + .WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates"); + } + + private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List reRoutes) + { + var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key)); + + return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count; } public async Task> IsValid(FileConfiguration configuration) @@ -37,10 +60,21 @@ namespace Ocelot.Configuration.Validator return new OkResponse(result); } - private static bool IsNotDuplicateIn(FileReRoute reRoute, List reRoutes) + private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute, + List reRoutes) + { + var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key)); + + return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey)); + } + + private static bool IsNotDuplicateIn(FileReRoute reRoute, + List reRoutes) { var matchingReRoutes = reRoutes - .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)).ToList(); + .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate + && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)) + .ToList(); if(matchingReRoutes.Count == 1) { @@ -62,5 +96,27 @@ namespace Ocelot.Configuration.Validator return true; } + + private static bool IsNotDuplicateIn(FileReRoute reRoute, + List aggregateReRoutes) + { + var duplicate = aggregateReRoutes + .Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate + && a.UpstreamHost == reRoute.UpstreamHost + && reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get")); + + return !duplicate; + } + + private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute, + List aggregateReRoutes) + { + var matchingReRoutes = aggregateReRoutes + .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate + && r.UpstreamHost == reRoute.UpstreamHost) + .ToList(); + + return matchingReRoutes.Count <= 1; + } } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index d51b8a8d..3dde5d54 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Options; +using Ocelot.Middleware.Multiplexer; namespace Ocelot.DependencyInjection { @@ -30,7 +31,6 @@ namespace Ocelot.DependencyInjection using Ocelot.Middleware; using Ocelot.QueryStrings; using Ocelot.RateLimit; - using Ocelot.Request.Builder; using Ocelot.Request.Mapper; using Ocelot.Requester; using Ocelot.Requester.QoS; @@ -114,7 +114,6 @@ namespace Ocelot.DependencyInjection _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); - _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); @@ -126,7 +125,7 @@ namespace Ocelot.DependencyInjection // 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 - _services.TryAddSingleton(); + _services.TryAddSingleton(); _services.TryAddSingleton(); _services.AddMemoryCache(); _services.TryAddSingleton(); @@ -149,6 +148,8 @@ namespace Ocelot.DependencyInjection _provider = new DelegatingHandlerHandlerProvider(); _services.TryAddSingleton(_provider); _services.AddTransient(); + _services.TryAddSingleton(); + _services.TryAddSingleton(); } public IOcelotAdministrationBuilder AddAdministration(string path, string secret) diff --git a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs b/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs index f2e219cb..ed04daef 100644 --- a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs +++ b/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs @@ -13,6 +13,5 @@ namespace Ocelot.DownstreamRouteFinder } public List TemplatePlaceholderNameAndValues { get; private set; } public ReRoute ReRoute { get; private set; } - public object UpstreamHeadersFindAndReplace {get;private set;} } } \ No newline at end of file diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs index 24765fb4..4b467b95 100644 --- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs +++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs @@ -1,71 +1,71 @@ -using System.Linq; -using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using Ocelot.Configuration; using Ocelot.Configuration.Provider; using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.Infrastructure.Extensions; -using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; namespace Ocelot.DownstreamRouteFinder.Middleware { public class DownstreamRouteFinderMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IOcelotLogger _logger; private readonly IOcelotConfigurationProvider _configProvider; + private readonly IMultiplexer _multiplexer; - public DownstreamRouteFinderMiddleware(RequestDelegate next, + public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IDownstreamRouteFinder downstreamRouteFinder, - IRequestScopedDataRepository requestScopedDataRepository, - IOcelotConfigurationProvider configProvider) - :base(requestScopedDataRepository) + IDownstreamRouteFinder downstreamRouteFinder, + IOcelotConfigurationProvider configProvider, + IMultiplexer multiplexer) { _configProvider = configProvider; + _multiplexer = multiplexer; _next = next; _downstreamRouteFinder = downstreamRouteFinder; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - var upstreamUrlPath = context.Request.Path.ToString(); + var upstreamUrlPath = context.HttpContext.Request.Path.ToString(); - var upstreamHost = context.Request.Headers["Host"]; + var upstreamHost = context.HttpContext.Request.Headers["Host"]; - var configuration = await _configProvider.Get(); - - if(configuration.IsError) + var configuration = await _configProvider.Get(); + + if (configuration.IsError) { _logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}"); - SetPipelineError(configuration.Errors); + SetPipelineError(context, configuration.Errors); return; } - SetServiceProviderConfigurationForThisRequest(configuration.Data.ServiceProviderConfiguration); + context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration; _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath); - var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method, configuration.Data, upstreamHost); + var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.HttpContext.Request.Method, configuration.Data, upstreamHost); if (downstreamRoute.IsError) { _logger.LogError($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}"); - SetPipelineError(downstreamRoute.Errors); + SetPipelineError(context, downstreamRoute.Errors); return; } - _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamPathTemplate); + //todo - put this back in + // _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamReRoute.DownstreamPathTemplate); - SetDownstreamRouteForThisRequest(downstreamRoute.Data); + context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues; - await _next.Invoke(context); + await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next); } } } diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs index 81d2dd2d..13dacac8 100644 --- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs +++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.DownstreamRouteFinder.Middleware { public static class DownstreamRouteFinderMiddlewareExtensions { - public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseDownstreamRouteFinderMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index 01a388c5..5b5e8346 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -5,22 +5,21 @@ using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; using System; +using Ocelot.DownstreamRouteFinder.Middleware; namespace Ocelot.DownstreamUrlCreator.Middleware { public class DownstreamUrlCreatorMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IDownstreamPathPlaceholderReplacer _replacer; private readonly IOcelotLogger _logger; private readonly IUrlBuilder _urlBuilder; - public DownstreamUrlCreatorMiddleware(RequestDelegate next, + public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, IDownstreamPathPlaceholderReplacer replacer, - IRequestScopedDataRepository requestScopedDataRepository, IUrlBuilder urlBuilder) - :base(requestScopedDataRepository) { _next = next; _replacer = replacer; @@ -28,30 +27,30 @@ namespace Ocelot.DownstreamUrlCreator.Middleware _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { var dsPath = _replacer - .Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues); + .Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues); if (dsPath.IsError) { _logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error"); - SetPipelineError(dsPath.Errors); + SetPipelineError(context, dsPath.Errors); return; } - var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri) + var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri) { Path = dsPath.Data.Value, - Scheme = DownstreamRoute.ReRoute.DownstreamScheme + Scheme = context.DownstreamReRoute.DownstreamScheme }; - DownstreamRequest.RequestUri = uriBuilder.Uri; + context.DownstreamRequest.RequestUri = uriBuilder.Uri; - _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri); + _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", context.DownstreamRequest.RequestUri); await _next.Invoke(context); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs index 238bc7ef..671636c5 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.DownstreamUrlCreator.Middleware { public static class DownstreamUrlCreatorMiddlewareExtensions { - public static IApplicationBuilder UseDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseDownstreamUrlCreatorMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs index ee1f0de5..9b49c84a 100644 --- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs +++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using Ocelot.Configuration.Provider; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.Extensions; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; @@ -16,24 +17,23 @@ namespace Ocelot.Errors.Middleware /// public class ExceptionHandlerMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; - private readonly IRequestScopedDataRepository _requestScopedDataRepository; - private readonly IOcelotConfigurationProvider _configProvider; + private readonly IOcelotConfigurationProvider _provider; + private readonly IRequestScopedDataRepository _repo; - public ExceptionHandlerMiddleware(RequestDelegate next, + public ExceptionHandlerMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, - IOcelotConfigurationProvider configProvider) - :base(requestScopedDataRepository) + IOcelotConfigurationProvider provider, + IRequestScopedDataRepository repo) { - _configProvider = configProvider; + _provider = provider; + _repo = repo; _next = next; - _requestScopedDataRepository = requestScopedDataRepository; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { try { @@ -57,12 +57,12 @@ namespace Ocelot.Errors.Middleware _logger.LogDebug("ocelot pipeline finished"); } - private async Task TrySetGlobalRequestId(HttpContext context) + private async Task TrySetGlobalRequestId(DownstreamContext context) { //try and get the global request id and set it for logs... //should this basically be immutable per request...i guess it should! //first thing is get config - var configuration = await _configProvider.Get(); + var configuration = await _provider.Get(); //if error throw to catch below.. if(configuration.IsError) @@ -74,22 +74,23 @@ namespace Ocelot.Errors.Middleware var key = configuration.Data.RequestId; StringValues upstreamRequestIds; - if (!string.IsNullOrEmpty(key) && context.Request.Headers.TryGetValue(key, out upstreamRequestIds)) + if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out upstreamRequestIds)) { - context.TraceIdentifier = upstreamRequestIds.First(); - _requestScopedDataRepository.Add("RequestId", context.TraceIdentifier); - } - } - - private void SetInternalServerErrorOnResponse(HttpContext context) - { - if (!context.Response.HasStarted) - { - context.Response.StatusCode = 500; + //todo fix looking in both places + context.HttpContext.TraceIdentifier = upstreamRequestIds.First(); + _repo.Add("RequestId", context.HttpContext.TraceIdentifier); } } - private string CreateMessage(HttpContext context, Exception e) + private void SetInternalServerErrorOnResponse(DownstreamContext context) + { + if (!context.HttpContext.Response.HasStarted) + { + context.HttpContext.Response.StatusCode = 500; + } + } + + private string CreateMessage(DownstreamContext context, Exception e) { var message = $"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}"; @@ -99,7 +100,7 @@ namespace Ocelot.Errors.Middleware message = $"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}"; } - return $"{message} RequestId: {context.TraceIdentifier}"; + return $"{message} RequestId: {context.HttpContext.TraceIdentifier}"; } } } diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs index 14731eb2..5d8874b0 100644 --- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs +++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs @@ -1,10 +1,11 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Errors.Middleware { public static class ExceptionHandlerMiddlewareExtensions { - public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseExceptionHandlerMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs index b86cc4d2..f4281a23 100644 --- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; @@ -8,17 +9,15 @@ namespace Ocelot.Headers.Middleware { public class HttpHeadersTransformationMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly IHttpContextRequestHeaderReplacer _preReplacer; private readonly IHttpResponseHeaderReplacer _postReplacer; - public HttpHeadersTransformationMiddleware(RequestDelegate next, + public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, IHttpContextRequestHeaderReplacer preReplacer, IHttpResponseHeaderReplacer postReplacer) - : base(requestScopedDataRepository) { _next = next; _postReplacer = postReplacer; @@ -26,17 +25,18 @@ namespace Ocelot.Headers.Middleware _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - var preFAndRs = this.DownstreamRoute.ReRoute.UpstreamHeadersFindAndReplace; + var preFAndRs = context.DownstreamReRoute.UpstreamHeadersFindAndReplace; - _preReplacer.Replace(context, preFAndRs); + //todo - this should be on httprequestmessage not httpcontext? + _preReplacer.Replace(context.HttpContext, preFAndRs); await _next.Invoke(context); - var postFAndRs = this.DownstreamRoute.ReRoute.DownstreamHeadersFindAndReplace; + var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace; - _postReplacer.Replace(HttpResponseMessage, postFAndRs, DownstreamRequest); + _postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs index ce920b1a..46bb84dc 100644 --- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs +++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Headers.Middleware { public static class HttpHeadersTransformationMiddlewareExtensions { - public static IApplicationBuilder UseHttpHeadersTransformationMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseHttpHeadersTransformationMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs index 35d2fe5a..6e8dc31e 100644 --- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; @@ -9,34 +10,32 @@ namespace Ocelot.Headers.Middleware { public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IAddHeadersToRequest _addHeadersToRequest; private readonly IOcelotLogger _logger; - public HttpRequestHeadersBuilderMiddleware(RequestDelegate next, + public HttpRequestHeadersBuilderMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, IAddHeadersToRequest addHeadersToRequest) - : base(requestScopedDataRepository) { _next = next; _addHeadersToRequest = addHeadersToRequest; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (DownstreamRoute.ReRoute.ClaimsToHeaders.Any()) + if (context.DownstreamReRoute.ClaimsToHeaders.Any()) { - _logger.LogDebug($"{ DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers"); + _logger.LogDebug($"{ context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers"); - var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest); + var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest); if (response.IsError) { _logger.LogDebug("Error setting headers on context, setting pipeline error"); - SetPipelineError(response.Errors); + SetPipelineError(context, response.Errors); return; } diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs index 2a8c50fa..69f23860 100644 --- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Headers.Middleware { public static class HttpRequestHeadersBuilderMiddlewareExtensions { - public static IApplicationBuilder UseHttpRequestHeadersBuilderMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseHttpRequestHeadersBuilderMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs index 8a9803fb..f02230ec 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs @@ -5,6 +5,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers { public interface ILoadBalancerFactory { - Task Get(ReRoute reRoute, ServiceProviderConfiguration config); + Task Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config); } } \ No newline at end of file diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs index a4711fbe..d9d051c2 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs @@ -6,6 +6,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers { public interface ILoadBalancerHouse { - Task> Get(ReRoute reRoute, ServiceProviderConfiguration config); + Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config); } } \ No newline at end of file diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs index 3a583b8c..29d84ba4 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs @@ -12,7 +12,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers _serviceProviderFactory = serviceProviderFactory; } - public async Task Get(ReRoute reRoute, ServiceProviderConfiguration config) + public async Task Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) { var serviceProvider = _serviceProviderFactory.Get(config, reRoute); diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs index 7fc1ae05..3d8059b9 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs @@ -18,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers _loadBalancers = new ConcurrentDictionary(); } - public async Task> Get(ReRoute reRoute, ServiceProviderConfiguration config) + public async Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) { try { diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index aa37196f..8c2e963a 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Logging; @@ -11,28 +12,26 @@ namespace Ocelot.LoadBalancer.Middleware { public class LoadBalancingMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly ILoadBalancerHouse _loadBalancerHouse; - public LoadBalancingMiddleware(RequestDelegate next, + public LoadBalancingMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, ILoadBalancerHouse loadBalancerHouse) - : base(requestScopedDataRepository) { _next = next; - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); _loadBalancerHouse = loadBalancerHouse; } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - var loadBalancer = await _loadBalancerHouse.Get(DownstreamRoute.ReRoute, ServiceProviderConfiguration); + var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration); if(loadBalancer.IsError) { _logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error"); - SetPipelineError(loadBalancer.Errors); + SetPipelineError(context, loadBalancer.Errors); return; } @@ -40,11 +39,11 @@ namespace Ocelot.LoadBalancer.Middleware if(hostAndPort.IsError) { _logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error"); - SetPipelineError(hostAndPort.Errors); + SetPipelineError(context, hostAndPort.Errors); return; } - var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri); + var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri); uriBuilder.Host = hostAndPort.Data.DownstreamHost; @@ -53,7 +52,7 @@ namespace Ocelot.LoadBalancer.Middleware uriBuilder.Port = hostAndPort.Data.DownstreamPort; } - DownstreamRequest.RequestUri = uriBuilder.Uri; + context.DownstreamRequest.RequestUri = uriBuilder.Uri; try { diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs index db026396..a2da1060 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.LoadBalancer.Middleware { public static class LoadBalancingMiddlewareExtensions { - public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseLoadBalancingMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Middleware/DownstreamContext.cs b/src/Ocelot/Middleware/DownstreamContext.cs new file mode 100644 index 00000000..84ebc77a --- /dev/null +++ b/src/Ocelot/Middleware/DownstreamContext.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Errors; + +namespace Ocelot.Middleware +{ + public class DownstreamContext + { + public DownstreamContext(HttpContext httpContext) + { + this.HttpContext = httpContext; + Errors = new List(); + } + + public List TemplatePlaceholderNameAndValues { get; set; } + public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;} + public HttpContext HttpContext { get; private set; } + public DownstreamReRoute DownstreamReRoute { get; set; } + public HttpRequestMessage DownstreamRequest { get; set; } + public HttpResponseMessage DownstreamResponse { get; set; } + public List Errors { get;set; } + //public string RequestId {get;set;} + //public string PreviousRequestId {get;set;} + public bool IsError => Errors.Count > 0; + } +} diff --git a/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs b/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs new file mode 100644 index 00000000..f3a2975d --- /dev/null +++ b/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Ocelot.Configuration; + +namespace Ocelot.Middleware.Multiplexer +{ + public interface IMultiplexer + { + Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next); + } +} diff --git a/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs new file mode 100644 index 00000000..b85c2cc8 --- /dev/null +++ b/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ocelot.Configuration; + +namespace Ocelot.Middleware.Multiplexer +{ + public interface IResponseAggregator + { + Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List downstreamContexts); + } +} diff --git a/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs new file mode 100644 index 00000000..c4ebb08b --- /dev/null +++ b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ocelot.Configuration; + +namespace Ocelot.Middleware.Multiplexer +{ + public class Multiplexer : IMultiplexer + { + private readonly IResponseAggregator _aggregator; + + public Multiplexer(IResponseAggregator aggregator) + { + _aggregator = aggregator; + } + + public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next) + { + var tasks = new Task[reRoute.DownstreamReRoute.Count]; + + for (var i = 0; i < reRoute.DownstreamReRoute.Count; i++) + { + var downstreamContext = new DownstreamContext(context.HttpContext) + { + TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues, + ServiceProviderConfiguration = context.ServiceProviderConfiguration, + DownstreamReRoute = reRoute.DownstreamReRoute[i], + }; + + tasks[i] = Fire(downstreamContext, next); + } + + await Task.WhenAll(tasks); + + var downstreamContexts = new List(); + + foreach (var task in tasks) + { + var finished = await task; + downstreamContexts.Add(finished); + } + + await _aggregator.Aggregate(reRoute, context, downstreamContexts); + } + + private async Task Fire(DownstreamContext context, OcelotRequestDelegate next) + { + await next.Invoke(context); + return context; + } + } +} diff --git a/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs new file mode 100644 index 00000000..3d9a997d --- /dev/null +++ b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Ocelot.Configuration; + +namespace Ocelot.Middleware.Multiplexer +{ + public class SimpleJsonResponseAggregator : IResponseAggregator + { + public async Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List downstreamContexts) + { + if (reRoute.DownstreamReRoute.Count > 1) + { + await MapAggregtes(originalContext, downstreamContexts); + } + else + { + MapNotAggregate(originalContext, downstreamContexts); + } + } + + private async Task MapAggregtes(DownstreamContext originalContext, List downstreamContexts) + { + await MapAggregateContent(originalContext, downstreamContexts); + } + + private static async Task MapAggregateContent(DownstreamContext originalContext, List downstreamContexts) + { + var contentBuilder = new StringBuilder(); + + contentBuilder.Append("{"); + + for (int i = 0; i < downstreamContexts.Count; i++) + { + if (downstreamContexts[i].IsError) + { + MapAggregateError(originalContext, downstreamContexts, i); + return; + } + + var content = await downstreamContexts[i].DownstreamResponse.Content.ReadAsStringAsync(); + + contentBuilder.Append($"\"{downstreamContexts[i].DownstreamReRoute.Key}\":{content}"); + + if (i + 1 < downstreamContexts.Count) + { + contentBuilder.Append(","); + } + } + + contentBuilder.Append("}"); + + originalContext.DownstreamResponse = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(contentBuilder.ToString()) + { + Headers = {ContentType = new MediaTypeHeaderValue("application/json")} + } + }; + } + + private static void MapAggregateError(DownstreamContext originalContext, List downstreamContexts, int i) + { + originalContext.Errors.AddRange(downstreamContexts[i].Errors); + originalContext.DownstreamResponse = downstreamContexts[i].DownstreamResponse; + } + + private void MapNotAggregate(DownstreamContext originalContext, List downstreamContexts) + { + //assume at least one..if this errors then it will be caught by global exception handler + var finished = downstreamContexts.First(); + + originalContext.Errors = finished.Errors; + + originalContext.DownstreamRequest = finished.DownstreamRequest; + + originalContext.DownstreamResponse = finished.DownstreamResponse; + } + } +} diff --git a/src/Ocelot/Middleware/OcelotMiddleware.cs b/src/Ocelot/Middleware/OcelotMiddleware.cs index 8b1eae2e..bf49fd03 100644 --- a/src/Ocelot/Middleware/OcelotMiddleware.cs +++ b/src/Ocelot/Middleware/OcelotMiddleware.cs @@ -1,67 +1,20 @@ using System.Collections.Generic; -using System.Net.Http; -using Ocelot.Configuration; -using Ocelot.DownstreamRouteFinder; using Ocelot.Errors; -using Ocelot.Infrastructure.RequestData; namespace Ocelot.Middleware { public abstract class OcelotMiddleware { - private readonly IRequestScopedDataRepository _requestScopedDataRepository; - - protected OcelotMiddleware(IRequestScopedDataRepository requestScopedDataRepository) + protected OcelotMiddleware() { - _requestScopedDataRepository = requestScopedDataRepository; MiddlewareName = this.GetType().Name; } public string MiddlewareName { get; } - public bool PipelineError => _requestScopedDataRepository.Get("OcelotMiddlewareError").Data; - - public List PipelineErrors => _requestScopedDataRepository.Get>("OcelotMiddlewareErrors").Data; - - public DownstreamRoute DownstreamRoute => _requestScopedDataRepository.Get("DownstreamRoute").Data; - - public Request.Request Request => _requestScopedDataRepository.Get("Request").Data; - - public HttpRequestMessage DownstreamRequest => _requestScopedDataRepository.Get("DownstreamRequest").Data; - - public HttpResponseMessage HttpResponseMessage => _requestScopedDataRepository.Get("HttpResponseMessage").Data; - - public ServiceProviderConfiguration ServiceProviderConfiguration => _requestScopedDataRepository.Get("ServiceProviderConfiguration").Data; - - public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute) + public void SetPipelineError(DownstreamContext context, List errors) { - _requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute); - } - - public void SetServiceProviderConfigurationForThisRequest(ServiceProviderConfiguration serviceProviderConfiguration) - { - _requestScopedDataRepository.Add("ServiceProviderConfiguration", serviceProviderConfiguration); - } - - 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); - } - - public void SetPipelineError(List errors) - { - _requestScopedDataRepository.Add("OcelotMiddlewareError", true); - _requestScopedDataRepository.Add("OcelotMiddlewareErrors", errors); + context.Errors = errors; } } } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 249dceb5..9dbb7c22 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using System.Threading.Tasks; - using Authorisation.Middleware; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -15,24 +14,11 @@ using Ocelot.Configuration.Provider; using Ocelot.Configuration.Repository; using Ocelot.Configuration.Setter; - using Ocelot.LoadBalancer.Middleware; using Ocelot.Responses; - using Ocelot.Authentication.Middleware; - using Ocelot.Cache.Middleware; - using Ocelot.Claims.Middleware; - using Ocelot.DownstreamRouteFinder.Middleware; - using Ocelot.DownstreamUrlCreator.Middleware; - using Ocelot.Errors.Middleware; - using Ocelot.Headers.Middleware; using Ocelot.Logging; - using Ocelot.QueryStrings.Middleware; - using Ocelot.Request.Middleware; - using Ocelot.Requester.Middleware; - using Ocelot.RequestId.Middleware; - using Ocelot.Responder.Middleware; - using Ocelot.RateLimit.Middleware; using Rafty.Concensus; using Rafty.Infrastructure; + using Ocelot.Middleware.Pipeline; public static class OcelotMiddlewareExtensions { @@ -43,7 +29,7 @@ /// public static async Task UseOcelot(this IApplicationBuilder builder) { - await builder.UseOcelot(new OcelotMiddlewareConfiguration()); + await builder.UseOcelot(new OcelotPipelineConfiguration()); return builder; } @@ -52,9 +38,9 @@ /// Registers Ocelot with a combination of default middlewares and optional middlewares in the configuration /// /// - /// + /// /// - public static async Task UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration) + public static async Task UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { var configuration = await CreateConfiguration(builder); @@ -67,91 +53,21 @@ ConfigureDiagnosticListener(builder); - // This is registered to catch any global exceptions that are not handled - // It also sets the Request Id if anything is set globally - builder.UseExceptionHandlerMiddleware(); + var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); - // Allow the user to respond with absolutely anything they want. - builder.UseIfNotNull(middlewareConfiguration.PreErrorResponderMiddleware); + pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration); - // This is registered first so it can catch any errors and issue an appropriate response - builder.UseResponderMiddleware(); + var firstDelegate = pipelineBuilder.Build(); - // Then we get the downstream route information - builder.UseDownstreamRouteFinderMiddleware(); + //inject first delegate into first piece of asp.net middleware..maybe not like this + //then because we are updating the http context in ocelot it comes out correct for + //rest of asp.net.. - // Now we have the ds route we can transform headers and stuff? - builder.UseHttpHeadersTransformationMiddleware(); - - // Initialises downstream request - builder.UseDownstreamRequestInitialiser(); - - // We check whether the request is ratelimit, and if there is no continue processing - builder.UseRateLimiting(); - - // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware) - // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten - // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware. - 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); - - // Now we know where the client is going to go we can authenticate them. - // We allow the ocelot middleware to be overriden by whatever the - // user wants - if (middlewareConfiguration.AuthenticationMiddleware == null) + builder.Use(async (context, task) => { - builder.UseAuthenticationMiddleware(); - } - else - { - builder.Use(middlewareConfiguration.AuthenticationMiddleware); - } - - // The next thing we do is look at any claims transforms in case this is important for authorisation - builder.UseClaimsBuilderMiddleware(); - - // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in. - builder.UseIfNotNull(middlewareConfiguration.PreAuthorisationMiddleware); - - // Now we have authenticated and done any claims transformation we - // can authorise the request - // We allow the ocelot middleware to be overriden by whatever the - // user wants - if (middlewareConfiguration.AuthorisationMiddleware == null) - { - builder.UseAuthorisationMiddleware(); - } - else - { - builder.Use(middlewareConfiguration.AuthorisationMiddleware); - } - - // Now we can run any header transformation logic - builder.UseHttpRequestHeadersBuilderMiddleware(); - - // Allow the user to implement their own query string manipulation logic - builder.UseIfNotNull(middlewareConfiguration.PreQueryStringBuilderMiddleware); - - // Now we can run any query string transformation logic - builder.UseQueryStringBuilderMiddleware(); - - // Get the load balancer for this request - builder.UseLoadBalancingMiddleware(); - - // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used - builder.UseDownstreamUrlCreatorMiddleware(); - - // Not sure if this is the best place for this but we use the downstream url - // as the basis for our cache key. - builder.UseOutputCacheMiddleware(); - - // Everything should now be ready to build or HttpRequest - builder.UseHttpRequestBuilderMiddleware(); - - //We fire off the request and set the response on the scoped data repo - builder.UseHttpRequesterMiddleware(); + var downstreamContext = new DownstreamContext(context); + await firstDelegate.Invoke(downstreamContext); + }); return builder; } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs similarity index 63% rename from src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs rename to src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index e3d22928..3cfded9b 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -1,44 +1,43 @@ -namespace Ocelot.Middleware -{ - using System; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - - public class OcelotMiddlewareConfiguration - { - /// - /// This is called after the global error handling middleware so any code before calling next.invoke - /// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called - /// in the Ocelot pipeline before we go to the global error handler. - /// - public Func, Task> PreErrorResponderMiddleware { get; set; } - - /// - /// This is to allow the user to run any extra authentication before the Ocelot authentication - /// kicks in - /// - public Func, Task> PreAuthenticationMiddleware { get; set; } - - /// - /// This allows the user to completely override the ocelot authentication middleware - /// - public Func, Task> AuthenticationMiddleware { get; set; } - - /// - /// This is to allow the user to run any extra authorisation before the Ocelot authentication - /// kicks in - /// - public Func, Task> PreAuthorisationMiddleware { get; set; } - - /// - /// This allows the user to completely override the ocelot authorisation middleware - /// - public Func, Task> AuthorisationMiddleware { get; set; } - - /// - /// This allows the user to implement there own query string manipulation logic - /// - public Func, Task> PreQueryStringBuilderMiddleware { get; set; } - - } -} \ No newline at end of file +namespace Ocelot.Middleware +{ + using System; + using System.Threading.Tasks; + + public class OcelotPipelineConfiguration + { + /// + /// This is called after the global error handling middleware so any code before calling next.invoke + /// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called + /// in the Ocelot pipeline before we go to the global error handler. + /// + public Func, Task> PreErrorResponderMiddleware { get; set; } + + /// + /// This is to allow the user to run any extra authentication before the Ocelot authentication + /// kicks in + /// + public Func, Task> PreAuthenticationMiddleware { get; set; } + + /// + /// This allows the user to completely override the ocelot authentication middleware + /// + public Func, Task> AuthenticationMiddleware { get; set; } + + /// + /// This is to allow the user to run any extra authorisation before the Ocelot authentication + /// kicks in + /// + public Func, Task> PreAuthorisationMiddleware { get; set; } + + /// + /// This allows the user to completely override the ocelot authorisation middleware + /// + public Func, Task> AuthorisationMiddleware { get; set; } + + /// + /// This allows the user to implement there own query string manipulation logic + /// + public Func, Task> PreQueryStringBuilderMiddleware { get; set; } + + } +} diff --git a/src/Ocelot/Middleware/OcelotRequestDelegate.cs b/src/Ocelot/Middleware/OcelotRequestDelegate.cs new file mode 100644 index 00000000..130dfd86 --- /dev/null +++ b/src/Ocelot/Middleware/OcelotRequestDelegate.cs @@ -0,0 +1,6 @@ +using System.Threading.Tasks; + +namespace Ocelot.Middleware +{ + public delegate Task OcelotRequestDelegate(DownstreamContext downstreamContext); +} diff --git a/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs new file mode 100644 index 00000000..3bc0d6b0 --- /dev/null +++ b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages + +using System; + +namespace Ocelot.Middleware.Pipeline +{ + public interface IOcelotPipelineBuilder + { + IServiceProvider ApplicationServices { get; } + OcelotPipelineBuilder Use(Func middleware); + OcelotRequestDelegate Build(); + } +} diff --git a/src/Ocelot/Middleware/Pipeline/LICENSE.txt b/src/Ocelot/Middleware/Pipeline/LICENSE.txt new file mode 100644 index 00000000..7b2956ec --- /dev/null +++ b/src/Ocelot/Middleware/Pipeline/LICENSE.txt @@ -0,0 +1,14 @@ +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs new file mode 100644 index 00000000..1e37514c --- /dev/null +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ocelot.Middleware.Pipeline +{ + public class OcelotPipelineBuilder : IOcelotPipelineBuilder + { + private readonly IList> _middlewares; + + public OcelotPipelineBuilder(IServiceProvider provider) + { + ApplicationServices = provider; + _middlewares = new List>(); + } + + public IServiceProvider ApplicationServices { get; } + + public OcelotPipelineBuilder Use(Func middleware) + { + _middlewares.Add(middleware); + return this; + } + + public OcelotRequestDelegate Build() + { + OcelotRequestDelegate app = context => + { + context.HttpContext.Response.StatusCode = 404; + return Task.CompletedTask; + }; + + foreach (var component in _middlewares.Reverse()) + { + app = component(app); + } + + return app; + } + } +} diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs new file mode 100644 index 00000000..422097fc --- /dev/null +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs @@ -0,0 +1,146 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Ocelot.Middleware.Pipeline +{ + public static class OcelotPipelineBuilderExtensions + { + internal const string InvokeMethodName = "Invoke"; + internal const string InvokeAsyncMethodName = "InvokeAsync"; + private static readonly MethodInfo GetServiceInfo = typeof(OcelotPipelineBuilderExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static); + + public static IOcelotPipelineBuilder UseMiddleware(this IOcelotPipelineBuilder app, params object[] args) + { + return app.UseMiddleware(typeof(TMiddleware), args); + } + + public static IOcelotPipelineBuilder Use(this IOcelotPipelineBuilder app, Func, Task> middleware) + { + return app.Use(next => + { + return context => + { + Func simpleNext = () => next(context); + return middleware(context, simpleNext); + }; + }); + } + + public static IOcelotPipelineBuilder UseMiddleware(this IOcelotPipelineBuilder app, Type middleware, params object[] args) + { + return app.Use(next => + { + var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); + var invokeMethods = methods.Where(m => + string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal) + || string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal) + ).ToArray(); + + if (invokeMethods.Length > 1) + { + throw new InvalidOperationException(); + } + + if (invokeMethods.Length == 0) + { + throw new InvalidOperationException(); + } + + var methodinfo = invokeMethods[0]; + if (!typeof(Task).IsAssignableFrom(methodinfo.ReturnType)) + { + throw new InvalidOperationException(); + } + + var parameters = methodinfo.GetParameters(); + if (parameters.Length == 0 || parameters[0].ParameterType != typeof(DownstreamContext)) + { + throw new InvalidOperationException(); + } + + var ctorArgs = new object[args.Length + 1]; + ctorArgs[0] = next; + Array.Copy(args, 0, ctorArgs, 1, args.Length); + var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs); + if (parameters.Length == 1) + { + return (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance); + } + + var factory = Compile(methodinfo, parameters); + + return context => + { + var serviceProvider = context.HttpContext.RequestServices ?? app.ApplicationServices; + if (serviceProvider == null) + { + throw new InvalidOperationException(); + } + + return factory(instance, context, serviceProvider); + }; + }); + } + + private static Func Compile(MethodInfo methodinfo, ParameterInfo[] parameters) + { + var middleware = typeof(T); + var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext"); + var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider"); + var instanceArg = Expression.Parameter(middleware, "middleware"); + + var methodArguments = new Expression[parameters.Length]; + methodArguments[0] = httpContextArg; + for (int i = 1; i < parameters.Length; i++) + { + var parameterType = parameters[i].ParameterType; + if (parameterType.IsByRef) + { + throw new NotSupportedException(); + } + + var parameterTypeExpression = new Expression[] + { + providerArg, + Expression.Constant(parameterType, typeof(Type)), + Expression.Constant(methodinfo.DeclaringType, typeof(Type)) + }; + + var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression); + methodArguments[i] = Expression.Convert(getServiceCall, parameterType); + } + + Expression middlewareInstanceArg = instanceArg; + if (methodinfo.DeclaringType != typeof(T)) + { + middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodinfo.DeclaringType); + } + + var body = Expression.Call(middlewareInstanceArg, methodinfo, methodArguments); + + var lambda = Expression.Lambda>(body, instanceArg, httpContextArg, providerArg); + + return lambda.Compile(); + } + + private static object GetService(IServiceProvider sp, Type type) + { + var service = sp.GetService(type); + if (service == null) + { + throw new InvalidOperationException(); + } + + return service; + } + } +} diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs new file mode 100644 index 00000000..374b867f --- /dev/null +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; +using Ocelot.Authentication.Middleware; +using Ocelot.Authorisation.Middleware; +using Ocelot.Cache.Middleware; +using Ocelot.Claims.Middleware; +using Ocelot.DownstreamRouteFinder.Middleware; +using Ocelot.DownstreamUrlCreator.Middleware; +using Ocelot.Errors.Middleware; +using Ocelot.Headers.Middleware; +using Ocelot.LoadBalancer.Middleware; +using Ocelot.QueryStrings.Middleware; +using Ocelot.RateLimit.Middleware; +using Ocelot.Request.Middleware; +using Ocelot.Requester.Middleware; +using Ocelot.RequestId.Middleware; +using Ocelot.Responder.Middleware; + +namespace Ocelot.Middleware.Pipeline +{ + public static class OcelotPipelineExtensions + { + public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder, + OcelotPipelineConfiguration pipelineConfiguration = null) + { + // This is registered to catch any global exceptions that are not handled + // It also sets the Request Id if anything is set globally + builder.UseExceptionHandlerMiddleware(); + + // Allow the user to respond with absolutely anything they want. + builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware); + + // This is registered first so it can catch any errors and issue an appropriate response + builder.UseResponderMiddleware(); + + // Then we get the downstream route information + builder.UseDownstreamRouteFinderMiddleware(); + + // Now we have the ds route we can transform headers and stuff? + builder.UseHttpHeadersTransformationMiddleware(); + + // Initialises downstream request + builder.UseDownstreamRequestInitialiser(); + + // We check whether the request is ratelimit, and if there is no continue processing + builder.UseRateLimiting(); + + // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware) + // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten + // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware. + builder.UseRequestIdMiddleware(); + + // Allow pre authentication logic. The idea being people might want to run something custom before what is built in. + builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware); + + // Now we know where the client is going to go we can authenticate them. + // We allow the ocelot middleware to be overriden by whatever the + // user wants + if (pipelineConfiguration.AuthenticationMiddleware == null) + { + builder.UseAuthenticationMiddleware(); + } + else + { + builder.Use(pipelineConfiguration.AuthenticationMiddleware); + } + + // The next thing we do is look at any claims transforms in case this is important for authorisation + builder.UseClaimsBuilderMiddleware(); + + // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in. + builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); + + // Now we have authenticated and done any claims transformation we + // can authorise the request + // We allow the ocelot middleware to be overriden by whatever the + // user wants + if (pipelineConfiguration.AuthorisationMiddleware == null) + { + builder.UseAuthorisationMiddleware(); + } + else + { + builder.Use(pipelineConfiguration.AuthorisationMiddleware); + } + + // Now we can run any header transformation logic + builder.UseHttpRequestHeadersBuilderMiddleware(); + + // Allow the user to implement their own query string manipulation logic + builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); + + // Now we can run any query string transformation logic + builder.UseQueryStringBuilderMiddleware(); + + // Get the load balancer for this request + builder.UseLoadBalancingMiddleware(); + + // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used + builder.UseDownstreamUrlCreatorMiddleware(); + + // Not sure if this is the best place for this but we use the downstream url + // as the basis for our cache key. + builder.UseOutputCacheMiddleware(); + + //We fire off the request and set the response on the scoped data repo + builder.UseHttpRequesterMiddleware(); + + return builder.Build(); + } + + private static void UseIfNotNull(this IOcelotPipelineBuilder builder, + Func, Task> middleware) + { + if (middleware != null) + { + builder.Use(middleware); + } + } + } +} diff --git a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs index bdd79730..ae5dca85 100644 --- a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs +++ b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddleware.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; @@ -9,34 +10,32 @@ namespace Ocelot.QueryStrings.Middleware { public class QueryStringBuilderMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IAddQueriesToRequest _addQueriesToRequest; private readonly IOcelotLogger _logger; - public QueryStringBuilderMiddleware(RequestDelegate next, + public QueryStringBuilderMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, IAddQueriesToRequest addQueriesToRequest) - : base(requestScopedDataRepository) { _next = next; _addQueriesToRequest = addQueriesToRequest; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - if (DownstreamRoute.ReRoute.ClaimsToQueries.Any()) + if (context.DownstreamReRoute.ClaimsToQueries.Any()) { - _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries"); + _logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries"); - var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest); + var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(context.DownstreamReRoute.ClaimsToQueries, context.HttpContext.User.Claims, context.DownstreamRequest); if (response.IsError) { _logger.LogDebug("there was an error setting queries on context, setting pipeline error"); - SetPipelineError(response.Errors); + SetPipelineError(context, response.Errors); return; } } diff --git a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddlewareExtensions.cs b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddlewareExtensions.cs index 647afb07..0a32f727 100644 --- a/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/QueryStrings/Middleware/QueryStringBuilderMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.QueryStrings.Middleware { public static class QueryStringBuilderMiddlewareExtensions { - public static IApplicationBuilder UseQueryStringBuilderMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseQueryStringBuilderMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs index e8dbaeef..064ec54b 100644 --- a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs +++ b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs @@ -7,21 +7,20 @@ using Ocelot.Infrastructure.RequestData; using Microsoft.AspNetCore.Http; using Ocelot.Logging; using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.Middleware; namespace Ocelot.RateLimit.Middleware { public class ClientRateLimitMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly IRateLimitCounterHandler _counterHandler; private readonly ClientRateLimitProcessor _processor; - public ClientRateLimitMiddleware(RequestDelegate next, + public ClientRateLimitMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, IRateLimitCounterHandler counterHandler) - : base(requestScopedDataRepository) { _next = next; _logger = loggerFactory.CreateLogger(); @@ -29,23 +28,23 @@ namespace Ocelot.RateLimit.Middleware _processor = new ClientRateLimitProcessor(counterHandler); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - var options = DownstreamRoute.ReRoute.RateLimitOptions; + var options = context.DownstreamReRoute.RateLimitOptions; // check if rate limiting is enabled - if (!DownstreamRoute.ReRoute.EnableEndpointEndpointRateLimiting) + if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting) { - _logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}"); + _logger.LogDebug($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate}"); await _next.Invoke(context); return; } // compute identity from request - var identity = SetIdentity(context, options); + var identity = SetIdentity(context.HttpContext, options); // check white list if (IsWhitelisted(identity, options)) { - _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting"); + _logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate} is white listed from rate limiting"); await _next.Invoke(context); return; } @@ -63,11 +62,11 @@ namespace Ocelot.RateLimit.Middleware var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule); // log blocked request - LogBlockedRequest(context, identity, counter, rule); + LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute); var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture); // break execution - await ReturnQuotaExceededResponse(context, options, retrystring); + await ReturnQuotaExceededResponse(context.HttpContext, options, retrystring); return; } @@ -76,8 +75,8 @@ namespace Ocelot.RateLimit.Middleware //set X-Rate-Limit headers for the longest period if (!options.DisableRateLimitHeaders) { - var headers = _processor.GetRateLimitHeaders(context, identity, options); - context.Response.OnStarting(SetRateLimitHeaders, state: headers); + var headers = _processor.GetRateLimitHeaders(context.HttpContext, identity, options); + context.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers); } await _next.Invoke(context); @@ -107,9 +106,9 @@ namespace Ocelot.RateLimit.Middleware return false; } - public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule) + public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute) { - _logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { DownstreamRoute.ReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}."); + _logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}."); } public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter) diff --git a/src/Ocelot/RateLimit/Middleware/RateLimitMiddlewareExtensions.cs b/src/Ocelot/RateLimit/Middleware/RateLimitMiddlewareExtensions.cs index a1533e00..2583be4a 100644 --- a/src/Ocelot/RateLimit/Middleware/RateLimitMiddlewareExtensions.cs +++ b/src/Ocelot/RateLimit/Middleware/RateLimitMiddlewareExtensions.cs @@ -1,14 +1,10 @@ -using Microsoft.AspNetCore.Builder; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using Ocelot.Middleware.Pipeline; namespace Ocelot.RateLimit.Middleware { public static class RateLimitMiddlewareExtensions { - public static IApplicationBuilder UseRateLimiting(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseRateLimiting(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } diff --git a/src/Ocelot/Request/Builder/HttpRequestCreator.cs b/src/Ocelot/Request/Builder/HttpRequestCreator.cs deleted file mode 100644 index 5b8ef968..00000000 --- a/src/Ocelot/Request/Builder/HttpRequestCreator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; -using Ocelot.Responses; -using Ocelot.Requester.QoS; -using System.Net.Http; - -namespace Ocelot.Request.Builder -{ - public sealed class HttpRequestCreator : IRequestCreator - { - public async Task> Build( - HttpRequestMessage httpRequestMessage, - bool isQos, - IQoSProvider qosProvider, - bool useCookieContainer, - bool allowAutoRedirect, - string reRouteKey, - bool isTracing) - { - return new OkResponse(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, reRouteKey, isTracing)); - } - } -} diff --git a/src/Ocelot/Request/Builder/IRequestCreator.cs b/src/Ocelot/Request/Builder/IRequestCreator.cs deleted file mode 100644 index abde4a6b..00000000 --- a/src/Ocelot/Request/Builder/IRequestCreator.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ocelot.Request.Builder -{ - using System.Net.Http; - using System.Threading.Tasks; - - using Ocelot.Requester.QoS; - using Ocelot.Responses; - - public interface IRequestCreator - { - Task> Build( - HttpRequestMessage httpRequestMessage, - bool isQos, - IQoSProvider qosProvider, - bool useCookieContainer, - bool allowAutoRedirect, - string reRouteKe, - bool isTracing); - } -} diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs index 3ade73d1..f14c1394 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -2,38 +2,36 @@ namespace Ocelot.Request.Middleware { using System.Threading.Tasks; using Microsoft.AspNetCore.Http; - + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly Mapper.IRequestMapper _requestMapper; - public DownstreamRequestInitialiserMiddleware(RequestDelegate next, + public DownstreamRequestInitialiserMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, Mapper.IRequestMapper requestMapper) - :base(requestScopedDataRepository) { _next = next; _logger = loggerFactory.CreateLogger(); _requestMapper = requestMapper; } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { - var downstreamRequest = await _requestMapper.Map(context.Request); + var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request); if (downstreamRequest.IsError) { - SetPipelineError(downstreamRequest.Errors); + SetPipelineError(context, downstreamRequest.Errors); return; } - SetDownstreamRequest(downstreamRequest.Data); + context.DownstreamRequest = downstreamRequest.Data; await _next.Invoke(context); } diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs deleted file mode 100644 index 4160db31..00000000 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs +++ /dev/null @@ -1,67 +0,0 @@ -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 HttpRequestBuilderMiddleware : OcelotMiddleware - { - private readonly RequestDelegate _next; - private readonly IRequestCreator _requestCreator; - private readonly IOcelotLogger _logger; - private readonly IQosProviderHouse _qosProviderHouse; - - public HttpRequestBuilderMiddleware(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) - { - var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute); - - if (qosProvider.IsError) - { - _logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error"); - - SetPipelineError(qosProvider.Errors); - - return; - } - - var buildResult = await _requestCreator.Build( - DownstreamRequest, - DownstreamRoute.ReRoute.IsQos, - qosProvider.Data, - DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer, - DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect, - DownstreamRoute.ReRoute.ReRouteKey, - DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing); - - if (buildResult.IsError) - { - _logger.LogDebug("IRequestCreator returned an error, setting pipeline error"); - SetPipelineError(buildResult.Errors); - return; - } - - _logger.LogDebug("setting upstream request"); - - SetUpstreamRequestForThisRequest(buildResult.Data); - - await _next.Invoke(context); - } - } -} diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs index 80a41998..35084659 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs @@ -1,17 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Request.Middleware { public static class HttpRequestBuilderMiddlewareExtensions { - public static IApplicationBuilder UseHttpRequestBuilderMiddleware(this IApplicationBuilder builder) - { - return builder.UseMiddleware(); - } - - public static IApplicationBuilder UseDownstreamRequestInitialiser(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseDownstreamRequestInitialiser(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Request/Request.cs b/src/Ocelot/Request/Request.cs deleted file mode 100644 index 3bb670f6..00000000 --- a/src/Ocelot/Request/Request.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Net.Http; -using Ocelot.Requester.QoS; - -namespace Ocelot.Request -{ - public class Request - { - public Request( - HttpRequestMessage httpRequestMessage, - bool isQos, - IQoSProvider qosProvider, - bool allowAutoRedirect, - bool useCookieContainer, - string reRouteKey, - bool isTracing - ) - { - HttpRequestMessage = httpRequestMessage; - IsQos = isQos; - QosProvider = qosProvider; - AllowAutoRedirect = allowAutoRedirect; - UseCookieContainer = useCookieContainer; - ReRouteKey = reRouteKey; - IsTracing = isTracing; - } - - public HttpRequestMessage HttpRequestMessage { get; private set; } - public bool IsQos { get; private set; } - public bool IsTracing { get; private set; } - public IQoSProvider QosProvider { get; private set; } - public bool AllowAutoRedirect { get; private set; } - public bool UseCookieContainer { get; private set; } - public string ReRouteKey { get; private set; } - } -} \ No newline at end of file diff --git a/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs b/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs index d4dfbe11..0b146b27 100644 --- a/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs +++ b/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs @@ -8,63 +8,65 @@ using Ocelot.Middleware; using System.Net.Http; using System.Net.Http.Headers; using System.Collections.Generic; +using Ocelot.DownstreamRouteFinder.Middleware; namespace Ocelot.RequestId.Middleware { public class ReRouteRequestIdMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IOcelotLogger _logger; private readonly IRequestScopedDataRepository _requestScopedDataRepository; - public ReRouteRequestIdMiddleware(RequestDelegate next, - IOcelotLoggerFactory loggerFactory, + + public ReRouteRequestIdMiddleware(OcelotRequestDelegate next, + IOcelotLoggerFactory loggerFactory, IRequestScopedDataRepository requestScopedDataRepository) - : base(requestScopedDataRepository) { _next = next; - _logger = loggerFactory.CreateLogger(); _requestScopedDataRepository = requestScopedDataRepository; + _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { SetOcelotRequestId(context); await _next.Invoke(context); } - private void SetOcelotRequestId(HttpContext context) + private void SetOcelotRequestId(DownstreamContext context) { // if get request ID is set on upstream request then retrieve it - var key = DownstreamRoute.ReRoute.RequestIdKey ?? DefaultRequestIdKey.Value; + var key = context.DownstreamReRoute.RequestIdKey ?? DefaultRequestIdKey.Value; StringValues upstreamRequestIds; - if (context.Request.Headers.TryGetValue(key, out upstreamRequestIds)) + if (context.HttpContext.Request.Headers.TryGetValue(key, out upstreamRequestIds)) { //set the traceidentifier - context.TraceIdentifier = upstreamRequestIds.First(); + context.HttpContext.TraceIdentifier = upstreamRequestIds.First(); - //check if we have previous id + //todo fix looking in both places + //check if we have previous id in scoped repo var previousRequestId = _requestScopedDataRepository.Get("RequestId"); - if(!previousRequestId.IsError && !string.IsNullOrEmpty(previousRequestId.Data)) + if (!previousRequestId.IsError && !string.IsNullOrEmpty(previousRequestId.Data)) { //we have a previous request id lets store it and update request id _requestScopedDataRepository.Add("PreviousRequestId", previousRequestId.Data); - _requestScopedDataRepository.Update("RequestId", context.TraceIdentifier); + _requestScopedDataRepository.Update("RequestId", context.HttpContext.TraceIdentifier); } else { //else just add request id - _requestScopedDataRepository.Add("RequestId", context.TraceIdentifier); + _requestScopedDataRepository.Add("RequestId", context.HttpContext.TraceIdentifier); } } // set request ID on downstream request, if required - var requestId = new RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier); + var requestId = new RequestId(context.DownstreamReRoute.RequestIdKey, context.HttpContext.TraceIdentifier); - if (ShouldAddRequestId(requestId, DownstreamRequest.Headers)) + if (ShouldAddRequestId(requestId, context.DownstreamRequest.Headers)) { - AddRequestIdHeader(requestId, DownstreamRequest); + AddRequestIdHeader(requestId, context.DownstreamRequest); } } @@ -86,4 +88,4 @@ namespace Ocelot.RequestId.Middleware httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs index 236c984a..f450f9a5 100644 --- a/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs +++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.RequestId.Middleware { public static class RequestIdMiddlewareExtensions { - public static IApplicationBuilder UseRequestIdMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseRequestIdMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs index 0a7d6947..eec395fb 100644 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs +++ b/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using Ocelot.Configuration; using Ocelot.Errors; using Ocelot.Responses; @@ -17,7 +18,7 @@ namespace Ocelot.Requester _housed = new ConcurrentDictionary(); } - public Response Get(Request.Request request) + public Response Get(DownstreamReRoute request) { try { @@ -28,7 +29,15 @@ namespace Ocelot.Requester return new OkResponse(provider); } - provider = _factory.Get(request); + //todo - unit test for this + var providerResponse = _factory.Get(request); + + if (providerResponse.IsError) + { + return new ErrorResponse(providerResponse.Errors); + } + + provider = providerResponse.Data; AddHoused(request.ReRouteKey, provider); return new OkResponse(provider); } diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs index 1e6cb4c4..c6267123 100644 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs +++ b/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs @@ -1,5 +1,8 @@ using System.Net.Http; +using Ocelot.Configuration; using Ocelot.Logging; +using Ocelot.Requester.QoS; +using Ocelot.Responses; namespace Ocelot.Requester { @@ -8,15 +11,20 @@ namespace Ocelot.Requester private readonly ITracingHandler _tracingHandler; private readonly IOcelotLoggerFactory _loggerFactory; private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider; + private readonly IQosProviderHouse _qosProviderHouse; - public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, IDelegatingHandlerHandlerProvider allRoutesProvider, ITracingHandler tracingHandler) + public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, + IDelegatingHandlerHandlerProvider allRoutesProvider, + ITracingHandler tracingHandler, + IQosProviderHouse qosProviderHouse) { _tracingHandler = tracingHandler; _loggerFactory = loggerFactory; _allRoutesProvider = allRoutesProvider; + _qosProviderHouse = qosProviderHouse; } - public IDelegatingHandlerHandlerProvider Get(Request.Request request) + public Response Get(DownstreamReRoute request) { var handlersAppliedToAll = _allRoutesProvider.Get(); @@ -27,17 +35,24 @@ namespace Ocelot.Requester provider.Add(handler); } - if (request.IsTracing) + if (request.HttpHandlerOptions.UseTracing) { provider.Add(() => (DelegatingHandler)_tracingHandler); } if (request.IsQos) { - provider.Add(() => new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _loggerFactory)); + var qosProvider = _qosProviderHouse.Get(request); + + if (qosProvider.IsError) + { + return new ErrorResponse(qosProvider.Errors); + } + + provider.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory)); } - return provider; + return new OkResponse(provider); } } } diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 561b5205..9c832b1d 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Net.Http; +using Ocelot.Configuration; namespace Ocelot.Requester { @@ -12,16 +13,16 @@ namespace Ocelot.Requester _house = house; } - public IHttpClient Create(Request.Request request) + public IHttpClient Create(DownstreamReRoute request) { - var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.AllowAutoRedirect, UseCookies = request.UseCookieContainer}; + var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.HttpHandlerOptions.AllowAutoRedirect, UseCookies = request.HttpHandlerOptions.UseCookieContainer}; var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request)); return new HttpClientWrapper(client); } - private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, Request.Request request) + private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request) { var provider = _house.Get(request); @@ -39,4 +40,4 @@ namespace Ocelot.Requester return httpMessageHandler; } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index 9fa3b271..a531d9b9 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -2,6 +2,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; using Ocelot.Logging; +using Ocelot.Middleware; using Ocelot.Responses; using Polly.CircuitBreaker; using Polly.Timeout; @@ -23,7 +24,7 @@ namespace Ocelot.Requester _house = house; } - public async Task> GetResponse(Request.Request request) + public async Task> GetResponse(DownstreamContext request) { var builder = new HttpClientBuilder(_house); @@ -33,7 +34,7 @@ namespace Ocelot.Requester try { - var response = await httpClient.SendAsync(request.HttpRequestMessage); + var response = await httpClient.SendAsync(request.DownstreamRequest); return new OkResponse(response); } catch (TimeoutRejectedException exception) @@ -57,28 +58,23 @@ namespace Ocelot.Requester } - private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, Request.Request request) + private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, DownstreamContext request) { var httpClient = _cacheHandlers.Get(cacheKey); if (httpClient == null) { - httpClient = builder.Create(request); + httpClient = builder.Create(request.DownstreamReRoute); } return httpClient; } - private string GetCacheKey(Request.Request request) + private string GetCacheKey(DownstreamContext request) { - var baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; - - if (request.IsQos) - { - baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; - } + var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}"; return baseUrl; } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs index 78dd1fc1..b236ed16 100644 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs +++ b/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs @@ -1,9 +1,10 @@ +using Ocelot.Configuration; using Ocelot.Responses; namespace Ocelot.Requester { public interface IDelegatingHandlerHandlerHouse { - Response Get(Request.Request request); + Response Get(DownstreamReRoute request); } } diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs index dcf007d4..c77a62bd 100644 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs +++ b/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs @@ -1,7 +1,10 @@ +using Ocelot.Configuration; +using Ocelot.Responses; + namespace Ocelot.Requester { public interface IDelegatingHandlerHandlerProviderFactory { - IDelegatingHandlerHandlerProvider Get(Request.Request request); + Response Get(DownstreamReRoute request); } } diff --git a/src/Ocelot/Requester/IHttpClientBuilder.cs b/src/Ocelot/Requester/IHttpClientBuilder.cs index d6b43501..f10e55f5 100644 --- a/src/Ocelot/Requester/IHttpClientBuilder.cs +++ b/src/Ocelot/Requester/IHttpClientBuilder.cs @@ -9,6 +9,6 @@ namespace Ocelot.Requester /// Creates the /// /// - IHttpClient Create(Request.Request request); + IHttpClient Create(DownstreamReRoute request); } } diff --git a/src/Ocelot/Requester/IHttpRequester.cs b/src/Ocelot/Requester/IHttpRequester.cs index 81d9fa22..5d9aa5dc 100644 --- a/src/Ocelot/Requester/IHttpRequester.cs +++ b/src/Ocelot/Requester/IHttpRequester.cs @@ -1,11 +1,12 @@ using System.Net.Http; using System.Threading.Tasks; +using Ocelot.Middleware; using Ocelot.Responses; namespace Ocelot.Requester { public interface IHttpRequester { - Task> GetResponse(Request.Request request); + Task> GetResponse(DownstreamContext request); } } diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs index ae3e7c5f..68397b9f 100644 --- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs +++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs @@ -3,42 +3,41 @@ using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; using System.Threading.Tasks; +using Ocelot.DownstreamRouteFinder.Middleware; +using Ocelot.Requester.QoS; namespace Ocelot.Requester.Middleware { public class HttpRequesterMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IHttpRequester _requester; private readonly IOcelotLogger _logger; - public HttpRequesterMiddleware(RequestDelegate next, + public HttpRequesterMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IHttpRequester requester, - IRequestScopedDataRepository requestScopedDataRepository -) - :base(requestScopedDataRepository) + IHttpRequester requester) { _next = next; _requester = requester; _logger = loggerFactory.CreateLogger(); } - public async Task Invoke(HttpContext context) - { - var response = await _requester.GetResponse(Request); + public async Task Invoke(DownstreamContext context) + { + var response = await _requester.GetResponse(context); if (response.IsError) { _logger.LogDebug("IHttpRequester returned an error, setting pipeline error"); - SetPipelineError(response.Errors); + SetPipelineError(context, response.Errors); return; } _logger.LogDebug("setting http response message"); - SetHttpResponseMessageThisRequest(response.Data); + context.DownstreamResponse = response.Data; } } } diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddlewareExtensions.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddlewareExtensions.cs index b32d3247..82013d4c 100644 --- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddlewareExtensions.cs +++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Requester.Middleware { public static class HttpRequesterMiddlewareExtensions { - public static IApplicationBuilder UseHttpRequesterMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseHttpRequesterMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/QoS/IQoSProvider.cs b/src/Ocelot/Requester/QoS/IQoSProvider.cs index 599a5b7a..7382cb06 100644 --- a/src/Ocelot/Requester/QoS/IQoSProvider.cs +++ b/src/Ocelot/Requester/QoS/IQoSProvider.cs @@ -4,4 +4,4 @@ { CircuitBreaker CircuitBreaker { get; } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs b/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs index 043ec19d..2152fb41 100644 --- a/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs +++ b/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs @@ -5,6 +5,6 @@ namespace Ocelot.Requester.QoS { public interface IQoSProviderFactory { - IQoSProvider Get(ReRoute reRoute); + IQoSProvider Get(DownstreamReRoute reRoute); } } diff --git a/src/Ocelot/Requester/QoS/IQosProviderHouse.cs b/src/Ocelot/Requester/QoS/IQosProviderHouse.cs index add24a6b..29f1324a 100644 --- a/src/Ocelot/Requester/QoS/IQosProviderHouse.cs +++ b/src/Ocelot/Requester/QoS/IQosProviderHouse.cs @@ -5,6 +5,6 @@ namespace Ocelot.Requester.QoS { public interface IQosProviderHouse { - Response Get(ReRoute reRoute); + Response Get(DownstreamReRoute reRoute); } } \ No newline at end of file diff --git a/src/Ocelot/Requester/QoS/PollyQoSProvider.cs b/src/Ocelot/Requester/QoS/PollyQoSProvider.cs index 7a23986c..029650e5 100644 --- a/src/Ocelot/Requester/QoS/PollyQoSProvider.cs +++ b/src/Ocelot/Requester/QoS/PollyQoSProvider.cs @@ -15,7 +15,7 @@ namespace Ocelot.Requester.QoS private readonly IOcelotLogger _logger; private readonly CircuitBreaker _circuitBreaker; - public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory) + public PollyQoSProvider(DownstreamReRoute reRoute, IOcelotLoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); @@ -48,4 +48,4 @@ namespace Ocelot.Requester.QoS public CircuitBreaker CircuitBreaker => _circuitBreaker; } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/QoS/QoSProviderFactory.cs b/src/Ocelot/Requester/QoS/QoSProviderFactory.cs index 0150cf8f..f985c9b5 100644 --- a/src/Ocelot/Requester/QoS/QoSProviderFactory.cs +++ b/src/Ocelot/Requester/QoS/QoSProviderFactory.cs @@ -12,7 +12,7 @@ namespace Ocelot.Requester.QoS _loggerFactory = loggerFactory; } - public IQoSProvider Get(ReRoute reRoute) + public IQoSProvider Get(DownstreamReRoute reRoute) { if (reRoute.IsQos) { @@ -22,4 +22,4 @@ namespace Ocelot.Requester.QoS return new NoQoSProvider(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Requester/QoS/QosProviderHouse.cs b/src/Ocelot/Requester/QoS/QosProviderHouse.cs index 6d09f274..64e2bb7d 100644 --- a/src/Ocelot/Requester/QoS/QosProviderHouse.cs +++ b/src/Ocelot/Requester/QoS/QosProviderHouse.cs @@ -17,7 +17,7 @@ namespace Ocelot.Requester.QoS _qoSProviders = new ConcurrentDictionary(); } - public Response Get(ReRoute reRoute) + public Response Get(DownstreamReRoute reRoute) { try { @@ -50,4 +50,4 @@ namespace Ocelot.Requester.QoS _qoSProviders.AddOrUpdate(key, qosProvider, (x, y) => qosProvider); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs b/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs index d0c12642..bac062f8 100644 --- a/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs +++ b/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs @@ -5,6 +5,7 @@ using Ocelot.Logging; using Ocelot.Middleware; using System.Collections.Generic; using System.Threading.Tasks; +using Ocelot.DownstreamRouteFinder.Middleware; namespace Ocelot.Responder.Middleware { @@ -13,18 +14,16 @@ namespace Ocelot.Responder.Middleware /// public class ResponderMiddleware : OcelotMiddleware { - private readonly RequestDelegate _next; + private readonly OcelotRequestDelegate _next; private readonly IHttpResponder _responder; private readonly IErrorsToHttpStatusCodeMapper _codeMapper; private readonly IOcelotLogger _logger; - public ResponderMiddleware(RequestDelegate next, + public ResponderMiddleware(OcelotRequestDelegate next, IHttpResponder responder, IOcelotLoggerFactory loggerFactory, - IRequestScopedDataRepository requestScopedDataRepository, IErrorsToHttpStatusCodeMapper codeMapper ) - :base(requestScopedDataRepository) { _next = next; _responder = responder; @@ -33,20 +32,20 @@ namespace Ocelot.Responder.Middleware } - public async Task Invoke(HttpContext context) + public async Task Invoke(DownstreamContext context) { await _next.Invoke(context); - if (PipelineError) + if (context.IsError) { - var errors = PipelineErrors; - _logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code"); - SetErrorResponse(context, errors); + var errors = context.Errors; + _logger.LogError($"{errors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code"); + SetErrorResponse(context.HttpContext, errors); } else { _logger.LogDebug("no pipeline errors, setting and returning completed response"); - await _responder.SetResponseOnHttpContext(context, HttpResponseMessage); + await _responder.SetResponseOnHttpContext(context.HttpContext, context.DownstreamResponse); } } diff --git a/src/Ocelot/Responder/Middleware/ResponderMiddlewareExtensions.cs b/src/Ocelot/Responder/Middleware/ResponderMiddlewareExtensions.cs index c998b534..9379708f 100644 --- a/src/Ocelot/Responder/Middleware/ResponderMiddlewareExtensions.cs +++ b/src/Ocelot/Responder/Middleware/ResponderMiddlewareExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware.Pipeline; namespace Ocelot.Responder.Middleware { public static class ResponderMiddlewareExtensions { - public static IApplicationBuilder UseResponderMiddleware(this IApplicationBuilder builder) + public static IOcelotPipelineBuilder UseResponderMiddleware(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware(); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs index c4ea0ac6..9f0bc93a 100644 --- a/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs @@ -4,6 +4,6 @@ namespace Ocelot.ServiceDiscovery { public interface IServiceDiscoveryProviderFactory { - IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute); + IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute); } } \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs index e8c97bd4..0e75cb1a 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -14,7 +14,7 @@ namespace Ocelot.ServiceDiscovery _factory = factory; } - public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute) + public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute) { if (reRoute.UseServiceDiscovery) { diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs new file mode 100644 index 00000000..c0ed2de2 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -0,0 +1,372 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration.File; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class AggregateTests : IDisposable + { + private IWebHost _serviceOneBuilder; + private IWebHost _serviceTwoBuilder; + private readonly Steps _steps; + private string _downstreamPathOne; + private string _downstreamPathTwo; + + public AggregateTests() + { + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51885, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51886, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_one_service_404() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51881, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51882, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51882", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_both_service_404() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51883, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51884, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":,\"Tom\":}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, "")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + + [Fact] + public void should_be_thread_safe() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway()) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceOneBuilder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if(_downstreamPathOne != basePath) + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + }); + }) + .Build(); + + _serviceOneBuilder.Start(); + } + + private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceOneBuilder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if(_downstreamPathTwo != basePath) + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + }); + }) + .Build(); + + _serviceOneBuilder.Start(); + } + + internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath) + { + _downstreamPathOne.ShouldBe(expectedDownstreamPathOne); + _downstreamPathTwo.ShouldBe(expectedDownstreamPath); + } + + public void Dispose() + { + _serviceOneBuilder?.Dispose(); + _serviceTwoBuilder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index 755237a5..f64e2ce2 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -37,7 +37,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51899, } }, DownstreamScheme = "http", @@ -51,13 +51,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51899, } }, DownstreamScheme = "http", @@ -94,13 +94,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -122,7 +122,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51899, } }, DownstreamScheme = "http", @@ -136,13 +136,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .And(x => x.GivenTheCacheExpires()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index 247338eb..7176ce26 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -36,7 +36,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -46,7 +46,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -69,7 +69,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -103,7 +103,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -114,7 +114,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -137,7 +137,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -148,7 +148,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -171,7 +171,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -182,7 +182,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -205,7 +205,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51877, } }, DownstreamScheme = "http", @@ -216,7 +216,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) diff --git a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs index 47a4c03d..11ec83b1 100644 --- a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs +++ b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs @@ -48,7 +48,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51876, } }, DownstreamScheme = "http", @@ -81,7 +81,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1)) @@ -109,7 +109,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51876, } }, DownstreamScheme = "http", @@ -140,7 +140,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4)) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 3a2cc730..0f30ba73 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -32,7 +32,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_pre_query_string_builder_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { AuthorisationMiddleware = async (ctx, next) => { @@ -75,7 +75,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_authorisation_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { AuthorisationMiddleware = async (ctx, next) => { @@ -118,7 +118,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_authentication_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { AuthenticationMiddleware = async (ctx, next) => { @@ -161,7 +161,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_pre_error_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { PreErrorResponderMiddleware = async (ctx, next) => { @@ -204,7 +204,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_pre_authorisation_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { PreAuthorisationMiddleware = async (ctx, next) => { @@ -247,7 +247,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_call_pre_http_authentication_middleware() { - var configuration = new OcelotMiddlewareConfiguration + var configuration = new OcelotPipelineConfiguration { PreAuthenticationMiddleware = async (ctx, next) => { diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index 46d7cd40..e0bd67c8 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51871, } }, UpstreamPathTemplate = "/", @@ -54,7 +54,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Laz")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenIAddAHeader("Laz", "D")) @@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51871, } }, UpstreamPathTemplate = "/", @@ -93,7 +93,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Location", "http://www.bbc.co.uk/")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/QoSTests.cs b/test/Ocelot.AcceptanceTests/QoSTests.cs index bf86455d..e4f8a026 100644 --- a/test/Ocelot.AcceptanceTests/QoSTests.cs +++ b/test/Ocelot.AcceptanceTests/QoSTests.cs @@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51872, } }, UpstreamPathTemplate = "/", @@ -57,7 +57,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura")) + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51872", "Hello from Laura")) .Given(x => _steps.GivenThereIsAConfiguration(configuration)) .Given(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -92,7 +92,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51872, } }, UpstreamPathTemplate = "/", @@ -122,7 +122,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura")) + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51872", "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index 444133d9..6ac271c9 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51873, } }, DownstreamScheme = "http", @@ -50,7 +50,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -73,7 +73,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51873, } }, DownstreamScheme = "http", @@ -85,7 +85,7 @@ namespace Ocelot.AcceptanceTests var requestId = Guid.NewGuid().ToString(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId)) @@ -108,7 +108,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51873, } }, DownstreamScheme = "http", @@ -124,7 +124,7 @@ namespace Ocelot.AcceptanceTests var requestId = Guid.NewGuid().ToString(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId)) diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 5fc72b27..ea748a04 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -298,7 +298,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51874, } }, UpstreamPathTemplate = "/vacancy/", @@ -315,7 +315,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51874, } }, UpstreamPathTemplate = "/vacancy/{vacancyId}", @@ -326,7 +326,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/api/v1/vacancy/1", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1")) diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 0f976473..b7f3e6ab 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -336,7 +338,7 @@ namespace Ocelot.AcceptanceTests /// /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. /// - public void GivenOcelotIsRunning(OcelotMiddlewareConfiguration ocelotMiddlewareConfig) + public void GivenOcelotIsRunning(OcelotPipelineConfiguration ocelotPipelineConfig) { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -373,7 +375,7 @@ namespace Ocelot.AcceptanceTests }) .Configure(a => { - a.UseOcelot(ocelotMiddlewareConfig).Wait(); + a.UseOcelot(ocelotPipelineConfig).Wait(); })); _ocelotClient = _ocelotServer.CreateClient(); @@ -552,7 +554,6 @@ namespace Ocelot.AcceptanceTests _response.StatusCode.ShouldBe(expectedHttpStatusCode); } - public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode) { var responseStatusCode = (int)_response.StatusCode; @@ -579,5 +580,51 @@ namespace Ocelot.AcceptanceTests { _response.Content.Headers.ContentLength.ShouldBe(expected); } + + public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway() + { + int numberOfRequests = 100; + var aggregateUrl = "/"; + var aggregateExpected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; + var tomUrl = "/tom"; + var tomExpected = "{Hello from Tom}"; + var lauraUrl = "/laura"; + var lauraExpected = "{Hello from Laura}"; + var random = new Random(); + + var aggregateTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + aggregateTasks[i] = Fire(aggregateUrl, aggregateExpected, random); + } + + var tomTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + tomTasks[i] = Fire(tomUrl, tomExpected, random); + } + + var lauraTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + lauraTasks[i] = Fire(lauraUrl, lauraExpected, random); + } + + Task.WaitAll(lauraTasks); + Task.WaitAll(tomTasks); + Task.WaitAll(aggregateTasks); + } + + private async Task Fire(string url, string expectedBody, Random random) + { + var request = new HttpRequestMessage(new HttpMethod("GET"), url); + await Task.Delay(random.Next(0, 2)); + var response = await _ocelotClient.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + content.ShouldBe(expectedBody); + } } } diff --git a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs index 18aa6c16..16e123a7 100644 --- a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs +++ b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs @@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51875, } }, UpstreamPathTemplate = "/", @@ -49,7 +49,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -75,7 +75,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51875, } }, UpstreamPathTemplate = "/", @@ -101,7 +101,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -142,7 +142,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51875, } }, UpstreamPathTemplate = "/", @@ -152,7 +152,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -192,7 +192,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51875, } }, UpstreamPathTemplate = "/", @@ -202,7 +202,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -227,7 +227,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51879, + Port = 51875, } }, UpstreamPathTemplate = "/", @@ -237,7 +237,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj index ac2b0191..00e25330 100644 --- a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj +++ b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs b/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs index 853fd7e5..d7f4434b 100644 --- a/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs +++ b/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs @@ -17,7 +17,7 @@ namespace Ocelot.Benchmarks Add(StatisticColumn.AllStatistics); } - [Setup] + [GlobalSetup] public void SetUp() { _urlPathMatcher = new RegExUrlMatcher(); @@ -37,4 +37,4 @@ namespace Ocelot.Benchmarks _urlPathMatcher.Match(_downstreamUrlPath, _downstreamUrlPathTemplate); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.ManualTest/configuration.json b/test/Ocelot.ManualTest/configuration.json index 93bf6ac6..98ce38b8 100644 --- a/test/Ocelot.ManualTest/configuration.json +++ b/test/Ocelot.ManualTest/configuration.json @@ -14,7 +14,7 @@ "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, - "UseTracing": true + "UseTracing": false } }, { @@ -99,7 +99,7 @@ "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, - "UseTracing": true + "UseTracing": false }, "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, @@ -121,7 +121,7 @@ "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, - "UseTracing": true + "UseTracing": false }, "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs index 20cd82ef..07368127 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs @@ -1,6 +1,11 @@ -namespace Ocelot.UnitTests.Authentication +using Ocelot.Configuration; +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Authentication { using System.Collections.Generic; + using System.IO; + using System.Text; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -8,6 +13,7 @@ using Ocelot.Authentication.Middleware; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Logging; using Ocelot.Responses; @@ -15,56 +21,75 @@ using TestStack.BDDfy; using Xunit; - public class AuthenticationMiddlewareTests : ServerHostedMiddlewareTest + public class AuthenticationMiddlewareTests { private OkResponse _downstreamRoute; + private AuthenticationMiddleware _middleware; + private Mock _factory; + private Mock _logger; + private OcelotRequestDelegate _next; + private DownstreamContext _downstreamContext; public AuthenticationMiddlewareTests() { - GivenTheTestServerIsConfigured(); + _factory = new Mock(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); } [Fact] public void should_call_next_middleware_if_route_is_not_authenticated() { - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List(), - new ReRouteBuilder().WithUpstreamHttpMethod(new List { "Get" }).Build()))) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheUserIsAuthenticated()) + this.Given(x => GivenTheDownStreamRouteIs( + new DownstreamReRouteBuilder().WithUpstreamHttpMethod(new List { "Get" }).Build())) + .And(x => GivenTheTestServerPipelineIsConfigured()) + .When(x => WhenICallTheMiddleware()) + .Then(x => ThenTheUserIsAuthenticated()) .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(ScopedRepository.Object); + _next = async (context) => { + byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); + MemoryStream stream = new MemoryStream(byteArray); + context.HttpContext.Response.Body = stream; + }; + _middleware = new AuthenticationMiddleware(_next, _factory.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) + private void GivenTheTestServerPipelineIsConfigured() { - app.UseAuthenticationMiddleware(); - - app.Run(async x => - { - await x.Response.WriteAsync("The user is authenticated"); - }); + _next = async (context) => { + byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); + MemoryStream stream = new MemoryStream(byteArray); + context.HttpContext.Response.Body = stream; + }; } private void ThenTheUserIsAuthenticated() { - var content = ResponseMessage.Content.ReadAsStringAsync().Result; + var content = _downstreamContext.HttpContext.Response.Body.AsString(); content.ShouldBe("The user is authenticated"); } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.DownstreamReRoute = downstreamRoute; + } + } + + public static class StreamExtensions + { + public static string AsString(this Stream stream) + { + using(var reader = new StreamReader(stream)) + { + string text = reader.ReadToEnd(); + return text; + }; } } } diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index ea89accb..6f1d54f3 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -1,9 +1,9 @@ -namespace Ocelot.UnitTests.Authorization +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Authorization { using System.Collections.Generic; using System.Security.Claims; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Authorisation; using Ocelot.Authorisation.Middleware; @@ -14,55 +14,57 @@ using Ocelot.Responses; using TestStack.BDDfy; using Xunit; + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.Configuration; - public class AuthorisationMiddlewareTests : ServerHostedMiddlewareTest + public class AuthorisationMiddlewareTests { private readonly Mock _authService; private readonly Mock _authScopesService; - private OkResponse _downstreamRoute; + private Mock _loggerFactory; + private Mock _logger; + private readonly AuthorisationMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public AuthorisationMiddlewareTests() { _authService = new Mock(); _authScopesService = new Mock(); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new AuthorisationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object); } [Fact] public void should_call_authorisation_service() { - this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), - new ReRouteBuilder() + this.Given(x => x.GivenTheDownStreamRouteIs(new List(), + new DownstreamReRouteBuilder() .WithIsAuthorised(true) .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) + .Build())) .And(x => x.GivenTheAuthServiceReturns(new OkResponse(true))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheAuthServiceIsCalledCorrectly()) .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_authService.Object); - services.AddSingleton(_authScopesService.Object); - services.AddSingleton(ScopedRepository.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) + private void GivenTheDownStreamRouteIs(List templatePlaceholderNameAndValues, DownstreamReRoute downstreamReRoute) { - app.UseAuthorisationMiddleware(); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) - { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamReRoute; } private void GivenTheAuthServiceReturns(Response expected) diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs index 70619c6b..4e9ef04d 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs @@ -1,4 +1,6 @@ -using Ocelot.Infrastructure.RequestData; +using Ocelot.Errors; +using Ocelot.Infrastructure.RequestData; +using Ocelot.Middleware; namespace Ocelot.UnitTests.Cache { @@ -22,20 +24,37 @@ namespace Ocelot.UnitTests.Cache using Ocelot.Responses; using TestStack.BDDfy; using Xunit; + using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; - public class OutputCacheMiddlewareRealCacheTests : ServerHostedMiddlewareTest + public class OutputCacheMiddlewareRealCacheTests { private IOcelotCache _cacheManager; private CachedResponse _response; - private IRequestScopedDataRepository _repo; + private OutputCacheMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + private Mock _loggerFactory; + private IRegionCreator _regionCreator; + private Mock _logger; public OutputCacheMiddlewareRealCacheTests() { - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); - - GivenTheTestServerIsConfigured(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _regionCreator = new RegionCreator(); + var cacheManagerOutputCache = CacheFactory.Build("OcelotOutputCache", x => + { + x.WithDictionaryHandle(); + }); + _cacheManager = new OcelotCacheManagerCache(cacheManagerOutputCache); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); + _next = async context => { + //do nothing.. + }; + _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator); } [Fact] @@ -54,12 +73,16 @@ namespace Ocelot.UnitTests.Cache this.Given(x => x.GivenResponseIsNotCached(response)) .And(x => x.GivenTheDownstreamRouteIs()) .And(x => x.GivenThereAreNoErrors()) - .And(x => x.GivenThereIsADownstreamUrl()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheContentTypeHeaderIsCached()) .BDDfy(); } + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + private void ThenTheContentTypeHeaderIsCached() { var result = _cacheManager.Get("GET-https://some.url/blah?abcd=123", "kanken"); @@ -67,65 +90,25 @@ namespace Ocelot.UnitTests.Cache header.First().ShouldBe("application/json"); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - var cacheManagerOutputCache = CacheFactory.Build("OcelotOutputCache", x => - { - x.WithDictionaryHandle(); - }); - - _cacheManager = new OcelotCacheManagerCache(cacheManagerOutputCache); - - services.AddSingleton>(cacheManagerOutputCache); - services.AddSingleton>(_cacheManager); - - services.AddSingleton(); - - services.AddLogging(); - services.AddSingleton(_cacheManager); - services.AddSingleton(ScopedRepository.Object); - services.AddSingleton(); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseOutputCacheMiddleware(); - } - private void GivenResponseIsNotCached(HttpResponseMessage message) { - ScopedRepository - .Setup(x => x.Get("HttpResponseMessage")) - .Returns(new OkResponse(message)); + _downstreamContext.DownstreamResponse = message; } private void GivenTheDownstreamRouteIs() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithIsCached(true) .WithCacheOptions(new CacheOptions(100, "kanken")) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - var downstreamRoute = new DownstreamRoute(new List(), reRoute); - - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse(downstreamRoute)); + _downstreamContext.DownstreamReRoute = reRoute; } private void GivenThereAreNoErrors() { - ScopedRepository - .Setup(x => x.Get("OcelotMiddlewareError")) - .Returns(new OkResponse(false)); - } - - private void GivenThereIsADownstreamUrl() - { - ScopedRepository - .Setup(x => x.Get("DownstreamUrl")) - .Returns(new OkResponse("anything")); + _downstreamContext.Errors = new List(); } } } diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 2a60e376..42c1e695 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -1,4 +1,6 @@ using System.Net; +using Ocelot.Errors; +using Ocelot.Middleware; namespace Ocelot.UnitTests.Cache { @@ -6,6 +8,7 @@ namespace Ocelot.UnitTests.Cache using System.Collections.Generic; using System.Net.Http; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Cache; @@ -13,26 +16,37 @@ namespace Ocelot.UnitTests.Cache using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Logging; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; - public class OutputCacheMiddlewareTests : ServerHostedMiddlewareTest + public class OutputCacheMiddlewareTests { - private readonly Mock> _cacheManager; + private readonly Mock> _cacheManager; + private Mock _loggerFactory; + private Mock _logger; + private OutputCacheMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; private CachedResponse _response; + private IRegionCreator _regionCreator; public OutputCacheMiddlewareTests() { _cacheManager = new Mock>(); + _regionCreator = new RegionCreator(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); - - GivenTheTestServerIsConfigured(); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); } [Fact] @@ -41,7 +55,6 @@ namespace Ocelot.UnitTests.Cache var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary>(), "", new Dictionary>()); this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) .And(x => x.GivenTheDownstreamRouteIs()) - .And(x => x.GivenThereIsADownstreamUrl()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheCacheGetIsCalledCorrectly()) .BDDfy(); @@ -53,24 +66,15 @@ namespace Ocelot.UnitTests.Cache this.Given(x => x.GivenResponseIsNotCached()) .And(x => x.GivenTheDownstreamRouteIs()) .And(x => x.GivenThereAreNoErrors()) - .And(x => x.GivenThereIsADownstreamUrl()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheCacheAddIsCalledCorrectly()) .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_cacheManager.Object); - services.AddSingleton(ScopedRepository.Object); - services.AddSingleton(); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseOutputCacheMiddleware(); + _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager.Object, _regionCreator); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void GivenThereIsACachedResponse(CachedResponse response) @@ -83,38 +87,30 @@ namespace Ocelot.UnitTests.Cache private void GivenResponseIsNotCached() { - ScopedRepository - .Setup(x => x.Get("HttpResponseMessage")) - .Returns(new OkResponse(new HttpResponseMessage())); + _downstreamContext.DownstreamResponse = new HttpResponseMessage(); } private void GivenTheDownstreamRouteIs() { var reRoute = new ReRouteBuilder() - .WithIsCached(true) - .WithCacheOptions(new CacheOptions(100, "kanken")) + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithIsCached(true) + .WithCacheOptions(new CacheOptions(100, "kanken")) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); var downstreamRoute = new DownstreamRoute(new List(), reRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse(downstreamRoute)); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void GivenThereAreNoErrors() { - ScopedRepository - .Setup(x => x.Get("OcelotMiddlewareError")) - .Returns(new OkResponse(false)); - } + _downstreamContext.Errors = new List(); - private void GivenThereIsADownstreamUrl() - { - ScopedRepository - .Setup(x => x.Get("DownstreamUrl")) - .Returns(new OkResponse("anything")); } private void ThenTheCacheGetIsCalledCorrectly() diff --git a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs index 44ab1dc0..be266f9e 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs @@ -1,9 +1,9 @@ -namespace Ocelot.UnitTests.Claims +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Claims { using System.Collections.Generic; - using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Claims; using Ocelot.Claims.Middleware; @@ -15,17 +15,29 @@ using Ocelot.Responses; using TestStack.BDDfy; using Xunit; + using Ocelot.DownstreamRouteFinder.Middleware; - public class ClaimsBuilderMiddlewareTests : ServerHostedMiddlewareTest + public class ClaimsBuilderMiddlewareTests { private readonly Mock _addHeaders; private Response _downstreamRoute; + private Mock _loggerFactory; + private Mock _logger; + private readonly ClaimsBuilderMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public ClaimsBuilderMiddlewareTests() { _addHeaders = new Mock(); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); } [Fact] @@ -33,11 +45,14 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToClaims(new List - { - new ClaimToThing("sub", "UserType", "|", 0) - }) + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToClaims(new List + { + new ClaimToThing("sub", "UserType", "|", 0) + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -48,25 +63,16 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_addHeaders.Object); - services.AddSingleton(ScopedRepository.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseClaimsBuilderMiddleware(); - } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void GivenTheAddClaimsToRequestReturns() diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationFluentValidationTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationFluentValidationTests.cs index a42dc2b6..e7e56178 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationFluentValidationTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationFluentValidationTests.cs @@ -20,7 +20,7 @@ namespace Ocelot.UnitTests.Configuration private readonly IConfigurationValidator _configurationValidator; private FileConfiguration _fileConfiguration; private Response _result; - private Mock _provider; + private readonly Mock _provider; public ConfigurationFluentValidationTests() { @@ -28,6 +28,373 @@ namespace Ocelot.UnitTests.Configuration _configurationValidator = new FileConfigurationFluentValidator(_provider.Object); } + [Fact] + public void configuration_is_valid_if_aggregates_are_valid() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregates_are_duplicate_of_re_routes() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom", + UpstreamHost = "localhost" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + }, + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /tom has duplicate aggregate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_aggregates_are_not_duplicate_of_re_routes() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Post" }, + Key = "Tom", + UpstreamHost = "localhost" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + }, + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregates_are_duplicate_of_aggregates() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/lol", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + }, + new FileAggregateReRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregate /tom has duplicate aggregate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_re_routes_dont_exist_for_aggregate() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct Key property")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregate_has_re_routes_with_specific_request_id_keys() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + RequestIdKey = "should_fail", + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregateReRoute / contains ReRoute with specific RequestIdKey, this is not possible with Aggregates")) + .BDDfy(); + } + [Fact] public void configuration_is_invalid_if_scheme_in_downstream_or_upstream_template() { diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index b2ff9a00..8bb0ecd2 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Castle.Components.DictionaryAdapter; using Microsoft.Extensions.Options; using Moq; using Ocelot.Cache; @@ -81,6 +82,125 @@ namespace Ocelot.UnitTests.Configuration _downstreamAddressesCreator.Object); } + [Fact] + public void should_set_up_aggregate_re_route() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + UpstreamHost = "localhost" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom", + UpstreamHost = "localhost", + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + var expected = new List(); + + var lauraDownstreamReRoute = new DownstreamReRouteBuilder() + .WithUpstreamHost("localhost") + .WithKey("Laura") + .WithDownstreamPathTemplate("/") + .WithDownstreamScheme("http") + .WithUpstreamHttpMethod(new List() {"Get"}) + .WithDownstreamAddresses(new List() {new DownstreamHostAndPort("localhost", 51878)}) + .Build(); + + var lauraReRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(new List() { "Get" }) + .WithUpstreamHost("localhost") + .WithUpstreamPathTemplate("/laura") + .WithDownstreamReRoute(lauraDownstreamReRoute) + .Build(); + + expected.Add(lauraReRoute); + + var tomDownstreamReRoute = new DownstreamReRouteBuilder() + .WithUpstreamHost("localhost") + .WithKey("Tom") + .WithDownstreamPathTemplate("/") + .WithDownstreamScheme("http") + .WithUpstreamHttpMethod(new List() { "Get" }) + .WithDownstreamAddresses(new List() { new DownstreamHostAndPort("localhost", 51878) }) + .Build(); + + var tomReRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(new List() { "Get" }) + .WithUpstreamHost("localhost") + .WithUpstreamPathTemplate("/tom") + .WithDownstreamReRoute(tomDownstreamReRoute) + .Build(); + + expected.Add(tomReRoute); + + var aggregateReReRoute = new ReRouteBuilder() + .WithUpstreamPathTemplate("/") + .WithUpstreamHost("localhost") + .WithDownstreamReRoute(lauraDownstreamReRoute) + .WithDownstreamReRoute(tomDownstreamReRoute) + .WithUpstreamHttpMethod(new List() { "Get" }) + .Build(); + + expected.Add(aggregateReReRoute); + + this.Given(x => x.GivenTheConfigIs(configuration)) + .And(x => x.GivenTheFollowingOptionsAreReturned(new ReRouteOptionsBuilder().Build())) + .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig)) + .And(x => GivenTheDownstreamAddresses()) + .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) + .And(x => x.GivenTheConfigIsValid()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly()) + .Then(x => x.ThenTheReRoutesAre(expected)) + .BDDfy(); + } + [Fact] public void should_call_service_provider_config_creator() { @@ -234,6 +354,13 @@ namespace Ocelot.UnitTests.Configuration var reRouteOptions = new ReRouteOptionsBuilder() .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamAddresses(new List() {new DownstreamHostAndPort("127.0.0.1", 80)}) + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build(); + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -261,8 +388,7 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamAddresses(new List(){new DownstreamHostAndPort("127.0.0.1", 80) }) - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .Build() @@ -276,6 +402,13 @@ namespace Ocelot.UnitTests.Configuration var reRouteOptions = new ReRouteOptionsBuilder() .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamScheme("https") + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build(); + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -297,8 +430,7 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamScheme("https") - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .Build() @@ -312,6 +444,14 @@ namespace Ocelot.UnitTests.Configuration var reRouteOptions = new ReRouteOptionsBuilder() .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithUseServiceDiscovery(true) + .WithServiceName("ProductService") + .Build(); + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -341,11 +481,9 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithUseServiceDiscovery(true) - .WithServiceName("ProductService") .Build() })) .BDDfy(); @@ -356,7 +494,15 @@ namespace Ocelot.UnitTests.Configuration { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); - + + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithUseServiceDiscovery(false) + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -378,10 +524,9 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithUseServiceDiscovery(false) .Build() })) .BDDfy(); @@ -392,7 +537,15 @@ namespace Ocelot.UnitTests.Configuration { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); - + + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -415,7 +568,7 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) @@ -430,6 +583,13 @@ namespace Ocelot.UnitTests.Configuration var reRouteOptions = new ReRouteOptionsBuilder() .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithRequestIdKey("blahhhh") + .Build(); + this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List @@ -456,10 +616,9 @@ namespace Ocelot.UnitTests.Configuration .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithRequestIdKey("blahhhh") .Build() })) .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly()) @@ -514,17 +673,23 @@ namespace Ocelot.UnitTests.Configuration .WithAllowedScopes(new List()) .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithAuthenticationOptions(authenticationOptions) + .WithClaimsToHeaders(new List + { + new ClaimToThing("CustomerId", "CustomerId", "", 0), + }) + .Build(); + var expected = new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithAuthenticationOptions(authenticationOptions) - .WithClaimsToHeaders(new List - { - new ClaimToThing("CustomerId", "CustomerId", "", 0), - }) .Build() }; @@ -554,13 +719,19 @@ namespace Ocelot.UnitTests.Configuration .WithAllowedScopes(new List()) .Build(); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithAuthenticationOptions(authenticationOptions) + .Build(); + var expected = new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithAuthenticationOptions(authenticationOptions) .Build() }; @@ -643,14 +814,16 @@ namespace Ocelot.UnitTests.Configuration var result = _config.Data.ReRoutes[i]; var expected = expectedReRoutes[i]; - result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); + result.DownstreamReRoute.Count.ShouldBe(expected.DownstreamReRoute.Count); + + result.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamReRoute[0].DownstreamPathTemplate.Value); result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value); result.UpstreamTemplatePattern?.Template.ShouldBe(expected.UpstreamTemplatePattern?.Template); - result.ClaimsToClaims.Count.ShouldBe(expected.ClaimsToClaims.Count); - result.ClaimsToHeaders.Count.ShouldBe(expected.ClaimsToHeaders.Count); - result.ClaimsToQueries.Count.ShouldBe(expected.ClaimsToQueries.Count); - result.RequestIdKey.ShouldBe(expected.RequestIdKey); + result.DownstreamReRoute[0].ClaimsToClaims.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToClaims.Count); + result.DownstreamReRoute[0].ClaimsToHeaders.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToHeaders.Count); + result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count); + result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey); } } @@ -659,8 +832,8 @@ namespace Ocelot.UnitTests.Configuration { for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { - var result = _config.Data.ReRoutes[i].AuthenticationOptions; - var expected = expectedReRoutes[i].AuthenticationOptions; + var result = _config.Data.ReRoutes[i].DownstreamReRoute[0].AuthenticationOptions; + var expected = expectedReRoutes[i].DownstreamReRoute[0].AuthenticationOptions; result.AllowedScopes.ShouldBe(expected.AllowedScopes); } } @@ -714,10 +887,10 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheQosOptionsAre(QoSOptions qosOptions) { - _config.Data.ReRoutes[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); - _config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); - _config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); } private void ThenTheServiceProviderCreatorIsCalledCorrectly() diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index ab5a9eea..cdc3f94d 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -44,7 +44,7 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheConfigurationIsReturned() { - _getResult.Data.ReRoutes[0].DownstreamPathTemplate.Value.ShouldBe("initial"); + _getResult.Data.ReRoutes[0].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("initial"); } private void WhenIGetTheConfiguration() @@ -83,13 +83,24 @@ namespace Ocelot.UnitTests.Configuration AdministrationPath = administrationPath; } - public List ReRoutes => new List + public List ReRoutes { - new ReRouteBuilder() - .WithDownstreamPathTemplate(_downstreamTemplatePath) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - }; + get + { + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate(_downstreamTemplatePath) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build(); + + return new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(downstreamReRoute) + .WithUpstreamHttpMethod(new List {"Get"}) + .Build() + }; + } + } public string AdministrationPath {get;} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index 54621aee..a5c18c22 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -1,7 +1,11 @@ -namespace Ocelot.UnitTests.DownstreamRouteFinder +using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; + +namespace Ocelot.UnitTests.DownstreamRouteFinder { using System.Collections.Generic; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; @@ -13,22 +17,36 @@ using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Logging; using Ocelot.Responses; + using Shouldly; using TestStack.BDDfy; using Xunit; - public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest + public class DownstreamRouteFinderMiddlewareTests { - private readonly Mock _downstreamRouteFinder; + private readonly Mock _finder; private readonly Mock _provider; private Response _downstreamRoute; private IOcelotConfiguration _config; + private Mock _loggerFactory; + private Mock _logger; + private DownstreamRouteFinderMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + private readonly Mock _multiplexer; public DownstreamRouteFinderMiddlewareTests() { _provider = new Mock(); - _downstreamRouteFinder = new Mock(); - - GivenTheTestServerIsConfigured(); + _finder = new Mock(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _multiplexer = new Mock(); + _middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _provider.Object, _multiplexer.Object); } [Fact] @@ -36,11 +54,16 @@ { var config = new OcelotConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), ""); + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build(); + this.Given(x => x.GivenTheDownStreamRouteFinderReturns( new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .And(x => GivenTheFollowingConfig(config)) @@ -49,6 +72,11 @@ .BDDfy(); } + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetType(); + } + private void GivenTheFollowingConfig(IOcelotConfiguration config) { _config = config; @@ -57,35 +85,18 @@ .ReturnsAsync(new OkResponse(_config)); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_downstreamRouteFinder.Object); - services.AddSingleton(_provider.Object); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseDownstreamRouteFinderMiddleware(); - } - private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) { _downstreamRoute = new OkResponse(downstreamRoute); - _downstreamRouteFinder + _finder .Setup(x => x.FindDownstreamRoute(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(_downstreamRoute); } private void ThenTheScopedDataRepositoryIsCalledCorrectly() { - ScopedRepository - .Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once()); - - ScopedRepository - .Verify(x => x.Add("ServiceProviderConfiguration", _config.ServiceProviderConfiguration), Times.Once()); + _downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues); + _downstreamContext.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration); } } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 7b1a4b04..b7761c60 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -47,13 +47,23 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .Build(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) @@ -64,7 +74,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) @@ -84,13 +99,23 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) .Build(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) @@ -101,7 +126,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build()) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .WithUpstreamHttpMethod(new List { "Post" }) .Build() @@ -121,7 +150,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -135,7 +169,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -157,7 +195,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -171,7 +214,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -193,7 +240,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -206,7 +258,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -227,13 +283,23 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) @@ -246,7 +312,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() @@ -263,7 +333,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("somPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("somPath") + .WithUpstreamPathTemplate("somePath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) + .Build()) .WithUpstreamPathTemplate("somePath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) @@ -292,7 +367,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get", "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) @@ -305,7 +385,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() @@ -326,7 +410,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List()) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) @@ -339,7 +428,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() @@ -360,7 +453,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) @@ -388,7 +486,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -403,7 +507,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -425,7 +533,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -439,7 +552,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -459,12 +576,17 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .WithUpstreamHost("MATCH") - .Build() }, string.Empty, serviceProviderConfig )) @@ -490,13 +612,24 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamPathTemplate("THENULLPATH") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("THENULLPATH") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) @@ -511,7 +644,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() @@ -592,7 +729,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void ThenTheFollowingIsReturned(DownstreamRoute expected) { - _result.Data.ReRoute.DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamPathTemplate.Value); + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value); _result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority); for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index b7e80e87..ec4829a7 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -1,4 +1,6 @@ -namespace Ocelot.UnitTests.DownstreamUrlCreator +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.DownstreamUrlCreator { using System; using System.Collections.Generic; @@ -19,39 +21,49 @@ using TestStack.BDDfy; using Xunit; using Shouldly; + using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; - public class DownstreamUrlCreatorMiddlewareTests : ServerHostedMiddlewareTest + public class DownstreamUrlCreatorMiddlewareTests { private readonly Mock _downstreamUrlTemplateVariableReplacer; private readonly Mock _urlBuilder; - private Response _downstreamRoute; private OkResponse _downstreamPath; - private HttpRequestMessage _downstreamRequest; + private Mock _loggerFactory; + private Mock _logger; + private DownstreamUrlCreatorMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public DownstreamUrlCreatorMiddlewareTests() { + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _downstreamUrlTemplateVariableReplacer = new Mock(); _urlBuilder = new Mock(); - - _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); - - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); + _next = async context => { + //do nothing + }; } [Fact] public void should_replace_scheme_and_path() { + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod(new List {"Get"}) + .WithDownstreamScheme("https") + .Build(); + this.Given(x => x.GivenTheDownStreamRouteIs( new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") + .WithDownstreamReRoute(downstreamReRoute) .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") .Build()))) .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) @@ -60,31 +72,21 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_downstreamUrlTemplateVariableReplacer.Object); - services.AddSingleton(ScopedRepository.Object); - services.AddSingleton(_urlBuilder.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseDownstreamUrlCreatorMiddleware(); + _middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object, _urlBuilder.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void GivenTheDownstreamRequestUriIs(string uri) { - _downstreamRequest.RequestUri = new Uri(uri); + _downstreamContext.DownstreamRequest.RequestUri = new Uri(uri); } private void GivenTheUrlReplacerWillReturn(string path) @@ -97,7 +99,7 @@ private void ThenTheDownstreamRequestUriIs(string expectedUri) { - _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); + _downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); } } } diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index de10cbb5..cd8cf3c8 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -29,6 +29,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new DownstreamRoute( new List(), new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -43,7 +46,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new DownstreamRoute( new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("/") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -56,7 +62,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer { this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("api") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("api") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -69,7 +78,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer { this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("api/") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("api/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -82,7 +94,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer { this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("api/product/products/") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("api/product/products/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -100,7 +115,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -118,7 +136,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/variants") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -137,7 +158,10 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) @@ -157,8 +181,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() - .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") - .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) @@ -172,7 +199,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer private void WhenIReplaceTheTemplateVariables() { - _result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamPathTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues); + _result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues); } private void ThenTheDownstreamUrlPathIsReturned(string expected) diff --git a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs index 1ea5fcf8..c5180f74 100644 --- a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs @@ -1,10 +1,10 @@ +using Ocelot.Middleware; + namespace Ocelot.UnitTests.Errors { using System; using System.Net; using System.Threading.Tasks; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.DependencyInjection; using Ocelot.Errors.Middleware; using Ocelot.Logging; using Shouldly; @@ -14,18 +14,41 @@ namespace Ocelot.UnitTests.Errors using Ocelot.Configuration.Provider; using Moq; using Ocelot.Configuration; - using Rafty.Concensus; using Ocelot.Errors; + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.Infrastructure.RequestData; - public class ExceptionHandlerMiddlewareTests : ServerHostedMiddlewareTest + public class ExceptionHandlerMiddlewareTests { bool _shouldThrowAnException = false; private Mock _provider; + private Mock _repo; + private Mock _loggerFactory; + private Mock _logger; + private ExceptionHandlerMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + public ExceptionHandlerMiddlewareTests() { _provider = new Mock(); - GivenTheTestServerIsConfigured(); + _repo = new Mock(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + await Task.CompletedTask; + + if (_shouldThrowAnException) + { + throw new Exception("BOOM"); + } + + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK; + }; + _middleware = new ExceptionHandlerMiddleware(_next, _loggerFactory.Object, _provider.Object, _repo.Object); } [Fact] @@ -99,6 +122,17 @@ namespace Ocelot.UnitTests.Errors .BDDfy(); } + private void WhenICallTheMiddlewareWithTheRequestIdKey(string key, string value) + { + _downstreamContext.HttpContext.Request.Headers.Add(key, value); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + private void GivenTheConfigThrows() { var ex = new Exception("outer", new Exception("inner")); @@ -108,7 +142,7 @@ namespace Ocelot.UnitTests.Errors private void ThenAnExceptionIsThrown() { - ResponseMessage.StatusCode.ShouldBe(HttpStatusCode.InternalServerError); + _downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500); } private void GivenTheConfigReturnsError() @@ -130,7 +164,7 @@ namespace Ocelot.UnitTests.Errors private void TheRequestIdIsSet(string key, string value) { - ScopedRepository.Verify(x => x.Add(key, value), Times.Once); + _repo.Verify(x => x.Add(key, value), Times.Once); } private void GivenTheConfigurationIs(IOcelotConfiguration config) @@ -140,31 +174,6 @@ namespace Ocelot.UnitTests.Errors .Setup(x => x.Get()).ReturnsAsync(response); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(ScopedRepository.Object); - services.AddSingleton(_provider.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseExceptionHandlerMiddleware(); - app.Run(DownstreamExceptionSimulator); - } - - private async Task DownstreamExceptionSimulator(HttpContext context) - { - await Task.CompletedTask; - - if (_shouldThrowAnException) - { - throw new Exception("BOOM"); - } - - context.Response.StatusCode = (int)HttpStatusCode.OK; - } private void GivenAnExceptionWillNotBeThrownDownstream() { @@ -178,17 +187,18 @@ namespace Ocelot.UnitTests.Errors private void ThenTheResponseIsOk() { - ResponseMessage.StatusCode.ShouldBe(HttpStatusCode.OK); + _downstreamContext.HttpContext.Response.StatusCode.ShouldBe(200); } private void ThenTheResponseIsError() { - ResponseMessage.StatusCode.ShouldBe(HttpStatusCode.InternalServerError); + + _downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500); } private void TheRequestIdIsNotSet() { - ScopedRepository.Verify(x => x.Add(It.IsAny(), It.IsAny()), Times.Never); + _repo.Verify(x => x.Add(It.IsAny(), It.IsAny()), Times.Never); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs index 0cfd4697..90a24eaa 100644 --- a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs @@ -1,36 +1,43 @@ using Xunit; -using Shouldly; using Ocelot.Logging; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Builder; using Ocelot.Headers.Middleware; using TestStack.BDDfy; -using System.Linq; -using System.Threading.Tasks; -using System; using Microsoft.AspNetCore.Http; using System.Collections.Generic; using Moq; using Ocelot.Configuration; using Ocelot.DownstreamRouteFinder; -using Ocelot.Responses; using Ocelot.Configuration.Builder; using Ocelot.Headers; using System.Net.Http; +using Ocelot.Authorisation.Middleware; +using Ocelot.DownstreamRouteFinder.Middleware; +using Ocelot.Middleware; namespace Ocelot.UnitTests.Headers { - public class HttpHeadersTransformationMiddlewareTests : ServerHostedMiddlewareTest + public class HttpHeadersTransformationMiddlewareTests { private Mock _preReplacer; private Mock _postReplacer; - + private Mock _loggerFactory; + private Mock _logger; + private HttpHeadersTransformationMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + public HttpHeadersTransformationMiddlewareTests() { _preReplacer = new Mock(); _postReplacer = new Mock(); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object); } [Fact] @@ -46,27 +53,34 @@ namespace Ocelot.UnitTests.Headers .BDDfy(); } + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + private void GivenTheDownstreamRequestIs() { - var request = new HttpRequestMessage(); - var response = new OkResponse(request); - ScopedRepository.Setup(x => x.Get("DownstreamRequest")).Returns(response); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(); } private void GivenTheHttpResponseMessageIs() { - var httpResponseMessage = new HttpResponseMessage(); - var response = new OkResponse(httpResponseMessage); - ScopedRepository.Setup(x => x.Get("HttpResponseMessage")).Returns(response); + _downstreamContext.DownstreamResponse = new HttpResponseMessage(); } private void GivenTheReRouteHasPreFindAndReplaceSetUp() { var fAndRs = new List(); - var reRoute = new ReRouteBuilder().WithUpstreamHeaderFindAndReplace(fAndRs).WithDownstreamHeaderFindAndReplace(fAndRs).Build(); + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder().WithUpstreamHeaderFindAndReplace(fAndRs) + .WithDownstreamHeaderFindAndReplace(fAndRs).Build()) + .Build(); + var dR = new DownstreamRoute(null, reRoute); - var response = new OkResponse(dR); - ScopedRepository.Setup(x => x.Get("DownstreamRoute")).Returns(response); + + _downstreamContext.TemplatePlaceholderNameAndValues = dR.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = dR.ReRoute.DownstreamReRoute[0]; + } private void ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly() @@ -81,21 +95,7 @@ namespace Ocelot.UnitTests.Headers private void GivenTheFollowingRequest() { - Client.DefaultRequestHeaders.Add("test", "test"); - } - - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(ScopedRepository.Object); - services.AddSingleton(_preReplacer.Object); - services.AddSingleton(_postReplacer.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseHttpHeadersTransformationMiddleware(); + _downstreamContext.HttpContext.Request.Headers.Add("test", "test"); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index f46c9482..2f3a54d3 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -1,13 +1,17 @@ -namespace Ocelot.UnitTests.Headers +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Headers { using System.Collections.Generic; using System.Net.Http; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Headers; using Ocelot.Headers.Middleware; @@ -16,22 +20,28 @@ using TestStack.BDDfy; using Xunit; - public class HttpRequestHeadersBuilderMiddlewareTests : ServerHostedMiddlewareTest + public class HttpRequestHeadersBuilderMiddlewareTests { private readonly Mock _addHeaders; - private readonly HttpRequestMessage _downstreamRequest; private Response _downstreamRoute; + private Mock _loggerFactory; + private Mock _logger; + private HttpRequestHeadersBuilderMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public HttpRequestHeadersBuilderMiddlewareTests() { _addHeaders = new Mock(); - - _downstreamRequest = new HttpRequestMessage(); - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(); } [Fact] @@ -39,11 +49,14 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToHeaders(new List - { - new ClaimToThing("UserId", "Subject", "", 0) - }) + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToHeaders(new List + { + new ClaimToThing("UserId", "Subject", "", 0) + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -54,25 +67,16 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_addHeaders.Object); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseHttpRequestHeadersBuilderMiddleware(); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void GivenTheAddHeadersToDownstreamRequestReturnsOk() @@ -91,7 +95,7 @@ .Verify(x => x.SetHeadersOnDownstreamRequest( It.IsAny>(), It.IsAny>(), - _downstreamRequest), Times.Once); + _downstreamContext.DownstreamRequest), Times.Once); } } } diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index e89d62b1..5d7fa8b6 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -12,7 +12,7 @@ namespace Ocelot.UnitTests.LoadBalancer { public class LoadBalancerFactoryTests { - private ReRoute _reRoute; + private DownstreamReRoute _reRoute; private LoadBalancerFactory _factory; private ILoadBalancer _result; private Mock _serviceProviderFactory; @@ -29,7 +29,7 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_return_no_load_balancer() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -44,9 +44,9 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_return_round_robin_load_balancer() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancer("RoundRobin") - .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamHttpMethod(new List {"Get"}) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -60,9 +60,9 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_return_round_least_connection_balancer() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancer("LeastConnection") - .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamHttpMethod(new List {"Get"}) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -76,9 +76,9 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_call_service_provider() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancer("RoundRobin") - .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamHttpMethod(new List {"Get"}) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -97,17 +97,17 @@ namespace Ocelot.UnitTests.LoadBalancer private void GivenTheServiceProviderFactoryReturns() { _serviceProviderFactory - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(_serviceProvider.Object); } private void ThenTheServiceProviderIsCalledCorrectly() { _serviceProviderFactory - .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); + .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); } - private void GivenAReRoute(ReRoute reRoute) + private void GivenAReRoute(DownstreamReRoute reRoute) { _reRoute = reRoute; } @@ -122,4 +122,4 @@ namespace Ocelot.UnitTests.LoadBalancer _result.ShouldBeOfType(); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs index 06c80eae..f5604ab1 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs @@ -14,13 +14,13 @@ namespace Ocelot.UnitTests.LoadBalancer { public class LoadBalancerHouseTests { - private ReRoute _reRoute; + private DownstreamReRoute _reRoute; private ILoadBalancer _loadBalancer; private readonly LoadBalancerHouse _loadBalancerHouse; private Response _addResult; private Response _getResult; private string _key; - private Mock _factory; + private readonly Mock _factory; private ServiceProviderConfiguration _serviceProviderConfig; public LoadBalancerHouseTests() @@ -32,7 +32,7 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_store_load_balancer_on_first_request() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithReRouteKey("test").Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) .Then(x => x.ThenItIsAdded()) @@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_not_store_load_balancer_on_second_request() { - var reRoute = new ReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) .When(x => x.WhenWeGetTheLoadBalancer(reRoute)) @@ -53,8 +53,8 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_store_load_balancers_by_key() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); - var reRouteTwo = new ReRouteBuilder().WithReRouteKey("testtwo").Build(); + var reRoute = new DownstreamReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build(); + var reRouteTwo = new DownstreamReRouteBuilder().WithLoadBalancer("FakeRoundRobinLoadBalancer").WithReRouteKey("testtwo").Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) .And(x => x.GivenThereIsALoadBalancer(reRouteTwo, new FakeRoundRobinLoadBalancer())) @@ -68,7 +68,7 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_return_error_if_exception() { - var reRoute = new ReRouteBuilder().Build(); + var reRoute = new DownstreamReRouteBuilder().Build(); this.When(x => x.WhenWeGetTheLoadBalancer(reRoute)) .Then(x => x.ThenAnErrorIsReturned()) @@ -78,9 +78,9 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_get_new_load_balancer_if_reroute_load_balancer_has_changed() { - var reRoute = new ReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build(); - var reRouteTwo = new ReRouteBuilder().WithLoadBalancer("LeastConnection").WithReRouteKey("test").Build(); + var reRouteTwo = new DownstreamReRouteBuilder().WithLoadBalancer("LeastConnection").WithReRouteKey("test").Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) .When(x => x.WhenWeGetTheLoadBalancer(reRoute)) @@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } - private void WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(ReRoute reRoute) + private void WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(DownstreamReRoute reRoute) { _reRoute = reRoute; _factory.Setup(x => x.Get(_reRoute, _serviceProviderConfig)).ReturnsAsync(new LeastConnection(null, null)); @@ -117,7 +117,7 @@ namespace Ocelot.UnitTests.LoadBalancer } - private void GivenThereIsALoadBalancer(ReRoute reRoute, ILoadBalancer loadBalancer) + private void GivenThereIsALoadBalancer(DownstreamReRoute reRoute, ILoadBalancer loadBalancer) { _reRoute = reRoute; _loadBalancer = loadBalancer; @@ -125,7 +125,7 @@ namespace Ocelot.UnitTests.LoadBalancer _getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig).Result; } - private void WhenWeGetTheLoadBalancer(ReRoute reRoute) + private void WhenWeGetTheLoadBalancer(DownstreamReRoute reRoute) { _getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig).Result; } diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 2442e362..310c1818 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -1,14 +1,18 @@ +using Ocelot.Middleware; + namespace Ocelot.UnitTests.LoadBalancer { using System.Collections.Generic; using System.Net.Http; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Provider; using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Errors; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.Middleware; @@ -19,7 +23,7 @@ namespace Ocelot.UnitTests.LoadBalancer using TestStack.BDDfy; using Xunit; - public class LoadBalancerMiddlewareTests : ServerHostedMiddlewareTest + public class LoadBalancerMiddlewareTests { private readonly Mock _loadBalancerHouse; private readonly Mock _loadBalancer; @@ -29,6 +33,11 @@ namespace Ocelot.UnitTests.LoadBalancer private ErrorResponse _getHostAndPortError; private HttpRequestMessage _downstreamRequest; private ServiceProviderConfiguration _config; + private Mock _loggerFactory; + private Mock _logger; + private LoadBalancingMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public LoadBalancerMiddlewareTests() { @@ -36,28 +45,29 @@ namespace Ocelot.UnitTests.LoadBalancer _loadBalancer = new Mock(); _loadBalancerHouse = new Mock(); _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, ""); - - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _downstreamContext.DownstreamRequest = _downstreamRequest; } [Fact] public void should_call_scoped_data_repository_correctly() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() + var downstreamRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .Build(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) - .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List())) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturns()) .When(x => x.WhenICallTheMiddleware()) @@ -68,17 +78,16 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_set_pipeline_error_if_cannot_get_load_balancer() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() + var downstreamRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .Build(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) - .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List())) .And(x => x.GivenTheLoadBalancerHouseReturnsAnError()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline()) @@ -88,17 +97,16 @@ namespace Ocelot.UnitTests.LoadBalancer [Fact] public void should_set_pipeline_error_if_cannot_get_least() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() + var downstreamRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .Build(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) - .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List())) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturnsAnError()) .When(x => x.WhenICallTheMiddleware()) @@ -106,24 +114,16 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } + private void WhenICallTheMiddleware() + { + _middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + private void GivenTheConfigurationIs(ServiceProviderConfiguration config) { _config = config; - ScopedRepository - .Setup(x => x.Get("ServiceProviderConfiguration")).Returns(new OkResponse(config)); - } - - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_loadBalancerHouse.Object); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseLoadBalancingMiddleware(); + _downstreamContext.ServiceProviderConfiguration = config; } private void GivenTheDownStreamUrlIs(string downstreamUrl) @@ -147,18 +147,16 @@ namespace Ocelot.UnitTests.LoadBalancer .ReturnsAsync(new OkResponse(_hostAndPort)); } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute, List placeholder) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = placeholder; + _downstreamContext.DownstreamReRoute = downstreamRoute; } private void GivenTheLoadBalancerHouseReturns() { _loadBalancerHouse - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(new OkResponse(_loadBalancer.Object)); } @@ -170,40 +168,32 @@ namespace Ocelot.UnitTests.LoadBalancer }); _loadBalancerHouse - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(_getLoadBalancerHouseError); } private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline() { - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); - - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareErrors", _getLoadBalancerHouseError.Errors), Times.Once); + _downstreamContext.IsError.ShouldBeTrue(); + _downstreamContext.Errors.ShouldBe(_getLoadBalancerHouseError.Errors); } private void ThenAnErrorSayingReleaseFailedIsSetOnThePipeline() { - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); - - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareErrors", It.IsAny>()), Times.Once); + _downstreamContext.IsError.ShouldBeTrue(); + _downstreamContext.Errors.ShouldBe(It.IsAny>()); } private void ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline() { - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); + _downstreamContext.IsError.ShouldBeTrue(); + _downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors); - ScopedRepository - .Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once); } private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) { - _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); + _downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); } } } diff --git a/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs b/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs new file mode 100644 index 00000000..8a0e5670 --- /dev/null +++ b/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs @@ -0,0 +1,69 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Middleware +{ + public class MultiplexerTests + { + private readonly Multiplexer _multiplexer; + private readonly DownstreamContext _context; + private ReRoute _reRoute; + private readonly OcelotRequestDelegate _pipeline; + private int _count; + private Mock _aggregator; + + public MultiplexerTests() + { + _aggregator = new Mock(); + _context = new DownstreamContext(new DefaultHttpContext()); + _pipeline = async context => { _count++; }; + _multiplexer = new Multiplexer(_aggregator.Object); + } + + [Fact] + public void should_multiplex() + { + var reRoute = new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).Build(); + + this.Given(x => GivenTheFollowing(reRoute)) + .When(x => WhenIMultiplex()) + .Then(x => ThePipelineIsCalled(2)) + .BDDfy(); + } + + [Fact] + public void should_not_multiplex() + { + var reRoute = new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).Build(); + + this.Given(x => GivenTheFollowing(reRoute)) + .When(x => WhenIMultiplex()) + .Then(x => ThePipelineIsCalled(1)) + .BDDfy(); + } + + private void GivenTheFollowing(ReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIMultiplex() + { + _multiplexer.Multiplex(_context, _reRoute, _pipeline).GetAwaiter().GetResult(); + + } + + private void ThePipelineIsCalled(int expected) + { + _count.ShouldBe(expected); + } + } +} diff --git a/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs new file mode 100644 index 00000000..dbb0ae63 --- /dev/null +++ b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs @@ -0,0 +1,87 @@ +namespace Ocelot.UnitTests.Middleware +{ + using System; + using System.Collections.Generic; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Hosting.Internal; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.DependencyInjection; + using Ocelot.Middleware; + using Ocelot.Middleware.Pipeline; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class OcelotPiplineBuilderTests + { + private readonly IServiceCollection _services; + private IServiceProvider _serviceProvider; + private readonly IConfiguration _configRoot; + private IOcelotBuilder _ocelotBuilder; + private DownstreamContext _downstreamContext; + private int _counter; + + public OcelotPiplineBuilderTests() + { + _configRoot = new ConfigurationRoot(new List()); + _services = new ServiceCollection(); + _services.AddSingleton(); + _services.AddSingleton(_configRoot); + _services.AddOcelot(); + } + + [Fact] + public void should_build_generic() + { + this.When(x => WhenIUseAGeneric()) + .Then(x => ThenTheGenericIsInThePipeline()) + .BDDfy(); + } + + [Fact] + public void should_build_func() + { + this.When(x => WhenIUseAFunc()) + .Then(x => ThenTheFuncIsInThePipeline()) + .BDDfy(); + } + + private void WhenIUseAGeneric() + { + var provider = _services.BuildServiceProvider(); + IOcelotPipelineBuilder builder = new OcelotPipelineBuilder(provider); + builder = builder.UseMiddleware(); + var del = builder.Build(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + del.Invoke(_downstreamContext); + } + + private void ThenTheGenericIsInThePipeline() + { + _downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500); + } + + private void WhenIUseAFunc() + { + _counter = 0; + var provider = _services.BuildServiceProvider(); + IOcelotPipelineBuilder builder = new OcelotPipelineBuilder(provider); + builder = builder.Use(async (ctx, next) => + { + _counter++; + await next.Invoke(); + }); + var del = builder.Build(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + del.Invoke(_downstreamContext); + } + + private void ThenTheFuncIsInThePipeline() + { + _counter.ShouldBe(1); + _downstreamContext.HttpContext.Response.StatusCode.ShouldBe(404); + } + } +} diff --git a/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs new file mode 100644 index 00000000..b5fabaee --- /dev/null +++ b/test/Ocelot.UnitTests/Middleware/SimpleJsonResponseAggregatorTests.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Errors; +using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; +using Ocelot.UnitTests.Responder; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Middleware +{ + public class SimpleJsonResponseAggregatorTests + { + private readonly SimpleJsonResponseAggregator _aggregator; + private List _downstreamContexts; + private DownstreamContext _upstreamContext; + private ReRoute _reRoute; + + public SimpleJsonResponseAggregatorTests() + { + _aggregator = new SimpleJsonResponseAggregator(); + } + + [Fact] + public void should_map_all_downstream_to_upstream_when_not_aggregate() + { + var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build(); + + var downstreamReRoutes = new List + { + billDownstreamReRoute, + }; + + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoutes(downstreamReRoutes) + .Build(); + + + var billDownstreamContext = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamResponse = + new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") }, + DownstreamReRoute = billDownstreamReRoute, + Errors = new List { new AnyError() }, + DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk")), + }; + + var downstreamContexts = new List { billDownstreamContext }; + + this.Given(x => GivenTheUpstreamContext(new DownstreamContext(new DefaultHttpContext()))) + .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheDownstreamContext(downstreamContexts)) + .When(x => WhenIAggregate()) + .Then(x => ThenTheContentIs("Bill says hi")) + .And(x => ThenTheUpstreamContextIsMappedForNonAggregate()) + .BDDfy(); + } + + [Fact] + public void should_aggregate_n_responses_and_set_response_content_on_upstream_context() + { + var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build(); + + var georgeDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("George").Build(); + + var downstreamReRoutes = new List + { + billDownstreamReRoute, + georgeDownstreamReRoute + }; + + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoutes(downstreamReRoutes) + .Build(); + + var billDownstreamContext = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamResponse = + new HttpResponseMessage(HttpStatusCode.OK) {Content = new StringContent("Bill says hi")}, + DownstreamReRoute = billDownstreamReRoute + }; + + var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamResponse = + new HttpResponseMessage(HttpStatusCode.OK) {Content = new StringContent("George says hi")}, + DownstreamReRoute = georgeDownstreamReRoute + }; + + var downstreamContexts = new List { billDownstreamContext, georgeDownstreamContext }; + + var expected = "{\"Bill\":Bill says hi,\"George\":George says hi}"; + + this.Given(x => GivenTheUpstreamContext(new DownstreamContext(new DefaultHttpContext()))) + .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheDownstreamContext(downstreamContexts)) + .When(x => WhenIAggregate()) + .Then(x => ThenTheContentIs(expected)) + .And(x => ThenTheContentTypeIs("application/json")) + .BDDfy(); + } + + [Fact] + public void should_return_error_if_any_downstreams_have_errored() + { + var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build(); + + var georgeDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("George").Build(); + + var downstreamReRoutes = new List + { + billDownstreamReRoute, + georgeDownstreamReRoute + }; + + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoutes(downstreamReRoutes) + .Build(); + + var billDownstreamContext = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamResponse = + new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") }, + DownstreamReRoute = billDownstreamReRoute + }; + + var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamResponse = + new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Error") }, + DownstreamReRoute = georgeDownstreamReRoute, + Errors = new List() { new AnyError() } + }; + + var downstreamContexts = new List { billDownstreamContext, georgeDownstreamContext }; + + var expected = "Error"; + + this.Given(x => GivenTheUpstreamContext(new DownstreamContext(new DefaultHttpContext()))) + .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheDownstreamContext(downstreamContexts)) + .When(x => WhenIAggregate()) + .Then(x => ThenTheContentIs(expected)) + .And(x => ThenTheErrorIsMapped()) + .BDDfy(); + } + + private void ThenTheErrorIsMapped() + { + _upstreamContext.Errors.ShouldBe(_downstreamContexts[1].Errors); + _upstreamContext.DownstreamResponse.ShouldBe(_downstreamContexts[1].DownstreamResponse); + } + + private void GivenTheReRoute(ReRoute reRoute) + { + _reRoute = reRoute; + } + + private void GivenTheUpstreamContext(DownstreamContext upstreamContext) + { + _upstreamContext = upstreamContext; + } + + private void GivenTheDownstreamContext(List downstreamContexts) + { + _downstreamContexts = downstreamContexts; + } + + private void WhenIAggregate() + { + _aggregator.Aggregate(_reRoute, _upstreamContext, _downstreamContexts).GetAwaiter().GetResult(); + } + + private void ThenTheContentIs(string expected) + { + var content = _upstreamContext.DownstreamResponse.Content.ReadAsStringAsync() + .GetAwaiter() + .GetResult(); + + content.ShouldBe(expected); + } + + private void ThenTheContentTypeIs(string expected) + { + _upstreamContext.DownstreamResponse.Content.Headers.ContentType.MediaType.ShouldBe(expected); + } + + private void ThenTheUpstreamContextIsMappedForNonAggregate() + { + _upstreamContext.DownstreamRequest.ShouldBe(_downstreamContexts[0].DownstreamRequest); + _upstreamContext.DownstreamResponse.ShouldBe(_downstreamContexts[0].DownstreamResponse); + _upstreamContext.Errors.ShouldBe(_downstreamContexts[0].Errors); + } + } +} diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index b4c851d2..cfc0395d 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -1,4 +1,6 @@ -namespace Ocelot.UnitTests.QueryStrings +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.QueryStrings { using System.Collections.Generic; using System.Net.Http; @@ -16,22 +18,30 @@ using Xunit; using System.Security.Claims; using Microsoft.AspNetCore.Builder; + using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; - public class QueryStringBuilderMiddlewareTests : ServerHostedMiddlewareTest + public class QueryStringBuilderMiddlewareTests { private readonly Mock _addQueries; - private readonly HttpRequestMessage _downstreamRequest; - private Response _downstreamRoute; + private Mock _loggerFactory; + private Mock _logger; + private QueryStringBuilderMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public QueryStringBuilderMiddlewareTests() { + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; _addQueries = new Mock(); - - _downstreamRequest = new HttpRequestMessage(); - ScopedRepository.Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); + _downstreamContext.DownstreamRequest = new HttpRequestMessage(); + _middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object); } [Fact] @@ -39,11 +49,14 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToQueries(new List - { - new ClaimToThing("UserId", "Subject", "", 0) - }) + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToQueries(new List + { + new ClaimToThing("UserId", "Subject", "", 0) + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -54,17 +67,9 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_addQueries.Object); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseQueryStringBuilderMiddleware(); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void GivenTheAddHeadersToRequestReturnsOk() @@ -83,15 +88,13 @@ .Verify(x => x.SetQueriesOnDownstreamRequest( It.IsAny>(), It.IsAny>(), - _downstreamRequest), Times.Once); + _downstreamContext.DownstreamRequest), Times.Once); } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } } } diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index a01a66cd..168781bc 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -1,11 +1,10 @@ -namespace Ocelot.UnitTests.RateLimit +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.RateLimit { using System.Collections.Generic; using System.Net.Http; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -13,27 +12,51 @@ using Ocelot.Logging; using Ocelot.RateLimit; using Ocelot.RateLimit.Middleware; - using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; + using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.Extensions.Caching.Memory; + using System.IO; - public class ClientRateLimitMiddlewareTests : ServerHostedMiddlewareTest + + public class ClientRateLimitMiddlewareTests { - private OkResponse _downstreamRoute; - private int responseStatusCode; + private int _responseStatusCode; + private IRateLimitCounterHandler _rateLimitCounterHandler; + private Mock _loggerFactory; + private Mock _logger; + private readonly ClientRateLimitMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + private readonly string _url; public ClientRateLimitMiddlewareTests() { - GivenTheTestServerIsConfigured(); + _url = "http://localhost:51879"; + var cacheEntryOptions = new MemoryCacheOptions(); + _rateLimitCounterHandler = new MemoryCacheRateLimitCounterHandler(new MemoryCache(cacheEntryOptions)); + var httpContext = new DefaultHttpContext(); + _downstreamContext = new DownstreamContext(httpContext); + _downstreamContext.HttpContext.Response.Body = new FakeStream(); + + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async (context) => { + }; + _middleware = new ClientRateLimitMiddleware(_next, _loggerFactory.Object, _rateLimitCounterHandler); } [Fact] public void should_call_middleware_and_ratelimiting() { var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions( - new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List(), false, "", "", new Ocelot.Configuration.RateLimitRule("1s", 100, 3), 429)) + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions( + new RateLimitOptions(true, "ClientId", new List(), false, "", "", new RateLimitRule("1s", 100, 3), 429)) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -49,8 +72,13 @@ public void should_call_middleware_withWhitelistClient() { var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions( - new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List() { "ocelotclient2" }, false, "", "", new RateLimitRule( "1s", 100,3),429)) + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithEnableRateLimiting(true) + .WithRateLimitOptions( + new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List() { "ocelotclient2" }, false, "", "", new RateLimitRule("1s", 100, 3), 429)) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -60,31 +88,10 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddMemoryCache(); - services.AddSingleton(); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseRateLimiting(); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("This is ratelimit test"); - }); - } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void WhenICallTheMiddlewareMultipleTime(int times) @@ -93,11 +100,12 @@ for (int i = 0; i < times; i++) { - var request = new HttpRequestMessage(new HttpMethod("GET"), Url); + var request = new HttpRequestMessage(new HttpMethod("GET"), _url); request.Headers.Add("ClientId", clientId); + _downstreamContext.DownstreamRequest = request; - var response = Client.SendAsync(request); - responseStatusCode = (int)response.Result.StatusCode; + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode; } } @@ -107,22 +115,58 @@ for (int i = 0; i < 10; i++) { - var request = new HttpRequestMessage(new HttpMethod("GET"), Url); + var request = new HttpRequestMessage(new HttpMethod("GET"), _url); request.Headers.Add("ClientId", clientId); + _downstreamContext.DownstreamRequest = request; + _downstreamContext.HttpContext.Request.Headers.TryAdd("ClientId", clientId); - var response = Client.SendAsync(request); - responseStatusCode = (int)response.Result.StatusCode; + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode; } } private void ThenresponseStatusCodeIs429() { - responseStatusCode.ShouldBe(429); + _responseStatusCode.ShouldBe(429); } private void ThenresponseStatusCodeIs200() { - responseStatusCode.ShouldBe(200); + _responseStatusCode.ShouldBe(200); } } + + class FakeStream : Stream + { + public override void Flush() + { + throw new System.NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new System.NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new System.NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new System.NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + //do nothing + } + + public override bool CanRead { get; } + public override bool CanSeek { get; } + public override bool CanWrite => true; + public override long Length { get; } + public override long Position { get; set; } + } } diff --git a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs index 0e62e6cd..5661ace6 100644 --- a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs @@ -1,4 +1,6 @@ -namespace Ocelot.UnitTests.Request +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Request { using System.Net.Http; using Microsoft.AspNetCore.Http; @@ -10,6 +12,8 @@ using TestStack.BDDfy; using Xunit; using Ocelot.Responses; + using Ocelot.DownstreamRouteFinder.Middleware; + using Shouldly; public class DownstreamRequestInitialiserMiddlewareTests { @@ -19,17 +23,16 @@ readonly Mock _httpRequest; - readonly Mock _next; + readonly Mock _next; readonly Mock _requestMapper; - readonly Mock _repo; - readonly Mock _loggerFactory; readonly Mock _logger; Response _mappedRequest; + private DownstreamContext _downstreamContext; public DownstreamRequestInitialiserMiddlewareTests() { @@ -37,8 +40,7 @@ _httpContext = new Mock(); _httpRequest = new Mock(); _requestMapper = new Mock(); - _repo = new Mock(); - _next = new Mock(); + _next = new Mock(); _logger = new Mock(); _loggerFactory = new Mock(); @@ -49,8 +51,9 @@ _middleware = new DownstreamRequestInitialiserMiddleware( _next.Object, _loggerFactory.Object, - _repo.Object, _requestMapper.Object); + + _downstreamContext = new DownstreamContext(_httpContext.Object); } [Fact] @@ -104,7 +107,7 @@ private void WhenTheMiddlewareIsInvoked() { - _middleware.Invoke(_httpContext.Object).GetAwaiter().GetResult(); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void ThenTheContexRequestIsMappedToADownstreamRequest() @@ -114,29 +117,28 @@ private void ThenTheDownstreamRequestIsStored() { - _repo.Verify(r => r.Add("DownstreamRequest", _mappedRequest.Data), Times.Once); + _downstreamContext.DownstreamRequest.ShouldNotBeNull(); } private void ThenTheDownstreamRequestIsNotStored() { - _repo.Verify(r => r.Add("DownstreamRequest", It.IsAny()), Times.Never); + _downstreamContext.DownstreamRequest.ShouldBeNull(); } private void ThenAPipelineErrorIsStored() { - _repo.Verify(r => r.Add("OcelotMiddlewareError", true), Times.Once); - _repo.Verify(r => r.Add("OcelotMiddlewareErrors", _mappedRequest.Errors), Times.Once); + _downstreamContext.IsError.ShouldBeTrue(); + _downstreamContext.Errors.ShouldBe(_mappedRequest.Errors); } private void ThenTheNextMiddlewareIsInvoked() { - _next.Verify(n => n(_httpContext.Object), Times.Once); + _next.Verify(n => n(_downstreamContext), Times.Once); } private void ThenTheNextMiddlewareIsNotInvoked() { - _next.Verify(n => n(It.IsAny()), Times.Never); + _next.Verify(n => n(It.IsAny()), Times.Never); } - } } diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs deleted file mode 100644 index b4d6acdc..00000000 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ /dev/null @@ -1,152 +0,0 @@ -namespace Ocelot.UnitTests.Request -{ - using System.Collections.Generic; - using System.Net.Http; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Infrastructure.RequestData; - using Ocelot.Logging; - using Ocelot.Request.Builder; - using Ocelot.Request.Middleware; - using Ocelot.Responses; - using TestStack.BDDfy; - using Xunit; - using Ocelot.Requester.QoS; - using Ocelot.Configuration; - using Microsoft.AspNetCore.Builder; - using Ocelot.Errors; - - public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest - { - private readonly Mock _requestBuilder; - private readonly Mock _scopedRepository; - private readonly Mock _qosProviderHouse; - private readonly HttpRequestMessage _downstreamRequest; - private OkResponse _request; - private OkResponse _downstreamUrl; - private OkResponse _downstreamRoute; - - public HttpRequestBuilderMiddlewareTests() - { - _qosProviderHouse = new Mock(); - _requestBuilder = new Mock(); - _scopedRepository = new Mock(); - - _downstreamRequest = new HttpRequestMessage(); - - _scopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); - } - - [Fact] - public void should_call_scoped_data_repository_correctly() - { - - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true,false)) - .Build()); - - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) - .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse(new NoQoSProvider()))) - .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false))) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_call_scoped_data_repository_QosProviderError() - { - - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) - .Build()); - - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) - .And(x => x.GivenTheQosProviderHouseReturns(new ErrorResponse(It.IsAny()))) - .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false))) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheScopedDataRepositoryQosProviderError()) - .BDDfy(); - } - - - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_qosProviderHouse.Object); - services.AddSingleton(_requestBuilder.Object); - services.AddSingleton(_scopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseHttpRequestBuilderMiddleware(); - } - - private void GivenTheDownStreamUrlIs(string downstreamUrl) - { - _downstreamUrl = new OkResponse(downstreamUrl); - _scopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamUrl); - } - - private void GivenTheQosProviderHouseReturns(Response qosProvider) - { - _qosProviderHouse - .Setup(x => x.Get(It.IsAny())) - .Returns(qosProvider); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) - { - _downstreamRoute = new OkResponse(downstreamRoute); - _scopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); - } - - 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())) - .ReturnsAsync(_request); - } - - private void ThenTheScopedDataRepositoryIsCalledCorrectly() - { - _scopedRepository - .Verify(x => x.Add("Request", _request.Data), Times.Once()); - } - - private void ThenTheScopedDataRepositoryQosProviderError() - { - _scopedRepository - .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once()); - } - } -} diff --git a/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs deleted file mode 100644 index dbfc8643..00000000 --- a/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -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 readonly bool _useCookieContainer; - private readonly bool _allowAutoRedirect; - private Response _response; - private string _reRouteKey; - private readonly bool _useTracing; - - public HttpRequestCreatorTests() - { - _requestCreator = new HttpRequestCreator(); - _isQos = true; - _qoSProvider = new NoQoSProvider(); - _useCookieContainer = false; - _allowAutoRedirect = false; - - _requestMessage = new HttpRequestMessage(); - } - - [Fact] - public void ShouldBuildRequest() - { - this.When(x => x.WhenIBuildARequest()) - .Then(x => x.ThenTheRequestContainsTheRequestMessage()) - .Then(x => x.ThenTheRequestContainsTheIsQos()) - .Then(x => x.ThenTheRequestContainsTheQosProvider()) - .Then(x => x.ThenTheRequestContainsUseCookieContainer()) - .Then(x => x.ThenTheRequestContainsUseTracing()) - .Then(x => x.ThenTheRequestContainsAllowAutoRedirect()) - .BDDfy(); - } - - private void WhenIBuildARequest() - { - _response = _requestCreator.Build(_requestMessage, - _isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _reRouteKey, _useTracing) - .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); - } - - private void ThenTheRequestContainsUseCookieContainer() - { - _response.Data.UseCookieContainer.ShouldBe(_useCookieContainer); - } - - private void ThenTheRequestContainsUseTracing() - { - _response.Data.IsTracing.ShouldBe(_useTracing); - } - - private void ThenTheRequestContainsAllowAutoRedirect() - { - _response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect); - } - } -} diff --git a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs index bd3130de..40276547 100644 --- a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs @@ -1,13 +1,15 @@ -namespace Ocelot.UnitTests.RequestId +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.RequestId { + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Primitives; + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.Infrastructure.RequestData; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; @@ -19,22 +21,32 @@ using TestStack.BDDfy; using Xunit; - public class ReRouteRequestIdMiddlewareTests : ServerHostedMiddlewareTest + public class ReRouteRequestIdMiddlewareTests { private readonly HttpRequestMessage _downstreamRequest; private Response _downstreamRoute; private string _value; private string _key; + private Mock _loggerFactory; + private Mock _logger; + private readonly ReRouteRequestIdMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + private readonly Mock _repo; public ReRouteRequestIdMiddlewareTests() { _downstreamRequest = new HttpRequestMessage(); - - ScopedRepository - .Setup(sr => sr.Get("DownstreamRequest")) - .Returns(new OkResponse(_downstreamRequest)); - - GivenTheTestServerIsConfigured(); + _repo = new Mock(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + context.HttpContext.Response.Headers.Add("LSRequestId", context.HttpContext.TraceIdentifier); + }; + _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object); + _downstreamContext.DownstreamRequest = _downstreamRequest; } [Fact] @@ -42,8 +54,11 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId") + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .Build()); @@ -62,10 +77,13 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()) + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => GivenThereIsNoGlobalRequestId()) @@ -79,10 +97,13 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()) + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()); var requestId = Guid.NewGuid().ToString(); @@ -100,10 +121,13 @@ { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()) + .WithUpstreamHttpMethod(new List {"Get"}) + .Build()); var requestId = Guid.NewGuid().ToString(); @@ -116,67 +140,57 @@ .BDDfy(); } + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + private void GivenThereIsNoGlobalRequestId() { - ScopedRepository.Setup(x => x.Get("RequestId")).Returns(new OkResponse(null)); + _repo.Setup(x => x.Get("RequestId")).Returns(new OkResponse(null)); } private void GivenTheRequestIdWasSetGlobally() { - ScopedRepository.Setup(x => x.Get("RequestId")).Returns(new OkResponse("alreadyset")); + _repo.Setup(x => x.Get("RequestId")).Returns(new OkResponse("alreadyset")); } private void ThenTheRequestIdIsSaved() { - ScopedRepository.Verify(x => x.Add("RequestId", _value), Times.Once); + _repo.Verify(x => x.Add("RequestId", _value), Times.Once); } private void ThenTheRequestIdIsUpdated() { - ScopedRepository.Verify(x => x.Update("RequestId", _value), Times.Once); + _repo.Verify(x => x.Update("RequestId", _value), Times.Once); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) - { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseRequestIdMiddleware(); - - app.Run(x => - { - x.Response.Headers.Add("LSRequestId", x.TraceIdentifier); - return Task.CompletedTask; - }); - } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_downstreamRoute); + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; } private void GivenTheRequestIdIsAddedToTheRequest(string key, string value) { _key = key; _value = value; - Client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value); + _downstreamContext.HttpContext.Request.Headers.TryAdd(_key, _value); } private void ThenTheTraceIdIsAnything() { - ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty(); + StringValues value; + _downstreamContext.HttpContext.Response.Headers.TryGetValue("LSRequestId", out value); + value.First().ShouldNotBeNullOrEmpty(); } private void ThenTheTraceIdIs(string expected) { - ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldBe(expected); + StringValues value; + _downstreamContext.HttpContext.Response.Headers.TryGetValue("LSRequestId", out value); + value.First().ShouldBe(expected); } } } diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs index f78917d4..8df95ec1 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs @@ -1,6 +1,9 @@ using System; using System.Net.Http; using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Errors; using Ocelot.Requester; using Ocelot.Responses; using Shouldly; @@ -14,7 +17,7 @@ namespace Ocelot.UnitTests.Requester private readonly DelegatingHandlerHandlerHouse _house; private Mock _factory; private readonly Mock _provider; - private Ocelot.Request.Request _request; + private DownstreamReRoute _request; private Response _result; public DelegatingHandlerHandlerHouseTests() @@ -27,9 +30,10 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_create_and_store_provider() { - var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - this.Given(x => GivenTheRequest(request)) + this.Given(x => GivenTheRequest(reRoute)) .And(x => GivenTheProviderReturns()) .When(x => WhenIGet()) .Then(x => ThenTheFactoryIsCalled(1)) @@ -40,9 +44,10 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_get_provider() { - var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - this.Given(x => GivenTheRequest(request)) + this.Given(x => GivenTheRequest(reRoute)) .And(x => GivenTheProviderReturns()) .And(x => WhenIGet()) .And(x => GivenTheFactoryIsCleared()) @@ -55,15 +60,34 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_return_error() { - var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - this.Given(x => GivenTheRequest(request)) + this.Given(x => GivenTheRequest(reRoute)) .And(x => GivenTheProviderThrows()) .When(x => WhenIGet()) .And(x => ThenAnErrorIsReturned()) .BDDfy(); } + [Fact] + public void should_return_error_if_factory_errors() + { + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); + + this.Given(x => GivenTheRequest(reRoute)) + .And(x => GivenTheProviderReturnsError()) + .When(x => WhenIGet()) + .Then(x => ThenAnUnknownErrorIsReturned()) + .BDDfy(); + } + + private void ThenAnUnknownErrorIsReturned() + { + _result.IsError.ShouldBeTrue(); + } + private void ThenAnErrorIsReturned() { _result.IsError.ShouldBeTrue(); @@ -72,7 +96,7 @@ namespace Ocelot.UnitTests.Requester private void GivenTheProviderThrows() { - _factory.Setup(x => x.Get(It.IsAny())).Throws(); + _factory.Setup(x => x.Get(It.IsAny())).Throws(); } private void GivenTheFactoryIsCleared() @@ -90,14 +114,19 @@ namespace Ocelot.UnitTests.Requester _result = _house.Get(_request); } - private void GivenTheRequest(Ocelot.Request.Request request) + private void GivenTheRequest(DownstreamReRoute request) { _request = request; } private void GivenTheProviderReturns() { - _factory.Setup(x => x.Get(It.IsAny())).Returns(_provider.Object); + _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(_provider.Object)); + } + + private void GivenTheProviderReturnsError() + { + _factory.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(It.IsAny())); } private void ThenTheFactoryIsCalled(int times) diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs index e64df1b9..8f861260 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs @@ -2,8 +2,13 @@ using System; using System.Collections.Generic; using System.Net.Http; using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Errors; using Ocelot.Logging; using Ocelot.Requester; +using Ocelot.Requester.QoS; +using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -14,17 +19,27 @@ namespace Ocelot.UnitTests.Requester { private readonly DelegatingHandlerHandlerProviderFactory _factory; private Mock _loggerFactory; - private Ocelot.Request.Request _request; - private IDelegatingHandlerHandlerProvider _provider; + private DownstreamReRoute _request; + private Response _provider; private readonly Mock _allRoutesProvider; + private readonly Mock _qosProviderHouse; public DelegatingHandlerHandlerProviderFactoryTests() { + _qosProviderHouse = new Mock(); _allRoutesProvider = new Mock(); _loggerFactory = new Mock(); - _factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, null); + _factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, null, _qosProviderHouse.Object); } + private void GivenTheQosProviderHouseReturns(Response qosProvider) + { + _qosProviderHouse + .Setup(x => x.Get(It.IsAny())) + .Returns(qosProvider); + } + + [Fact] public void should_all_from_all_routes_provider_and_qos() { @@ -34,9 +49,11 @@ namespace Ocelot.UnitTests.Requester () => new FakeDelegatingHandler(1) }; - var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); - this.Given(x => GivenTheFollowingRequest(request)) + this.Given(x => GivenTheFollowingRequest(reRoute)) + .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) .And(x => GivenTheAllRoutesProviderReturns(handlers)) .When(x => WhenIGet()) .Then(x => ThenThereIsDelegatesInProvider(3)) @@ -48,9 +65,10 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_return_provider_with_no_delegates() { - var request = new Ocelot.Request.Request(new HttpRequestMessage(), false, null, true, true, "", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); - this.Given(x => GivenTheFollowingRequest(request)) + this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheAllRoutesProviderReturns()) .When(x => WhenIGet()) .Then(x => ThenNoDelegatesAreInTheProvider()) @@ -60,9 +78,11 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_return_provider_with_qos_delegate() { - var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); - this.Given(x => GivenTheFollowingRequest(request)) + this.Given(x => GivenTheFollowingRequest(reRoute)) + .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) .And(x => GivenTheAllRoutesProviderReturns()) .When(x => WhenIGet()) .Then(x => ThenThereIsDelegatesInProvider(1)) @@ -70,9 +90,28 @@ namespace Ocelot.UnitTests.Requester .BDDfy(); } + [Fact] + public void should_return_error() + { + var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); + + this.Given(x => GivenTheFollowingRequest(reRoute)) + .And(x => GivenTheQosProviderHouseReturns(new ErrorResponse(It.IsAny()))) + .And(x => GivenTheAllRoutesProviderReturns()) + .When(x => WhenIGet()) + .Then(x => ThenAnErrorIsReturned()) + .BDDfy(); + } + + private void ThenAnErrorIsReturned() + { + _provider.IsError.ShouldBeTrue(); + } + private void ThenTheDelegatesAreAddedCorrectly() { - var delegates = _provider.Get(); + var delegates = _provider.Data.Get(); var del = delegates[0].Invoke(); var handler = (FakeDelegatingHandler) del; handler.Order.ShouldBe(0); @@ -94,7 +133,7 @@ namespace Ocelot.UnitTests.Requester private void ThenItIsPolly(int i) { - var delegates = _provider.Get(); + var delegates = _provider.Data.Get(); var del = delegates[i].Invoke(); del.ShouldBeOfType(); } @@ -102,10 +141,10 @@ namespace Ocelot.UnitTests.Requester private void ThenThereIsDelegatesInProvider(int count) { _provider.ShouldNotBeNull(); - _provider.Get().Count.ShouldBe(count); + _provider.Data.Get().Count.ShouldBe(count); } - private void GivenTheFollowingRequest(Ocelot.Request.Request request) + private void GivenTheFollowingRequest(DownstreamReRoute request) { _request = request; } @@ -118,7 +157,7 @@ namespace Ocelot.UnitTests.Requester private void ThenNoDelegatesAreInTheProvider() { _provider.ShouldNotBeNull(); - _provider.Get().Count.ShouldBe(0); + _provider.Data.Get().Count.ShouldBe(0); } } } diff --git a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs index 6081590a..b5d7e1e6 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Net.Http; using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; using Ocelot.Requester; using Ocelot.Responses; using Shouldly; @@ -18,7 +20,7 @@ namespace Ocelot.UnitTests.Requester private IHttpClientBuilder _builderResult; private IHttpClient _httpClient; private HttpResponseMessage _response; - private Ocelot.Request.Request _request; + private DownstreamReRoute _request; public HttpClientBuilderTests() { @@ -62,13 +64,16 @@ namespace Ocelot.UnitTests.Requester private void GivenARequest() { - _request = new Ocelot.Request.Request(null, false, null, false, false, "", false); + var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)).WithReRouteKey("").Build(); + + _request = reRoute; } private void GivenTheHouseReturns() { _house - .Setup(x => x.Get(It.IsAny())) + .Setup(x => x.Get(It.IsAny())) .Returns(new OkResponse(_provider.Object)); } diff --git a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs index 29271671..6b01a8c1 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs @@ -1,13 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; -using Moq; +using Moq; using Ocelot.Logging; using Ocelot.Requester; -using Ocelot.Requester.QoS; using Ocelot.Responses; using System; using System.Collections.Generic; using System.Net.Http; -using System.Text; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Middleware; using TestStack.BDDfy; using Xunit; using Shouldly; @@ -21,7 +22,7 @@ namespace Ocelot.UnitTests.Requester private Mock _provider; private Response _response; private readonly HttpClientHttpRequester _httpClientRequester; - private Ocelot.Request.Request _request; + private DownstreamContext _request; private Mock _loggerFactory; private Mock _logger; @@ -30,7 +31,7 @@ namespace Ocelot.UnitTests.Requester _provider = new Mock(); _provider.Setup(x => x.Get()).Returns(new List>()); _house = new Mock(); - _house.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(_provider.Object)); + _house.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(_provider.Object)); _logger = new Mock(); _loggerFactory = new Mock(); _loggerFactory @@ -43,7 +44,16 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_call_request_correctly() { - this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, "", false))) + var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)).WithReRouteKey("").Build(); + + var context = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamReRoute = reRoute, + DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, + }; + + this.Given(x=>x.GivenTheRequestIs(context)) .When(x=>x.WhenIGetResponse()) .Then(x => x.ThenTheResponseIsCalledCorrectly()) .BDDfy(); @@ -52,13 +62,22 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_call_request_unable_to_complete_request() { - this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, "", false))) + var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)).WithReRouteKey("").Build(); + + var context = new DownstreamContext(new DefaultHttpContext()) + { + DownstreamReRoute = reRoute, + DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, + }; + + this.Given(x => x.GivenTheRequestIs(context)) .When(x => x.WhenIGetResponse()) .Then(x => x.ThenTheResponseIsCalledError()) .BDDfy(); } - private void GivenTheRequestIs(Ocelot.Request.Request request) + private void GivenTheRequestIs(DownstreamContext request) { _request = request; } diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index 5658984a..154beee8 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -1,81 +1,73 @@ +using Ocelot.Configuration.Builder; +using Ocelot.Middleware; + namespace Ocelot.UnitTests.Requester { + using Microsoft.AspNetCore.Http; using System.Net.Http; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Logging; using Ocelot.Requester; using Ocelot.Requester.Middleware; - using Ocelot.Requester.QoS; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; + using Shouldly; - public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest + public class HttpRequesterMiddlewareTests { private readonly Mock _requester; private OkResponse _response; - private OkResponse _request; + private Mock _loggerFactory; + private Mock _logger; + private readonly HttpRequesterMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; public HttpRequesterMiddlewareTests() { _requester = new Mock(); - - GivenTheTestServerIsConfigured(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object); } [Fact] - public void should_call_scoped_data_repository_correctly() + public void should_call_services_correctly() { - this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false, "", false))) + this.Given(x => x.GivenTheRequestIs()) .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage())) - .And(x => x.GivenTheScopedRepoReturns()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedRepoIsCalledCorrectly()) .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_requester.Object); - services.AddSingleton(ScopedRepository.Object); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) + private void GivenTheRequestIs() { - app.UseHttpRequesterMiddleware(); - } - - private void GivenTheRequestIs(Ocelot.Request.Request request) - { - _request = new OkResponse(request); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_request); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _downstreamContext.DownstreamReRoute = new DownstreamReRouteBuilder().Build(); } private void GivenTheRequesterReturns(HttpResponseMessage response) { _response = new OkResponse(response); _requester - .Setup(x => x.GetResponse(It.IsAny())) + .Setup(x => x.GetResponse(It.IsAny())) .ReturnsAsync(_response); } - private void GivenTheScopedRepoReturns() - { - ScopedRepository - .Setup(x => x.Add(It.IsAny(), _response.Data)) - .Returns(new OkResponse()); - } - private void ThenTheScopedRepoIsCalledCorrectly() { - ScopedRepository - .Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once()); + _downstreamContext.DownstreamResponse.ShouldBe(_response.Data); } } } diff --git a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs index 2c595f6c..bc10e89e 100644 --- a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs @@ -13,7 +13,7 @@ namespace Ocelot.UnitTests.Requester public class QoSProviderFactoryTests { private readonly IQoSProviderFactory _factory; - private ReRoute _reRoute; + private DownstreamReRoute _reRoute; private IQoSProvider _result; private Mock _loggerFactory; private Mock _logger; @@ -31,7 +31,7 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_return_no_qos_provider() { - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "get" }) .WithIsQos(false) .Build(); @@ -51,7 +51,7 @@ namespace Ocelot.UnitTests.Requester .WithExceptionsAllowedBeforeBreaking(100) .Build(); - var reRoute = new ReRouteBuilder() + var reRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "get" }) .WithIsQos(true) .WithQosOptions(qosOptions) @@ -63,7 +63,7 @@ namespace Ocelot.UnitTests.Requester .BDDfy(); } - private void GivenAReRoute(ReRoute reRoute) + private void GivenAReRoute(DownstreamReRoute reRoute) { _reRoute = reRoute; } diff --git a/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs index b3c91c5b..487a3509 100644 --- a/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs +++ b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs @@ -15,7 +15,7 @@ namespace Ocelot.UnitTests.Requester private readonly QosProviderHouse _qosProviderHouse; private Response _addResult; private Response _getResult; - private ReRoute _reRoute; + private DownstreamReRoute _reRoute; private readonly Mock _factory; public QosProviderHouseTests() @@ -27,7 +27,7 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_store_qos_provider_on_first_request() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithReRouteKey("test").Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .Then(x => x.ThenItIsAdded()) @@ -37,7 +37,7 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_not_store_qos_provider_on_first_request() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithReRouteKey("test").Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .When(x => x.WhenWeGetTheQoSProvider(reRoute)) @@ -48,8 +48,8 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_store_qos_providers_by_key() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); - var reRouteTwo = new ReRouteBuilder().WithReRouteKey("testTwo").Build(); + var reRoute = new DownstreamReRouteBuilder().WithReRouteKey("test").Build(); + var reRouteTwo = new DownstreamReRouteBuilder().WithReRouteKey("testTwo").Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider())) @@ -63,7 +63,7 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_return_error_if_no_qos_provider_with_key() { - var reRoute = new ReRouteBuilder().Build(); + var reRoute = new DownstreamReRouteBuilder().Build(); this.When(x => x.WhenWeGetTheQoSProvider(reRoute)) .Then(x => x.ThenAnErrorIsReturned()) @@ -73,9 +73,9 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed() { - var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build(); + var reRoute = new DownstreamReRouteBuilder().WithReRouteKey("test").Build(); - var reRouteTwo = new ReRouteBuilder().WithReRouteKey("test").WithIsQos(true).Build(); + var reRouteTwo = new DownstreamReRouteBuilder().WithReRouteKey("test").WithIsQos(true).Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .When(x => x.WhenWeGetTheQoSProvider(reRoute)) @@ -85,7 +85,7 @@ namespace Ocelot.UnitTests.Requester .BDDfy(); } - private void WhenIGetTheReRouteWithTheSameKeyButDifferentQosProvider(ReRoute reRoute) + private void WhenIGetTheReRouteWithTheSameKeyButDifferentQosProvider(DownstreamReRoute reRoute) { _reRoute = reRoute; _factory.Setup(x => x.Get(_reRoute)).Returns(new FakePollyQoSProvider()); @@ -112,7 +112,7 @@ namespace Ocelot.UnitTests.Requester } - private void GivenThereIsAQoSProvider(ReRoute reRoute, IQoSProvider qoSProvider) + private void GivenThereIsAQoSProvider(DownstreamReRoute reRoute, IQoSProvider qoSProvider) { _reRoute = reRoute; _qoSProvider = qoSProvider; @@ -120,7 +120,7 @@ namespace Ocelot.UnitTests.Requester _getResult = _qosProviderHouse.Get(reRoute); } - private void WhenWeGetTheQoSProvider(ReRoute reRoute) + private void WhenWeGetTheQoSProvider(DownstreamReRoute reRoute) { _getResult = _qosProviderHouse.Get(reRoute); } diff --git a/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs b/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs index ce98a972..fb0720b4 100644 --- a/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs @@ -1,32 +1,45 @@ -namespace Ocelot.UnitTests.Responder +using System.Collections.Generic; +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Responder { - using System.Collections.Generic; + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; using System.Net.Http; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.Errors; using Ocelot.Logging; - using Ocelot.Requester; using Ocelot.Responder; using Ocelot.Responder.Middleware; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; - public class ResponderMiddlewareTests : ServerHostedMiddlewareTest + public class ResponderMiddlewareTests { private readonly Mock _responder; private readonly Mock _codeMapper; private OkResponse _response; + private Mock _loggerFactory; + private Mock _logger; + private readonly ResponderMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + public ResponderMiddlewareTests() { _responder = new Mock(); _codeMapper = new Mock(); - - GivenTheTestServerIsConfigured(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = async context => { + //do nothing + }; + _middleware = new ResponderMiddleware(_next, _responder.Object, _loggerFactory.Object, _codeMapper.Object); } [Fact] @@ -39,7 +52,6 @@ .BDDfy(); } - [Fact] public void should_return_any_errors() { @@ -50,33 +62,19 @@ .BDDfy(); } - protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) + private void WhenICallTheMiddleware() { - services.AddSingleton(); - services.AddLogging(); - services.AddSingleton(_codeMapper.Object); - services.AddSingleton(_responder.Object); - services.AddSingleton(ScopedRepository.Object); - } - - protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) - { - app.UseResponderMiddleware(); + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } private void GivenTheHttpResponseMessageIs(HttpResponseMessage response) { - _response = new OkResponse(response); - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(_response); + _downstreamContext.DownstreamResponse = response; } private void GivenThereAreNoPipelineErrors() { - ScopedRepository - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse(false)); + _downstreamContext.Errors = new List(); } private void ThenThereAreNoErrors() @@ -86,11 +84,7 @@ private void GivenThereArePipelineErrors(Error error) { - ScopedRepository - .Setup(x => x.Get("OcelotMiddlewareError")) - .Returns(new OkResponse(true)); - ScopedRepository.Setup(x => x.Get>("OcelotMiddlewareErrors")) - .Returns(new OkResponse>(new List() { error })); + _downstreamContext.Errors = new List(){error}; } } } diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs index 625ffaa3..f8c60bb7 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs @@ -16,7 +16,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery private ServiceProviderConfiguration _serviceConfig; private IServiceDiscoveryProvider _result; private readonly ServiceDiscoveryProviderFactory _factory; - private ReRoute _reRoute; + private DownstreamReRoute _reRoute; private Mock _loggerFactory; public ServiceProviderFactoryTests() @@ -31,7 +31,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery var serviceConfig = new ServiceProviderConfigurationBuilder() .Build(); - var reRoute = new ReRouteBuilder().Build(); + var reRoute = new DownstreamReRouteBuilder().Build(); this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) .When(x => x.WhenIGetTheServiceProvider()) @@ -51,7 +51,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery new DownstreamHostAndPort("abc.com", 80) }; - var reRoute = new ReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build(); + var reRoute = new DownstreamReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build(); this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) .When(x => x.WhenIGetTheServiceProvider()) @@ -60,6 +60,23 @@ namespace Ocelot.UnitTests.ServiceDiscovery .BDDfy(); } + [Fact] + public void should_return_consul_service_provider() + { + var reRoute = new DownstreamReRouteBuilder() + .WithServiceName("product") + .WithUseServiceDiscovery(true) + .Build(); + + var serviceConfig = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheServiceProviderIs()) + .BDDfy(); + } + private void ThenTheFollowingServicesAreReturned(List downstreamAddresses) { var result = (ConfigurationServiceProvider)_result; @@ -75,24 +92,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery } } - [Fact] - public void should_return_consul_service_provider() - { - var reRoute = new ReRouteBuilder() - .WithServiceName("product") - .WithUseServiceDiscovery(true) - .Build(); - - var serviceConfig = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheServiceProviderIs()) - .BDDfy(); - } - - private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, ReRoute reRoute) + private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute) { _serviceConfig = serviceConfig; _reRoute = reRoute;