From d802d7e9367e82c3e25573e89582cc561344d3b2 Mon Sep 17 00:00:00 2001
From: Yash Shinde
Date: Fri, 10 Oct 2025 18:56:22 +0530
Subject: [PATCH 1/2] Added support to specify Custom Hop-By-Hop Headers
---
.../java/io/vertx/httpproxy/ProxyOptions.java | 43 +++++++++++++++
.../java/io/vertx/httpproxy/ProxyRequest.java | 13 +++++
.../vertx/httpproxy/impl/ProxiedRequest.java | 53 ++++++++++++-------
.../io/vertx/httpproxy/impl/ReverseProxy.java | 4 +-
4 files changed, 94 insertions(+), 19 deletions(-)
diff --git a/src/main/java/io/vertx/httpproxy/ProxyOptions.java b/src/main/java/io/vertx/httpproxy/ProxyOptions.java
index bae6bed..8846c8e 100644
--- a/src/main/java/io/vertx/httpproxy/ProxyOptions.java
+++ b/src/main/java/io/vertx/httpproxy/ProxyOptions.java
@@ -4,6 +4,12 @@
import io.vertx.codegen.json.annotations.JsonGen;
import io.vertx.core.json.JsonObject;
import io.vertx.httpproxy.cache.CacheOptions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static io.vertx.core.http.HttpHeaders.*;
+import static io.vertx.core.http.HttpHeaders.UPGRADE;
/**
* Proxy options.
@@ -17,8 +23,20 @@ public class ProxyOptions {
*/
public static final boolean DEFAULT_SUPPORT_WEBSOCKET = true;
+ public static final List DEFAULT_HOP_BY_HOP_HEADERS = new ArrayList<>(Arrays.asList(
+ CONNECTION.toString(),
+ KEEP_ALIVE.toString(),
+ PROXY_AUTHENTICATE.toString(),
+ PROXY_AUTHORIZATION.toString(),
+ TRANSFER_ENCODING.toString(),
+ UPGRADE.toString(),
+ "te",
+ "trailer"
+ ));
+
private CacheOptions cacheOptions;
private boolean supportWebSocket;
+ private List customHopHeaders;
public ProxyOptions(JsonObject json) {
ProxyOptionsConverter.fromJson(json, this);
@@ -26,6 +44,7 @@ public ProxyOptions(JsonObject json) {
public ProxyOptions() {
supportWebSocket = DEFAULT_SUPPORT_WEBSOCKET;
+ customHopHeaders = DEFAULT_HOP_BY_HOP_HEADERS;
}
/**
@@ -66,6 +85,30 @@ public ProxyOptions setSupportWebSocket(boolean supportWebSocket) {
return this;
}
+ /**
+ * @return custom hop-by-hop headers
+ */
+ public List getCustomHopHeaders() {
+ return customHopHeaders;
+ }
+
+ /**
+ * Sets custom hop-by-hop headers, overriding the default {@code DEFAULT_HOP_BY_HOP_HEADERS} headers.
+ *
+ * Warning: Please read the following specification before removing or modifying
+ * any hop-by-hop header:
+ *
+ * RFC 2616, Section 13.5.1
+ *
+ *
+ *
+ * @param customHopHeaders the list of hop-by-hop headers to set
+ */
+ public ProxyOptions setCustomHopHeaders(List customHopHeaders){
+ this.customHopHeaders = customHopHeaders;
+ return this;
+ }
+
@Override
public String toString() {
return toJson().toString();
diff --git a/src/main/java/io/vertx/httpproxy/ProxyRequest.java b/src/main/java/io/vertx/httpproxy/ProxyRequest.java
index 30d96d5..3d6e676 100644
--- a/src/main/java/io/vertx/httpproxy/ProxyRequest.java
+++ b/src/main/java/io/vertx/httpproxy/ProxyRequest.java
@@ -21,6 +21,7 @@
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.HostAndPort;
import io.vertx.httpproxy.impl.ProxiedRequest;
+import java.util.List;
/**
*
@@ -42,6 +43,18 @@ static ProxyRequest reverseProxy(HttpServerRequest proxiedRequest) {
return new ProxiedRequest(proxiedRequest);
}
+ /**
+ * Create a new {@code ProxyRequest} instance, the proxied request will be paused.
+ *
+ * @param proxiedRequest the {@code HttpServerRequest} that is proxied
+ * @param customHopHeaders list of Custom Hop-By-Hop Headers
+ * @return a reference to this, so the API can be used fluently
+ */
+ static ProxyRequest reverseProxy(HttpServerRequest proxiedRequest, List customHopHeaders) {
+ proxiedRequest.pause();
+ return new ProxiedRequest(proxiedRequest, customHopHeaders);
+ }
+
/**
* @return the HTTP version of the proxied request
*/
diff --git a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
index 29926f5..9cb1335 100644
--- a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
+++ b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
@@ -23,35 +23,23 @@
import io.vertx.core.net.HostAndPort;
import io.vertx.core.streams.Pipe;
import io.vertx.httpproxy.Body;
-import io.vertx.httpproxy.MediaType;
import io.vertx.httpproxy.ProxyRequest;
import io.vertx.httpproxy.ProxyResponse;
-
+import io.vertx.httpproxy.ProxyOptions;
import java.util.Map;
import java.util.Objects;
+import java.util.HashSet;
+import java.util.List;
-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(CONNECTION, "whatever")
- .add(KEEP_ALIVE, "whatever")
- .add(PROXY_AUTHENTICATE, "whatever")
- .add(PROXY_AUTHORIZATION, "whatever")
- .add("te", "whatever")
- .add("trailer", "whatever")
- .add(TRANSFER_ENCODING, "whatever")
- .add(UPGRADE, "whatever");
+ private static final HashSet DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(ProxyOptions.DEFAULT_HOP_BY_HOP_HEADERS);
+ private final HashSet HOP_BY_HOP_HEADERS;
final ContextInternal context;
private HttpMethod method;
private final HttpVersion version;
@@ -88,6 +76,35 @@ public ProxiedRequest(HttpServerRequest proxiedRequest) {
this.proxiedRequest = proxiedRequest;
this.context = ((HttpServerRequestInternal) proxiedRequest).context();
this.authority = null; // null is used as a signal to indicate an unchanged authority
+ this.HOP_BY_HOP_HEADERS = DEFAULT_HOP_BY_HOP_HEADERS;
+ }
+
+ public ProxiedRequest(HttpServerRequest proxiedRequest, List customHopHeaders) {
+
+ // Determine content length
+ long contentLength = -1L;
+ String contentLengthHeader = proxiedRequest.getHeader(CONTENT_LENGTH);
+ if (contentLengthHeader != null) {
+ try {
+ contentLength = Long.parseLong(contentLengthHeader);
+ } catch (NumberFormatException e) {
+ // Ignore ???
+ }
+ }
+
+ // Content type
+ String contentType = proxiedRequest.getHeader(HttpHeaders.CONTENT_TYPE);
+
+ this.method = proxiedRequest.method();
+ this.version = proxiedRequest.version();
+ this.body = Body.body(proxiedRequest, contentLength, contentType);
+ this.uri = proxiedRequest.uri();
+ this.headers = MultiMap.caseInsensitiveMultiMap().addAll(proxiedRequest.headers());
+ this.absoluteURI = proxiedRequest.absoluteURI();
+ this.proxiedRequest = proxiedRequest;
+ this.context = ((HttpServerRequestInternal) proxiedRequest).context();
+ this.authority = null; // null is used as a signal to indicate an unchanged authority
+ this.HOP_BY_HOP_HEADERS = new HashSet<>(customHopHeaders);
}
@Override
@@ -173,7 +190,7 @@ Future sendRequest() {
for (Map.Entry header : headers) {
String name = header.getKey();
String value = header.getValue();
- if (!HOP_BY_HOP_HEADERS.contains(name) && !name.equalsIgnoreCase(HttpHeaders.HOST.toString())) {
+ if (!HOP_BY_HOP_HEADERS.contains(name)) {
request.headers().add(name, value);
}
}
diff --git a/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java b/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
index cda186f..cbffa37 100644
--- a/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
+++ b/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
@@ -38,6 +38,7 @@ public class ReverseProxy implements HttpProxy {
private final boolean supportWebSocket;
private OriginRequestProvider originRequestProvider = (pc) -> Future.failedFuture("No origin available");
private final List interceptors = new ArrayList<>();
+ private final List hopByHopHeaders;
public ReverseProxy(ProxyOptions options, HttpClient client) {
CacheOptions cacheOptions = options.getCacheOptions();
@@ -47,6 +48,7 @@ public ReverseProxy(ProxyOptions options, HttpClient client) {
}
this.client = client;
this.supportWebSocket = options.getSupportWebSocket();
+ this.hopByHopHeaders = options.getCustomHopHeaders();
}
public Cache newCache(CacheOptions options, Vertx vertx) {
@@ -73,7 +75,7 @@ public HttpProxy addInterceptor(ProxyInterceptor interceptor, boolean supportsWe
@Override
public void handle(HttpServerRequest request) {
- ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);
+ ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request, hopByHopHeaders);
// Encoding sanity check
Boolean chunked = HttpUtils.isChunked(request.headers());
From 1f8fa9bee17ee28f55d4b31ba8b5f09d478759c8 Mon Sep 17 00:00:00 2001
From: Yash Shinde
Date: Mon, 13 Oct 2025 10:29:36 +0530
Subject: [PATCH 2/2] Replaced List with Set for custom hop headers, Added a
function addCustomHopHeader to allow adding headers to current hop headers
set
---
.../java/io/vertx/httpproxy/ProxyOptions.java | 35 ++++++++++----
.../java/io/vertx/httpproxy/ProxyRequest.java | 30 +++++++-----
.../vertx/httpproxy/impl/ProxiedRequest.java | 47 ++++++-------------
.../io/vertx/httpproxy/impl/ReverseProxy.java | 8 ++--
4 files changed, 62 insertions(+), 58 deletions(-)
diff --git a/src/main/java/io/vertx/httpproxy/ProxyOptions.java b/src/main/java/io/vertx/httpproxy/ProxyOptions.java
index 8846c8e..f1b8683 100644
--- a/src/main/java/io/vertx/httpproxy/ProxyOptions.java
+++ b/src/main/java/io/vertx/httpproxy/ProxyOptions.java
@@ -4,15 +4,20 @@
import io.vertx.codegen.json.annotations.JsonGen;
import io.vertx.core.json.JsonObject;
import io.vertx.httpproxy.cache.CacheOptions;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
-
-import static io.vertx.core.http.HttpHeaders.*;
+import java.util.HashSet;
+import java.util.Set;
+
+import static io.vertx.core.http.HttpHeaders.CONNECTION;
+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;
/**
* Proxy options.
+ *
*/
@DataObject
@JsonGen(publicConverter = false)
@@ -23,7 +28,7 @@ public class ProxyOptions {
*/
public static final boolean DEFAULT_SUPPORT_WEBSOCKET = true;
- public static final List DEFAULT_HOP_BY_HOP_HEADERS = new ArrayList<>(Arrays.asList(
+ public static final Set DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(Arrays.asList(
CONNECTION.toString(),
KEEP_ALIVE.toString(),
PROXY_AUTHENTICATE.toString(),
@@ -36,7 +41,7 @@ public class ProxyOptions {
private CacheOptions cacheOptions;
private boolean supportWebSocket;
- private List customHopHeaders;
+ private Set customHopHeaders;
public ProxyOptions(JsonObject json) {
ProxyOptionsConverter.fromJson(json, this);
@@ -88,7 +93,7 @@ public ProxyOptions setSupportWebSocket(boolean supportWebSocket) {
/**
* @return custom hop-by-hop headers
*/
- public List getCustomHopHeaders() {
+ public Set getCustomHopHeaders() {
return customHopHeaders;
}
@@ -103,9 +108,21 @@ public List getCustomHopHeaders() {
*
*
* @param customHopHeaders the list of hop-by-hop headers to set
+ * @return a reference to this, so the API can be used fluently
+ */
+ public ProxyOptions setCustomHopHeaders(Set customHopHeaders){
+ this.customHopHeaders = new HashSet(customHopHeaders);
+ return this;
+ }
+
+ /**
+ * Add a custom hop-by-hop header
+ *
+ * @param customHopHeader a custom hop-by-hop header
+ * @return a reference to this, so the API can be used fluently
*/
- public ProxyOptions setCustomHopHeaders(List customHopHeaders){
- this.customHopHeaders = customHopHeaders;
+ public ProxyOptions addCustomHopHeader(String customHopHeader) {
+ this.customHopHeaders.add(customHopHeader);
return this;
}
diff --git a/src/main/java/io/vertx/httpproxy/ProxyRequest.java b/src/main/java/io/vertx/httpproxy/ProxyRequest.java
index 3d6e676..3c20351 100644
--- a/src/main/java/io/vertx/httpproxy/ProxyRequest.java
+++ b/src/main/java/io/vertx/httpproxy/ProxyRequest.java
@@ -21,7 +21,7 @@
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.HostAndPort;
import io.vertx.httpproxy.impl.ProxiedRequest;
-import java.util.List;
+import java.util.Set;
/**
*
@@ -43,18 +43,6 @@ static ProxyRequest reverseProxy(HttpServerRequest proxiedRequest) {
return new ProxiedRequest(proxiedRequest);
}
- /**
- * Create a new {@code ProxyRequest} instance, the proxied request will be paused.
- *
- * @param proxiedRequest the {@code HttpServerRequest} that is proxied
- * @param customHopHeaders list of Custom Hop-By-Hop Headers
- * @return a reference to this, so the API can be used fluently
- */
- static ProxyRequest reverseProxy(HttpServerRequest proxiedRequest, List customHopHeaders) {
- proxiedRequest.pause();
- return new ProxiedRequest(proxiedRequest, customHopHeaders);
- }
-
/**
* @return the HTTP version of the proxied request
*/
@@ -191,4 +179,20 @@ default Future proxy(HttpClientRequest request) {
*/
ProxyResponse response();
+ /**
+ * Set Custom Hop-By-Hop Headers
+ *
+ * @return a reference to this, so the API can be used fluently
+ */
+ @Fluent
+ ProxyRequest setCustomHopHeaders(Set customHopHeaders);
+
+ /**
+ * Add Custom Hop-By-Hop Header
+ *
+ * @return a reference to this, so the API can be used fluently
+ */
+ @Fluent
+ ProxyRequest addCustomHopHeader(String customHopHeader);
+
}
diff --git a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
index 9cb1335..b9c40a9 100644
--- a/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
+++ b/src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
@@ -29,17 +29,16 @@
import java.util.Map;
import java.util.Objects;
import java.util.HashSet;
-import java.util.List;
-
+import java.util.Set;
import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH;
public class ProxiedRequest implements ProxyRequest {
private static final CharSequence X_FORWARDED_HOST = HttpHeaders.createOptimized("x-forwarded-host");
- private static final HashSet DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(ProxyOptions.DEFAULT_HOP_BY_HOP_HEADERS);
+ private static final Set DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(ProxyOptions.DEFAULT_HOP_BY_HOP_HEADERS);
- private final HashSet HOP_BY_HOP_HEADERS;
+ private Set HOP_BY_HOP_HEADERS;
final ContextInternal context;
private HttpMethod method;
private final HttpVersion version;
@@ -79,34 +78,6 @@ public ProxiedRequest(HttpServerRequest proxiedRequest) {
this.HOP_BY_HOP_HEADERS = DEFAULT_HOP_BY_HOP_HEADERS;
}
- public ProxiedRequest(HttpServerRequest proxiedRequest, List customHopHeaders) {
-
- // Determine content length
- long contentLength = -1L;
- String contentLengthHeader = proxiedRequest.getHeader(CONTENT_LENGTH);
- if (contentLengthHeader != null) {
- try {
- contentLength = Long.parseLong(contentLengthHeader);
- } catch (NumberFormatException e) {
- // Ignore ???
- }
- }
-
- // Content type
- String contentType = proxiedRequest.getHeader(HttpHeaders.CONTENT_TYPE);
-
- this.method = proxiedRequest.method();
- this.version = proxiedRequest.version();
- this.body = Body.body(proxiedRequest, contentLength, contentType);
- this.uri = proxiedRequest.uri();
- this.headers = MultiMap.caseInsensitiveMultiMap().addAll(proxiedRequest.headers());
- this.absoluteURI = proxiedRequest.absoluteURI();
- this.proxiedRequest = proxiedRequest;
- this.context = ((HttpServerRequestInternal) proxiedRequest).context();
- this.authority = null; // null is used as a signal to indicate an unchanged authority
- this.HOP_BY_HOP_HEADERS = new HashSet<>(customHopHeaders);
- }
-
@Override
public HttpVersion version() {
return version;
@@ -258,4 +229,16 @@ public Future send(HttpClientRequest request) {
this.request = request;
return sendRequest();
}
+
+ @Override
+ public ProxyRequest setCustomHopHeaders(Set customHopHeaders) {
+ this.HOP_BY_HOP_HEADERS = new HashSet<>(customHopHeaders);
+ return this;
+ }
+
+ @Override
+ public ProxyRequest addCustomHopHeader(String customHopHeader) {
+ this.HOP_BY_HOP_HEADERS.add(customHopHeader);
+ return this;
+ }
}
diff --git a/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java b/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
index cbffa37..eb8e31e 100644
--- a/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
+++ b/src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
@@ -24,9 +24,7 @@
import io.vertx.httpproxy.*;
import io.vertx.httpproxy.cache.CacheOptions;
import io.vertx.httpproxy.spi.cache.Cache;
-
import java.util.*;
-
import static io.vertx.core.http.HttpHeaders.CONNECTION;
import static io.vertx.core.http.HttpHeaders.HOST;
import static io.vertx.core.http.HttpHeaders.UPGRADE;
@@ -38,7 +36,7 @@ public class ReverseProxy implements HttpProxy {
private final boolean supportWebSocket;
private OriginRequestProvider originRequestProvider = (pc) -> Future.failedFuture("No origin available");
private final List interceptors = new ArrayList<>();
- private final List hopByHopHeaders;
+ private final Set hopByHopHeaders;
public ReverseProxy(ProxyOptions options, HttpClient client) {
CacheOptions cacheOptions = options.getCacheOptions();
@@ -75,7 +73,9 @@ public HttpProxy addInterceptor(ProxyInterceptor interceptor, boolean supportsWe
@Override
public void handle(HttpServerRequest request) {
- ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request, hopByHopHeaders);
+ ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);
+
+ proxyRequest.setCustomHopHeaders(hopByHopHeaders);
// Encoding sanity check
Boolean chunked = HttpUtils.isChunked(request.headers());