Hi guys,
So I am currently working on implementing a new project I am using MVC .NET Core 3.1 to do all of my developments.
I have a requirement to create a reverse proxy which all of my client side applications will connect to. The one I am having the biggest issue with is a connection to an on premise Analysis Services Instance, because the response content can get quite large it results in slow response times for my client.
Here is the code that I have:
using Microsoft.AspNetCore.Http; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; namespace SSASProxyTest2 { public class ReverseProxyMiddleware { private static readonly HttpClient _httpClient = new HttpClient(); private readonly RequestDelegate _nextMiddleware; public ReverseProxyMiddleware(RequestDelegate nextMiddleware) { _nextMiddleware = nextMiddleware; } public async Task Invoke(HttpContext context) { var targetUri = BuildTargetUri(context.Request); if (targetUri != null) { var targetRequestMessage = CreateTargetMessage(context, targetUri); using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted)) { context.Response.StatusCode = (int)responseMessage.StatusCode; CopyFromTargetResponseHeaders(context, responseMessage); await responseMessage.Content.CopyToAsync(context.Response.Body); } return; } await _nextMiddleware(context); } private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri) { var requestMessage = new HttpRequestMessage(); CopyFromOriginalRequestContentAndHeaders(context, requestMessage); String username = "username"; String password = "password"; String encoded = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); requestMessage.Headers.Add("Authorization", "Basic " + encoded); requestMessage.RequestUri = targetUri; requestMessage.Headers.Host = targetUri.Host; requestMessage.Method = GetMethod(context.Request.Method); return requestMessage; } private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage) { var requestMethod = context.Request.Method; if (!HttpMethods.IsGet(requestMethod) && !HttpMethods.IsHead(requestMethod) && !HttpMethods.IsDelete(requestMethod) && !HttpMethods.IsTrace(requestMethod)) { var streamContent = new StreamContent(context.Request.Body); requestMessage.Content = streamContent; } foreach (var header in context.Request.Headers) { requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); } } private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage) { foreach (var header in responseMessage.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } foreach (var header in responseMessage.Content.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } context.Response.Headers.Remove("transfer-encoding"); } private static HttpMethod GetMethod(string method) { if (HttpMethods.IsDelete(method)) return HttpMethod.Delete; if (HttpMethods.IsGet(method)) return HttpMethod.Get; if (HttpMethods.IsHead(method)) return HttpMethod.Head; if (HttpMethods.IsOptions(method)) return HttpMethod.Options; if (HttpMethods.IsPost(method)) return HttpMethod.Post; if (HttpMethods.IsPut(method)) return HttpMethod.Put; if (HttpMethods.IsTrace(method)) return HttpMethod.Trace; return new HttpMethod(method); } private Uri BuildTargetUri(HttpRequest request) { Uri targetUri = new Uri("https://Example.com/msmdpump.dll"); return targetUri; } } }
As you can see I am taking the original request changing the target, adding credentials then returning the response.
This is where the block seems to be on large requests:
await responseMessage.Content.CopyToAsync(context.Response.Body);
This is going to be MiddelWare in my application. For completeness my startupclass has this:
app.Map("/api/test", api => { api.UseMiddleware<ReverseProxyMiddleware>(); });
Can anyone tell me if this can be optimised further to deal with large responses?
Really appreciate any help.
Joe