Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to clone HttpContext for scatter gather scenarios #53565

Closed
1 task done
danielmarbach opened this issue Jan 23, 2024 · 3 comments
Closed
1 task done

Ability to clone HttpContext for scatter gather scenarios #53565

danielmarbach opened this issue Jan 23, 2024 · 3 comments
Labels
area-signalr Includes: SignalR clients and servers

Comments

@danielmarbach
Copy link
Contributor

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

We are using ASP.NET Core to expose various APIs. Some of the API endpoints do forward requests to downstream APIs our "gateway" is aware of. For those scenarios, we are using Yarps direct forwarding capability to forward requests.

We have a number of "scatter gather" scenarios where the request is sent to a number of identical "downstream" endpoints that return slices of the data that is then composed together on the "gateway" side of things to be returned to the caller. Given the flexibility of Yarp we started wondering if we could leverage the forwarder capability to build those scatter gather scenarios and wondered whether that would even be a good approach, a potential "misuse" or what other "simple" alternatives might be suitable.

When going the path of exploring the direct forwarding capability, we noticed that if we had the capability of cloning the HttpContext similar to what is done in SignalR the scatter gather scenario could look something like

async Task<JsonNode?> Fetch(HttpContext originalContext, IHttpForwarder httpForwarder, KeyValuePair<string, string> keyValuePair,
    HttpMessageInvoker httpMessageInvoker, ForwarderRequestConfig forwarderRequestConfig)
{
    var clonedContext = originalContext.Clone();
    var error = await httpForwarder.SendAsync(clonedContext, keyValuePair.Value,
        httpMessageInvoker, forwarderRequestConfig, new ScatterTransformer());

    await using var responseBody = clonedContext.Response.Body;
    responseBody.Seek(0, SeekOrigin.Begin);

    var jsonNode = await JsonNode.ParseAsync(responseBody);
    return jsonNode;
}

and

app.Map("/weatherforecastscatter", async (HttpContext httpContext, IHttpForwarder forwarder) =>
{
    // scatter
    var fetchTasks = instances
        .Select(instance => Fetch(httpContext, forwarder, instance, httpClient, requestConfig))
        .ToArray();
    await Task.WhenAll(fetchTasks);
    
    // gather
    var array = new JsonArray();
    foreach (var task in fetchTasks)
    {
        var jsonNode = await task;
        if (jsonNode != null)
        {
            array.Add(jsonNode);
        }
    }
    return array;
});

I'm using JSON DOM to stitch stuff together loosely typed, but of course it would also be possible to use a strongly typed approach.

To achieve the cloning capability, we have copied the code from SignalR, removed the SignalR specific part from it and then started using that customized code in our spikes.

Describe the solution you'd like

A mechanism to clone HttpContext. For example, we could see this approach being freed from SignalR specific parts and moved to an HttpContext based utility and then SignalR could use that ability to clone contexts too and add the signalr specific parts after the clone.

Alternatives:

  • Using HttpClient manually

Additional context

I wasn't entirely sure whether I should open it here or in the reverse proxy repository. I decided to put it here because the ability to clone HttpContext is already implemented internally in SignalR.

Yes we could look at GraphQL but we felt that might be another bigger change that we are not sure yet is worth for our scope.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-signalr Includes: SignalR clients and servers label Jan 23, 2024
@Tratcher
Copy link
Member

It seems like you don't want the whole IHttpForwarder, just the first half that creates the HttpRequestMessage and runs the transforms. You need to handle the response yourself to get the composition you want. You can ask over in YARP about generalizing this logic.
https://github.com/microsoft/reverse-proxy/blob/471140d1531b44878bb2ce7e51fb82fcf570d129/src/ReverseProxy/Forwarder/HttpForwarder.cs#L338

This wouldn't require a clone of the HttpContext.

@danielmarbach
Copy link
Contributor Author

I opened an issue in the Yarp repository microsoft/reverse-proxy#2386

@BrennanConroy
Copy link
Member

Don't think there is anything to do here. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-signalr Includes: SignalR clients and servers
Projects
None yet
Development

No branches or pull requests

4 participants
@danielmarbach @Tratcher @BrennanConroy and others