diff --git a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java index e388b08..2a26485 100644 --- a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java +++ b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation + * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -32,19 +32,27 @@ import java.util.Map; import java.util.Objects; +import static io.vertx.core.http.HttpHeaders.CONNECTION; +import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH; +import static io.vertx.core.http.HttpHeaders.KEEP_ALIVE; +import static io.vertx.core.http.HttpHeaders.PROXY_AUTHENTICATE; +import static io.vertx.core.http.HttpHeaders.PROXY_AUTHORIZATION; +import static io.vertx.core.http.HttpHeaders.TRANSFER_ENCODING; +import static io.vertx.core.http.HttpHeaders.UPGRADE; + public class ProxiedRequest implements ProxyRequest { private static final CharSequence X_FORWARDED_HOST = HttpHeaders.createOptimized("x-forwarded-host"); private static final MultiMap HOP_BY_HOP_HEADERS = MultiMap.caseInsensitiveMultiMap() - .add(HttpHeaders.CONNECTION, "whatever") - .add(HttpHeaders.KEEP_ALIVE, "whatever") - .add(HttpHeaders.PROXY_AUTHENTICATE, "whatever") - .add(HttpHeaders.PROXY_AUTHORIZATION, "whatever") + .add(CONNECTION, "whatever") + .add(KEEP_ALIVE, "whatever") + .add(PROXY_AUTHENTICATE, "whatever") + .add(PROXY_AUTHORIZATION, "whatever") .add("te", "whatever") .add("trailer", "whatever") - .add(HttpHeaders.TRANSFER_ENCODING, "whatever") - .add(HttpHeaders.UPGRADE, "whatever"); + .add(TRANSFER_ENCODING, "whatever") + .add(UPGRADE, "whatever"); final ContextInternal context; private HttpMethod method; @@ -61,7 +69,7 @@ public ProxiedRequest(HttpServerRequest proxiedRequest) { // Determine content length long contentLength = -1L; - String contentLengthHeader = proxiedRequest.getHeader(HttpHeaders.CONTENT_LENGTH); + String contentLengthHeader = proxiedRequest.getHeader(CONTENT_LENGTH); if (contentLengthHeader != null) { try { contentLength = Long.parseLong(contentLengthHeader); @@ -184,22 +192,29 @@ void sendRequest(Handler> responseHandler) { } } - long len = body.length(); - if (len >= 0) { - request.putHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(len)); + if (body == null) { + if (proxiedRequest.headers().contains(CONTENT_LENGTH)) { + request.putHeader(CONTENT_LENGTH, "0"); + } + request.end(); } else { - Boolean isChunked = HttpUtils.isChunked(proxiedRequest.headers()); - request.setChunked(len == -1 && Boolean.TRUE == isChunked); - } - - Pipe pipe = body.stream().pipe(); - pipe.endOnComplete(true); - pipe.endOnFailure(false); - pipe.to(request, ar -> { - if (ar.failed()) { - request.reset(); + long len = body.length(); + if (len >= 0) { + request.putHeader(CONTENT_LENGTH, Long.toString(len)); + } else { + Boolean isChunked = HttpUtils.isChunked(proxiedRequest.headers()); + request.setChunked(len == -1 && Boolean.TRUE == isChunked); } - }); + + Pipe pipe = body.stream().pipe(); + pipe.endOnComplete(true); + pipe.endOnFailure(false); + pipe.to(request, ar -> { + if (ar.failed()) { + request.reset(); + } + }); + } } private static boolean equals(HostAndPort hp1, HostAndPort hp2) { diff --git a/src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java b/src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java index 5080734..e4d3a3c 100644 --- a/src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java +++ b/src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation + * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -31,6 +31,8 @@ import java.util.Iterator; import java.util.List; +import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH; + class ProxiedResponse implements ProxyResponse { private final ProxiedRequest request; @@ -215,9 +217,11 @@ public void send(Handler> completionHandler) { } }); - // if (body == null) { - proxiedResponse.end(); + if (response != null && response.headers().contains(CONTENT_LENGTH)) { + proxiedResponse.putHeader(CONTENT_LENGTH, "0"); + } + proxiedResponse.end().onComplete(completionHandler); return; } diff --git a/src/test/java/io/vertx/httpproxy/ProxyTest.java b/src/test/java/io/vertx/httpproxy/ProxyTest.java index 235f826..c6c9ad7 100644 --- a/src/test/java/io/vertx/httpproxy/ProxyTest.java +++ b/src/test/java/io/vertx/httpproxy/ProxyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation + * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -27,6 +27,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH; + /** * @author Julien Viet */ @@ -103,6 +105,46 @@ public Future handleProxyResponse(ProxyContext context) { }); } + @Test + public void testFilterNullBodies(TestContext ctx) { + Async latch = ctx.async(3); + SocketAddress backend = startHttpBackend(ctx, 8081, req -> { + req.body().onComplete(ctx.asyncAssertSuccess(body -> { + ctx.assertEquals(0, body.length()); + ctx.assertEquals("0", req.getHeader(CONTENT_LENGTH)); + req.response().end("IGNORED_BACKEND_RESPONSE_BODY"); + })); + }); + startProxy(proxy -> proxy.origin(backend).addInterceptor(new ProxyInterceptor() { + @Override + public Future handleProxyRequest(ProxyContext context) { + context.request().setBody(null); + Future fut = context.sendRequest(); + fut.onComplete(ctx.asyncAssertSuccess(v -> latch.countDown())); + return fut; + } + + @Override + public Future handleProxyResponse(ProxyContext context) { + context.response().setBody(null); + Future fut = context.sendResponse(); + fut.onComplete(ctx.asyncAssertSuccess(v -> latch.countDown())); + return fut; + } + })); + HttpClient client = vertx.createHttpClient(); + client + .request(HttpMethod.POST, 8080, "localhost", "/") + .compose(req -> req + .send("IGNORED_CLIENT_REQUEST_BODY") + .compose(resp -> resp.body().map(resp)) + ).onComplete(ctx.asyncAssertSuccess(resp -> { + ctx.assertEquals(0, resp.body().result().length()); + ctx.assertEquals("0", resp.getHeader(CONTENT_LENGTH)); + latch.countDown(); + })); + } + @Test public void testUpstreamRefuse(TestContext ctx) { SocketAddress backend = SocketAddress.inetSocketAddress(8081, "localhost");