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
CopyCookieHandlerand it only gets created once and reused by all requestsUPDATE 2 I just read that
HttpMessageHandlerinstances results inCookieContainerobjects being shared...which may possible explain this... https://docs.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;
}
}
