Fixing HttpClient warning "Invalid expires attribute" using fluent API
Asked Answered
C

6

84

I'm using the fluent API of HttpClient to make a GET request:

String jsonResult = Request.Get(requestUrl)
            .connectTimeout(2000)
            .socketTimeout(2000)
            .execute().returnContent().asString();

But for each request I get the following warning:

apr 07, 2016 12:26:46 PM org.apache.http.client.protocol.ResponseProcessCookies processCookies
WARNING: Invalid cookie header: "Set-Cookie: WMF-Last-Access=07-Apr-2016;Path=/;HttpOnly;Expires=Mon, 09 May 2016 00:00:00 GMT". Invalid 'expires' attribute: Mon, 09 May 2016 00:00:00 GMT

How can I fix this and keep using the fluent interface? Ideally I'd want a proper way to fix it, but since I don't really care about the cookies in my use case any solution that just allows me to stop displaying the warnings (besides redirecting stderr, cause I need that) is welcome.

Cassius answered 7/4, 2016 at 10:29 Comment(0)
K
159

The default HttpClient has difficulty understanding the latest RFC-compliant headers.

Instead of hiding the warning, just switch to a standard cookie spec like this (HttpClient 4.4+):

HttpClient httpClient = HttpClients.custom()
    .setDefaultRequestConfig(RequestConfig.custom()
        .setCookieSpec(CookieSpecs.STANDARD)
        .build())
    .build();

Karonkaross answered 19/11, 2016 at 20:1 Comment(5)
you nailed it. I am using springboot resttemplate and it required an extra step of setting httpclient of HttpComponentsClientHttpRequestFactory object which is then passed on to resttemplateEggplant
Here is report of this behaviour in HttpClient's jira with developer suggesting same thing: issues.apache.org/jira/browse/HTTPCLIENT-1763Pfaff
Is there a version of http client for which this is no longer a required setting? Setting the cookie spec to 'standard' seems like a sensible default to me.Turkestan
@MerijnVogel Apache HttpClient 5.1.3 is fully compliant with RFC 6265 by default and doesn't produce those kinds of warnings. See https://mcmap.net/q/240158/-fixing-httpclient-warning-quot-invalid-expires-attribute-quot-using-fluent-apiWindage
As @Eggplant said, my use case was also for Spring Boot. Adding the snippet here for easy reference :- ``` @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplateBuilder() .setReadTimeout(Duration.ofSeconds(30)).build(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create() .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()).build())); return restTemplate; } ```Trilobate
D
26

If you want to use HttpClientBuilder you can use the following sytax:

        HttpClient httpClient = HttpClientBuilder.create()
            .setDefaultRequestConfig(RequestConfig.custom()
                    .setCookieSpec(CookieSpecs.STANDARD).build()).build();
Dramatist answered 17/11, 2017 at 6:40 Comment(0)
P
18

For the developers don't want to think on the object model, wrapping the HttpClient for a RestTemplate might be used as below ( as @comiventor mentioned above especially for Spring Boot Developers).

a Customizer for RestTemplate,

public class RestTemplateStandardCookieCustomizer 
                         implements RestTemplateCustomizer {

    @Override
    public void customize(final RestTemplate restTemplate) {

        final HttpClient httpClient = HttpClients.custom()
            .setDefaultRequestConfig(RequestConfig.custom()
                .setCookieSpec(CookieSpecs.STANDARD).build())
            .build();

        restTemplate.setRequestFactory(
          new HttpComponentsClientHttpRequestFactory(httpClient)
        );
    }
}

and using it with the RestTemplate Builder

var restTemplate = restTemplateBuilder.additionalCustomizers(
            new RestTemplateStandardCookieCustomizer()
        ).build();
Pickings answered 12/10, 2019 at 18:28 Comment(1)
Setting the customizer as a Component is enough for it to work. SpringBoot picks it up automatically, no need to use the restTemplate builder.Strephonn
C
3

Solved with:

System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http.client.protocol.ResponseProcessCookies", "fatal");
Cassius answered 8/4, 2016 at 15:43 Comment(4)
Just to note that you may need different syntax depending on which logging library is in use. See #5188618 for alternatives.Vagrancy
For log4j2: Configurator.setLevel("org.apache.http.client.protocol.ResponseProcessCookies", Level.FATAL);Inchoate
That's not "solving" anything, it's just hiding the error.Strephonn
Quoting the question: "I don't really care about the cookies in my use case any solution that just allows me to stop displaying the warnings (besides redirecting stderr, cause I need that) is welcome."Cassius
W
2

Apache HttpClient 5.1.3 is fully compliant with RFC 6265 by default. Hence the following code doesn't produce warnings anymore:

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;

//...

try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
    HttpGet httpGet = new HttpGet(url);
    try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
        System.out.println(response.getCode() + " " + response.getReasonPhrase());
    }
}
Windage answered 18/9, 2022 at 15:49 Comment(1)
Specifically, Section 4.1.1 of RFC 6265, which is actually just a pointer to the HTTP 1.1 RFC 2616 Section-3.3.1. But that has now been obsoleted by the HTTP Semantics RFC 9110. Section 5.6.7 of that points to the Internet Message Format RFC 5322 which has the actual, current spec for all date-time attributes. Phew.Polysepalous
W
2

This answer is not directly related to the OP, but to help those looking for a solution to the problem. This is the first result on Google when searching Invalid expires attribute.

I am using Resteasy as a transitive dependency of Keycloak Admin REST Client.

The problem is resteasy-client does not provide an easy way to configure the Apache HttpClient HttpClient#RequestConfig#CookieSpec. See the default ClientHttpEngineBuilder used by resteasy-client ClientHttpEngineBuilder43.

To fix it, since the ClientHttpEngineBuilder43#createEngine method is protected, I have extended the class and specified the suggested CookieSpecs.STANDARD:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.http.HttpHost;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.jboss.resteasy.client.jaxrs.ClientHttpEngine;
import org.jboss.resteasy.client.jaxrs.ClientHttpEngineBuilder43;

public class StandardCookieSpecClientHttpEngineBuilder extends ClientHttpEngineBuilder43 {

  @Override
  protected ClientHttpEngine createEngine(
      HttpClientConnectionManager cm,
      Builder rcBuilder,
      HttpHost defaultProxy,
      int responseBufferSize,
      HostnameVerifier verifier,
      SSLContext context) {
    // CookieSpecs.DEFAULT does not handle the latest RFC correctly
    // <https://mcmap.net/q/240158/-fixing-httpclient-warning-quot-invalid-expires-attribute-quot-using-fluent-api>
    rcBuilder.setCookieSpec(CookieSpecs.STANDARD);
    return super.createEngine(cm, rcBuilder, defaultProxy, responseBufferSize, verifier, context);
  }
}

Then, the corresponding ResteasyClient and Keycloak are built as:

import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class KeycloakConfig {

  @Bean
  public Keycloak keycloak() {
    return KeycloakBuilder.builder()
        // ... Some other configuration to the builder
        .resteasyClient(buildResteasyClient())
        .build();
  }

  private ResteasyClient buildResteasyClient() {
    ResteasyClientBuilder resteasyClientBuilder = new ResteasyClientBuilder();
    // ... Some other configuration to the client builder
    return resteasyClientBuilder
        .httpEngine(
            new StandardCookieSpecClientHttpEngineBuilder()
                .resteasyClientBuilder(resteasyClientBuilder)
                .build())
        .build();
  }
}
Wellthoughtof answered 24/11, 2022 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.