microsoft / reverse-proxy

A toolkit for developing high-performance HTTP reverse proxy applications.

Home Page:https://microsoft.github.io/reverse-proxy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Response not being sent for non-2xx status codes

adnan-kamili opened this issue · comments

Describe the bug

We are getting a 500 response when the target returns non-2xx code. If the target returns a 200 response, all works well we get the response body in the client. In case the target returns 400, the proxy logs 400 but the client gets a 500 error.

To Reproduce

Since we don't know before hand if the request is to be proxied, we enable request buffering in a middleware. In the controller we get to know if request is to be proxied so an exception is thrown which the CustomExceptionFilter handles in the following manner:

public override async Task OnExceptionAsync(ExceptionContext context)
{
    ...
    if (context.Exception is TenantMigratedException tenantMigratedException)
    {
        context.HttpContext.Request.Body.Position = 0;
        await _reverseProxyService.ForwardRequestAsync(context.HttpContext, tenantMigratedException.Region);
        return;
    }
    ...
}        

Here is the reverse proxy service:

public class ReverseProxyService
{
    private readonly IHttpForwarder _forwarder;
    private readonly HttpMessageInvoker _httpClient;
    private readonly ForwarderRequestConfig _requestOptions;

    public ReverseProxyService(IHttpForwarder forwarder)
    {
        _forwarder = forwarder;
        _httpClient = new HttpMessageInvoker(new SocketsHttpHandler
        {
            UseProxy = false,
            AllowAutoRedirect = false,
            AutomaticDecompression = System.Net.DecompressionMethods.None,
            UseCookies = false,
            EnableMultipleHttp2Connections = true,
            ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
            ConnectTimeout = TimeSpan.FromSeconds(15),
            SslOptions = new SslClientAuthenticationOptions
            {
                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
            }
        });
        _requestOptions = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) };
    }

    public async Task ForwardRequestAsync(HttpContext context, string region)
    {
        string destinationPrefix = null;
        if (region == RegionName.EU)
        {
            destinationPrefix = $"https://{AppConstants.EuWebApiHost}/";
        }
        if (string.IsNullOrEmpty(destinationPrefix))
        {
            return;
        }
        var error = await _forwarder.SendAsync(context, destinationPrefix, _httpClient, _requestOptions, HttpTransformer.Default);
        if (error != ForwarderError.None)
        {
            var errorFeature = context.Features.Get<IForwarderErrorFeature>();
            var exception = errorFeature?.Exception;
            context.Response.StatusCode = 500;
            await context.Response.WriteAsync(JsonSerializer.Serialize(new
            {
                message = exception?.Message ?? "An error occurred while forwarding the request."
            }));
        }
    }
}

Got Exceptions? Include both the message and the stack trace

We don't see any specific exception, here are some YARP logs:

[17:28:19 INF] [0HN3MD2GKNKEA:00000001] - Proxying to https://api.eu.xxxxx.com/v3/accounts/login HTTP/2 RequestVersionOrLower 
[17:28:20 INF] [0HN3MD2GKNKEA:00000001] - Received HTTP/2.0 response 400.

Further technical details

  • Yarp.ReverseProxy" Version="2.1.0"
  • .NET 7.0.x
  • macOS

Are you indicating to MVC that you're handling these exceptions (context.ExceptionHandled = true; in OnExceptionAsync)?

If you are and you're still seeing issues, could you share a minimal runnable example we could test with?

Thanks this fixed the issue