Tuesday, March 1, 2011

HttpClient Cookie Path Validation

I noticed some unusual behavior while using HttpClient 4 (version is important since it's a complete rewrite/namespace change from version 3) to log into a website.  The login was failing and I saw the following message in the log (I've changed names to protect the innocent).

2011-02-25 16:41:27,154 [pool-1-thread-2] WARN  org.apache.http.client.protocol.ResponseProcessCookies[126] - Cookie rejected: "[version: 0][name: tracker][value: +][domain: .rawr.com][path: /splash][expiry: Thu Feb 25 16:41:27 MST 2010]". Illegal path attribute "/splash". Path of origin: "/login.php"

In this case the server is setting a cookie with an invalid path and HttpClient is properly rejecting the cookie.  According to the RFC spec, a server must set the cookie path using the URL path, or an ancestor path.  For example, when accessing http://foo.com/a/b/, the server may set a cookie with path /a/b/, /a, or /, but not /a/c.  But since most all browsers (I only checked Firefox) ignore the spec and accept the cookie, I need HttpClient to do the same.

It's important to note that I'm using the browser compatibility cookie policy, but apparently it is missing some compatibility:

client.getParams().setParameter(ClientPNames.COOKIE_POLICY,  CookiePolicy.BROWSER_COMPATIBILITY);

Diving into the HttpClient code reveals that the cookie is being rejected by BasicPathHandler.java.  This class is registered by BrowserCompatSpec.java (the implementation for CookiePolicy.BROWSER_COMPATIBILITY).  In order to accept the cookie, it was necessary to subclass BrowserCompatSpec.java and and overwrite the existing path handler with an implementation that performs no validation:

HtmlUnitBrowserCompatCookieSpec() {
     super();
     
     final BasicPathHandler pathHandler = new BasicPathHandler() {
      @Override
      public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException {
       // nothing, browsers seem not to perform any validation
      }
     };
     registerAttribHandler(ClientCookie.PATH_ATTR, pathHandler);
    }

Credit goes to HtmlUnit for the solution.

No comments:

Post a Comment