mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-22 14:09:25 +00:00
Clean HTTP response headers after decompression in JDK client
Prior to this commit, gh-35225 introduced HTTP response body decompression support for "gzip" and "deflate" encodings for the `JdkClientHttpRequestFactory`. While body decompression works, the client keeps the "Content-Encoding" and "Content-Length" response headers intact, which misleads further response handling: the body size has changed and it is not compressed anymore. This commit ensures that the relevant response headers are removed from the HTTP response after decompression. Fixes gh-35668
This commit is contained in:
@@ -43,6 +43,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
@@ -113,16 +114,15 @@ class JdkClientHttpRequest extends AbstractStreamingClientHttpRequest {
|
||||
try {
|
||||
HttpRequest request = buildRequest(headers, body);
|
||||
responseFuture = this.httpClient.sendAsync(request, this.compression ? new DecompressingBodyHandler() : HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
if (this.timeout != null) {
|
||||
timeoutHandler = new TimeoutHandler(responseFuture, this.timeout);
|
||||
HttpResponse<InputStream> response = responseFuture.get();
|
||||
InputStream inputStream = timeoutHandler.wrapInputStream(response);
|
||||
return new JdkClientHttpResponse(response, inputStream);
|
||||
return new JdkClientHttpResponse(response, processResponseHeaders(), inputStream);
|
||||
}
|
||||
else {
|
||||
HttpResponse<InputStream> response = responseFuture.get();
|
||||
return new JdkClientHttpResponse(response, response.body());
|
||||
return new JdkClientHttpResponse(response, processResponseHeaders(), response.body());
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
@@ -231,6 +231,19 @@ class JdkClientHttpRequest extends AbstractStreamingClientHttpRequest {
|
||||
return Collections.unmodifiableSet(headers);
|
||||
}
|
||||
|
||||
private Consumer<HttpHeaders> processResponseHeaders() {
|
||||
if (this.compression) {
|
||||
return headers -> {
|
||||
String encoding = headers.getFirst(HttpHeaders.CONTENT_ENCODING);
|
||||
if (encoding != null && SUPPORTED_ENCODINGS.contains(encoding)) {
|
||||
headers.remove(HttpHeaders.CONTENT_ENCODING);
|
||||
headers.remove(HttpHeaders.CONTENT_LENGTH);
|
||||
}
|
||||
};
|
||||
}
|
||||
return headers -> {};
|
||||
}
|
||||
|
||||
|
||||
private static final class ByteBufferMapper implements OutputStreamPublisher.ByteMapper<ByteBuffer> {
|
||||
|
||||
|
||||
+8
-11
@@ -21,17 +21,14 @@ import java.io.InputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
@@ -50,18 +47,18 @@ class JdkClientHttpResponse implements ClientHttpResponse {
|
||||
private final InputStream body;
|
||||
|
||||
|
||||
public JdkClientHttpResponse(HttpResponse<InputStream> response, @Nullable InputStream body) {
|
||||
JdkClientHttpResponse(HttpResponse<InputStream> response, Consumer<HttpHeaders> headersConsumer, @Nullable InputStream body) {
|
||||
this.response = response;
|
||||
this.headers = adaptHeaders(response);
|
||||
this.headers = adaptHeaders(response, headersConsumer);
|
||||
this.body = (body != null ? body : InputStream.nullInputStream());
|
||||
}
|
||||
|
||||
private static HttpHeaders adaptHeaders(HttpResponse<?> response) {
|
||||
private static HttpHeaders adaptHeaders(HttpResponse<?> response, Consumer<HttpHeaders> headersConsumer) {
|
||||
Map<String, List<String>> rawHeaders = response.headers().map();
|
||||
Map<String, List<String>> map = new LinkedCaseInsensitiveMap<>(rawHeaders.size(), Locale.ROOT);
|
||||
MultiValueMap<String, String> multiValueMap = CollectionUtils.toMultiValueMap(map);
|
||||
multiValueMap.putAll(rawHeaders);
|
||||
return HttpHeaders.readOnlyHttpHeaders(multiValueMap);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
rawHeaders.forEach(headers::put);
|
||||
headersConsumer.accept(headers);
|
||||
return HttpHeaders.readOnlyHttpHeaders(headers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user