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
70 changes: 45 additions & 25 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 All @@ -10,9 +10,14 @@
*/
package io.vertx.httpproxy.impl;

import io.vertx.core.*;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.internal.ContextInternal;
import io.vertx.core.internal.http.HttpServerRequestInternal;
import io.vertx.core.net.HostAndPort;
Expand All @@ -24,19 +29,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 @@ -53,7 +66,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 @@ -170,24 +183,31 @@ Future<ProxyResponse> sendRequest() {
}
}

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).onComplete(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);
}
});

return request.response().<ProxyResponse>map(r -> {
Pipe<Buffer> pipe = body.stream().pipe();
pipe.endOnComplete(true);
pipe.endOnFailure(false);
pipe.to(request).onComplete(ar -> {
if (ar.failed()) {
request.reset();
}
});
}

return request.response().map(r -> {
r.pause(); // Pause it
return new ProxiedResponse(this, proxiedRequest.response(), r);
});
Expand Down
11 changes: 6 additions & 5 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 All @@ -10,11 +10,8 @@
*/
package io.vertx.httpproxy.impl;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpHeaders;
Expand All @@ -31,6 +28,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 @@ -214,8 +213,10 @@ public Future<Void> send() {
}
});

//
if (body == null) {
if (response != null && response.headers().contains(CONTENT_LENGTH)) {
proxiedResponse.putHeader(CONTENT_LENGTH, "0");
}
return proxiedResponse.end();
} else {
long len = body.length();
Expand Down
44 changes: 43 additions & 1 deletion src/test/java/io/vertx/tests/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 @@ -31,6 +31,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 @@ -115,6 +117,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;
}
}));
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