9Answers
  • 11
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

How to parse a cookie string

I would like to take a Cookie string (as it might be returned in a Set-Cookie header) and be able to easily modify parts of it, specifically the expiration date.

I see there are several different Cookie classes, such as BasicClientCookie, available but I don't see any easy way to parse the string into one of those objects.

I see in api level 9 they added HttpCookie which has a parse method, but I need something to work in previous versions.

Any ideas?

Thanks

      • 1
    • I am using HttpClient for connections but I have been using CookieSyncManager for persisting the cookies. I'm not using the HttpClient's CookieStore at the moment.

How about java.net.HttpCookie:

List<HttpCookie> cookies = HttpCookie.parse(header);
  • 102
Reply Report
      • 2
    • @MattWolfe Correct, but 90% of the people who find this question in 2009 do not worry about API 9 any more. That's why this answer is being upvoted now.
      • 2
    • Fails for a value like: SSID PHPSESSID=d6cb3d4a5e6fd3bbe5078f37d616fac1; path=/, ci_session=a%3A0%3A%7B%7D; expires=Mon, 10-Nov-2014 12:37:06 GMT; Max-Age=-31500000; path=/, ci_session=a%3A5%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22ca963cd5e9b6d9631c5afc8f26eb7374%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A14%3A%22182.59.202.107%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A60%3A%22Dalvik%2F2.1.0+%28Linux%3B+U%3B+Android+5.1.1%3B+Nexus+5+Build%2FLMY48B%29%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1447123026%3Bs%3A9%3A%22user_data%22%3Bs
      • 1
    • @User3: The cookie you've posted violates RFC 2109 §4.1 which reads token (informally) – a sequence of non-special, non-white space characters, so SSID PHPSESSID should not contain a space. @atasoyh: Would be nice to see a concrete example...

I believe you'll have to parse it out manually. Try this:

BasicClientCookie parseRawCookie(String rawCookie) throws Exception {
    String[] rawCookieParams = rawCookie.split(";");

    String[] rawCookieNameAndValue = rawCookieParams[0].split("=");
    if (rawCookieNameAndValue.length != 2) {
        throw new Exception("Invalid cookie: missing name and value.");
    }

    String cookieName = rawCookieNameAndValue[0].trim();
    String cookieValue = rawCookieNameAndValue[1].trim();
    BasicClientCookie cookie = new BasicClientCookie(cookieName, cookieValue);
    for (int i = 1; i < rawCookieParams.length; i++) {
        String rawCookieParamNameAndValue[] = rawCookieParams[i].trim().split("=");

        String paramName = rawCookieParamNameAndValue[0].trim();

        if (paramName.equalsIgnoreCase("secure")) {
            cookie.setSecure(true);
        } else {
            if (rawCookieParamNameAndValue.length != 2) {
                throw new Exception("Invalid cookie: attribute not a flag or missing value.");
            }

            String paramValue = rawCookieParamNameAndValue[1].trim();

            if (paramName.equalsIgnoreCase("expires")) {
                Date expiryDate = DateFormat.getDateTimeInstance(DateFormat.FULL)
                        .parse(paramValue);
                cookie.setExpiryDate(expiryDate);
            } else if (paramName.equalsIgnoreCase("max-age")) {
                long maxAge = Long.parseLong(paramValue);
                Date expiryDate = new Date(System.getCurrentTimeMillis() + maxAge);
                cookie.setExpiryDate(expiryDate);
            } else if (paramName.equalsIgnoreCase("domain")) {
                cookie.setDomain(paramValue);
            } else if (paramName.equalsIgnoreCase("path")) {
                cookie.setPath(paramValue);
            } else if (paramName.equalsIgnoreCase("comment")) {
                cookie.setPath(paramValue);
            } else {
                throw new Exception("Invalid cookie: invalid attribute name.");
            }
        }
    }

    return cookie;
}

I haven't actually compiled or run this code, but it should be a strong start. You'll probably have to mess with the date parsing a bit: I'm not sure that the date format used in cookies is actually the same as DateFormat.FULL. (Check out this related question, which addresses handling the date format in cookies.) Also, note that there are some cookie attributes not handled by BasicClientCookie such as version and httponly.

Finally, this code assumes that the name and value of the cookie appear as the first attribute: I'm not sure if that's necessarily true, but that's how every cookie I've ever seen is ordered.

  • 14
Reply Report

You can use Apache HttpClient's facilities for that.
Here's an excerpt from CookieJar:

CookieSpec cookieSpec = new BrowserCompatSpec();

List<Cookie> parseCookies(URI uri, List<String> cookieHeaders) {
    ArrayList<Cookie> cookies = new ArrayList<Cookie>();
    int port = (uri.getPort() < 0) ? 80 : uri.getPort();
    boolean secure = "https".equals(uri.getScheme());
    CookieOrigin origin = new CookieOrigin(uri.getHost(), port,
            uri.getPath(), secure);
    for (String cookieHeader : cookieHeaders) {
        BasicHeader header = new BasicHeader(SM.SET_COOKIE, cookieHeader);
        try {
            cookies.addAll(cookieSpec.parse(header, origin));
        } catch (MalformedCookieException e) {
            L.d(e);
        }
    }
    return cookies;
}
  • 8
Reply Report

Funny enough, but java.net.HttpCookie class cannot parse cookie strings with domain and/or path parts that this exact java.net.HttpCookie class has converted to strings.

For example:

HttpCookie newCookie = new HttpCookie("cookieName", "cookieValue");
newCookie.setDomain("cookieDomain.com");
newCookie.setPath("/");

As this class implements neither Serializable nor Parcelable, it's tempting to store cookies as strings. So you write something like:

saveMyCookieAsString(newCookie.toString());

This statement will save the cookie in the following format:

cookieName="cookieValue";$Path="/";$Domain="cookiedomain.com"

And then you want to restore this cookie, so you get the string:

String cookieAsString = restoreMyCookieString();

and try to parse it:

List<HttpCookie> cookiesList = HttpCookie.parse(cookieAsString);
StringBuilder myCookieAsStringNow = new StringBuilder();
for(HttpCookie httpCookie: cookiesList) {
    myCookieAsStringNow.append(httpCookie.toString());
}

now myCookieAsStringNow.toString(); produces

cookieName=cookieValue

Domain and path parts are just gone. The reason: parse method is case sensitive to words like "domain" and "path".
Possible workaround: provide another toString() method like:

public static String httpCookieToString(HttpCookie httpCookie) {
    StringBuilder result = new StringBuilder()
            .append(httpCookie.getName())
            .append("=")
            .append("\"")
            .append(httpCookie.getValue())
            .append("\"");

    if(!TextUtils.isEmpty(httpCookie.getDomain())) {
        result.append("; domain=")
                .append(httpCookie.getDomain());
    }
    if(!TextUtils.isEmpty(httpCookie.getPath())){
        result.append("; path=")
                .append(httpCookie.getPath());
    }

    return result.toString();
}

I find it funny (especially, for classes like java.net.HttpCookie which are aimed to be used by a lot and lot of people) and I hope it will be useful for someone.

  • 7
Reply Report

With a regular expression like :

([^=]+)=([^\;]+);\s?

you can parse a cookie like this :

.COOKIEAUTH=5DEF0BF530F749AD46F652BDF31C372526A42FEB9D40162167CB39C4D43FC8AF1C4B6DF0C24ECB1945DFF7952C70FDA1E4AF12C1803F9D089E78348C4B41802279897807F85905D6B6D2D42896BA2A267E9F564814631B4B31EE41A483C886B14B5A1E76FD264FB230E87877CB9A4A2A7BDB0B0101BC2C1AF3A029CC54EE4FBC; 
expires=Sat, 30-Jul-2011 01:22:34 GMT; 
path=/; HttpOnly

in a few lines of code.

  • 6
Reply Report

The advantage of Yanchenko's approach with Apache Http client is that is validates the cookies consistent with the spec based on the origin. The regular expression approach won't do that, but perhaps you don't need to.

public class CookieUtil {

    public List<Cookie> parseCookieString(String cookies) {
        List<Cookie> cookieList = new ArrayList<Cookie>();
        Pattern cookiePattern = Pattern.compile("([^=]+)=([^\\;]*);?\\s?");
        Matcher matcher = cookiePattern.matcher(cookies);
        while (matcher.find()) {
            int groupCount = matcher.groupCount();
            System.out.println("matched: " + matcher.group(0));
            for (int groupIndex = 0; groupIndex <= groupCount; ++groupIndex) {
                System.out.println("group[" + groupIndex + "]=" + matcher.group(groupIndex));
            }
            String cookieKey = matcher.group(1);
            String cookieValue = matcher.group(2);
            Cookie cookie = new BasicClientCookie(cookieKey, cookieValue);
            cookieList.add(cookie);
        }
        return cookieList;
    }
}

I've attached a small example using yanchenkos regex. It needs to be tweaked just a little. Without the '?' quantity modifer on the trailing ';' the trailing attribute for any cookie will not be matched. After that, if you care about the other attributes you can use Doug's code, properly encapsulated, to parse the other match groups.

Edit: Also, note '*' qualifier on the value of the cookie itself. Values are optional and you can get cookies like "de=", i.e. no value at all. Looking at the regex again, I don't think it will handle the secure and discard cookie attributes which do not have an '='.

  • 0
Reply Report
CookieManager cookieManager = new CookieManager();
        CookieHandler.setDefault(cookieManager);
        HttpCookie cookie = new HttpCookie("lang", "en");
        cookie.setDomain("Your URL");
        cookie.setPath("/");
        cookie.setVersion(0);

        cookieManager.getCookieStore().add(new URI("https://Your URL/"), cookie);
        List<HttpCookie> Cookies =  cookieManager.getCookieStore().get(new URI("https://Your URL/"));
        String s = Cookies.get(0).getValue();
  • -1
Reply Report
        val headers = ..........


        val headerBuilder = Headers.Builder()

        headers?.forEach {
            val values = it.split(";")
            values.forEach { v ->
                if (v.contains("=")) {
                    headerBuilder.add(v.replace("=", ":"))
                }
            }
        }

        val headers = headerBuilder.build()
  • -2
Reply Report