Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jetty 12 Cookie format incompatible by default with Apache HTTP Client 4.5.14 #12771

Open
adamretter opened this issue Feb 6, 2025 · 5 comments
Labels
Third Party Issues with third party libraries or projects

Comments

@adamretter
Copy link
Contributor

adamretter commented Feb 6, 2025

Jetty version(s)

  • Jetty 12.0.16

Jetty Environment

  • ee10

Java version/vendor

  • Liberica - 17.0.12 (2024-07-16 LTS)

OS type/version

  • macOS 14.4.1

Description
In the HTTP response, the format of the Expires parameter within the HTTP Set-Cookie header has changed between Jetty 11.0.24 (same as Jetty 9.4.54), and Jetty 12.0.16.

When using Apache HTTP Client 4.5.14 (the latest 4.x.x version at the time of writing), a warning is logged as it is unable to parse the Set-Cookie header from Jetty 12.0.16, and the cookie is ignored. The exception is raised on line 64 of org.apache.http.impl.cookie.BasicExpiresHandler in the parse(SetCookie, String) method, see: https://github.com/apache/httpcomponents-client/blob/rel/v4.5.14/httpclient/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java#L64.

How to reproduce?
When using either Jetty 9, 11, and 12 I have the org.eclipse.jetty.server.HttpConfiguration#responseCookieCompliance parameter set to org.eclipse.jetty.http.CookieCompliance#RFC6265.

The response header generated by Jetty 11.0.24 (same as Jetty 9.4.54) looks like:

Set-Cookie: org.exist.login=KrZPlSTEdvx/kyA1XQcKuQ==:Qcc26dFRBy8OPEeSEzS9Cw==; Expires=Fri, 07-Feb-2025 03:52:11 GMT; Max-Age=86400

However, the response header generated by Jetty 12.0.16 looks like:

Set-Cookie: org.exist.login=B4nJL1ycwWu6K/8kjZNUIw==:MumLm0DlHRK6gb75WBIIXg==; Expires=Fri, 7 Feb 2025 03:54:08 GMT; Max-Age=86400

Note that the difference is 07 vs 7 in the Expires parameter.

Whilst RFC6265 allows either of those formulations, it seems a shame that upgrading Jetty from version 11 to version 12 breaks client applications (that use Cookies) that are using the Apache HTTP Client.

There is a workaround which is to update any client code to modify the default configuration used by Apache HTTP Client so that it uses CookieSpecs.STANDARD instead of CookieSpecs.DEFAULT. As an example of achieving that, you can change client code that is like:

BasicCookieStore store = new BasicCookieStore();
HttpClient client = HttpClientBuilder.create()
            .setDefaultCookieStore(store)
            .build();

to this:

RequestConfig defaultRequestConfig = RequestConfig.custom()
            .setCookieSpec(CookieSpecs.STANDARD)
            .build();

BasicCookieStore store = new BasicCookieStore();
HttpClient client = HttpClientBuilder.create()
            .setDefaultCookieStore(store)
            .setDefaultRequestConfig(defaultRequestConfig)
            .build();
@adamretter adamretter added the Bug For general bugs on Jetty side label Feb 6, 2025
@joakime
Copy link
Contributor

joakime commented Feb 6, 2025

The syntax Fri, 07-Feb-2025 03:52:11 GMT is considered a non-format.
It closely resembles the obsolete RFC850 format, but technically violates that spec by using a 4 digit year. (it should have been a 2 digit year to be conformant).

But that doesn't matter, as Apache HttpClient is actually violating the HTTP spec by not accepting all 3 date/time formats.

Also, the standard RFC1123 format has been preferred since 1997.

The rule, since 1997, has been that User-Agents and Servers MUST generate the RFC1123 style dates everywhere in the HTTP headers (even the various Cookie headers), and accept all 3 date formats. (this part, the "accept all 3 date formats", is where Apache HttpClient is failing to follow the spec)

This was discussed and fixed in Jetty 12.0.9 for both Jetty Server and Jetty HttpClient ...

@joakime joakime added Third Party Issues with third party libraries or projects and removed Bug For general bugs on Jetty side labels Feb 6, 2025
@adamretter
Copy link
Contributor Author

adamretter commented Feb 6, 2025

Thanks @joakime for the detailed and comprehensive response. I did a little more debugging, and found that by default Apache HTTP Client uses this pattern for matching the Expires parameter: EEE, dd-MMM-yy HH:mm:ss z, see: https://github.com/apache/httpcomponents-client/blob/rel/v4.5.14/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpec.java#L64

Whilst I don't doubt that Jetty is following the specs (and I am very thankful for that), and I agree that the issue is with the Apache HTTP Client, it does however seem strange to me that this change in Jetty was not behind some sort of configuration knob. It feels a bit jarring to upgrade Jetty from version 11 to 12 whilst keeping the Jetty configuration options the same, and only then to find out that our (and other peoples) client applications have broken.

In this instance, I can of course fix our client code... but I can't fix other people's clients for them.

Anyway - thank you very much for Jetty and your and others hard work on it :-)

@joakime
Copy link
Contributor

joakime commented Feb 6, 2025

Thanks @joakime for the detailed and comprehensive response. I did a little more debugging, and found that by default Apache HTTP Client uses this pattern for matching the Expires parameter: EEE, dd-MMM-yy HH:mm:ss z, see: https://github.com/apache/httpcomponents-client/blob/rel/v4.5.14/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpec.java#L64

Yup, and the format you copy/pasted, Fri, 07-Feb-2025 03:52:11 GMT does not follow that pattern and would fail to match in that code.

@adamretter
Copy link
Contributor Author

adamretter commented Feb 6, 2025

Yup, and the format you copy/pasted, Fri, 07-Feb-2025 03:52:11 GMT does not follow that pattern and would fail to match in that code.

@joakime Actually I think there must be something more happening:

  1. The parameter produced by Jetty 11 (and Jetty 9): Expires=Fri, 07-Feb-2025 03:52:11 GMT does work with Apache HTTP Client.

    So I did some further digging and whilst the pattern in Apache HTTP Client is set to EEE, dd-MMM-yy HH:mm:ss z, it's DateUtils.parseDate function will happily parse 2 or 4 digit years even with that pattern. The code to reproduce is:

    java.util.Date date = org.apache.http.client.utils.DateUtils.parseDate("Fri, 07-Feb-2025 20:48:55 GMT", new String[]{"EEE, dd-MMM-yy HH:mm:ss z"});
    assertNotNull(date);
  2. The parameter produced by Jetty 12: Expires=Fri, 7 Feb 2025 03:54:08 GMT does NOT work with Apache HTTP Client.

@joakime
Copy link
Contributor

joakime commented Feb 6, 2025

2. The parameter produced by Jetty 12: Expires=Fri, 7 Feb 2025 03:54:08 GMT does NOT work with Apache HTTP Client.

Correction, it doesn't work with your old Apache HTTPClient and Apache HTTPClient configuration.

You have 2 choices.

  1. Specify the CookieSpecProvider and CookieSpecRegistry on your version of Apache HTTPClient to use the standard options already built into Apache HTTPClient, and stop using the deprecated Netscape / Browser compatibility modes that your 10 year old version of Apache HTTPClient is using.

See example: https://github.com/apache/httpcomponents-client/blob/rel/v4.5.14/httpclient/src/examples/org/apache/http/examples/client/ClientCustomPublicSuffixList.java#L63-L73

  1. Upgrade to Apache HTTPClient 5.x, which only supports the RFC 6265 spec, all of the old options (default, browser-compatibility, netscape, best-match, etc) have been removed from the Apache HTTPClient codebase.

Just to note, even the ships-with-java java.net.HttpURLConnection supports the RFC6265 and RFC1123 specs properly since Java 1.3.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Third Party Issues with third party libraries or projects
Projects
None yet
Development

No branches or pull requests

2 participants