I am using HttpClient
to consume a website. The website uses cookies, but one of the cookies has no path
data assigned. HttpClient will use the relative path to the requested file as path information, while browsers only use the directory information.
Example:
Request-URL:
https://someurl.org/dir1/file.php
HttpClient:
path=/dir1/file.php
browser:
path=/dir1
The C# behavior is problematic since the cookie would only be sent to that specific file instead of all files in the same directory. As a workaround I am extracting that cookie, makeing a copy, assign the correct path to the copy, mark the original as expired and than adding the copy to the cookie container.
This works, but may be there is a better more offical option to make the HttpClient act like a browser in that case?
This question is very similar to my own question (instead of HttpClient
(my scenario) HttpWebRequest
is used directly) and it also puts more background information to the problem.
Altough, I didn't end up with the solution provided there, I did modify the answer from this question to extend a DelegatingHandler
and managing the cookies myself (fixing the problem by inserting path information if missing) instead of letting the HttpMessageHandler
handle the cookies.
public class FixMissingPathOnPlainCookiesHandler : DelegatingHandler
{
public CookieContainer Container { get; private set; }
public FixMissingPathOnPlainCookiesHandler(HttpClientHandler clientHandler, CookieContainer cc = null)
: base(clientHandler)
{
if (clientHandler == null)
throw new ArgumentNullException(nameof(clientHandler));
if (clientHandler.UseCookies)
throw new InvalidOperationException("The HttpClientHandler property '.UseCookies' must be false");
Container = cc ?? new CookieContainer();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var uri = request.RequestUri;
var cookies = Container.GetCookieHeader(uri);
if (!string.IsNullOrEmpty(cookies))
request.Headers.Add("Cookie", cookies);
var response = await base.SendAsync(request, cancellationToken);
if (response.Headers.TryGetValues("Set-Cookie", out var cookieHeaders))
{
foreach (var cookie in cookieHeaders)
{
var local = cookie;
if (local.IndexOf("path=", StringComparison.InvariantCultureIgnoreCase) == -1)
local += "; path=" + uri.AbsolutePath.Substring(0, uri.AbsolutePath.LastIndexOf('/'));
Container.SetCookies(uri, local);
}
}
return response;
}
}