private static ProxyOptions CreateFrom( ProxyOptions old, bool?shouldAddForwardedHeaders = null, Func <HttpContext, Exception, Task> handleFailure = null, Func <HttpContext, HttpRequestMessage, Task> beforeSend = null, Func <HttpContext, HttpResponseMessage, Task> afterReceive = null) { return(new ProxyOptions( shouldAddForwardedHeaders ?? old.ShouldAddForwardedHeaders, handleFailure ?? old.HandleFailure, beforeSend ?? old.BeforeSend, afterReceive ?? old.AfterReceive)); }
internal static async Task ExecuteWsProxyOperationAsync(this HttpContext context, string uri, ProxyOptions options = null) { using (var socketToEndpoint = new ClientWebSocket()) { foreach (var protocol in context.WebSockets.WebSocketRequestedProtocols) { socketToEndpoint.Options.AddSubProtocol(protocol); } foreach (var headerEntry in context.Request.Headers) { if (!Helpers.WebSocketNotForwardedHeaders.Contains(headerEntry.Key, StringComparer.OrdinalIgnoreCase)) { socketToEndpoint.Options.SetRequestHeader(headerEntry.Key, headerEntry.Value); } } // TODO: Add a proxy options for keep alive and set it here. //client.Options.KeepAliveInterval = proxyService.Options.WebSocketKeepAliveInterval.Value; // TODO make a proxy option action to edit the web socket options. await socketToEndpoint.ConnectAsync(new Uri(uri), context.RequestAborted); using (var socketToClient = await context.WebSockets.AcceptWebSocketAsync(socketToEndpoint.SubProtocol)) { // TODO: Add a buffer size option and set it here. var bufferSize = 4096; await Task.WhenAll(PumpWebSocket(socketToEndpoint, socketToClient, bufferSize, context.RequestAborted), PumpWebSocket(socketToClient, socketToEndpoint, bufferSize, context.RequestAborted)); } } }
internal static async Task ExecuteProxyOperation(HttpContext context, string uri, ProxyOptions options = null) { try { var proxiedRequest = context.CreateProxiedHttpRequest(uri, options?.ShouldAddForwardedHeaders ?? true); if (options?.BeforeSend != null) { await options.BeforeSend(context, proxiedRequest).ConfigureAwait(false); } var proxiedResponse = await context.SendProxiedHttpRequest(proxiedRequest).ConfigureAwait(false); if (options?.AfterReceive != null) { await options.AfterReceive(context, proxiedResponse).ConfigureAwait(false); } await context.WriteProxiedHttpResponse(proxiedResponse).ConfigureAwait(false); } catch (Exception e) { if (options?.HandleFailure == null) { // If the failures are not caught, then write a generic response. context.Response.StatusCode = 502; await context.Response.WriteAsync($"Request could not be proxied.\n\n{e.Message}\n\n{e.StackTrace}.").ConfigureAwait(false); return; } await options.HandleFailure(context, e).ConfigureAwait(false); } }
internal static async Task ExecuteProxyOperationAsync(this HttpContext context, string uri, ProxyOptions options = null) { try { if (context.WebSockets.IsWebSocketRequest) { if (!uri.StartsWith("ws", System.StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("A WebSocket request must forward to a WebSocket (ws[s]) endpoint."); } await context.ExecuteWsProxyOperationAsync(uri, options).ConfigureAwait(false); return; } // Assume HTTP if not WebSocket. if (!uri.StartsWith("http", System.StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("An HTTP request must forward to an HTTP (http[s]) endpoint."); } await context.ExecuteHttpProxyOperationAsync(uri, options).ConfigureAwait(false); } catch (Exception e) { if (!context.Response.HasStarted) { if (options?.HandleFailure == null) { // If the failures are not caught, then write a generic response. context.Response.StatusCode = 502; await context.Response.WriteAsync($"Request could not be proxied.\n\n{e.Message}\n\n{e.StackTrace}").ConfigureAwait(false); return; } await options.HandleFailure(context, e).ConfigureAwait(false); } } }
/// <summary> /// A <see cref="Controller"/> extension method which allows for a single, simple call to use a proxy /// in existing controllers. /// </summary> /// <param name="controller">The calling controller.</param> /// <param name="uri">The URI to proxy.</param> /// <param name="options">Extra options to apply during proxying.</param> /// <returns> /// A <see cref="Task"/> which, upon completion, has proxied the specified address and copied the response contents into /// the response for the <see cref="HttpContext"/>. /// </returns> public static Task ProxyAsync(this ControllerBase controller, string uri, ProxyOptions options = null) { return(Helpers.ExecuteProxyOperation(controller.HttpContext, uri, options)); }
// Provides the "default" implementation for `UseProxy` where the proxied address is computed synchronously. private static void UseProxy_GpaSync(this IApplicationBuilder app, string endpoint, Func <HttpContext, IDictionary <string, object>, string> getProxiedAddress, ProxyOptions options = null) { app.UseRouter(builder => { builder.MapMiddlewareRoute(endpoint, proxyApp => { proxyApp.Run(async context => { var uri = getProxiedAddress(context, context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)); await Helpers.ExecuteProxyOperation(context, uri, options); }); }); }); }
/// <summary> /// Middleware which creates an ad hoc proxy over a specified endpoint. /// </summary> /// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param> /// <param name="endpoint">The local route endpoint.</param> /// <param name="getProxiedAddress">A lambda { () => <see cref="string"/> } which returns the address to which the request is proxied.</param> /// <param name="options">Extra options to apply during proxying.</param> public static void UseProxy(this IApplicationBuilder app, string endpoint, Func <string> getProxiedAddress, ProxyOptions options = null) { UseProxy_GpaSync( app, endpoint, (context, args) => getProxiedAddress(), options); }
/// <summary> /// Middleware which creates an ad hoc proxy over a specified endpoint. /// </summary> /// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param> /// <param name="endpoint">The local route endpoint.</param> /// <param name="getProxiedAddress">A lambda { (args) => <see cref="Task{String}"/> } which returns the address to which the request is proxied.</param> /// <param name="options">Extra options to apply during proxying.</param> public static void UseProxy(this IApplicationBuilder app, string endpoint, Func <IDictionary <string, object>, Task <string> > getProxiedAddress, ProxyOptions options = null) { UseProxy_GpaAsync( app, endpoint, (context, args) => getProxiedAddress(args), options); }
/// <summary> /// Terminating middleware which creates a proxy over a specified endpoint. /// </summary> /// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param> /// <param name="getProxiedAddress">A lambda { (context) => <see cref="string"/> } which returns the address to which the request is proxied.</param> /// <param name="options">Extra options to apply during proxying.</param> public static void RunProxy(this IApplicationBuilder app, Func <HttpContext, string> getProxiedAddress, ProxyOptions options = null) { app.Run(context => { return(context.ExecuteProxyOperationAsync($"{getProxiedAddress(context)}{context.Request.Path}", options)); }); }
internal static async Task ExecuteHttpProxyOperationAsync(this HttpContext context, string uri, ProxyOptions options = null) { // If `true`, this proxy call has been intercepted. if (options?.Intercept != null && await options.Intercept(context)) { return; } var proxiedRequest = context.CreateProxiedHttpRequest(uri, options?.ShouldAddForwardedHeaders ?? true); if (options?.BeforeSend != null) { await options.BeforeSend(context, proxiedRequest).ConfigureAwait(false); } var proxiedResponse = await context .SendProxiedHttpRequestAsync(proxiedRequest, options?.HttpClientName ?? Helpers.HttpProxyClientName) .ConfigureAwait(false); if (options?.AfterReceive != null) { await options.AfterReceive(context, proxiedResponse).ConfigureAwait(false); } await context.WriteProxiedHttpResponseAsync(proxiedResponse).ConfigureAwait(false); }