79

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.

Tom Solid
  • 2,226
  • 1
  • 13
  • 32
The Coding Monk
  • 7,684
  • 12
  • 41
  • 56

6 Answers6

149

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();

waXve
  • 792
  • 2
  • 9
  • 30
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 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 resttemplate – comiventor Mar 13 '18 at 02:19
  • 2
    Here is report of this behaviour in HttpClient's jira with developer suggesting same thing: https://issues.apache.org/jira/browse/HTTPCLIENT-1763 – Sankozi Mar 06 '19 at 09:13
  • 4
    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. – Merijn Vogel Jun 29 '21 at 14:50
  • @MerijnVogel Apache HttpClient 5.1.3 is fully compliant with RFC 6265 by default and doesn't produce those kinds of warnings. See https://stackoverflow.com/a/73764182/15647 – wheleph Sep 18 '22 at 15:51
  • 1
    As @comiventor 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; } ``` – rohimsh Apr 24 '23 at 11:08
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();
hnaderi
  • 409
  • 8
  • 16
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();
waXve
  • 792
  • 2
  • 9
  • 30
A. Yasar G.
  • 362
  • 3
  • 11
  • Setting the customizer as a Component is enough for it to work. SpringBoot picks it up automatically, no need to use the restTemplate builder. – sebnukem Nov 08 '19 at 00:56
3

Solved with:

System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http.client.protocol.ResponseProcessCookies", "fatal");
The Coding Monk
  • 7,684
  • 12
  • 41
  • 56
  • 2
    Just to note that you may need different syntax depending on which logging library is in use. See http://stackoverflow.com/questions/5188118/cant-turn-off-htmlunit-logging-messages for alternatives. – CfSimplicity Nov 01 '16 at 09:20
  • For log4j2: Configurator.setLevel("org.apache.http.client.protocol.ResponseProcessCookies", Level.FATAL); – Costi Muraru Nov 01 '18 at 21:51
  • 13
    That's not "solving" anything, it's just hiding the error. – sebnukem Oct 18 '19 at 15:55
  • 9
    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." – The Coding Monk Apr 20 '20 at 09:01
1

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://stackoverflow.com/a/40697322/13688761>
    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();
  }
}
Miguel Alorda
  • 623
  • 5
  • 13
0

Apache HttpClient 5.1.3 is fully compliant with RFC 6265 by default. Hence the following code doesn't produce any 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());
    }
}
wheleph
  • 7,974
  • 7
  • 40
  • 57