Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -184,22 +192,29 @@ void sendRequest(Handler<AsyncResult<ProxyResponse>> 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<Buffer> 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<Buffer> 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) {
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -215,9 +217,11 @@ public void send(Handler<AsyncResult<Void>> completionHandler) {
}
});

//
if (body == null) {
proxiedResponse.end();
if (response != null && response.headers().contains(CONTENT_LENGTH)) {
proxiedResponse.putHeader(CONTENT_LENGTH, "0");
}
proxiedResponse.end().onComplete(completionHandler);
return;
}

Expand Down
44 changes: 43 additions & 1 deletion src/test/java/io/vertx/httpproxy/ProxyTest.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -27,6 +27,8 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH;

/**
* @author <a href="mailto:[email protected]">Julien Viet</a>
*/
Expand Down Expand Up @@ -103,6 +105,46 @@ public Future<Void> 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<ProxyResponse> handleProxyRequest(ProxyContext context) {
context.request().setBody(null);
Future<ProxyResponse> fut = context.sendRequest();
fut.onComplete(ctx.asyncAssertSuccess(v -> latch.countDown()));
return fut;
}

@Override
public Future<Void> handleProxyResponse(ProxyContext context) {
context.response().setBody(null);
Future<Void> 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");
Expand Down
Loading