From 02a00f52928adc5e2f03b2dfef4aee3787b4c915 Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Mon, 5 Dec 2022 16:17:23 +0100 Subject: [PATCH] Response#end should throw ISE if used inside headers end handler (#4549) Fixes #4546 This aligns behaviors of Http 1 and Http2 servers. Signed-off-by: Thomas Segismont --- .../core/http/impl/Http1xServerResponse.java | 7 +- .../java/io/vertx/core/http/HttpTest.java | 81 +++++++++++++++++-- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/vertx/core/http/impl/Http1xServerResponse.java b/src/main/java/io/vertx/core/http/impl/Http1xServerResponse.java index ae25db5f3b5..c7dd7723dce 100644 --- a/src/main/java/io/vertx/core/http/impl/Http1xServerResponse.java +++ b/src/main/java/io/vertx/core/http/impl/Http1xServerResponse.java @@ -36,8 +36,8 @@ import io.vertx.core.http.HttpServerResponse; import io.vertx.core.http.impl.headers.HeadersMultiMap; import io.vertx.core.impl.ContextInternal; -import io.vertx.core.impl.future.PromiseInternal; import io.vertx.core.impl.VertxInternal; +import io.vertx.core.impl.future.PromiseInternal; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.net.NetSocket; @@ -48,10 +48,9 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; -import java.util.Map; import java.util.Set; -import static io.vertx.core.http.HttpHeaders.SET_COOKIE; +import static io.vertx.core.http.HttpHeaders.*; /** * @@ -399,6 +398,7 @@ private void end(Buffer chunk, PromiseInternal listener) { if (written) { throw new IllegalStateException(RESPONSE_WRITTEN); } + written = true; ByteBuf data = chunk.getByteBuf(); bytesWritten += data.readableBytes(); HttpObject msg; @@ -411,7 +411,6 @@ private void end(Buffer chunk, PromiseInternal listener) { msg = new AssembledLastHttpContent(data, trailingHeaders); } conn.writeToChannel(msg, listener); - written = true; conn.responseComplete(); if (bodyEndHandler != null) { bodyEndHandler.handle(null); diff --git a/src/test/java/io/vertx/core/http/HttpTest.java b/src/test/java/io/vertx/core/http/HttpTest.java index f21830de98c..b9d5aa17141 100644 --- a/src/test/java/io/vertx/core/http/HttpTest.java +++ b/src/test/java/io/vertx/core/http/HttpTest.java @@ -17,14 +17,27 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http2.Http2Exception; import io.vertx.codegen.annotations.Nullable; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.AsyncResult; +import io.vertx.core.Context; +import io.vertx.core.DeploymentOptions; import io.vertx.core.Future; -import io.vertx.core.*; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.VertxException; +import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; import io.vertx.core.dns.AddressResolverOptions; import io.vertx.core.http.impl.HttpServerRequestInternal; import io.vertx.core.http.impl.ServerCookie; import io.vertx.core.impl.Utils; -import io.vertx.core.net.*; +import io.vertx.core.net.NetClient; +import io.vertx.core.net.NetClientOptions; +import io.vertx.core.net.NetServerOptions; +import io.vertx.core.net.NetSocket; +import io.vertx.core.net.SocketAddress; import io.vertx.core.net.impl.HAProxyMessageCompletionHandler; import io.vertx.core.streams.Pump; import io.vertx.core.streams.ReadStream; @@ -39,24 +52,49 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.*; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.IntStream; -import static io.vertx.core.http.HttpMethod.PUT; +import static io.vertx.core.http.HttpMethod.*; import static io.vertx.test.core.TestUtils.*; -import static java.util.Collections.singletonList; +import static java.util.Collections.*; /** * @author Julien Viet @@ -6705,4 +6743,31 @@ private void testResponseEndFutureCompletes(final Function complete())); await(); } + + @Test + public void shouldThrowISEIfSendingResponseFromHeadersEndHandler() throws Exception { + AtomicBoolean flag = new AtomicBoolean(); + waitFor(3); + server.requestHandler(req -> { + HttpServerResponse resp = req.response(); + resp.headersEndHandler(v -> { + if (flag.compareAndSet(false, true)) { + try { + resp.end("bar"); + } catch (IllegalStateException e) { + complete(); + } + } + }); + resp.end("foo"); + complete(); + } + ); + startServer(testAddress); + client.request(requestOptions) + .compose(HttpClientRequest::send) + .compose(HttpClientResponse::end) + .onComplete(onSuccess(nothing -> complete())); + await(); + } }