Parsing cookies
Asked Answered
G

2

10

I have a self-hosted WCF service with an endpoint set up with WebHttpBinding listening for HTTP requests. I need to access cookies passed with these requests. I can get the Cookie: header value, but I am stuck with actual parsing. My implementation attempt was using CookieContainer:

var container = new CookieContainer();
var uri = new Uri("http://localhost/");
container.SetCookies(uri, cookieHeader);
var cookies = container.GetCookies(uri).OfType<Cookie>();
foreach (var cookie in cookies)
{
    Console.WriteLine("{0} = {1}", cookie.Name, cookie.Value);
}

Issue with this code is that CookieContainer expects cookies to be separated by comma (cookieHeader="c1=v1,c2=v2"), while browsers tend to use semicolon (cookieHeader="c1=v1;c2=v2") for that. Since RFC 6265 permits only semicolon to be used as separator (older RFC's permit both, though) I am a bit puzzled why CookieContainer only supports comma. Now I am struggling for finding an alternative solution for the problem that seems to be quite typical. How do I parse cookies properly? ASP.NET should be able to do it, does it expose any publicly usable classes for that?

Goody answered 11/3, 2015 at 6:20 Comment(0)
R
16

Why does it not work?

As I understand it, the separators vary for a Set-Cookie: and the Cookie: headers. Set-Cookie: is normally duplicated if multiple headers exist, whereas Cookie: has multiple cookies in a single header. From RFC6265:

== Server -> User Agent ==

Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie: lang=en-US; Path=/; Domain=example.com

== User Agent -> Server ==

Cookie: SID=31d4d96e407aad42; lang=en-US

The Set-Cookie: header can be combined into a single string by appending each header separated by a comma:

Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly,lang=en-US; Path=/; Domain=example.com

CookieContainer is designed to be used on the client side (by a user-agent), so the SetCookies(Uri, string) method only parses the syntax used by Set-Cookie:.

How does ASP.Net do it

ASP.Net uses an internal method to parse the Cookie: header, and it does not appear to be exposed by any public methods. See HttpListenerRequest.Cookies:

private CookieCollection ParseCookies(Uri uri, string setCookieHeader) {
    GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() uri:" + uri + " setCookieHeader:" + setCookieHeader);
    CookieCollection cookies = new CookieCollection();
    CookieParser parser = new CookieParser(setCookieHeader);
    for (;;) {
        Cookie cookie = parser.GetServer();
        GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
        if (cookie==null) {
            // EOF, done.
            break;
        }
        if (cookie.Name.Length==0) {
            continue;
        }
        cookies.InternalAdd(cookie, true);
    }
    return cookies;
}

How can I parse the header, then?

Nancy has a very concise parser that could be used if licensing permits:

private IDictionary<string, string> GetCookieData()
{
    var cookieDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    if (!this.Headers.Cookie.Any())
    {
        return cookieDictionary;
    }

    var values = this.Headers["cookie"].First().TrimEnd(';').Split(';');
    foreach (var parts in values.Select(c => c.Split(new[] { '=' }, 2)))
    {
        var cookieName = parts[0].Trim();
        string cookieValue;

        if (parts.Length == 1)
        {
            //Cookie attribute
            cookieValue = string.Empty;
        }
        else
        {
            cookieValue = parts[1];
        }

        cookieDictionary[cookieName] = cookieValue;
    }

    return cookieDictionary;
}
Retake answered 7/9, 2015 at 21:7 Comment(0)
A
1

I'm building off the answer by Mitch. There is a way to call that cookie parser that Microsoft provides in System.Net.CookieParser that is marked internal. System.Net.CookieContainer that is public provides a SetCookie.

public void SetCookies(Uri uri, string cookieHeader)

So you can write a function like this

public static System.Net.CookieContainer ParseCookieHeader(string cookieHeader, System.Uri uri)
{
    var cookies = new System.Net.CookieContainer();
    cookies.SetCookies(uri: uri,
                       cookieHeader: cookieHeader);

    return cookies;
}

Then to loop through all the cookies you would do something like this

string cookieHeaderValue = "a=b,c=1";
System.Uri requestUri = new System.Uri("");

var cookies = ParseCookieHeader(cookieHeaderValue, uri: requestUri);

foreach (System.Net.Cookie cookie in cookies.GetAllCookies())
{
    // do something with cookie
}
Anastaciaanastas answered 20/7, 2024 at 18:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.