From 1d96f6a266553d1c5512fbd9de9e91d53390ecf1 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 13 Oct 2020 22:59:45 +0100 Subject: [PATCH 1/2] HttpHeaderResponseDecorator checks for "Transfer-Encoding" This commit extends the fix from b86c11cc9b00693fc3446724ffd37ae379189b7f by checking for both existing Content-Length and Transfer-Encoding. Closes gh-25908 --- .../reactive/HttpHeadResponseDecorator.java | 40 ++++++++++--------- .../HttpHeadResponseDecoratorTests.java | 9 ++++- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java b/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java index d387abd329..4935e3222e 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -16,8 +16,6 @@ package org.springframework.http.server.reactive; -import java.util.function.BiFunction; - import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -41,24 +39,30 @@ public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator { /** - * Apply {@link Flux#reduce(Object, BiFunction) reduce} on the body, count - * the number of bytes produced, release data buffers without writing, and - * set the {@literal Content-Length} header. + * Consume and release the body without writing. + *

If the headers contain neither Content-Length nor Transfer-Encoding, + * count the bytes and set Content-Length. */ @Override public final Mono writeWith(Publisher body) { - return Flux.from(body) - .reduce(0, (current, buffer) -> { - int next = current + buffer.readableByteCount(); - DataBufferUtils.release(buffer); - return next; - }) - .doOnNext(length -> { - if (length > 0 || getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH) == null) { - getHeaders().setContentLength(length); - } - }) - .then(); + if (shouldSetContentLength()) { + return Flux.from(body) + .reduce(0, (current, buffer) -> { + int next = current + buffer.readableByteCount(); + DataBufferUtils.release(buffer); + return next; + }) + .doOnNext(length -> getHeaders().setContentLength(length)) + .then(); + } + else { + return Flux.from(body).then(); + } + } + + private boolean shouldSetContentLength() { + return (getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH) == null && + getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING) == null); } /** diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java index a3d32c5f13..36ca9a5b93 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -25,6 +25,7 @@ import reactor.core.publisher.Flux; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.core.testfixture.io.buffer.LeakAwareDataBufferFactory; +import org.springframework.http.HttpHeaders; import org.springframework.web.testfixture.http.server.reactive.MockServerHttpResponse; import static org.assertj.core.api.Assertions.assertThat; @@ -63,6 +64,12 @@ public class HttpHeadResponseDecoratorTests { assertThat(this.response.getHeaders().getContentLength()).isEqualTo(length); } + @Test // gh-25908 + public void writeWithGivenTransferEncoding() { + this.response.getHeaders().add(HttpHeaders.TRANSFER_ENCODING, "chunked"); + this.response.writeWith(Flux.empty()).block(); + assertThat(this.response.getHeaders().getContentLength()).isEqualTo(-1); + } private DataBuffer toDataBuffer(String s) { DataBuffer buffer = this.bufferFactory.allocateBuffer(); From 431ec906c4e301a8b88373e5ce3f2d884ad96b70 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 13 Oct 2020 23:38:59 +0100 Subject: [PATCH 2/2] Ensure buffers released in HttpHeadResponseDecorator See gh-25908 --- .../http/server/reactive/HttpHeadResponseDecorator.java | 4 +++- .../http/server/reactive/HttpHeadResponseDecoratorTests.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java b/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java index 4935e3222e..b52a38f722 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/HttpHeadResponseDecorator.java @@ -56,7 +56,9 @@ public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator { .then(); } else { - return Flux.from(body).then(); + return Flux.from(body) + .doOnNext(DataBufferUtils::release) + .then(); } } diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java index 36ca9a5b93..d98b19356f 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/HttpHeadResponseDecoratorTests.java @@ -66,8 +66,9 @@ public class HttpHeadResponseDecoratorTests { @Test // gh-25908 public void writeWithGivenTransferEncoding() { + Flux body = Flux.just(toDataBuffer("data1"), toDataBuffer("data2")); this.response.getHeaders().add(HttpHeaders.TRANSFER_ENCODING, "chunked"); - this.response.writeWith(Flux.empty()).block(); + this.response.writeWith(body).block(); assertThat(this.response.getHeaders().getContentLength()).isEqualTo(-1); }