reverse-proxy icon indicating copy to clipboard operation
reverse-proxy copied to clipboard

ProxyResponse is null in AddResponseTransform

Open arnonax-tr opened this issue 6 months ago • 5 comments

Describe the bug

I'm not 100% sure it's a bug, but either it is, or something is unclear to me.

Occasionally* I'm seeing that ResponseTransformContext.ProxyResponse is null inside a delegate I pass to TransformBuilderContext.AddResponseTransform, and I don't understand how that's possible. I see that the summary of ResponseTransformContext.ProxyResponse says it "can be null if the destination did not respond", but I see from the logs of the proxied service that it did send a response (with Status code 401- Unauthorized, though I'm not sure it's related). Even if for some reason that response message was lost, then I would expect to have a way to get the error of the root cause (e.g. timeout, DNS issue, network disconnected, etc.). but I don't find any property that return that information. Note that I don't think that the problem is timeout because from the logs I see that I got the callback immediately after the proxied service responded.

*I saw it only twice out of 632 responses from the same endpoint (over a week period), and out of 9635 responses from all (3) endpoints. All of the responses from that endpoint return status 401. In most cases ResponseTransformContext.ProxyResponse is not null, but in those two cases out of 632 it is.

To Reproduce

I cannot reproduce it deterministically (and the chances to hit are very low as I mentioned above), but I can outline the structure of my code in regard to YARP. (It may be possible to reproduce it deterministically using load testing, but I don't have the time and resources for it now)

// In Bootstrapper:
private void ConfigureReverseProxy(IServiceCollection services)
{
        services.AddReverseProxy().LoadFromMemory(new[]
        {
            new RouteConfig
            {
                RouteId = "route1",
                ClusterId = "cluster1",
                Match = new RouteMatch
                {
                    Path = "{**catch-all}"
                }
            }
        },
        new[]
        {
            new ClusterConfig
            {
                ClusterId = "cluster1",
                Destinations = new Dictionary<string, DestinationConfig>
                {
                    ["destination1"] = new() { Address = ConfigurationProvider.StsUrl }
                }
            }
        })
    .AddTransforms<StsProxyTransforms>();
}

internal class MyProxyTransforms : ITransformProvider
{
    void ITransformProvider.ValidateRoute(TransformRouteValidationContext context)
    {
    }

    void ITransformProvider.ValidateCluster(TransformClusterValidationContext context)
    {
    }
    
    public void Apply(TransformBuilderContext context)
    {
        context.AddRequestTransform(async requestContext =>
        {
            // Here we examine the request and do some stuff, but we don't modify the request
            // ...
        });

        context.AddResponseTransform(async responseContext =>
        {
            // THE FOLLOWING LINE OCCASIONALLY THROWS NullReferenceException BECAUSE responseContext.ProxyResponse is null...
            if (!responseContext.ProxyResponse.IsSuccessStatusCode)
            // ...
        }
    }
}

Further technical details

  • Package: Yarp.ReverseProxy (2.1.0)
  • The platform where the error occurs: .Net 6 on Linux

arnonax-tr avatar Aug 01 '24 14:08 arnonax-tr