Reputation: 945
We have a Web API (.NET Core 5) which passes the request on to another remote Web API.
The Web API grabs some cookies from the incoming request and appends them to the outgoing request.
Normally this works fine and we see the request cookies arriving at the remote Web API.
But when multiple requests are sent simultaneously, the cookies from one incoming request are somehow leaking over into the outgoing request of another.
This even happens when using totally separate users and totally separate browsers.
Things I've tried and confirmed:
The code which copies the cookies from incoming request to outgoing request works perfectly fine. In fact even when the cookies appearing on the remote API are "leaked", my custom logging suggests it still worked as expected
I can see the expected/leaky request cookie on the remote Web API (in its raw IIS logs), so it can't be the remote API adding it to the request in its pipeline.
Added logging to the HttpClient call but can't see the unexpected cookies being sent.
This doesn't happen locally
My feeling is something is happening in HttpClient somehow??
UPDATE 1 I added logging to CopyCookieHandler
and it only gets created once and reused by all requests
UPDATE 2 I just read that HttpMessageHandler
instances results in CookieContainer
objects being shared...which may possible explain this... https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0#cookies
The first API has this setup to use HttpClient:
services.AddHttpContextAccessor()
services.AddHttpClient<IRemoteService, RemoteService>()
.AddHttpMessageHandler<CopyCookieHandler>();
services.AddTransient<CopyCookieHandler>();
where
public class RemoteService : IRemoteService
{
private HttpClient _client;
public RemoteService(HttpClient client)
{
_client = client;
}
public async Task Get()
{
var request = new HttpRequestMessage("POST", "http://example.com");
await MakeCall(request);
}
}
The CopyCookieHandler
is:
public class CopyCookieHandler : DelegatingHandler
{
public IHttpContextAccessor _context;
public CopyCookieHandler(IHttpContextAccessor context)
{
_context = context;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//Copies the incoming request cookie and adds to the outgoing request
var productId = _context.HttpContext.Request.Cookies["productId"];
request.Headers.Add(HeaderNames.Cookie, $"productId={productId});
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Upvotes: 6
Views: 4889
Reputation: 945
It turns out that, by default, the HttpClientHandler
will store the response cookies in a CookieContainer
and then append them onto the next request.
This explains why I was seeing extra cookies on the remote API's requests, but they were actually coming from the response of a previously completed request.
This documentation led me to the fix
So by adding this code:
services.AddHttpClient<IRemoteService, RemoteService>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
})
.AddHttpMessageHandler<CopyCookieHandler>();
will prevent the HttpClientHandler
from sharing your cookies between request on your HttpClient
.
Upvotes: 5