mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 21:48:15 +08:00
Remove Ocelot specific Middleware to make Ocelot more compatible with kestrel middleware and get ready for YARP
This commit is contained in:
@ -1,11 +1,11 @@
|
||||
namespace Ocelot.Provider.Consul
|
||||
{
|
||||
using Errors;
|
||||
using Ocelot.Errors;
|
||||
|
||||
public class UnableToSetConfigInConsulError : Error
|
||||
{
|
||||
public UnableToSetConfigInConsulError(string s)
|
||||
: base(s, OcelotErrorCode.UnknownError)
|
||||
: base(s, OcelotErrorCode.UnknownError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace Ocelot.Provider.Polly
|
||||
{
|
||||
using Errors;
|
||||
using Ocelot.Errors;
|
||||
using System;
|
||||
|
||||
public class RequestTimedOutError : Error
|
||||
{
|
||||
public RequestTimedOutError(Exception exception)
|
||||
: base($"Timeout making http request, exception: {exception}", OcelotErrorCode.RequestTimedOutError)
|
||||
: base($"Timeout making http request, exception: {exception}", OcelotErrorCode.RequestTimedOutError, 503)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
public class UnableToSaveAcceptCommand : Error
|
||||
{
|
||||
public UnableToSaveAcceptCommand(string message)
|
||||
: base(message, OcelotErrorCode.UnknownError)
|
||||
: base(message, OcelotErrorCode.UnknownError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,56 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class AuthenticationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
||||
public AuthenticationMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
if (context.HttpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (httpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(downstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||
Logger.LogInformation($"{httpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||
|
||||
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
||||
var result = await httpContext.AuthenticateAsync(downstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
||||
|
||||
context.HttpContext.User = result.Principal;
|
||||
httpContext.User = result.Principal;
|
||||
|
||||
if (context.HttpContext.User.Identity.IsAuthenticated)
|
||||
if (httpContext.User.Identity.IsAuthenticated)
|
||||
{
|
||||
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
||||
await _next.Invoke(context);
|
||||
Logger.LogInformation($"Client has been authenticated for {httpContext.Request.Path}");
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = new UnauthenticatedError(
|
||||
$"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated");
|
||||
$"Request for authenticated route {httpContext.Request.Path} by {httpContext.User.Identity.Name} was unauthenticated");
|
||||
|
||||
Logger.LogWarning($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error}");
|
||||
Logger.LogWarning($"Client has NOT been authenticated for {httpContext.Request.Path} and pipeline error set. {error}");
|
||||
|
||||
SetPipelineError(context, error);
|
||||
httpContext.Items.SetError(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}");
|
||||
Logger.LogInformation($"No authentication needed for {httpContext.Request.Path}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
{
|
||||
public static class AuthenticationMiddlewareMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseAuthenticationMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<AuthenticationMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class AuthenticationMiddlewareMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<AuthenticationMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
using System.Net;
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
public class ClaimValueNotAuthorisedError : Error
|
||||
{
|
||||
public ClaimValueNotAuthorisedError(string message)
|
||||
: base(message, OcelotErrorCode.ClaimValueNotAuthorisedError)
|
||||
: base(message, OcelotErrorCode.ClaimValueNotAuthorisedError, 403)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +1,90 @@
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Infrastructure.Claims.Parser;
|
||||
|
||||
public class ClaimsAuthoriser : IClaimsAuthoriser
|
||||
{
|
||||
private readonly IClaimsParser _claimsParser;
|
||||
|
||||
public ClaimsAuthoriser(IClaimsParser claimsParser)
|
||||
{
|
||||
_claimsParser = claimsParser;
|
||||
}
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public class ClaimsAuthoriser : IClaimsAuthoriser
|
||||
{
|
||||
private readonly IClaimsParser _claimsParser;
|
||||
|
||||
public ClaimsAuthoriser(IClaimsParser claimsParser)
|
||||
{
|
||||
_claimsParser = claimsParser;
|
||||
}
|
||||
|
||||
public Response<bool> Authorise(
|
||||
ClaimsPrincipal claimsPrincipal,
|
||||
Dictionary<string, string> routeClaimsRequirement,
|
||||
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
||||
ClaimsPrincipal claimsPrincipal,
|
||||
Dictionary<string, string> routeClaimsRequirement,
|
||||
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
||||
)
|
||||
{
|
||||
foreach (var required in routeClaimsRequirement)
|
||||
{
|
||||
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key);
|
||||
|
||||
if (values.IsError)
|
||||
{
|
||||
return new ErrorResponse<bool>(values.Errors);
|
||||
}
|
||||
|
||||
if (values.Data != null)
|
||||
{
|
||||
foreach (var required in routeClaimsRequirement)
|
||||
{
|
||||
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key);
|
||||
|
||||
if (values.IsError)
|
||||
{
|
||||
// dynamic claim
|
||||
var match = Regex.Match(required.Value, @"^{(?<variable>.+)}$");
|
||||
if (match.Success)
|
||||
{
|
||||
var variableName = match.Captures[0].Value;
|
||||
|
||||
var matchingPlaceholders = urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Take(2).ToArray();
|
||||
if (matchingPlaceholders.Length == 1)
|
||||
{
|
||||
// match
|
||||
var actualValue = matchingPlaceholders[0].Value;
|
||||
var authorised = values.Data.Contains(actualValue);
|
||||
if (!authorised)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// config error
|
||||
if (matchingPlaceholders.Length == 0)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p => p.Name))}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Select(p => p.Value))}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return new ErrorResponse<bool>(values.Errors);
|
||||
}
|
||||
|
||||
if (values.Data != null)
|
||||
{
|
||||
// dynamic claim
|
||||
var match = Regex.Match(required.Value, @"^{(?<variable>.+)}$");
|
||||
if (match.Success)
|
||||
{
|
||||
// static claim
|
||||
var authorised = values.Data.Contains(required.Value);
|
||||
if (!authorised)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ErrorResponse<bool>(new UserDoesNotHaveClaimError($"user does not have claim {required.Key}"));
|
||||
}
|
||||
}
|
||||
|
||||
return new OkResponse<bool>(true);
|
||||
}
|
||||
}
|
||||
var variableName = match.Captures[0].Value;
|
||||
|
||||
var matchingPlaceholders = urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Take(2).ToArray();
|
||||
if (matchingPlaceholders.Length == 1)
|
||||
{
|
||||
// match
|
||||
var actualValue = matchingPlaceholders[0].Value;
|
||||
var authorised = values.Data.Contains(actualValue);
|
||||
if (!authorised)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// config error
|
||||
if (matchingPlaceholders.Length == 0)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p => p.Name))}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Select(p => p.Value))}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// static claim
|
||||
var authorised = values.Data.Contains(required.Value);
|
||||
if (!authorised)
|
||||
{
|
||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ErrorResponse<bool>(new UserDoesNotHaveClaimError($"user does not have claim {required.Key}"));
|
||||
}
|
||||
}
|
||||
|
||||
return new OkResponse<bool>(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,108 +1,112 @@
|
||||
namespace Ocelot.Authorisation.Middleware
|
||||
{
|
||||
using Configuration;
|
||||
using Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Responses;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class AuthorisationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IClaimsAuthoriser _claimsAuthoriser;
|
||||
private readonly IScopesAuthoriser _scopesAuthoriser;
|
||||
|
||||
public AuthorisationMiddleware(OcelotRequestDelegate next,
|
||||
IClaimsAuthoriser claimsAuthoriser,
|
||||
IScopesAuthoriser scopesAuthoriser,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
: base(loggerFactory.CreateLogger<AuthorisationMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_claimsAuthoriser = claimsAuthoriser;
|
||||
_scopesAuthoriser = scopesAuthoriser;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (!IsOptionsHttpMethod(context) && IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation("route is authenticated scopes must be checked");
|
||||
|
||||
var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes);
|
||||
|
||||
if (authorised.IsError)
|
||||
{
|
||||
Logger.LogWarning("error authorising user scopes");
|
||||
|
||||
SetPipelineError(context, authorised.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAuthorised(authorised))
|
||||
{
|
||||
Logger.LogInformation("user scopes is authorised calling next authorisation checks");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("user scopes is not authorised setting pipeline error");
|
||||
|
||||
SetPipelineError(context, new UnauthorisedError(
|
||||
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsOptionsHttpMethod(context) && IsAuthorisedRoute(context.DownstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation("route is authorised");
|
||||
namespace Ocelot.Authorisation.Middleware
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (authorised.IsError)
|
||||
{
|
||||
Logger.LogWarning($"Error whilst authorising {context.HttpContext.User.Identity.Name}. Setting pipeline error");
|
||||
|
||||
SetPipelineError(context, authorised.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAuthorised(authorised))
|
||||
{
|
||||
Logger.LogInformation($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}.");
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
|
||||
|
||||
SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAuthorised(Response<bool> authorised)
|
||||
{
|
||||
return authorised.Data;
|
||||
}
|
||||
|
||||
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthenticated;
|
||||
}
|
||||
|
||||
private static bool IsAuthorisedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthorised;
|
||||
public class AuthorisationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IClaimsAuthoriser _claimsAuthoriser;
|
||||
private readonly IScopesAuthoriser _scopesAuthoriser;
|
||||
|
||||
public AuthorisationMiddleware(RequestDelegate next,
|
||||
IClaimsAuthoriser claimsAuthoriser,
|
||||
IScopesAuthoriser scopesAuthoriser,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
: base(loggerFactory.CreateLogger<AuthorisationMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_claimsAuthoriser = claimsAuthoriser;
|
||||
_scopesAuthoriser = scopesAuthoriser;
|
||||
}
|
||||
|
||||
private static bool IsOptionsHttpMethod(DownstreamContext context)
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
return context.HttpContext.Request.Method.ToUpper() == "OPTIONS";
|
||||
}
|
||||
}
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (!IsOptionsHttpMethod(httpContext) && IsAuthenticatedRoute(downstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation("route is authenticated scopes must be checked");
|
||||
|
||||
var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamReRoute.AuthenticationOptions.AllowedScopes);
|
||||
|
||||
if (authorised.IsError)
|
||||
{
|
||||
Logger.LogWarning("error authorising user scopes");
|
||||
|
||||
httpContext.Items.UpsertErrors(authorised.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAuthorised(authorised))
|
||||
{
|
||||
Logger.LogInformation("user scopes is authorised calling next authorisation checks");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("user scopes is not authorised setting pipeline error");
|
||||
|
||||
httpContext.Items.SetError(new UnauthorisedError(
|
||||
$"{httpContext.User.Identity.Name} unable to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation("route is authorised");
|
||||
|
||||
var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamReRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues());
|
||||
|
||||
if (authorised.IsError)
|
||||
{
|
||||
Logger.LogWarning($"Error whilst authorising {httpContext.User.Identity.Name}. Setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(authorised.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAuthorised(authorised))
|
||||
{
|
||||
Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamReRoute.UpstreamPathTemplate.OriginalValue}.");
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
|
||||
|
||||
httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAuthorised(Response<bool> authorised)
|
||||
{
|
||||
return authorised.Data;
|
||||
}
|
||||
|
||||
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthenticated;
|
||||
}
|
||||
|
||||
private static bool IsAuthorisedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthorised;
|
||||
}
|
||||
|
||||
private static bool IsOptionsHttpMethod(HttpContext httpContext)
|
||||
{
|
||||
return httpContext.Request.Method.ToUpper() == "OPTIONS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Authorisation.Middleware
|
||||
{
|
||||
public static class AuthorisationMiddlewareMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<AuthorisationMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Authorisation.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class AuthorisationMiddlewareMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<AuthorisationMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Ocelot.Errors;
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
public class ScopeNotAuthorisedError : Error
|
||||
{
|
||||
public ScopeNotAuthorisedError(string message)
|
||||
: base(message, OcelotErrorCode.ScopeNotAuthorisedError)
|
||||
: base(message, OcelotErrorCode.ScopeNotAuthorisedError, 403)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
public class UnauthorisedError : Error
|
||||
{
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
public class UnauthorisedError : Error
|
||||
{
|
||||
public UnauthorisedError(string message)
|
||||
: base(message, OcelotErrorCode.UnauthorizedError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base(message, OcelotErrorCode.UnauthorizedError, 403)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Ocelot.Errors;
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Authorisation
|
||||
{
|
||||
public class UserDoesNotHaveClaimError : Error
|
||||
{
|
||||
public UserDoesNotHaveClaimError(string message)
|
||||
: base(message, OcelotErrorCode.UserDoesNotHaveClaimError)
|
||||
: base(message, OcelotErrorCode.UserDoesNotHaveClaimError, 403)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
using Ocelot.Middleware;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Cache
|
||||
namespace Ocelot.Cache
|
||||
{
|
||||
using Ocelot.Request.Middleware;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class CacheKeyGenerator : ICacheKeyGenerator
|
||||
{
|
||||
public string GenerateRequestCacheKey(DownstreamContext context)
|
||||
public string GenerateRequestCacheKey(DownstreamRequest downstreamRequest)
|
||||
{
|
||||
string hashedContent = null;
|
||||
StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}");
|
||||
if (context.DownstreamRequest.Content != null)
|
||||
StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{downstreamRequest.Method}-{downstreamRequest.OriginalString}");
|
||||
if (downstreamRequest.Content != null)
|
||||
{
|
||||
string requestContentString = Task.Run(async () => await context.DownstreamRequest.Content.ReadAsStringAsync()).Result;
|
||||
string requestContentString = Task.Run(async () => await downstreamRequest.Content.ReadAsStringAsync()).Result;
|
||||
downStreamUrlKeyBuilder.Append(requestContentString);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Cache
|
||||
namespace Ocelot.Cache
|
||||
{
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
public interface ICacheKeyGenerator
|
||||
{
|
||||
string GenerateRequestCacheKey(DownstreamContext context);
|
||||
string GenerateRequestCacheKey(DownstreamRequest downstreamRequest);
|
||||
}
|
||||
}
|
||||
|
@ -1,122 +1,129 @@
|
||||
namespace Ocelot.Cache.Middleware
|
||||
{
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.Cache.Middleware
|
||||
{
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class OutputCacheMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IOcelotCache<CachedResponse> _outputCache;
|
||||
private readonly ICacheKeyGenerator _cacheGeneratot;
|
||||
|
||||
public OutputCacheMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IOcelotCache<CachedResponse> outputCache,
|
||||
ICacheKeyGenerator cacheGeneratot)
|
||||
: base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_outputCache = outputCache;
|
||||
_cacheGeneratot = cacheGeneratot;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (!context.DownstreamReRoute.IsCached)
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
return;
|
||||
public class OutputCacheMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IOcelotCache<CachedResponse> _outputCache;
|
||||
private readonly ICacheKeyGenerator _cacheGenerator;
|
||||
|
||||
public OutputCacheMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IOcelotCache<CachedResponse> outputCache,
|
||||
ICacheKeyGenerator cacheGenerator)
|
||||
: base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_outputCache = outputCache;
|
||||
_cacheGenerator = cacheGenerator;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (!downstreamReRoute.IsCached)
|
||||
{
|
||||
await _next.Invoke(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
|
||||
string downStreamRequestCacheKey = _cacheGeneratot.GenerateRequestCacheKey(context);
|
||||
|
||||
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
|
||||
|
||||
var cached = _outputCache.Get(downStreamRequestCacheKey, context.DownstreamReRoute.CacheOptions.Region);
|
||||
|
||||
if (cached != null)
|
||||
{
|
||||
Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
|
||||
|
||||
var response = CreateHttpResponseMessage(cached);
|
||||
SetHttpResponseMessageThisRequest(context, response);
|
||||
|
||||
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
|
||||
|
||||
return;
|
||||
var downstreamRequest = httpContext.Items.DownstreamRequest();
|
||||
|
||||
var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}";
|
||||
string downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest);
|
||||
|
||||
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
|
||||
|
||||
var cached = _outputCache.Get(downStreamRequestCacheKey, downstreamReRoute.CacheOptions.Region);
|
||||
|
||||
if (cached != null)
|
||||
{
|
||||
Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
|
||||
|
||||
var response = CreateHttpResponseMessage(cached);
|
||||
SetHttpResponseMessageThisRequest(httpContext, response);
|
||||
|
||||
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
|
||||
if (httpContext.Items.Errors().Count > 0)
|
||||
{
|
||||
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
|
||||
if (context.IsError)
|
||||
{
|
||||
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cached = await CreateCachedResponse(context.DownstreamResponse);
|
||||
|
||||
_outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
|
||||
|
||||
Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
|
||||
}
|
||||
|
||||
private void SetHttpResponseMessageThisRequest(DownstreamContext context,
|
||||
DownstreamResponse response)
|
||||
{
|
||||
context.DownstreamResponse = response;
|
||||
}
|
||||
|
||||
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
|
||||
{
|
||||
if (cached == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
|
||||
|
||||
var streamContent = new StreamContent(content);
|
||||
|
||||
foreach (var header in cached.ContentHeaders)
|
||||
{
|
||||
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase);
|
||||
}
|
||||
|
||||
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var statusCode = response.StatusCode;
|
||||
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values);
|
||||
string body = null;
|
||||
|
||||
if (response.Content != null)
|
||||
{
|
||||
var content = await response.Content.ReadAsByteArrayAsync();
|
||||
body = Convert.ToBase64String(content);
|
||||
}
|
||||
|
||||
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
|
||||
|
||||
var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
var downstreamResponse = httpContext.Items.DownstreamResponse();
|
||||
|
||||
cached = await CreateCachedResponse(downstreamResponse);
|
||||
|
||||
_outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(downstreamReRoute.CacheOptions.TtlSeconds), downstreamReRoute.CacheOptions.Region);
|
||||
|
||||
Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
|
||||
}
|
||||
|
||||
private void SetHttpResponseMessageThisRequest(HttpContext context,
|
||||
DownstreamResponse response)
|
||||
{
|
||||
context.Items.UpsertDownstreamResponse(response);
|
||||
}
|
||||
|
||||
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
|
||||
{
|
||||
if (cached == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
|
||||
|
||||
var streamContent = new StreamContent(content);
|
||||
|
||||
foreach (var header in cached.ContentHeaders)
|
||||
{
|
||||
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase);
|
||||
}
|
||||
|
||||
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var statusCode = response.StatusCode;
|
||||
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values);
|
||||
string body = null;
|
||||
|
||||
if (response.Content != null)
|
||||
{
|
||||
var content = await response.Content.ReadAsByteArrayAsync();
|
||||
body = Convert.ToBase64String(content);
|
||||
}
|
||||
|
||||
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
|
||||
|
||||
var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Cache.Middleware
|
||||
{
|
||||
public static class OutputCacheMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseOutputCacheMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<OutputCacheMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Cache.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class OutputCacheMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<OutputCacheMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Claims.Middleware
|
||||
namespace Ocelot.Claims.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class ClaimsBuilderMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToClaimsMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IApplicationBuilder UseClaimsToClaimsMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToClaimsMiddleware>();
|
||||
}
|
||||
|
@ -1,42 +1,46 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Claims.Middleware
|
||||
{
|
||||
public class ClaimsToClaimsMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||
|
||||
public ClaimsToClaimsMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddClaimsToRequest addClaimsToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addClaimsToRequest = addClaimsToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToClaims.Any())
|
||||
{
|
||||
Logger.LogDebug("this route has instructions to convert claims to other claims");
|
||||
|
||||
var result = _addClaimsToRequest.SetClaimsOnContext(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext);
|
||||
|
||||
if (result.IsError)
|
||||
{
|
||||
Logger.LogDebug("error converting claims to other claims, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, result.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Claims.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class ClaimsToClaimsMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||
|
||||
public ClaimsToClaimsMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddClaimsToRequest addClaimsToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addClaimsToRequest = addClaimsToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (downstreamReRoute.ClaimsToClaims.Any())
|
||||
{
|
||||
Logger.LogDebug("this route has instructions to convert claims to other claims");
|
||||
|
||||
var result = _addClaimsToRequest.SetClaimsOnContext(downstreamReRoute.ClaimsToClaims, httpContext);
|
||||
|
||||
if (result.IsError)
|
||||
{
|
||||
Logger.LogDebug("error converting claims to other claims, setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(result.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class InstructionNotForClaimsError : Error
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class InstructionNotForClaimsError : Error
|
||||
{
|
||||
public InstructionNotForClaimsError()
|
||||
: base("instructions did not contain claims, at the moment we only support claims extraction", OcelotErrorCode.InstructionNotForClaimsError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base("instructions did not contain claims, at the moment we only support claims extraction", OcelotErrorCode.InstructionNotForClaimsError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class NoInstructionsError : Error
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class NoInstructionsError : Error
|
||||
{
|
||||
public NoInstructionsError(string splitToken)
|
||||
: base($"There we no instructions splitting on {splitToken}", OcelotErrorCode.NoInstructionsError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base($"There we no instructions splitting on {splitToken}", OcelotErrorCode.NoInstructionsError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
using Ocelot.Errors;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class ParsingConfigurationHeaderError : Error
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Configuration.Parser
|
||||
{
|
||||
public class ParsingConfigurationHeaderError : Error
|
||||
{
|
||||
public ParsingConfigurationHeaderError(Exception exception)
|
||||
: base($"error parsing configuration eception is {exception.Message}", OcelotErrorCode.ParsingConfigurationHeaderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base($"error parsing configuration eception is {exception.Message}", OcelotErrorCode.ParsingConfigurationHeaderError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Values;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
|
||||
public class ReRoute
|
||||
{
|
||||
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
|
||||
List<AggregateReRouteConfig> downstreamReRouteConfig,
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Values;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
|
||||
public class ReRoute
|
||||
{
|
||||
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
|
||||
List<AggregateReRouteConfig> downstreamReRouteConfig,
|
||||
List<HttpMethod> upstreamHttpMethod,
|
||||
UpstreamPathTemplate upstreamTemplatePattern,
|
||||
string upstreamHost,
|
||||
string aggregator)
|
||||
{
|
||||
UpstreamHost = upstreamHost;
|
||||
DownstreamReRoute = downstreamReRoute;
|
||||
DownstreamReRouteConfig = downstreamReRouteConfig;
|
||||
UpstreamHttpMethod = upstreamHttpMethod;
|
||||
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||
Aggregator = aggregator;
|
||||
}
|
||||
|
||||
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
|
||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||
public string UpstreamHost { get; private set; }
|
||||
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
||||
public List<AggregateReRouteConfig> DownstreamReRouteConfig { get; private set; }
|
||||
public string Aggregator { get; private set; }
|
||||
}
|
||||
string upstreamHost,
|
||||
string aggregator)
|
||||
{
|
||||
UpstreamHost = upstreamHost;
|
||||
DownstreamReRoute = downstreamReRoute;
|
||||
DownstreamReRouteConfig = downstreamReRouteConfig;
|
||||
UpstreamHttpMethod = upstreamHttpMethod;
|
||||
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||
Aggregator = aggregator;
|
||||
}
|
||||
|
||||
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
|
||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||
public string UpstreamHost { get; private set; }
|
||||
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
||||
public List<AggregateReRouteConfig> DownstreamReRouteConfig { get; private set; }
|
||||
public string Aggregator { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using Errors;
|
||||
|
||||
public class FileValidationFailedError : Error
|
||||
{
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using Errors;
|
||||
|
||||
public class FileValidationFailedError : Error
|
||||
{
|
||||
public FileValidationFailedError(string message)
|
||||
: base(message, OcelotErrorCode.FileValidationFailedError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base(message, OcelotErrorCode.FileValidationFailedError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Multiplexer;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
|
@ -27,7 +27,7 @@ namespace Ocelot.DependencyInjection
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Multiplexer;
|
||||
using Ocelot.PathManipulation;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.RateLimit;
|
||||
@ -128,7 +128,6 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
|
||||
Services.AddMemoryCache();
|
||||
Services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||
Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
||||
|
@ -1,42 +1,50 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.DownstreamPathManipulation.Middleware
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.PathManipulation;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
namespace Ocelot.PathManipulation.Middleware
|
||||
{
|
||||
public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate;
|
||||
public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate;
|
||||
|
||||
public ClaimsToDownstreamPathMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IChangeDownstreamPathTemplate changeDownstreamPathTemplate)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToDownstreamPathMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_changeDownstreamPathTemplate = changeDownstreamPathTemplate;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (downstreamReRoute.ClaimsToPath.Any())
|
||||
{
|
||||
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
|
||||
|
||||
public ClaimsToDownstreamPathMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IChangeDownstreamPathTemplate changeDownstreamPathTemplate)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToDownstreamPathMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_changeDownstreamPathTemplate = changeDownstreamPathTemplate;
|
||||
}
|
||||
var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToPath.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
|
||||
var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(context.DownstreamReRoute.ClaimsToPath, context.HttpContext.User.Claims,
|
||||
context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(downstreamReRoute.ClaimsToPath, httpContext.User.Claims,
|
||||
downstreamReRoute.DownstreamPathTemplate, templatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(response.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.PathManipulation.Middleware
|
||||
namespace Ocelot.DownstreamPathManipulation.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class ClaimsToDownstreamPathMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToDownstreamPathMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IApplicationBuilder UseClaimsToDownstreamPathMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToDownstreamPathMiddleware>();
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder
|
||||
{
|
||||
public class DownstreamRoute
|
||||
{
|
||||
public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;
|
||||
ReRoute = reRoute;
|
||||
}
|
||||
|
||||
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
||||
public ReRoute ReRoute { get; private set; }
|
||||
}
|
||||
namespace Ocelot.DownstreamRouteFinder
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class DownstreamRoute
|
||||
{
|
||||
public DownstreamRoute()
|
||||
{
|
||||
}
|
||||
|
||||
public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;
|
||||
ReRoute = reRoute;
|
||||
}
|
||||
|
||||
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
||||
public ReRoute ReRoute { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
{
|
||||
public class UnableToFindDownstreamRouteError : Error
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
{
|
||||
public class UnableToFindDownstreamRouteError : Error
|
||||
{
|
||||
public UnableToFindDownstreamRouteError(string path, string httpVerb)
|
||||
: base($"Failed to match ReRoute configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base($"Failed to match ReRoute configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,61 @@
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IDownstreamRouteProviderFactory _factory;
|
||||
private readonly IMultiplexer _multiplexer;
|
||||
|
||||
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
||||
public DownstreamRouteFinderMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamRouteProviderFactory downstreamRouteFinder,
|
||||
IMultiplexer multiplexer)
|
||||
IDownstreamRouteProviderFactory downstreamRouteFinder
|
||||
)
|
||||
: base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
|
||||
{
|
||||
_multiplexer = multiplexer;
|
||||
_next = next;
|
||||
_factory = downstreamRouteFinder;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
|
||||
var upstreamUrlPath = httpContext.Request.Path.ToString();
|
||||
|
||||
var upstreamQueryString = context.HttpContext.Request.QueryString.ToString();
|
||||
var upstreamQueryString = httpContext.Request.QueryString.ToString();
|
||||
|
||||
var upstreamHost = context.HttpContext.Request.Headers["Host"];
|
||||
var upstreamHost = httpContext.Request.Headers["Host"];
|
||||
|
||||
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
|
||||
|
||||
var provider = _factory.Get(context.Configuration);
|
||||
var internalConfiguration = httpContext.Items.IInternalConfiguration();
|
||||
|
||||
var downstreamRoute = provider.Get(upstreamUrlPath, upstreamQueryString, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
|
||||
var provider = _factory.Get(internalConfiguration);
|
||||
|
||||
if (downstreamRoute.IsError)
|
||||
var response = provider.Get(upstreamUrlPath, upstreamQueryString, httpContext.Request.Method, internalConfiguration, upstreamHost);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
|
||||
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {response.Errors.ToErrorString()}");
|
||||
|
||||
SetPipelineError(context, downstreamRoute.Errors);
|
||||
httpContext.Items.UpsertErrors(response.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
|
||||
|
||||
var downstreamPathTemplates = string.Join(", ", response.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
|
||||
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
|
||||
|
||||
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
|
||||
// why set both of these on HttpContext
|
||||
httpContext.Items.UpsertTemplatePlaceholderNameAndValues(response.Data.TemplatePlaceholderNameAndValues);
|
||||
|
||||
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
|
||||
httpContext.Items.UpsertDownstreamRoute(response.Data);
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class DownstreamRouteFinderMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseDownstreamRouteFinderMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
|
||||
}
|
||||
|
@ -1,132 +1,149 @@
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
using System.Text.RegularExpressions;
|
||||
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||
|
||||
public DownstreamUrlCreatorMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamPathPlaceholderReplacer replacer
|
||||
)
|
||||
: base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_replacer = replacer;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||
var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
|
||||
|
||||
var response = _replacer
|
||||
.Replace(downstreamReRoute.DownstreamPathTemplate.Value, templatePlaceholderNameAndValues);
|
||||
|
||||
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamPathPlaceholderReplacer replacer)
|
||||
: base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_replacer = replacer;
|
||||
}
|
||||
var downstreamRequest = httpContext.Items.DownstreamRequest();
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var response = _replacer
|
||||
.Replace(context.DownstreamReRoute.DownstreamPathTemplate.Value, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(response.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(downstreamReRoute.DownstreamScheme))
|
||||
{
|
||||
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
//todo make sure this works, hopefully there is a test ;E
|
||||
httpContext.Items.DownstreamRequest().Scheme = downstreamReRoute.DownstreamScheme;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme))
|
||||
{
|
||||
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||
}
|
||||
var internalConfiguration = httpContext.Items.IInternalConfiguration();
|
||||
|
||||
if (ServiceFabricRequest(context))
|
||||
if (ServiceFabricRequest(internalConfiguration, downstreamReRoute))
|
||||
{
|
||||
var pathAndQuery = CreateServiceFabricUri(context, response);
|
||||
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dsPath = response.Data;
|
||||
var pathAndQuery = CreateServiceFabricUri(downstreamRequest, downstreamReRoute, templatePlaceholderNameAndValues, response);
|
||||
|
||||
if (ContainsQueryString(dsPath))
|
||||
//todo check this works again hope there is a test..
|
||||
downstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||
downstreamRequest.Query = pathAndQuery.query;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dsPath = response.Data;
|
||||
|
||||
if (ContainsQueryString(dsPath))
|
||||
{
|
||||
context.DownstreamRequest.AbsolutePath = GetPath(dsPath);
|
||||
|
||||
if (string.IsNullOrEmpty(context.DownstreamRequest.Query))
|
||||
{
|
||||
context.DownstreamRequest.Query = GetQueryString(dsPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.DownstreamRequest.Query += GetQueryString(dsPath).Replace('?', '&');
|
||||
}
|
||||
}
|
||||
else
|
||||
downstreamRequest.AbsolutePath = GetPath(dsPath);
|
||||
|
||||
if (string.IsNullOrEmpty(downstreamRequest.Query))
|
||||
{
|
||||
downstreamRequest.Query = GetQueryString(dsPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
downstreamRequest.Query += GetQueryString(dsPath).Replace('?', '&');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveQueryStringParametersThatHaveBeenUsedInTemplate(context);
|
||||
|
||||
context.DownstreamRequest.AbsolutePath = dsPath.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
|
||||
private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamContext context)
|
||||
{
|
||||
foreach (var nAndV in context.TemplatePlaceholderNameAndValues)
|
||||
{
|
||||
var name = nAndV.Name.Replace("{", "").Replace("}", "");
|
||||
|
||||
if (context.DownstreamRequest.Query.Contains(name) &&
|
||||
context.DownstreamRequest.Query.Contains(nAndV.Value))
|
||||
{
|
||||
var questionMarkOrAmpersand = context.DownstreamRequest.Query.IndexOf(name, StringComparison.Ordinal);
|
||||
context.DownstreamRequest.Query = context.DownstreamRequest.Query.Remove(questionMarkOrAmpersand - 1, 1);
|
||||
|
||||
var rgx = new Regex($@"\b{name}={nAndV.Value}\b");
|
||||
context.DownstreamRequest.Query = rgx.Replace(context.DownstreamRequest.Query, "");
|
||||
|
||||
if (!string.IsNullOrEmpty(context.DownstreamRequest.Query))
|
||||
{
|
||||
context.DownstreamRequest.Query = '?' + context.DownstreamRequest.Query.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPath(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(0, dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private string GetQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private bool ContainsQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Contains("?");
|
||||
}
|
||||
|
||||
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
||||
{
|
||||
var query = context.DownstreamRequest.Query;
|
||||
var serviceName = _replacer.Replace(context.DownstreamReRoute.ServiceName, context.TemplatePlaceholderNameAndValues);
|
||||
var pathTemplate = $"/{serviceName.Data.Value}{dsPath.Data.Value}";
|
||||
return (pathTemplate, query);
|
||||
}
|
||||
|
||||
private static bool ServiceFabricRequest(DownstreamContext context)
|
||||
{
|
||||
return context.Configuration.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && context.DownstreamReRoute.UseServiceDiscovery;
|
||||
}
|
||||
}
|
||||
}
|
||||
RemoveQueryStringParametersThatHaveBeenUsedInTemplate(downstreamRequest, templatePlaceholderNameAndValues);
|
||||
|
||||
downstreamRequest.AbsolutePath = dsPath.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Downstream url is {downstreamRequest}");
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
|
||||
private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamRequest downstreamRequest, List<PlaceholderNameAndValue> templatePlaceholderNameAndValues)
|
||||
{
|
||||
foreach (var nAndV in templatePlaceholderNameAndValues)
|
||||
{
|
||||
var name = nAndV.Name.Replace("{", "").Replace("}", "");
|
||||
|
||||
if (downstreamRequest.Query.Contains(name) &&
|
||||
downstreamRequest.Query.Contains(nAndV.Value))
|
||||
{
|
||||
var questionMarkOrAmpersand = downstreamRequest.Query.IndexOf(name, StringComparison.Ordinal);
|
||||
downstreamRequest.Query = downstreamRequest.Query.Remove(questionMarkOrAmpersand - 1, 1);
|
||||
|
||||
var rgx = new Regex($@"\b{name}={nAndV.Value}\b");
|
||||
downstreamRequest.Query = rgx.Replace(downstreamRequest.Query, "");
|
||||
|
||||
if (!string.IsNullOrEmpty(downstreamRequest.Query))
|
||||
{
|
||||
downstreamRequest.Query = '?' + downstreamRequest.Query.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPath(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(0, dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private string GetQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private bool ContainsQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Contains("?");
|
||||
}
|
||||
|
||||
private (string path, string query) CreateServiceFabricUri(DownstreamRequest downstreamRequest, DownstreamReRoute downstreamReRoute, List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, Response<DownstreamPath> dsPath)
|
||||
{
|
||||
var query = downstreamRequest.Query;
|
||||
var serviceName = _replacer.Replace(downstreamReRoute.ServiceName, templatePlaceholderNameAndValues);
|
||||
var pathTemplate = $"/{serviceName.Data.Value}{dsPath.Data.Value}";
|
||||
return (pathTemplate, query);
|
||||
}
|
||||
|
||||
private static bool ServiceFabricRequest(IInternalConfiguration config, DownstreamReRoute downstreamReRoute)
|
||||
{
|
||||
return config.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && downstreamReRoute.UseServiceDiscovery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
public static class DownstreamUrlCreatorMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseDownstreamUrlCreatorMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class DownstreamUrlCreatorMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Ocelot.Errors
|
||||
{
|
||||
public abstract class Error
|
||||
{
|
||||
protected Error(string message, OcelotErrorCode code)
|
||||
{
|
||||
protected Error(string message, OcelotErrorCode code, int httpStatusCode)
|
||||
{
|
||||
HttpStatusCode = httpStatusCode;
|
||||
Message = message;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public string Message { get; private set; }
|
||||
public OcelotErrorCode Code { get; private set; }
|
||||
public OcelotErrorCode Code { get; private set; }
|
||||
public int HttpStatusCode { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -1,102 +1,89 @@
|
||||
namespace Ocelot.Errors.Middleware
|
||||
{
|
||||
using Configuration;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500
|
||||
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500.
|
||||
/// </summary>
|
||||
public class ExceptionHandlerMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IInternalConfigurationRepository _configRepo;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRequestScopedDataRepository _repo;
|
||||
|
||||
public ExceptionHandlerMiddleware(OcelotRequestDelegate next,
|
||||
public ExceptionHandlerMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IInternalConfigurationRepository configRepo,
|
||||
IRequestScopedDataRepository repo)
|
||||
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
|
||||
{
|
||||
_configRepo = configRepo;
|
||||
_repo = repo;
|
||||
_next = next;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.HttpContext.RequestAborted.ThrowIfCancellationRequested();
|
||||
httpContext.RequestAborted.ThrowIfCancellationRequested();
|
||||
|
||||
//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 = _configRepo.Get();
|
||||
var internalConfiguration = httpContext.Items.IInternalConfiguration();
|
||||
|
||||
if (configuration.IsError)
|
||||
{
|
||||
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
||||
}
|
||||
|
||||
TrySetGlobalRequestId(context, configuration.Data);
|
||||
|
||||
context.Configuration = configuration.Data;
|
||||
TrySetGlobalRequestId(httpContext, internalConfiguration);
|
||||
|
||||
Logger.LogDebug("ocelot pipeline started");
|
||||
|
||||
await _next.Invoke(context);
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
catch (OperationCanceledException) when (context.HttpContext.RequestAborted.IsCancellationRequested)
|
||||
catch (OperationCanceledException) when (httpContext.RequestAborted.IsCancellationRequested)
|
||||
{
|
||||
Logger.LogDebug("operation canceled");
|
||||
if (!context.HttpContext.Response.HasStarted)
|
||||
if (!httpContext.Response.HasStarted)
|
||||
{
|
||||
context.HttpContext.Response.StatusCode = 499;
|
||||
httpContext.Response.StatusCode = 499;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogDebug("error calling middleware");
|
||||
|
||||
var message = CreateMessage(context, e);
|
||||
var message = CreateMessage(httpContext, e);
|
||||
|
||||
Logger.LogError(message, e);
|
||||
|
||||
SetInternalServerErrorOnResponse(context);
|
||||
SetInternalServerErrorOnResponse(httpContext);
|
||||
}
|
||||
|
||||
Logger.LogDebug("ocelot pipeline finished");
|
||||
}
|
||||
|
||||
private void TrySetGlobalRequestId(DownstreamContext context, IInternalConfiguration configuration)
|
||||
private void TrySetGlobalRequestId(HttpContext httpContext, IInternalConfiguration configuration)
|
||||
{
|
||||
var key = configuration.RequestId;
|
||||
|
||||
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
|
||||
if (!string.IsNullOrEmpty(key) && httpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
|
||||
{
|
||||
context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
|
||||
httpContext.TraceIdentifier = upstreamRequestIds.First();
|
||||
}
|
||||
|
||||
_repo.Add("RequestId", context.HttpContext.TraceIdentifier);
|
||||
_repo.Add("RequestId", httpContext.TraceIdentifier);
|
||||
}
|
||||
|
||||
private void SetInternalServerErrorOnResponse(DownstreamContext context)
|
||||
private void SetInternalServerErrorOnResponse(HttpContext httpContext)
|
||||
{
|
||||
if (!context.HttpContext.Response.HasStarted)
|
||||
if (!httpContext.Response.HasStarted)
|
||||
{
|
||||
context.HttpContext.Response.StatusCode = 500;
|
||||
httpContext.Response.StatusCode = 500;
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateMessage(DownstreamContext context, Exception e)
|
||||
private string CreateMessage(HttpContext httpContext, Exception e)
|
||||
{
|
||||
var message =
|
||||
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
|
||||
@ -107,7 +94,7 @@ namespace Ocelot.Errors.Middleware
|
||||
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
|
||||
}
|
||||
|
||||
return $"{message} RequestId: {context.HttpContext.TraceIdentifier}";
|
||||
return $"{message} RequestId: {httpContext.TraceIdentifier}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Errors.Middleware
|
||||
{
|
||||
public static class ExceptionHandlerMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseExceptionHandlerMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||
}
|
||||
}
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Errors.Middleware
|
||||
{
|
||||
public static class ExceptionHandlerMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
namespace Ocelot.Errors
|
||||
{
|
||||
public enum OcelotErrorCode
|
||||
{
|
||||
namespace Ocelot.Errors
|
||||
{
|
||||
public enum OcelotErrorCode
|
||||
{
|
||||
UnauthenticatedError = 0,
|
||||
UnknownError = 1,
|
||||
DownstreampathTemplateAlreadyUsedError = 2,
|
||||
UnableToFindDownstreamRouteError = 3,
|
||||
CannotAddDataError = 4,
|
||||
CannotFindDataError = 5,
|
||||
UnableToCompleteRequestError = 6,
|
||||
UnableToCreateAuthenticationHandlerError = 7,
|
||||
UnsupportedAuthenticationProviderError = 8,
|
||||
CannotFindClaimError = 9,
|
||||
ParsingConfigurationHeaderError = 10,
|
||||
NoInstructionsError = 11,
|
||||
InstructionNotForClaimsError = 12,
|
||||
UnauthorizedError = 13,
|
||||
ClaimValueNotAuthorisedError = 14,
|
||||
ScopeNotAuthorisedError = 15,
|
||||
UserDoesNotHaveClaimError = 16,
|
||||
DownstreamPathTemplateContainsSchemeError = 17,
|
||||
DownstreamPathNullOrEmptyError = 18,
|
||||
DownstreamSchemeNullOrEmptyError = 19,
|
||||
DownstreamHostNullOrEmptyError = 20,
|
||||
ServicesAreNullError = 21,
|
||||
ServicesAreEmptyError = 22,
|
||||
UnableToFindServiceDiscoveryProviderError = 23,
|
||||
UnableToFindLoadBalancerError = 24,
|
||||
RequestTimedOutError = 25,
|
||||
UnableToFindQoSProviderError = 26,
|
||||
UnmappableRequestError = 27,
|
||||
RateLimitOptionsError = 28,
|
||||
PathTemplateDoesntStartWithForwardSlash = 29,
|
||||
FileValidationFailedError = 30,
|
||||
UnableToFindDelegatingHandlerProviderError = 31,
|
||||
CouldNotFindPlaceholderError = 32,
|
||||
CouldNotFindAggregatorError = 33,
|
||||
CannotAddPlaceholderError = 34,
|
||||
CannotRemovePlaceholderError = 35,
|
||||
UnknownError = 1,
|
||||
DownstreampathTemplateAlreadyUsedError = 2,
|
||||
UnableToFindDownstreamRouteError = 3,
|
||||
CannotAddDataError = 4,
|
||||
CannotFindDataError = 5,
|
||||
UnableToCompleteRequestError = 6,
|
||||
UnableToCreateAuthenticationHandlerError = 7,
|
||||
UnsupportedAuthenticationProviderError = 8,
|
||||
CannotFindClaimError = 9,
|
||||
ParsingConfigurationHeaderError = 10,
|
||||
NoInstructionsError = 11,
|
||||
InstructionNotForClaimsError = 12,
|
||||
UnauthorizedError = 13,
|
||||
ClaimValueNotAuthorisedError = 14,
|
||||
ScopeNotAuthorisedError = 15,
|
||||
UserDoesNotHaveClaimError = 16,
|
||||
DownstreamPathTemplateContainsSchemeError = 17,
|
||||
DownstreamPathNullOrEmptyError = 18,
|
||||
DownstreamSchemeNullOrEmptyError = 19,
|
||||
DownstreamHostNullOrEmptyError = 20,
|
||||
ServicesAreNullError = 21,
|
||||
ServicesAreEmptyError = 22,
|
||||
UnableToFindServiceDiscoveryProviderError = 23,
|
||||
UnableToFindLoadBalancerError = 24,
|
||||
RequestTimedOutError = 25,
|
||||
UnableToFindQoSProviderError = 26,
|
||||
UnmappableRequestError = 27,
|
||||
RateLimitOptionsError = 28,
|
||||
PathTemplateDoesntStartWithForwardSlash = 29,
|
||||
FileValidationFailedError = 30,
|
||||
UnableToFindDelegatingHandlerProviderError = 31,
|
||||
CouldNotFindPlaceholderError = 32,
|
||||
CouldNotFindAggregatorError = 33,
|
||||
CannotAddPlaceholderError = 34,
|
||||
CannotRemovePlaceholderError = 35,
|
||||
QuotaExceededError = 36,
|
||||
RequestCanceled = 37,
|
||||
ConnectionToDownstreamServiceError = 38,
|
||||
CouldNotFindLoadBalancerCreator = 39,
|
||||
ErrorInvokingLoadBalancerCreator = 40,
|
||||
}
|
||||
ConnectionToDownstreamServiceError = 38,
|
||||
CouldNotFindLoadBalancerCreator = 39,
|
||||
ErrorInvokingLoadBalancerCreator = 40,
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ namespace Ocelot.Headers
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
||||
{
|
||||
@ -17,10 +19,10 @@ namespace Ocelot.Headers
|
||||
_placeholders = placeholders;
|
||||
}
|
||||
|
||||
public Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs)
|
||||
public Response Replace(HttpContext httpContext, List<HeaderFindAndReplace> fAndRs)
|
||||
{
|
||||
var response = context.DownstreamResponse;
|
||||
var request = context.DownstreamRequest;
|
||||
var response = httpContext.Items.DownstreamResponse();
|
||||
var request = httpContext.Items.DownstreamRequest();
|
||||
|
||||
foreach (var f in fAndRs)
|
||||
{
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IHttpResponseHeaderReplacer
|
||||
{
|
||||
Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs);
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
public interface IHttpResponseHeaderReplacer
|
||||
{
|
||||
public Response Replace(HttpContext httpContext, List<HeaderFindAndReplace> fAndRs);
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,50 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class ClaimsToHeadersMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||
|
||||
public ClaimsToHeadersMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddHeadersToRequest addHeadersToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addHeadersToRequest = addHeadersToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (downstreamReRoute.ClaimsToHeaders.Any())
|
||||
{
|
||||
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public class ClaimsToHeadersMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||
|
||||
public ClaimsToHeadersMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddHeadersToRequest addHeadersToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addHeadersToRequest = addHeadersToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToHeaders.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
||||
|
||||
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("Error setting headers on context, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInformation("headers have been set on context");
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
var downstreamRequest = httpContext.Items.DownstreamRequest();
|
||||
|
||||
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.ClaimsToHeaders, httpContext.User.Claims, downstreamRequest);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("Error setting headers on context, setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(response.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInformation("headers have been set on context");
|
||||
}
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public static class ClaimsToHeadersMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToHeadersMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IApplicationBuilder UseClaimsToHeadersMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToHeadersMiddleware>();
|
||||
}
|
||||
|
@ -1,23 +1,26 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class HttpHeadersTransformationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
|
||||
private readonly IHttpResponseHeaderReplacer _postReplacer;
|
||||
private readonly IAddHeadersToResponse _addHeadersToResponse;
|
||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||
|
||||
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
|
||||
public HttpHeadersTransformationMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IHttpContextRequestHeaderReplacer preReplacer,
|
||||
IHttpResponseHeaderReplacer postReplacer,
|
||||
IAddHeadersToResponse addHeadersToResponse,
|
||||
IAddHeadersToRequest addHeadersToRequest)
|
||||
IAddHeadersToRequest addHeadersToRequest
|
||||
)
|
||||
: base(loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>())
|
||||
{
|
||||
_addHeadersToResponse = addHeadersToResponse;
|
||||
@ -27,27 +30,33 @@ namespace Ocelot.Headers.Middleware
|
||||
_preReplacer = preReplacer;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var preFAndRs = context.DownstreamReRoute.UpstreamHeadersFindAndReplace;
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
var preFAndRs = downstreamReRoute.UpstreamHeadersFindAndReplace;
|
||||
|
||||
//todo - this should be on httprequestmessage not httpcontext?
|
||||
_preReplacer.Replace(context.HttpContext, preFAndRs);
|
||||
_preReplacer.Replace(httpContext, preFAndRs);
|
||||
|
||||
_addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.AddHeadersToUpstream, context.HttpContext);
|
||||
_addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.AddHeadersToUpstream, httpContext);
|
||||
|
||||
await _next.Invoke(context);
|
||||
await _next.Invoke(httpContext);
|
||||
|
||||
if (context.IsError)
|
||||
// todo check errors is ok
|
||||
//todo put this check on the base class?
|
||||
if (httpContext.Items.Errors().Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||
var postFAndRs = downstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||
|
||||
_postReplacer.Replace(context, postFAndRs);
|
||||
_postReplacer.Replace(httpContext, postFAndRs);
|
||||
|
||||
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
||||
var downstreamResponse = httpContext.Items.DownstreamResponse();
|
||||
|
||||
_addHeadersToResponse.Add(downstreamReRoute.AddHeadersToDownstream, downstreamResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public static class HttpHeadersTransformationMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseHttpHeadersTransformationMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<HttpHeadersTransformationMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class HttpHeadersTransformationMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseHttpHeadersTransformationMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<HttpHeadersTransformationMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
public class CannotAddPlaceholderError : Error
|
||||
{
|
||||
public CannotAddPlaceholderError(string message)
|
||||
: base(message, OcelotErrorCode.CannotAddPlaceholderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
public class CannotAddPlaceholderError : Error
|
||||
{
|
||||
public CannotAddPlaceholderError(string message)
|
||||
: base(message, OcelotErrorCode.CannotAddPlaceholderError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ namespace Ocelot.Infrastructure
|
||||
public class CannotRemovePlaceholderError : Error
|
||||
{
|
||||
public CannotRemovePlaceholderError(string message)
|
||||
: base(message, OcelotErrorCode.CannotRemovePlaceholderError)
|
||||
: base(message, OcelotErrorCode.CannotRemovePlaceholderError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace Ocelot.Infrastructure.Claims.Parser
|
||||
{
|
||||
using Errors;
|
||||
|
||||
public class CannotFindClaimError : Error
|
||||
{
|
||||
namespace Ocelot.Infrastructure.Claims.Parser
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
public class CannotFindClaimError : Error
|
||||
{
|
||||
public CannotFindClaimError(string message)
|
||||
: base(message, OcelotErrorCode.CannotFindClaimError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base(message, OcelotErrorCode.CannotFindClaimError, 403)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace Ocelot.Infrastructure
|
||||
public class CouldNotFindPlaceholderError : Error
|
||||
{
|
||||
public CouldNotFindPlaceholderError(string placeholder)
|
||||
: base($"Unable to find placeholder called {placeholder}", OcelotErrorCode.CouldNotFindPlaceholderError)
|
||||
: base($"Unable to find placeholder called {placeholder}", OcelotErrorCode.CouldNotFindPlaceholderError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure.RequestData
|
||||
{
|
||||
public class CannotAddDataError : Error
|
||||
{
|
||||
public CannotAddDataError(string message) : base(message, OcelotErrorCode.CannotAddDataError)
|
||||
{
|
||||
}
|
||||
}
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure.RequestData
|
||||
{
|
||||
public class CannotAddDataError : Error
|
||||
{
|
||||
public CannotAddDataError(string message) : base(message, OcelotErrorCode.CannotAddDataError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure.RequestData
|
||||
{
|
||||
public class CannotFindDataError : Error
|
||||
{
|
||||
public CannotFindDataError(string message) : base(message, OcelotErrorCode.CannotFindDataError)
|
||||
{
|
||||
}
|
||||
}
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Infrastructure.RequestData
|
||||
{
|
||||
public class CannotFindDataError : Error
|
||||
{
|
||||
public CannotFindDataError(string message) : base(message, OcelotErrorCode.CannotFindDataError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Middleware;
|
||||
using Responses;
|
||||
using Ocelot.Responses;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using Values;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Values;
|
||||
|
||||
public class CookieStickySessions : ILoadBalancer
|
||||
{
|
||||
@ -41,9 +42,9 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context)
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
|
||||
{
|
||||
var key = context.HttpContext.Request.Cookies[_key];
|
||||
var key = httpContext.Request.Cookies[_key];
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
@ -61,7 +62,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
}
|
||||
}
|
||||
|
||||
var next = await _loadBalancer.Lease(context);
|
||||
var next = await _loadBalancer.Lease(httpContext);
|
||||
|
||||
if (next.IsError)
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
public class CouldNotFindLoadBalancerCreator : Error
|
||||
{
|
||||
public CouldNotFindLoadBalancerCreator(string message)
|
||||
: base(message, OcelotErrorCode.CouldNotFindLoadBalancerCreator)
|
||||
: base(message, OcelotErrorCode.CouldNotFindLoadBalancerCreator, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
public class ErrorInvokingLoadBalancerCreator : Error
|
||||
{
|
||||
public ErrorInvokingLoadBalancerCreator(Exception e) : base($"Error when invoking user provided load balancer creator function, Message: {e.Message}, StackTrace: {e.StackTrace}", OcelotErrorCode.ErrorInvokingLoadBalancerCreator)
|
||||
public ErrorInvokingLoadBalancerCreator(Exception e) : base($"Error when invoking user provided load balancer creator function, Message: {e.Message}, StackTrace: {e.StackTrace}", OcelotErrorCode.ErrorInvokingLoadBalancerCreator, 500)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface ILoadBalancer
|
||||
{
|
||||
Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context);
|
||||
Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext);
|
||||
|
||||
void Release(ServiceHostAndPort hostAndPort);
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class LeastConnection : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
@ -22,7 +23,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
_leases = new List<Lease>();
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
|
||||
{
|
||||
var services = await _services.Invoke();
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class NoLoadBalancer : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
@ -17,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
|
||||
{
|
||||
var services = await _services();
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
public class RoundRobin : ILoadBalancer
|
||||
{
|
||||
@ -19,7 +20,7 @@
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
|
||||
{
|
||||
var services = await _services();
|
||||
lock (_lock)
|
||||
|
@ -5,8 +5,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
public class ServicesAreEmptyError : Error
|
||||
{
|
||||
public ServicesAreEmptyError(string message)
|
||||
: base(message, OcelotErrorCode.ServicesAreEmptyError)
|
||||
: base(message, OcelotErrorCode.ServicesAreEmptyError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
public class ServicesAreNullError : Error
|
||||
{
|
||||
public ServicesAreNullError(string message)
|
||||
: base(message, OcelotErrorCode.ServicesAreNullError)
|
||||
: base(message, OcelotErrorCode.ServicesAreNullError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class UnableToFindLoadBalancerError : Errors.Error
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class UnableToFindLoadBalancerError : Errors.Error
|
||||
{
|
||||
public UnableToFindLoadBalancerError(string message)
|
||||
: base(message, OcelotErrorCode.UnableToFindLoadBalancerError)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base(message, OcelotErrorCode.UnableToFindLoadBalancerError, 404)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,68 +1,78 @@
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class LoadBalancingMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||
|
||||
public LoadBalancingMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
ILoadBalancerHouse loadBalancerHouse)
|
||||
: base(loggerFactory.CreateLogger<LoadBalancingMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_loadBalancerHouse = loadBalancerHouse;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
public class LoadBalancingMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||
|
||||
public LoadBalancingMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
ILoadBalancerHouse loadBalancerHouse)
|
||||
: base(loggerFactory.CreateLogger<LoadBalancingMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_loadBalancerHouse = loadBalancerHouse;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var loadBalancer = _loadBalancerHouse.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration);
|
||||
if (loadBalancer.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, loadBalancer.Errors);
|
||||
return;
|
||||
var internalConfiguration = httpContext.Items.IInternalConfiguration();
|
||||
|
||||
var loadBalancer = _loadBalancerHouse.Get(downstreamReRoute, internalConfiguration.ServiceProviderConfiguration);
|
||||
|
||||
if (loadBalancer.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
||||
httpContext.Items.UpsertErrors(loadBalancer.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostAndPort = await loadBalancer.Data.Lease(httpContext);
|
||||
if (hostAndPort.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
||||
httpContext.Items.UpsertErrors(hostAndPort.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostAndPort = await loadBalancer.Data.Lease(context);
|
||||
if (hostAndPort.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, hostAndPort.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
|
||||
{
|
||||
context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogDebug("Exception calling next middleware, exception will be thrown to global handler");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadBalancer.Data.Release(hostAndPort.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var downstreamRequest = httpContext.Items.DownstreamRequest();
|
||||
|
||||
//todo check downstreamRequest is ok
|
||||
downstreamRequest.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
downstreamRequest.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
|
||||
{
|
||||
downstreamRequest.Scheme = hostAndPort.Data.Scheme;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogDebug("Exception calling next middleware, exception will be thrown to global handler");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadBalancer.Data.Release(hostAndPort.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
public static class LoadBalancingMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseLoadBalancingMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<LoadBalancingMiddleware>();
|
||||
}
|
||||
}
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class LoadBalancingMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<LoadBalancingMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DiagnosticAdapter;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DiagnosticAdapter;
|
||||
using System;
|
||||
|
||||
public class OcelotDiagnosticListener
|
||||
{
|
||||
private readonly IOcelotLogger _logger;
|
||||
@ -17,27 +16,6 @@ namespace Ocelot.Logging
|
||||
_tracer = serviceProvider.GetService<ITracer>();
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareException")]
|
||||
public virtual void OcelotMiddlewareException(Exception exception, DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"Ocelot.MiddlewareException: {name}; {exception.Message};");
|
||||
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareStarted")]
|
||||
public virtual void OcelotMiddlewareStarted(DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareFinished")]
|
||||
public virtual void OcelotMiddlewareFinished(DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"Ocelot.MiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
|
||||
Event(context.HttpContext, $"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
|
||||
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
|
||||
{
|
||||
|
@ -1,23 +1,23 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public class BaseUrlFinder : IBaseUrlFinder
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public BaseUrlFinder(IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public string Find()
|
||||
{
|
||||
//tries to get base url out of file...
|
||||
var baseUrl = _config.GetValue("GlobalConfiguration:BaseUrl", "");
|
||||
|
||||
//falls back to memory config then finally default..
|
||||
return string.IsNullOrEmpty(baseUrl) ? _config.GetValue("BaseUrl", "http://localhost:5000") : baseUrl;
|
||||
}
|
||||
}
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public class BaseUrlFinder : IBaseUrlFinder
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public BaseUrlFinder(IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public string Find()
|
||||
{
|
||||
//tries to get base url out of file...
|
||||
var baseUrl = _config.GetValue("GlobalConfiguration:BaseUrl", "");
|
||||
|
||||
//falls back to memory config then finally default..
|
||||
return string.IsNullOrEmpty(baseUrl) ? _config.GetValue("BaseUrl", "http://localhost:5000") : baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
src/Ocelot/Middleware/ConfigurationMiddleware.cs
Normal file
37
src/Ocelot/Middleware/ConfigurationMiddleware.cs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Errors.Middleware;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Logging;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class ConfigurationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IInternalConfigurationRepository _configRepo;
|
||||
|
||||
public ConfigurationMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, IInternalConfigurationRepository configRepo)
|
||||
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_configRepo = configRepo;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
//todo check the config is actually ok?
|
||||
var config = _configRepo.Get();
|
||||
|
||||
if(config.IsError)
|
||||
{
|
||||
throw new System.Exception("OOOOPS this should not happen raise an issue in GitHub");
|
||||
}
|
||||
|
||||
httpContext.Items.SetIInternalConfiguration(config.Data);
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Request.Middleware;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public class DownstreamContext
|
||||
{
|
||||
public DownstreamContext(HttpContext httpContext)
|
||||
{
|
||||
HttpContext = httpContext;
|
||||
Errors = new List<Error>();
|
||||
}
|
||||
|
||||
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
|
||||
|
||||
public HttpContext HttpContext { get; }
|
||||
|
||||
public DownstreamReRoute DownstreamReRoute { get; set; }
|
||||
|
||||
public DownstreamRequest DownstreamRequest { get; set; }
|
||||
|
||||
public DownstreamResponse DownstreamResponse { get; set; }
|
||||
|
||||
public List<Error> Errors { get; }
|
||||
|
||||
public IInternalConfiguration Configuration { get; set; }
|
||||
|
||||
public bool IsError => Errors.Count > 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class DownstreamContextMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseDownstreamContextMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ConfigurationMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
117
src/Ocelot/Middleware/HttpItemsExtensions.cs
Normal file
117
src/Ocelot/Middleware/HttpItemsExtensions.cs
Normal file
@ -0,0 +1,117 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Request.Middleware;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class HttpItemsExtensions
|
||||
{
|
||||
public static void UpsertDownstreamRequest(this IDictionary<object, object> input, DownstreamRequest downstreamRequest)
|
||||
{
|
||||
input.Upsert("DownstreamRequest", downstreamRequest);
|
||||
}
|
||||
|
||||
public static void UpsertDownstreamResponse(this IDictionary<object, object> input, DownstreamResponse downstreamResponse)
|
||||
{
|
||||
input.Upsert("DownstreamResponse", downstreamResponse);
|
||||
}
|
||||
|
||||
public static void UpsertDownstreamReRoute(this IDictionary<object, object> input, DownstreamReRoute downstreamReRoute)
|
||||
{
|
||||
input.Upsert("DownstreamReRoute", downstreamReRoute);
|
||||
}
|
||||
|
||||
public static void UpsertTemplatePlaceholderNameAndValues(this IDictionary<object, object> input, List<PlaceholderNameAndValue> tPNV)
|
||||
{
|
||||
input.Upsert("TemplatePlaceholderNameAndValues", tPNV);
|
||||
}
|
||||
|
||||
public static void UpsertDownstreamRoute(this IDictionary<object, object> input, DownstreamRoute downstreamRoute)
|
||||
{
|
||||
input.Upsert("DownstreamRoute", downstreamRoute);
|
||||
}
|
||||
|
||||
public static void UpsertErrors(this IDictionary<object, object> input, List<Error> errors)
|
||||
{
|
||||
input.Upsert("Errors", errors);
|
||||
}
|
||||
|
||||
public static void SetError(this IDictionary<object, object> input, Error error)
|
||||
{
|
||||
var errors = new List<Error>() { error };
|
||||
input.Upsert("Errors", errors);
|
||||
}
|
||||
|
||||
public static void SetIInternalConfiguration(this IDictionary<object, object> input, IInternalConfiguration config)
|
||||
{
|
||||
input.Upsert("IInternalConfiguration", config);
|
||||
}
|
||||
|
||||
public static IInternalConfiguration IInternalConfiguration(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<IInternalConfiguration>("IInternalConfiguration");
|
||||
}
|
||||
|
||||
public static List<Error> Errors(this IDictionary<object, object> input)
|
||||
{
|
||||
var errors = input.Get<List<Error>>("Errors");
|
||||
return errors == null ? new List<Error>() : errors;
|
||||
}
|
||||
|
||||
public static DownstreamRoute DownstreamRoute(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<DownstreamRoute>("DownstreamRoute");
|
||||
}
|
||||
|
||||
public static List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<List<PlaceholderNameAndValue>>("TemplatePlaceholderNameAndValues");
|
||||
}
|
||||
|
||||
public static DownstreamRequest DownstreamRequest(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<DownstreamRequest>("DownstreamRequest");
|
||||
}
|
||||
|
||||
public static DownstreamResponse DownstreamResponse(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<DownstreamResponse>("DownstreamResponse");
|
||||
}
|
||||
|
||||
public static DownstreamReRoute DownstreamReRoute(this IDictionary<object, object> input)
|
||||
{
|
||||
return input.Get<DownstreamReRoute>("DownstreamReRoute");
|
||||
}
|
||||
|
||||
private static T Get<T>(this IDictionary<object, object> input, string key)
|
||||
{
|
||||
if (input.TryGetValue(key, out var value))
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
private static void Upsert<T>(this IDictionary<object, object> input, string key, T value)
|
||||
{
|
||||
if (input.DoesntExist(key))
|
||||
{
|
||||
input.Add(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Remove(key);
|
||||
input.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DoesntExist(this IDictionary<object, object> input, string key)
|
||||
{
|
||||
return !input.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
{
|
||||
public interface IDefinedAggregator
|
||||
{
|
||||
Task<DownstreamResponse> Aggregate(List<DownstreamContext> responses);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using Ocelot.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
{
|
||||
public interface IMultiplexer
|
||||
{
|
||||
Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using Ocelot.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
{
|
||||
public interface IResponseAggregator
|
||||
{
|
||||
Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamResponses);
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
{
|
||||
public class Multiplexer : IMultiplexer
|
||||
{
|
||||
private readonly IResponseAggregatorFactory _factory;
|
||||
|
||||
public Multiplexer(IResponseAggregatorFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next)
|
||||
{
|
||||
var reRouteKeysConfigs = reRoute.DownstreamReRouteConfig;
|
||||
if (reRouteKeysConfigs == null || !reRouteKeysConfigs.Any())
|
||||
{
|
||||
var tasks = new Task<DownstreamContext>[reRoute.DownstreamReRoute.Count];
|
||||
|
||||
for (var i = 0; i < reRoute.DownstreamReRoute.Count; i++)
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
|
||||
Configuration = context.Configuration,
|
||||
DownstreamReRoute = reRoute.DownstreamReRoute[i],
|
||||
};
|
||||
|
||||
tasks[i] = Fire(downstreamContext, next);
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
var contexts = new List<DownstreamContext>();
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
var finished = await task;
|
||||
contexts.Add(finished);
|
||||
}
|
||||
|
||||
await Map(reRoute, context, contexts);
|
||||
}
|
||||
else
|
||||
{
|
||||
var downstreamContextMain = new DownstreamContext(context.HttpContext)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
|
||||
Configuration = context.Configuration,
|
||||
DownstreamReRoute = reRoute.DownstreamReRoute[0],
|
||||
};
|
||||
var mainResponse = await Fire(downstreamContextMain, next);
|
||||
|
||||
if (reRoute.DownstreamReRoute.Count == 1)
|
||||
{
|
||||
MapNotAggregate(context, new List<DownstreamContext>() { mainResponse });
|
||||
return;
|
||||
}
|
||||
|
||||
var tasks = new List<Task<DownstreamContext>>();
|
||||
if (mainResponse.DownstreamResponse == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var content = await mainResponse.DownstreamResponse.Content.ReadAsStringAsync();
|
||||
var jObject = Newtonsoft.Json.Linq.JToken.Parse(content);
|
||||
|
||||
for (var i = 1; i < reRoute.DownstreamReRoute.Count; i++)
|
||||
{
|
||||
var templatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues;
|
||||
var downstreamReRoute = reRoute.DownstreamReRoute[i];
|
||||
var matchAdvancedAgg = reRouteKeysConfigs.FirstOrDefault(q => q.ReRouteKey == downstreamReRoute.Key);
|
||||
if (matchAdvancedAgg != null)
|
||||
{
|
||||
var values = jObject.SelectTokens(matchAdvancedAgg.JsonPath).Select(s => s.ToString()).Distinct().ToList();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
|
||||
Configuration = context.Configuration,
|
||||
DownstreamReRoute = downstreamReRoute,
|
||||
};
|
||||
downstreamContext.TemplatePlaceholderNameAndValues.Add(new PlaceholderNameAndValue("{" + matchAdvancedAgg.Parameter + "}", value.ToString()));
|
||||
tasks.Add(Fire(downstreamContext, next));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
|
||||
Configuration = context.Configuration,
|
||||
DownstreamReRoute = downstreamReRoute,
|
||||
};
|
||||
tasks.Add(Fire(downstreamContext, next));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
var contexts = new List<DownstreamContext>() { mainResponse };
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
var finished = await task;
|
||||
contexts.Add(finished);
|
||||
}
|
||||
|
||||
await Map(reRoute, context, contexts);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Map(ReRoute reRoute, DownstreamContext context, List<DownstreamContext> contexts)
|
||||
{
|
||||
if (reRoute.DownstreamReRoute.Count > 1)
|
||||
{
|
||||
var aggregator = _factory.Get(reRoute);
|
||||
await aggregator.Aggregate(reRoute, context, contexts);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapNotAggregate(context, contexts);
|
||||
}
|
||||
}
|
||||
|
||||
private void MapNotAggregate(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||
{
|
||||
//assume at least one..if this errors then it will be caught by global exception handler
|
||||
var finished = downstreamContexts.First();
|
||||
|
||||
originalContext.Errors.AddRange(finished.Errors);
|
||||
|
||||
originalContext.DownstreamRequest = finished.DownstreamRequest;
|
||||
|
||||
originalContext.DownstreamResponse = finished.DownstreamResponse;
|
||||
}
|
||||
|
||||
private async Task<DownstreamContext> Fire(DownstreamContext context, OcelotRequestDelegate next)
|
||||
{
|
||||
await next.Invoke(context);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,16 @@
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Logging;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public abstract class OcelotMiddleware
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Ocelot.Logging;
|
||||
|
||||
public abstract class OcelotMiddleware
|
||||
{
|
||||
protected OcelotMiddleware(IOcelotLogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
MiddlewareName = this.GetType().Name;
|
||||
}
|
||||
|
||||
public IOcelotLogger Logger { get; }
|
||||
|
||||
public string MiddlewareName { get; }
|
||||
|
||||
public void SetPipelineError(DownstreamContext context, List<Error> errors)
|
||||
{
|
||||
foreach (var error in errors)
|
||||
{
|
||||
SetPipelineError(context, error);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPipelineError(DownstreamContext context, Error error)
|
||||
{
|
||||
Logger.LogWarning(error.Message);
|
||||
context.Errors.Add(error);
|
||||
}
|
||||
}
|
||||
protected OcelotMiddleware(IOcelotLogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
MiddlewareName = GetType().Name;
|
||||
}
|
||||
|
||||
public IOcelotLogger Logger { get; }
|
||||
public string MiddlewareName { get; }
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -11,7 +11,6 @@
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
using Ocelot.Responses;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@ -42,37 +41,25 @@
|
||||
return CreateOcelotPipeline(builder, pipelineConfiguration);
|
||||
}
|
||||
|
||||
public static Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction)
|
||||
public static Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction)
|
||||
=> UseOcelot(app, builderAction, new OcelotPipelineConfiguration());
|
||||
|
||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
|
||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
|
||||
{
|
||||
await CreateConfiguration(app); // initConfiguration
|
||||
await CreateConfiguration(app);
|
||||
|
||||
ConfigureDiagnosticListener(app);
|
||||
|
||||
var ocelotPipelineBuilder = new OcelotPipelineBuilder(app.ApplicationServices);
|
||||
builderAction?.Invoke(ocelotPipelineBuilder, configuration ?? new OcelotPipelineConfiguration());
|
||||
builderAction?.Invoke(app, configuration ?? new OcelotPipelineConfiguration());
|
||||
|
||||
var ocelotDelegate = ocelotPipelineBuilder.Build();
|
||||
app.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
|
||||
|
||||
app.Use(async (context, task) =>
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context);
|
||||
await ocelotDelegate.Invoke(downstreamContext);
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
|
||||
{
|
||||
var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);
|
||||
|
||||
pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);
|
||||
|
||||
var firstDelegate = pipelineBuilder.Build();
|
||||
builder.BuildOcelotPipeline(pipelineConfiguration);
|
||||
|
||||
/*
|
||||
inject first delegate into first piece of asp.net middleware..maybe not like this
|
||||
@ -82,12 +69,6 @@
|
||||
|
||||
builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
|
||||
|
||||
builder.Use(async (context, task) =>
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context);
|
||||
await firstDelegate.Invoke(downstreamContext);
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
public class OcelotPipelineConfiguration
|
||||
{
|
||||
@ -12,38 +13,64 @@
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>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.</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is to allow the user to run any extra authentication before the Ocelot authentication
|
||||
/// kicks in
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>This is to allow the user to run any extra authentication before the Ocelot authentication
|
||||
/// kicks in</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This allows the user to completely override the ocelot authentication middleware
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>This allows the user to completely override the ocelot authentication middleware</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is to allow the user to run any extra authorisation before the Ocelot authentication
|
||||
/// kicks in
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>This is to allow the user to run any extra authorisation before the Ocelot authentication
|
||||
/// kicks in</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This allows the user to completely override the ocelot authorisation middleware
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>This allows the user to completely override the ocelot authorisation middleware</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This allows the user to implement there own query string manipulation logic
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
||||
/// <value>
|
||||
/// <placeholder>This allows the user to implement there own query string manipulation logic</placeholder>
|
||||
/// </value>
|
||||
public Func<HttpContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is an extension that will branch to different pipes
|
||||
/// </summary>
|
||||
public List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>> MapWhenOcelotPipeline { get; } = new List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>>();
|
||||
/// <value>
|
||||
/// <placeholder>This is an extension that will branch to different pipes</placeholder>
|
||||
/// </value>
|
||||
// todo fix this data structure
|
||||
public Dictionary<Func<HttpContext, bool>, Action<IApplicationBuilder>> MapWhenOcelotPipeline { get; } = new Dictionary<Func<HttpContext, bool>, Action<IApplicationBuilder>>();
|
||||
}
|
||||
}
|
||||
|
@ -1,101 +1,112 @@
|
||||
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.PathManipulation.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;
|
||||
using Ocelot.Security.Middleware;
|
||||
using Ocelot.WebSockets.Middleware;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
namespace Ocelot.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;
|
||||
using Ocelot.Security.Middleware;
|
||||
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 System;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.DownstreamPathManipulation.Middleware;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.WebSockets.Middleware;
|
||||
using Ocelot.Multiplexer;
|
||||
|
||||
public static class OcelotPipelineExtensions
|
||||
{
|
||||
public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
|
||||
public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app,
|
||||
OcelotPipelineConfiguration pipelineConfiguration)
|
||||
{
|
||||
// this sets up the downstream context and gets the config
|
||||
app.UseDownstreamContextMiddleware();
|
||||
|
||||
// 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();
|
||||
app.UseExceptionHandlerMiddleware();
|
||||
|
||||
// If the request is for websockets upgrade we fork into a different pipeline
|
||||
builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
|
||||
app =>
|
||||
app.MapWhen(httpContext => httpContext.WebSockets.IsWebSocketRequest,
|
||||
wenSocketsApp =>
|
||||
{
|
||||
app.UseDownstreamRouteFinderMiddleware();
|
||||
app.UseDownstreamRequestInitialiser();
|
||||
app.UseLoadBalancingMiddleware();
|
||||
app.UseDownstreamUrlCreatorMiddleware();
|
||||
app.UseWebSocketsProxyMiddleware();
|
||||
wenSocketsApp.UseDownstreamRouteFinderMiddleware();
|
||||
wenSocketsApp.UseMultiplexingMiddleware();
|
||||
wenSocketsApp.UseDownstreamRequestInitialiser();
|
||||
wenSocketsApp.UseLoadBalancingMiddleware();
|
||||
wenSocketsApp.UseDownstreamUrlCreatorMiddleware();
|
||||
wenSocketsApp.UseWebSocketsProxyMiddleware();
|
||||
});
|
||||
|
||||
// Allow the user to respond with absolutely anything they want.
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
|
||||
app.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
|
||||
|
||||
// This is registered first so it can catch any errors and issue an appropriate response
|
||||
builder.UseResponderMiddleware();
|
||||
app.UseResponderMiddleware();
|
||||
|
||||
// Then we get the downstream route information
|
||||
builder.UseDownstreamRouteFinderMiddleware();
|
||||
app.UseDownstreamRouteFinderMiddleware();
|
||||
|
||||
// Multiplex the request if required
|
||||
app.UseMultiplexingMiddleware();
|
||||
|
||||
// This security module, IP whitelist blacklist, extended security mechanism
|
||||
builder.UseSecurityMiddleware();
|
||||
app.UseSecurityMiddleware();
|
||||
|
||||
//Expand other branch pipes
|
||||
if (pipelineConfiguration.MapWhenOcelotPipeline != null)
|
||||
{
|
||||
foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline)
|
||||
{
|
||||
builder.MapWhen(pipeline);
|
||||
// todo why is this asking for an app app?
|
||||
app.MapWhen(pipeline.Key, pipeline.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have the ds route we can transform headers and stuff?
|
||||
builder.UseHttpHeadersTransformationMiddleware();
|
||||
app.UseHttpHeadersTransformationMiddleware();
|
||||
|
||||
// Initialises downstream request
|
||||
builder.UseDownstreamRequestInitialiser();
|
||||
app.UseDownstreamRequestInitialiser();
|
||||
|
||||
// We check whether the request is ratelimit, and if there is no continue processing
|
||||
builder.UseRateLimiting();
|
||||
app.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();
|
||||
app.UseRequestIdMiddleware();
|
||||
|
||||
// Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);
|
||||
app.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();
|
||||
app.UseAuthenticationMiddleware();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Use(pipelineConfiguration.AuthenticationMiddleware);
|
||||
app.Use(pipelineConfiguration.AuthenticationMiddleware);
|
||||
}
|
||||
|
||||
// The next thing we do is look at any claims transforms in case this is important for authorisation
|
||||
builder.UseClaimsToClaimsMiddleware();
|
||||
app.UseClaimsToClaimsMiddleware();
|
||||
|
||||
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
||||
app.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
||||
|
||||
// Now we have authenticated and done any claims transformation we
|
||||
// can authorise the request
|
||||
@ -103,42 +114,42 @@ namespace Ocelot.Middleware.Pipeline
|
||||
// user wants
|
||||
if (pipelineConfiguration.AuthorisationMiddleware == null)
|
||||
{
|
||||
builder.UseAuthorisationMiddleware();
|
||||
app.UseAuthorisationMiddleware();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Use(pipelineConfiguration.AuthorisationMiddleware);
|
||||
app.Use(pipelineConfiguration.AuthorisationMiddleware);
|
||||
}
|
||||
|
||||
// Now we can run the claims to headers transformation middleware
|
||||
builder.UseClaimsToHeadersMiddleware();
|
||||
app.UseClaimsToHeadersMiddleware();
|
||||
|
||||
// Allow the user to implement their own query string manipulation logic
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
||||
app.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
||||
|
||||
// Now we can run any claims to query string transformation middleware
|
||||
builder.UseClaimsToQueryStringMiddleware();
|
||||
app.UseClaimsToQueryStringMiddleware();
|
||||
|
||||
builder.UseClaimsToDownstreamPathMiddleware();
|
||||
app.UseClaimsToDownstreamPathMiddleware();
|
||||
|
||||
// Get the load balancer for this request
|
||||
builder.UseLoadBalancingMiddleware();
|
||||
app.UseLoadBalancingMiddleware();
|
||||
|
||||
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
|
||||
builder.UseDownstreamUrlCreatorMiddleware();
|
||||
app.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();
|
||||
app.UseOutputCacheMiddleware();
|
||||
|
||||
//We fire off the request and set the response on the scoped data repo
|
||||
builder.UseHttpRequesterMiddleware();
|
||||
app.UseHttpRequesterMiddleware();
|
||||
|
||||
return builder.Build();
|
||||
return app.Build();
|
||||
}
|
||||
|
||||
private static void UseIfNotNull(this IOcelotPipelineBuilder builder,
|
||||
Func<DownstreamContext, Func<Task>, Task> middleware)
|
||||
private static void UseIfNotNull(this IApplicationBuilder builder,
|
||||
Func<HttpContext, Func<Task>, Task> middleware)
|
||||
{
|
||||
if (middleware != null)
|
||||
{
|
@ -1,6 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public delegate Task OcelotRequestDelegate(DownstreamContext downstreamContext);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// 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; }
|
||||
|
||||
IOcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware);
|
||||
|
||||
OcelotRequestDelegate Build();
|
||||
|
||||
IOcelotPipelineBuilder New();
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
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.
|
@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
{
|
||||
public class MapWhenMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly MapWhenOptions _options;
|
||||
|
||||
public MapWhenMiddleware(OcelotRequestDelegate next, MapWhenOptions options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (_options.Predicate(context))
|
||||
{
|
||||
await _options.Branch(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
{
|
||||
public class MapWhenOptions
|
||||
{
|
||||
private Func<DownstreamContext, bool> _predicate;
|
||||
|
||||
public Func<DownstreamContext, bool> Predicate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _predicate;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_predicate = value;
|
||||
}
|
||||
}
|
||||
|
||||
public OcelotRequestDelegate Branch { get; set; }
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
// 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<Func<OcelotRequestDelegate, OcelotRequestDelegate>> _middlewares;
|
||||
|
||||
public OcelotPipelineBuilder(IServiceProvider provider)
|
||||
{
|
||||
ApplicationServices = provider;
|
||||
_middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
|
||||
}
|
||||
|
||||
public OcelotPipelineBuilder(IOcelotPipelineBuilder builder)
|
||||
{
|
||||
ApplicationServices = builder.ApplicationServices;
|
||||
_middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
|
||||
}
|
||||
|
||||
public IServiceProvider ApplicationServices { get; }
|
||||
|
||||
public IOcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> 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;
|
||||
}
|
||||
|
||||
public IOcelotPipelineBuilder New()
|
||||
{
|
||||
return new OcelotPipelineBuilder(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
// 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 Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
{
|
||||
using Predicate = Func<DownstreamContext, bool>;
|
||||
|
||||
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<TMiddleware>(this IOcelotPipelineBuilder app, params object[] args)
|
||||
{
|
||||
return app.UseMiddleware(typeof(TMiddleware), args);
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder Use(this IOcelotPipelineBuilder app, Func<DownstreamContext, Func<Task>, Task> middleware)
|
||||
{
|
||||
return app.Use(next =>
|
||||
{
|
||||
return context =>
|
||||
{
|
||||
Func<Task> 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)
|
||||
{
|
||||
var ocelotDelegate = (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
|
||||
var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener));
|
||||
var middlewareName = ocelotDelegate.Target.GetType().Name;
|
||||
|
||||
OcelotRequestDelegate wrapped = async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context);
|
||||
await ocelotDelegate(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteException(diagnosticListener, ex, "Ocelot.MiddlewareException", middlewareName, context);
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareFinished", middlewareName, context);
|
||||
}
|
||||
};
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
var factory = Compile<object>(methodinfo, parameters);
|
||||
|
||||
return context =>
|
||||
{
|
||||
var serviceProvider = context.HttpContext.RequestServices ?? app.ApplicationServices;
|
||||
if (serviceProvider == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return factory(instance, context, serviceProvider);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static void Write(DiagnosticListener diagnosticListener, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if (diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context });
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteException(DiagnosticListener diagnosticListener, Exception exception, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if (diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context, exception = exception });
|
||||
}
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Predicate predicate, Action<IOcelotPipelineBuilder> configuration)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
if (predicate == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(predicate));
|
||||
}
|
||||
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
var branchBuilder = app.New();
|
||||
configuration(branchBuilder);
|
||||
var branch = branchBuilder.Build();
|
||||
|
||||
var options = new MapWhenOptions
|
||||
{
|
||||
Predicate = predicate,
|
||||
Branch = branch,
|
||||
};
|
||||
return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Func<IOcelotPipelineBuilder, Predicate> pipelineBuilderFunc)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
if (pipelineBuilderFunc == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(pipelineBuilderFunc));
|
||||
}
|
||||
|
||||
var branchBuilder = app.New();
|
||||
var predicate = pipelineBuilderFunc.Invoke(branchBuilder);
|
||||
var branch = branchBuilder.Build();
|
||||
|
||||
var options = new MapWhenOptions
|
||||
{
|
||||
Predicate = predicate,
|
||||
Branch = branch
|
||||
};
|
||||
return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
|
||||
}
|
||||
|
||||
private static Func<T, DownstreamContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
|
||||
{
|
||||
var middleware = typeof(T);
|
||||
var httpContextArg = Expression.Parameter(typeof(DownstreamContext), "downstreamContext");
|
||||
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))
|
||||
};
|
||||
|
||||
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<Func<T, DownstreamContext, IServiceProvider, Task>>(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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
public class UnauthenticatedError : Error
|
||||
{
|
||||
public UnauthenticatedError(string message) : base(message, OcelotErrorCode.UnauthenticatedError)
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Ocelot.Errors;
|
||||
|
||||
public class UnauthenticatedError : Error
|
||||
{
|
||||
public UnauthenticatedError(string message)
|
||||
: base(message, OcelotErrorCode.UnauthenticatedError, 401)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
public class CouldNotFindAggregatorError : Error
|
||||
{
|
||||
public CouldNotFindAggregatorError(string aggregator)
|
||||
: base($"Could not find Aggregator: {aggregator}", OcelotErrorCode.CouldNotFindAggregatorError)
|
||||
: base($"Could not find Aggregator: {aggregator}", OcelotErrorCode.CouldNotFindAggregatorError, 404)
|
||||
{
|
||||
}
|
||||
}
|
12
src/Ocelot/Multiplexer/IDefinedAggregator.cs
Normal file
12
src/Ocelot/Multiplexer/IDefinedAggregator.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface IDefinedAggregator
|
||||
{
|
||||
Task<DownstreamResponse> Aggregate(List<HttpContext> responses);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
public interface IDefinedAggregatorProvider
|
||||
{
|
12
src/Ocelot/Multiplexer/IResponseAggregator.cs
Normal file
12
src/Ocelot/Multiplexer/IResponseAggregator.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface IResponseAggregator
|
||||
{
|
||||
Task Aggregate(ReRoute reRoute, HttpContext originalContext, List<HttpContext> downstreamResponses);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
|
||||
public interface IResponseAggregatorFactory
|
||||
{
|
||||
IResponseAggregator Get(ReRoute reRoute);
|
@ -1,6 +1,6 @@
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
public class InMemoryResponseAggregatorFactory : IResponseAggregatorFactory
|
||||
{
|
221
src/Ocelot/Multiplexer/MultiplexingMiddleware.cs
Normal file
221
src/Ocelot/Multiplexer/MultiplexingMiddleware.cs
Normal file
@ -0,0 +1,221 @@
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class MultiplexingMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IResponseAggregatorFactory _factory;
|
||||
|
||||
public MultiplexingMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IResponseAggregatorFactory factory
|
||||
)
|
||||
: base(loggerFactory.CreateLogger<MultiplexingMiddleware>())
|
||||
{
|
||||
_factory = factory;
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
if (httpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
//todo this is obviously stupid
|
||||
httpContext.Items.UpsertDownstreamReRoute(httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[0]);
|
||||
await _next.Invoke(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var reRouteKeysConfigs = httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRouteConfig;
|
||||
if (reRouteKeysConfigs == null || !reRouteKeysConfigs.Any())
|
||||
{
|
||||
var downstreamRoute = httpContext.Items.DownstreamRoute();
|
||||
|
||||
var tasks = new Task<HttpContext>[downstreamRoute.ReRoute.DownstreamReRoute.Count];
|
||||
|
||||
for (var i = 0; i < downstreamRoute.ReRoute.DownstreamReRoute.Count; i++)
|
||||
{
|
||||
var newHttpContext = Copy(httpContext);
|
||||
|
||||
newHttpContext.Items
|
||||
.Add("RequestId", httpContext.Items["RequestId"]);
|
||||
newHttpContext.Items
|
||||
.SetIInternalConfiguration(httpContext.Items.IInternalConfiguration());
|
||||
newHttpContext.Items
|
||||
.UpsertTemplatePlaceholderNameAndValues(httpContext.Items.TemplatePlaceholderNameAndValues());
|
||||
newHttpContext.Items
|
||||
.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[i]);
|
||||
|
||||
tasks[i] = Fire(newHttpContext, _next);
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
var contexts = new List<HttpContext>();
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
var finished = await task;
|
||||
contexts.Add(finished);
|
||||
}
|
||||
|
||||
await Map(httpContext, downstreamRoute.ReRoute, contexts);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpContext.Items.UpsertDownstreamReRoute(httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[0]);
|
||||
var mainResponse = await Fire(httpContext, _next);
|
||||
|
||||
if (httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute.Count == 1)
|
||||
{
|
||||
MapNotAggregate(httpContext, new List<HttpContext>() { mainResponse });
|
||||
return;
|
||||
}
|
||||
|
||||
var tasks = new List<Task<HttpContext>>();
|
||||
|
||||
if (mainResponse.Items.DownstreamResponse() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var content = await mainResponse.Items.DownstreamResponse().Content.ReadAsStringAsync();
|
||||
|
||||
var jObject = Newtonsoft.Json.Linq.JToken.Parse(content);
|
||||
|
||||
for (var i = 1; i < httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute.Count; i++)
|
||||
{
|
||||
var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
|
||||
|
||||
var downstreamReRoute = httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[i];
|
||||
|
||||
var matchAdvancedAgg = reRouteKeysConfigs
|
||||
.FirstOrDefault(q => q.ReRouteKey == downstreamReRoute.Key);
|
||||
|
||||
if (matchAdvancedAgg != null)
|
||||
{
|
||||
var values = jObject.SelectTokens(matchAdvancedAgg.JsonPath).Select(s => s.ToString()).Distinct().ToList();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
var newHttpContext = Copy(httpContext);
|
||||
|
||||
var tPNV = httpContext.Items.TemplatePlaceholderNameAndValues();
|
||||
tPNV.Add(new PlaceholderNameAndValue("{" + matchAdvancedAgg.Parameter + "}", value.ToString()));
|
||||
|
||||
newHttpContext.Items
|
||||
.Add("RequestId", httpContext.Items["RequestId"]);
|
||||
|
||||
newHttpContext.Items
|
||||
.SetIInternalConfiguration(httpContext.Items.IInternalConfiguration());
|
||||
|
||||
newHttpContext.Items
|
||||
.UpsertTemplatePlaceholderNameAndValues(tPNV);
|
||||
|
||||
newHttpContext.Items
|
||||
.UpsertDownstreamReRoute(downstreamReRoute);
|
||||
|
||||
tasks.Add(Fire(newHttpContext, _next));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newHttpContext = Copy(httpContext);
|
||||
|
||||
newHttpContext.Items
|
||||
.Add("RequestId", httpContext.Items["RequestId"]);
|
||||
|
||||
newHttpContext.Items
|
||||
.SetIInternalConfiguration(httpContext.Items.IInternalConfiguration());
|
||||
|
||||
newHttpContext.Items
|
||||
.UpsertTemplatePlaceholderNameAndValues(templatePlaceholderNameAndValues);
|
||||
|
||||
newHttpContext.Items
|
||||
.UpsertDownstreamReRoute(downstreamReRoute);
|
||||
|
||||
tasks.Add(Fire(newHttpContext, _next));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
var contexts = new List<HttpContext>() { mainResponse };
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
var finished = await task;
|
||||
contexts.Add(finished);
|
||||
}
|
||||
|
||||
await Map(httpContext, httpContext.Items.DownstreamRoute().ReRoute, contexts);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpContext Copy(HttpContext source)
|
||||
{
|
||||
var target = new DefaultHttpContext();
|
||||
|
||||
foreach (var header in source.Request.Headers)
|
||||
{
|
||||
target.Request.Headers.TryAdd(header.Key, header.Value);
|
||||
}
|
||||
|
||||
target.Request.Body = source.Request.Body;
|
||||
target.Request.ContentLength = source.Request.ContentLength;
|
||||
target.Request.ContentType = source.Request.ContentType;
|
||||
target.Request.Host = source.Request.Host;
|
||||
target.Request.Method = source.Request.Method;
|
||||
target.Request.Path = source.Request.Path;
|
||||
target.Request.PathBase = source.Request.PathBase;
|
||||
target.Request.Protocol = source.Request.Protocol;
|
||||
target.Request.Query = source.Request.Query;
|
||||
target.Request.QueryString = source.Request.QueryString;
|
||||
target.Request.Scheme = source.Request.Scheme;
|
||||
target.Request.IsHttps = source.Request.IsHttps;
|
||||
target.Request.RouteValues = source.Request.RouteValues;
|
||||
target.Connection.RemoteIpAddress = source.Connection.RemoteIpAddress;
|
||||
target.RequestServices = source.RequestServices;
|
||||
return target;
|
||||
}
|
||||
|
||||
private async Task Map(HttpContext httpContext, ReRoute reRoute, List<HttpContext> contexts)
|
||||
{
|
||||
if (reRoute.DownstreamReRoute.Count > 1)
|
||||
{
|
||||
var aggregator = _factory.Get(reRoute);
|
||||
await aggregator.Aggregate(reRoute, httpContext, contexts);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapNotAggregate(httpContext, contexts);
|
||||
}
|
||||
}
|
||||
|
||||
private void MapNotAggregate(HttpContext httpContext, List<HttpContext> downstreamContexts)
|
||||
{
|
||||
//assume at least one..if this errors then it will be caught by global exception handler
|
||||
var finished = downstreamContexts.First();
|
||||
|
||||
httpContext.Items.UpsertErrors(finished.Items.Errors());
|
||||
|
||||
httpContext.Items.UpsertDownstreamRequest(finished.Items.DownstreamRequest());
|
||||
|
||||
httpContext.Items.UpsertDownstreamResponse(finished.Items.DownstreamResponse());
|
||||
}
|
||||
|
||||
private async Task<HttpContext> Fire(HttpContext httpContext, RequestDelegate next)
|
||||
{
|
||||
await next.Invoke(httpContext);
|
||||
return httpContext;
|
||||
}
|
||||
}
|
||||
}
|
12
src/Ocelot/Multiplexer/MultiplexingMiddlewareExtensions.cs
Normal file
12
src/Ocelot/Multiplexer/MultiplexingMiddlewareExtensions.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class MultiplexingMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseMultiplexingMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<MultiplexingMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
{
|
||||
public class ServiceLocatorDefinedAggregatorProvider : IDefinedAggregatorProvider
|
||||
{
|
||||
private readonly Dictionary<string, IDefinedAggregator> _aggregators;
|
||||
|
||||
public ServiceLocatorDefinedAggregatorProvider(IServiceProvider services)
|
||||
{
|
||||
_aggregators = services.GetServices<IDefinedAggregator>().ToDictionary(x => x.GetType().Name);
|
||||
}
|
||||
|
||||
public Response<IDefinedAggregator> Get(ReRoute reRoute)
|
||||
{
|
||||
if (_aggregators.ContainsKey(reRoute.Aggregator))
|
||||
{
|
||||
return new OkResponse<IDefinedAggregator>(_aggregators[reRoute.Aggregator]);
|
||||
}
|
||||
|
||||
return new ErrorResponse<IDefinedAggregator>(new CouldNotFindAggregatorError(reRoute.Aggregator));
|
||||
}
|
||||
}
|
||||
}
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
public class ServiceLocatorDefinedAggregatorProvider : IDefinedAggregatorProvider
|
||||
{
|
||||
private readonly Dictionary<string, IDefinedAggregator> _aggregators;
|
||||
|
||||
public ServiceLocatorDefinedAggregatorProvider(IServiceProvider services)
|
||||
{
|
||||
_aggregators = services.GetServices<IDefinedAggregator>().ToDictionary(x => x.GetType().Name);
|
||||
}
|
||||
|
||||
public Response<IDefinedAggregator> Get(ReRoute reRoute)
|
||||
{
|
||||
if (_aggregators.ContainsKey(reRoute.Aggregator))
|
||||
{
|
||||
return new OkResponse<IDefinedAggregator>(_aggregators[reRoute.Aggregator]);
|
||||
}
|
||||
|
||||
return new ErrorResponse<IDefinedAggregator>(new CouldNotFindAggregatorError(reRoute.Aggregator));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using Ocelot.Configuration;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -7,35 +9,35 @@ using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
public class SimpleJsonResponseAggregator : IResponseAggregator
|
||||
{
|
||||
public async Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||
public async Task Aggregate(ReRoute reRoute, HttpContext originalContext, List<HttpContext> downstreamContexts)
|
||||
{
|
||||
await MapAggregateContent(originalContext, downstreamContexts);
|
||||
}
|
||||
|
||||
private static async Task MapAggregateContent(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||
private static async Task MapAggregateContent(HttpContext originalContext, List<HttpContext> downstreamContexts)
|
||||
{
|
||||
var contentBuilder = new StringBuilder();
|
||||
|
||||
contentBuilder.Append("{");
|
||||
|
||||
var responseKeys = downstreamContexts.Select(s => s.DownstreamReRoute.Key).Distinct().ToList();
|
||||
var responseKeys = downstreamContexts.Select(s => s.Items.DownstreamReRoute().Key).Distinct().ToList();
|
||||
|
||||
for (var k = 0; k < responseKeys.Count; k++)
|
||||
{
|
||||
var contexts = downstreamContexts.Where(w => w.DownstreamReRoute.Key == responseKeys[k]).ToList();
|
||||
var contexts = downstreamContexts.Where(w => w.Items.DownstreamReRoute().Key == responseKeys[k]).ToList();
|
||||
if (contexts.Count == 1)
|
||||
{
|
||||
if (contexts[0].IsError)
|
||||
if (contexts[0].Items.Errors().Count > 0)
|
||||
{
|
||||
MapAggregateError(originalContext, contexts[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
var content = await contexts[0].DownstreamResponse.Content.ReadAsStringAsync();
|
||||
var content = await contexts[0].Items.DownstreamResponse().Content.ReadAsStringAsync();
|
||||
contentBuilder.Append($"\"{responseKeys[k]}\":{content}");
|
||||
}
|
||||
else
|
||||
@ -45,13 +47,13 @@ namespace Ocelot.Middleware.Multiplexer
|
||||
|
||||
for (var i = 0; i < contexts.Count; i++)
|
||||
{
|
||||
if (contexts[i].IsError)
|
||||
if (contexts[i].Items.Errors().Count > 0)
|
||||
{
|
||||
MapAggregateError(originalContext, contexts[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
var content = await contexts[i].DownstreamResponse.Content.ReadAsStringAsync();
|
||||
var content = await contexts[i].Items.DownstreamResponse().Content.ReadAsStringAsync();
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
continue;
|
||||
@ -81,13 +83,13 @@ namespace Ocelot.Middleware.Multiplexer
|
||||
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
|
||||
};
|
||||
|
||||
originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "cannot return from aggregate..which reason phrase would you use?");
|
||||
originalContext.Items.UpsertDownstreamResponse(new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "cannot return from aggregate..which reason phrase would you use?"));
|
||||
}
|
||||
|
||||
private static void MapAggregateError(DownstreamContext originalContext, DownstreamContext downstreamContext)
|
||||
private static void MapAggregateError(HttpContext originalContext, HttpContext downstreamContext)
|
||||
{
|
||||
originalContext.Errors.AddRange(downstreamContext.Errors);
|
||||
originalContext.DownstreamResponse = downstreamContext.DownstreamResponse;
|
||||
originalContext.Items.UpsertErrors(downstreamContext.Items.Errors());
|
||||
originalContext.Items.UpsertDownstreamResponse(downstreamContext.Items.DownstreamResponse());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
using Ocelot.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Middleware.Multiplexer
|
||||
namespace Ocelot.Multiplexer
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class UserDefinedResponseAggregator : IResponseAggregator
|
||||
{
|
||||
private readonly IDefinedAggregatorProvider _provider;
|
||||
@ -13,7 +15,7 @@ namespace Ocelot.Middleware.Multiplexer
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public async Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamResponses)
|
||||
public async Task Aggregate(ReRoute reRoute, HttpContext originalContext, List<HttpContext> downstreamResponses)
|
||||
{
|
||||
var aggregator = _provider.Get(reRoute);
|
||||
|
||||
@ -22,11 +24,11 @@ namespace Ocelot.Middleware.Multiplexer
|
||||
var aggregateResponse = await aggregator.Data
|
||||
.Aggregate(downstreamResponses);
|
||||
|
||||
originalContext.DownstreamResponse = aggregateResponse;
|
||||
originalContext.Items.UpsertDownstreamResponse(aggregateResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
originalContext.Errors.AddRange(aggregator.Errors);
|
||||
originalContext.Items.UpsertErrors(aggregator.Errors);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,12 +24,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="8.6.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.1">
|
||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.3">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,42 +1,48 @@
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
|
||||
public class ClaimsToQueryStringMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
||||
public class ClaimsToQueryStringMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
||||
|
||||
public ClaimsToQueryStringMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddQueriesToRequest addQueriesToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToQueryStringMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addQueriesToRequest = addQueriesToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
if (downstreamReRoute.ClaimsToQueries.Any())
|
||||
{
|
||||
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
||||
|
||||
public ClaimsToQueryStringMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddQueriesToRequest addQueriesToRequest)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToQueryStringMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addQueriesToRequest = addQueriesToRequest;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToQueries.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
||||
|
||||
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(context.DownstreamReRoute.ClaimsToQueries, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
var downstreamRequest = httpContext.Items.DownstreamRequest();
|
||||
|
||||
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(downstreamReRoute.ClaimsToQueries, httpContext.User.Claims, downstreamRequest);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
|
||||
|
||||
httpContext.Items.UpsertErrors(response.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
public static class ClaimsToQueryStringMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToQueryStringMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IApplicationBuilder UseClaimsToQueryStringMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToQueryStringMiddleware>();
|
||||
}
|
||||
|
@ -1,149 +1,156 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.RateLimit.Middleware
|
||||
{
|
||||
public class ClientRateLimitMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IRateLimitCounterHandler _counterHandler;
|
||||
private readonly ClientRateLimitProcessor _processor;
|
||||
|
||||
public ClientRateLimitMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRateLimitCounterHandler counterHandler)
|
||||
: base(loggerFactory.CreateLogger<ClientRateLimitMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_counterHandler = counterHandler;
|
||||
_processor = new ClientRateLimitProcessor(counterHandler);
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var options = context.DownstreamReRoute.RateLimitOptions;
|
||||
namespace Ocelot.RateLimit.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class ClientRateLimitMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ClientRateLimitProcessor _processor;
|
||||
|
||||
public ClientRateLimitMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRateLimitCounterHandler counterHandler)
|
||||
: base(loggerFactory.CreateLogger<ClientRateLimitMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_processor = new ClientRateLimitProcessor(counterHandler);
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
|
||||
|
||||
var options = downstreamReRoute.RateLimitOptions;
|
||||
|
||||
// check if rate limiting is enabled
|
||||
if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
|
||||
{
|
||||
Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}");
|
||||
await _next.Invoke(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// compute identity from request
|
||||
var identity = SetIdentity(context.HttpContext, options);
|
||||
|
||||
// check white list
|
||||
if (IsWhitelisted(identity, options))
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
|
||||
await _next.Invoke(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var rule = options.RateLimitRule;
|
||||
if (rule.Limit > 0)
|
||||
{
|
||||
// increment counter
|
||||
var counter = _processor.ProcessRequest(identity, options);
|
||||
|
||||
// check if limit is reached
|
||||
if (counter.TotalRequests > rule.Limit)
|
||||
{
|
||||
//compute retry after value
|
||||
var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);
|
||||
|
||||
// log blocked request
|
||||
LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute);
|
||||
|
||||
if (!downstreamReRoute.EnableEndpointEndpointRateLimiting)
|
||||
{
|
||||
Logger.LogInformation($"EndpointRateLimiting is not enabled for {downstreamReRoute.DownstreamPathTemplate.Value}");
|
||||
await _next.Invoke(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// compute identity from request
|
||||
var identity = SetIdentity(httpContext, options);
|
||||
|
||||
// check white list
|
||||
if (IsWhitelisted(identity, options))
|
||||
{
|
||||
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
|
||||
await _next.Invoke(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var rule = options.RateLimitRule;
|
||||
if (rule.Limit > 0)
|
||||
{
|
||||
// increment counter
|
||||
var counter = _processor.ProcessRequest(identity, options);
|
||||
|
||||
// check if limit is reached
|
||||
if (counter.TotalRequests > rule.Limit)
|
||||
{
|
||||
//compute retry after value
|
||||
var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);
|
||||
|
||||
// log blocked request
|
||||
LogBlockedRequest(httpContext, identity, counter, rule, downstreamReRoute);
|
||||
|
||||
var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
// break execution
|
||||
await ReturnQuotaExceededResponse(context.HttpContext, options, retrystring);
|
||||
var ds = ReturnQuotaExceededResponse(httpContext, options, retrystring);
|
||||
httpContext.Items.UpsertDownstreamResponse(ds);
|
||||
|
||||
// Set Error
|
||||
context.Errors.Add(new QuotaExceededError(this.GetResponseMessage(options)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//set X-Rate-Limit headers for the longest period
|
||||
if (!options.DisableRateLimitHeaders)
|
||||
{
|
||||
var headers = _processor.GetRateLimitHeaders(context.HttpContext, identity, options);
|
||||
context.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
|
||||
public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)
|
||||
{
|
||||
var clientId = "client";
|
||||
if (httpContext.Request.Headers.Keys.Contains(option.ClientIdHeader))
|
||||
{
|
||||
clientId = httpContext.Request.Headers[option.ClientIdHeader].First();
|
||||
}
|
||||
|
||||
return new ClientRequestIdentity(
|
||||
clientId,
|
||||
httpContext.Request.Path.ToString().ToLowerInvariant(),
|
||||
httpContext.Request.Method.ToLowerInvariant()
|
||||
);
|
||||
}
|
||||
|
||||
public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
|
||||
{
|
||||
if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"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.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
||||
httpContext.Items.SetError(new QuotaExceededError(this.GetResponseMessage(options), options.HttpStatusCode));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//set X-Rate-Limit headers for the longest period
|
||||
if (!options.DisableRateLimitHeaders)
|
||||
{
|
||||
var headers = _processor.GetRateLimitHeaders(httpContext, identity, options);
|
||||
httpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
|
||||
}
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
|
||||
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
||||
{
|
||||
var message = this.GetResponseMessage(option);
|
||||
|
||||
if (!option.DisableRateLimitHeaders)
|
||||
{
|
||||
httpContext.Response.Headers["Retry-After"] = retryAfter;
|
||||
}
|
||||
|
||||
httpContext.Response.StatusCode = option.HttpStatusCode;
|
||||
return httpContext.Response.WriteAsync(message);
|
||||
}
|
||||
|
||||
private string GetResponseMessage(RateLimitOptions option)
|
||||
{
|
||||
var message = string.IsNullOrEmpty(option.QuotaExceededMessage)
|
||||
? $"API calls quota exceeded! maximum admitted {option.RateLimitRule.Limit} per {option.RateLimitRule.Period}."
|
||||
: option.QuotaExceededMessage;
|
||||
return message;
|
||||
}
|
||||
|
||||
private Task SetRateLimitHeaders(object rateLimitHeaders)
|
||||
{
|
||||
var headers = (RateLimitHeaders)rateLimitHeaders;
|
||||
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Limit"] = headers.Limit;
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Remaining"] = headers.Remaining;
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Reset"] = headers.Reset;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)
|
||||
{
|
||||
var clientId = "client";
|
||||
if (httpContext.Request.Headers.Keys.Contains(option.ClientIdHeader))
|
||||
{
|
||||
clientId = httpContext.Request.Headers[option.ClientIdHeader].First();
|
||||
}
|
||||
|
||||
return new ClientRequestIdentity(
|
||||
clientId,
|
||||
httpContext.Request.Path.ToString().ToLowerInvariant(),
|
||||
httpContext.Request.Method.ToLowerInvariant()
|
||||
);
|
||||
}
|
||||
|
||||
public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
|
||||
{
|
||||
if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"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.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
||||
}
|
||||
|
||||
public virtual DownstreamResponse ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
||||
{
|
||||
var message = GetResponseMessage(option);
|
||||
|
||||
var http = new HttpResponseMessage((HttpStatusCode)option.HttpStatusCode);
|
||||
|
||||
http.Content = new StringContent(message);
|
||||
|
||||
if (!option.DisableRateLimitHeaders)
|
||||
{
|
||||
http.Headers.TryAddWithoutValidation("Retry-After", retryAfter);
|
||||
}
|
||||
|
||||
return new DownstreamResponse(http);
|
||||
}
|
||||
|
||||
private string GetResponseMessage(RateLimitOptions option)
|
||||
{
|
||||
var message = string.IsNullOrEmpty(option.QuotaExceededMessage)
|
||||
? $"API calls quota exceeded! maximum admitted {option.RateLimitRule.Limit} per {option.RateLimitRule.Period}."
|
||||
: option.QuotaExceededMessage;
|
||||
return message;
|
||||
}
|
||||
|
||||
private Task SetRateLimitHeaders(object rateLimitHeaders)
|
||||
{
|
||||
var headers = (RateLimitHeaders)rateLimitHeaders;
|
||||
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Limit"] = headers.Limit;
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Remaining"] = headers.Remaining;
|
||||
headers.Context.Response.Headers["X-Rate-Limit-Reset"] = headers.Reset;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.RateLimit.Middleware
|
||||
{
|
||||
public static class RateLimitMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseRateLimiting(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClientRateLimitMiddleware>();
|
||||
}
|
||||
}
|
||||
namespace Ocelot.RateLimit.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
public static class RateLimitMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseRateLimiting(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClientRateLimitMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ namespace Ocelot.RateLimit
|
||||
{
|
||||
public class QuotaExceededError : Error
|
||||
{
|
||||
public QuotaExceededError(string message)
|
||||
: base(message, OcelotErrorCode.QuotaExceededError)
|
||||
public QuotaExceededError(string message, int httpStatusCode)
|
||||
: base(message, OcelotErrorCode.QuotaExceededError, httpStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user