diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java index feda5aa59a..5e652669f8 100644 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -63,8 +63,8 @@ public class CheckstyleConventions { project.getPlugins().apply(NoHttpPlugin.class); NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class); noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines")); - noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", - "**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/**"); + noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath", + "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**"); List buildFolders = List.of("bin", "build", "out"); project.allprojects(subproject -> { Path rootPath = project.getRootDir().toPath(); diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java index 54c5da2c05..98021ce6dd 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpResponse.java @@ -90,6 +90,7 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse { .maxAge(getMaxAgeSeconds(cookie)) .secure(cookie.isSecure()) .httpOnly(cookie.containsAttribute("httponly")) + .partitioned(cookie.containsAttribute("partitioned")) .sameSite(cookie.getAttribute("samesite")) .build()); } diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java index 3d6ae019fd..29d4aab1e4 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java @@ -134,6 +134,7 @@ class ReactorClientHttpResponse implements ClientHttpResponse { .secure(cookie.isSecure()) .httpOnly(cookie.isHttpOnly()) .sameSite(getSameSite(cookie)) + .partitioned(getPartitioned(cookie)) .build())); return CollectionUtils.unmodifiableMultiValueMap(result); } @@ -145,6 +146,13 @@ class ReactorClientHttpResponse implements ClientHttpResponse { return null; } + private static boolean getPartitioned(Cookie cookie) { + if (cookie instanceof DefaultCookie defaultCookie) { + return defaultCookie.isPartitioned(); + } + return false; + } + /** * Called by {@link ReactorClientHttpConnector} when a cancellation is detected * but the content has not been subscribed to. If the subscription never diff --git a/spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java b/spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java index dcd98a835f..7095feb315 100644 --- a/spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java +++ b/spring-web/src/test/java/org/springframework/http/client/reactive/ClientHttpConnectorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import okhttp3.mockwebserver.RecordedRequest; import okio.Buffer; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; @@ -201,6 +202,25 @@ class ClientHttpConnectorTests { .verifyComplete(); } + @ParameterizedConnectorTest + void partitionedCookieSupport(ClientHttpConnector connector) { + Assumptions.assumeFalse(connector instanceof JettyClientHttpConnector, "Jetty client does not support partitioned cookies"); + Assumptions.assumeFalse(connector instanceof JdkClientHttpConnector, "JDK client does not support partitioned cookies"); + prepareResponse(response -> { + response.setResponseCode(200); + response.addHeader("Set-Cookie", "id=test; Partitioned;"); + }); + Mono 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").isPartitioned()).isTrue(); + } + ) + .verifyComplete(); + } + @Test void disableCookieWithHttpComponents() { ClientHttpConnector connector = new HttpComponentsClientHttpConnector(