diff --git a/src/Core/Extensions/Basic.cs b/src/Core/Extensions/Basic.cs index 20788fc..e42c2a3 100644 --- a/src/Core/Extensions/Basic.cs +++ b/src/Core/Extensions/Basic.cs @@ -60,7 +60,13 @@ public static IApplicationBuilder UseProxies(this IApplicationBuilder app, Actio foreach(var proxy in proxiesBuilder.Build()) { - builder.MapMiddlewareRoute(proxy.Route, proxyApp => proxyApp.Run(context => context.ExecuteProxyOperationAsync(proxy))); + builder.MapMiddlewareRoute(proxy.Route, proxyApp => proxyApp.Use(async (context, next) => + { + if (!await context.ExecuteProxyOperationAsync(proxy).ConfigureAwait(false)) + { + await next(); + } + })); } }); @@ -82,8 +88,14 @@ public static void RunProxy(this IApplicationBuilder app, Action proxy.HttpProxy.EndpointComputer = GetRunProxyComputer(oldHttpEndpointComputer); if (proxy.WsProxy?.EndpointComputer.Clone() is EndpointComputerToValueTask oldWsEndpointComputer) proxy.WsProxy.EndpointComputer = GetRunProxyComputer(oldWsEndpointComputer); - - app.Run(context => context.ExecuteProxyOperationAsync(proxy)); + + app.Use(async (context, next) => + { + if (!await context.ExecuteProxyOperationAsync(proxy).ConfigureAwait(false)) + { + await next(); + } + }); } /// @@ -272,12 +284,12 @@ public static void RunWsProxy(this IApplicationBuilder app, string wsEndpoint, A /// The HTTP options. /// The WS options. /// A which completes when the request has been successfully proxied and written to the response. - public static Task ProxyAsync(this ControllerBase controller, string httpEndpoint, string wsEndpoint, HttpProxyOptions httpProxyOptions = null, WsProxyOptions wsProxyOptions = null) + public static async Task ProxyAsync(this ControllerBase controller, string httpEndpoint, string wsEndpoint, HttpProxyOptions httpProxyOptions = null, WsProxyOptions wsProxyOptions = null) { var httpProxy = new HttpProxy((c, a) => new ValueTask(httpEndpoint), httpProxyOptions); var wsProxy = new WsProxy((c, a) => new ValueTask(wsEndpoint), wsProxyOptions); var proxy = new Builders.Proxy(null, httpProxy, wsProxy); - return controller.HttpContext.ExecuteProxyOperationAsync(proxy); + await controller.HttpContext.ExecuteProxyOperationAsync(proxy).ConfigureAwait(false); } /// @@ -287,10 +299,10 @@ public static Task ProxyAsync(this ControllerBase controller, string httpEndpoin /// The HTTP endpoint to use. /// The HTTP options. /// A which completes when the request has been successfully proxied and written to the response. - public static Task HttpProxyAsync(this ControllerBase controller, string httpEndpoint, HttpProxyOptions httpProxyOptions = null) + public static async Task HttpProxyAsync(this ControllerBase controller, string httpEndpoint, HttpProxyOptions httpProxyOptions = null) { var httpProxy = new HttpProxy((c, a) => new ValueTask(httpEndpoint), httpProxyOptions); - return controller.HttpContext.ExecuteHttpProxyOperationAsync(httpProxy); + await controller.HttpContext.ExecuteHttpProxyOperationAsync(httpProxy).ConfigureAwait(false); } /// @@ -300,29 +312,28 @@ public static Task HttpProxyAsync(this ControllerBase controller, string httpEnd /// The WS endpoint to use. /// The WS options. /// A which completes when the request has been successfully proxied and written to the response. - public static Task WsProxyAsync(this ControllerBase controller, string wsEndpoint, WsProxyOptions wsProxyOptions = null) + public static async Task WsProxyAsync(this ControllerBase controller, string wsEndpoint, WsProxyOptions wsProxyOptions = null) { var wsProxy = new WsProxy((c, a) => new ValueTask(wsEndpoint), wsProxyOptions); - return controller.HttpContext.ExecuteWsProxyOperationAsync(wsProxy); + await controller.HttpContext.ExecuteWsProxyOperationAsync(wsProxy); } #endregion #region Extension Helpers - internal static async Task ExecuteProxyOperationAsync(this HttpContext context, Builders.Proxy proxy) + internal static async Task ExecuteProxyOperationAsync(this HttpContext context, Builders.Proxy proxy) { var isWebSocket = context.WebSockets.IsWebSocketRequest; if(isWebSocket && proxy.WsProxy != null) { await context.ExecuteWsProxyOperationAsync(proxy.WsProxy).ConfigureAwait(false); - return; + return true; } if(!isWebSocket && proxy.HttpProxy != null) { - await context.ExecuteHttpProxyOperationAsync(proxy.HttpProxy).ConfigureAwait(false); - return; + return await context.ExecuteHttpProxyOperationAsync(proxy.HttpProxy).ConfigureAwait(false); } var requestType = isWebSocket ? "WebSocket" : "HTTP(S)"; @@ -330,6 +341,8 @@ internal static async Task ExecuteProxyOperationAsync(this HttpContext context, // If the failures are not caught, then write a generic response. context.Response.StatusCode = 502 /* BAD GATEWAY */; await context.Response.WriteAsync($"Request could not be proxied.\n\nThe {requestType} request cannot be proxied because the underlying proxy definition does not have a definition of that type.").ConfigureAwait(false); + + return true; } internal static ValueTask GetEndpointFromComputerAsync(this HttpContext context, EndpointComputerToValueTask computer) => computer(context, context.GetRouteData().Values); diff --git a/src/Core/Extensions/Http.cs b/src/Core/Extensions/Http.cs index a9d35c6..5fa4e46 100644 --- a/src/Core/Extensions/Http.cs +++ b/src/Core/Extensions/Http.cs @@ -12,7 +12,7 @@ namespace AspNetCore.Proxy { internal static class HttpExtensions { - internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext context, HttpProxy httpProxy) + internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext context, HttpProxy httpProxy) { var uri = await context.GetEndpointFromComputerAsync(httpProxy.EndpointComputer).ConfigureAwait(false); var options = httpProxy.Options; @@ -23,9 +23,14 @@ internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext conte .GetService() .CreateClient(options?.HttpClientName ?? Helpers.HttpProxyClientName); + if (options?.Filter != null && !options.Filter(context)) + { + return false; + } + // If `true`, this proxy call has been intercepted. if(options?.Intercept != null && await options.Intercept(context).ConfigureAwait(false)) - return; + return true; if(context.WebSockets.IsWebSocketRequest) throw new InvalidOperationException("A WebSocket request cannot be routed as an HTTP proxy operation."); @@ -49,6 +54,7 @@ internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext conte if(options?.AfterReceive != null) await options.AfterReceive(context, proxiedResponse).ConfigureAwait(false); await context.WriteProxiedHttpResponseAsync(proxiedResponse).ConfigureAwait(false); + } catch (Exception e) { @@ -59,12 +65,15 @@ internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext conte // If the failures are not caught, then write a generic response. context.Response.StatusCode = 502 /* BAD GATEWAY */; await context.Response.WriteAsync($"Request could not be proxied.\n\n{e.Message}\n\n{e.StackTrace}").ConfigureAwait(false); - return; + + } + else + { + await options.HandleFailure(context, e).ConfigureAwait(false); } - - await options.HandleFailure(context, e).ConfigureAwait(false); } } + return true; } private static HttpRequestMessage CreateProxiedHttpRequest(this HttpContext context, string uriString, bool shouldAddForwardedHeaders) diff --git a/src/Core/Options/HttpProxyOptions.cs b/src/Core/Options/HttpProxyOptions.cs index d40f8bc..11b446a 100644 --- a/src/Core/Options/HttpProxyOptions.cs +++ b/src/Core/Options/HttpProxyOptions.cs @@ -34,6 +34,14 @@ public interface IHttpProxyOptionsBuilder : IBuilderThis instance. IHttpProxyOptionsBuilder WithIntercept(Func> intercept); + /// + /// A that is invoked upon a call. + /// The result should be `true` if the call should go ahead and not be filtered + /// + /// + /// This instance. + IHttpProxyOptionsBuilder WithFilter(Func filter); + /// /// An that is invoked before the call to the remote endpoint. /// The can be edited before the call. @@ -66,6 +74,7 @@ public sealed class HttpProxyOptionsBuilder : IHttpProxyOptionsBuilder private bool _shouldAddForwardedHeaders = true; private string _httpClientName; private Func> _intercept; + private Func _filter; private Func _beforeSend; private Func _afterReceive; private Func _handleFailure; @@ -90,6 +99,7 @@ public IHttpProxyOptionsBuilder New() .WithShouldAddForwardedHeaders(_shouldAddForwardedHeaders) .WithHttpClientName(_httpClientName) .WithIntercept(_intercept) + .WithFilter(_filter) .WithBeforeSend(_beforeSend) .WithAfterReceive(_afterReceive) .WithHandleFailure(_handleFailure); @@ -103,6 +113,7 @@ public HttpProxyOptions Build() _httpClientName, _handleFailure, _intercept, + _filter, _beforeSend, _afterReceive); } @@ -143,6 +154,18 @@ public IHttpProxyOptionsBuilder WithIntercept(Func> return this; } + /// + /// A that is invoked upon a call. + /// The result should be `true` if the call should go ahead and not be filtered + /// + /// + /// This instance. + public IHttpProxyOptionsBuilder WithFilter(Func filter) + { + _filter = filter; + return this; + } + /// /// Sets the that is invoked before the call to the remote endpoint. /// The can be edited before the call. @@ -210,6 +233,15 @@ public sealed class HttpProxyOptions /// The result should be `true` if the call is intercepted and **not** meant to be forwarded. /// public Func> Intercept { get; } + + /// + /// Intercept property. + /// + /// + /// A that is invoked upon a call. + /// The result should be `true` if the call should go ahead and not be filtered + /// + public Func Filter { get; } /// /// BeforeSend property. @@ -235,11 +267,11 @@ public sealed class HttpProxyOptions /// A that is invoked once if the proxy operation fails. public Func HandleFailure { get; } - internal HttpProxyOptions( - bool shouldAddForwardedHeaders, + internal HttpProxyOptions(bool shouldAddForwardedHeaders, string httpClientName, Func handleFailure, Func> intercept, + Func filter, Func beforeSend, Func afterReceive) { @@ -247,6 +279,7 @@ internal HttpProxyOptions( HttpClientName = httpClientName; HandleFailure = handleFailure; Intercept = intercept; + Filter = filter; BeforeSend = beforeSend; AfterReceive = afterReceive; }