Browse Source

Read Expires cookie attribute in HttpComponents connector

Prior to this commit, the HttpComponents implementation for the
`WebClient` would only consider the max-age attribute of response
cookies when parsing the response. This is not aligned with other client
implementations that consider the max-age attribute first, and then the
expires if the former was not present. The expires date is then
translated into a max-age duration. This behavior is done naturally by
several implementations.

This commit updates the `HttpComponentsClientHttpResponse` to do the
same.

Fixes gh-33157
pull/33720/head
Brian Clozel 1 year ago
parent
commit
5efb385e64
  1. 21
      spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java
  2. 29
      spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java

21
spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java

@ -17,6 +17,10 @@ @@ -17,6 +17,10 @@
package org.springframework.http.client.reactive;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@ -70,9 +74,22 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse { @@ -70,9 +74,22 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
}
private static long getMaxAgeSeconds(Cookie cookie) {
String expiresAttribute = cookie.getAttribute(Cookie.EXPIRES_ATTR);
String maxAgeAttribute = cookie.getAttribute(Cookie.MAX_AGE_ATTR);
return (maxAgeAttribute != null ? Long.parseLong(maxAgeAttribute) : -1);
if (maxAgeAttribute != null) {
return Long.parseLong(maxAgeAttribute);
}
// only consider expires if max-age is not present
else if (expiresAttribute != null) {
try {
ZonedDateTime expiresDate = ZonedDateTime.parse(expiresAttribute, DateTimeFormatter.RFC_1123_DATE_TIME);
return Duration.between(ZonedDateTime.now(expiresDate.getZone()), expiresDate).toSeconds();
}
catch (DateTimeParseException ex) {
// ignore
}
}
return -1;
}
}

29
spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java

@ -23,6 +23,10 @@ import java.lang.annotation.RetentionPolicy; @@ -23,6 +23,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -57,7 +61,9 @@ import static org.assertj.core.api.Assertions.fail; @@ -57,7 +61,9 @@ import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Named.named;
/**
* Tests for {@link ClientHttpConnector} implementations.
* @author Arjen Poutsma
* @author Brian Clozel
*/
class ClientHttpConnectorTests {
@ -172,6 +178,26 @@ class ClientHttpConnectorTests { @@ -172,6 +178,26 @@ class ClientHttpConnectorTests {
.verify();
}
@ParameterizedConnectorTest
void cookieExpireValueSetAsMaxAge(ClientHttpConnector connector) {
ZonedDateTime tomorrow = ZonedDateTime.now(ZoneId.of("UTC")).plusDays(1);
String formattedDate = tomorrow.format(DateTimeFormatter.RFC_1123_DATE_TIME);
prepareResponse(response -> {
response.setResponseCode(200);
response.addHeader("Set-Cookie", "id=test; Expires= " + formattedDate + ";");
});
Mono<ClientHttpResponse> futureResponse =
connector.connect(HttpMethod.GET, this.server.url("/").uri(), ReactiveHttpOutputMessage::setComplete);
StepVerifier.create(futureResponse)
.assertNext(response -> {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getCookies().getFirst("id").getMaxAge()).isCloseTo(Duration.ofDays(1), Duration.ofSeconds(10));
}
)
.verifyComplete();
}
private Buffer randomBody(int size) {
Buffer responseBody = new Buffer();
Random rnd = new Random();
@ -211,7 +237,8 @@ class ClientHttpConnectorTests { @@ -211,7 +237,8 @@ class ClientHttpConnectorTests {
return Arrays.asList(
named("Reactor Netty", new ReactorClientHttpConnector()),
named("Jetty", new JettyClientHttpConnector()),
named("HttpComponents", new HttpComponentsClientHttpConnector())
named("HttpComponents", new HttpComponentsClientHttpConnector()),
named("Jdk", new JdkClientHttpConnector())
);
}

Loading…
Cancel
Save