J
JoeyStyles
Guest
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
Continue reading...
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
Continue reading...