Skip to content
Open
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
60 changes: 60 additions & 0 deletions src/main/java/io/vertx/httpproxy/ProxyOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@
import io.vertx.codegen.json.annotations.JsonGen;
import io.vertx.core.json.JsonObject;
import io.vertx.httpproxy.cache.CacheOptions;
import java.util.Arrays;
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)
Expand All @@ -17,15 +28,28 @@ public class ProxyOptions {
*/
public static final boolean DEFAULT_SUPPORT_WEBSOCKET = true;

public static final Set<String> DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(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 Set<String> customHopHeaders;

public ProxyOptions(JsonObject json) {
ProxyOptionsConverter.fromJson(json, this);
}

public ProxyOptions() {
supportWebSocket = DEFAULT_SUPPORT_WEBSOCKET;
customHopHeaders = DEFAULT_HOP_BY_HOP_HEADERS;
}

/**
Expand Down Expand Up @@ -66,6 +90,42 @@ public ProxyOptions setSupportWebSocket(boolean supportWebSocket) {
return this;
}

/**
* @return custom hop-by-hop headers
*/
public Set<String> getCustomHopHeaders() {
return customHopHeaders;
}

/**
* Sets custom hop-by-hop headers, overriding the default {@code DEFAULT_HOP_BY_HOP_HEADERS} headers.
* <p>
* <b>Warning:</b> Please read the following specification before removing or modifying
* any hop-by-hop header:
* <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1">
* RFC 2616, Section 13.5.1
* </a>
* </p>
*
* @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<String> customHopHeaders){
this.customHopHeaders = new HashSet<String>(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 addCustomHopHeader(String customHopHeader) {
this.customHopHeaders.add(customHopHeader);
return this;
}

@Override
public String toString() {
return toJson().toString();
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/io/vertx/httpproxy/ProxyRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.Set;

/**
*
Expand Down Expand Up @@ -178,4 +179,20 @@ default Future<Void> 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<String> customHopHeaders);

/**
* Add Custom Hop-By-Hop Header
*
* @return a reference to this, so the API can be used fluently
*/
@Fluent
ProxyRequest addCustomHopHeader(String customHopHeader);

}
38 changes: 19 additions & 19 deletions src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,22 @@
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 static io.vertx.core.http.HttpHeaders.CONNECTION;
import java.util.HashSet;
import java.util.Set;
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 Set<String> DEFAULT_HOP_BY_HOP_HEADERS = new HashSet<>(ProxyOptions.DEFAULT_HOP_BY_HOP_HEADERS);

private Set<String> HOP_BY_HOP_HEADERS;
final ContextInternal context;
private HttpMethod method;
private final HttpVersion version;
Expand Down Expand Up @@ -88,6 +75,7 @@ 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;
}

@Override
Expand Down Expand Up @@ -173,7 +161,7 @@ Future<ProxyResponse> sendRequest() {
for (Map.Entry<String, String> 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);
}
}
Expand Down Expand Up @@ -241,4 +229,16 @@ public Future<ProxyResponse> send(HttpClientRequest request) {
this.request = request;
return sendRequest();
}

@Override
public ProxyRequest setCustomHopHeaders(Set<String> 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;
}
}
6 changes: 4 additions & 2 deletions src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,6 +36,7 @@ public class ReverseProxy implements HttpProxy {
private final boolean supportWebSocket;
private OriginRequestProvider originRequestProvider = (pc) -> Future.failedFuture("No origin available");
private final List<ProxyInterceptorEntry> interceptors = new ArrayList<>();
private final Set<String> hopByHopHeaders;

public ReverseProxy(ProxyOptions options, HttpClient client) {
CacheOptions cacheOptions = options.getCacheOptions();
Expand All @@ -47,6 +46,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) {
Expand Down Expand Up @@ -75,6 +75,8 @@ public HttpProxy addInterceptor(ProxyInterceptor interceptor, boolean supportsWe
public void handle(HttpServerRequest request) {
ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);

proxyRequest.setCustomHopHeaders(hopByHopHeaders);

// Encoding sanity check
Boolean chunked = HttpUtils.isChunked(request.headers());
if (chunked == null) {
Expand Down
Loading