Skip to content

Commit 55fdd36

Browse files
authored
Merge branch 'main' into issue/67
2 parents 341e286 + de37cc1 commit 55fdd36

31 files changed

+667
-476
lines changed

.github/workflows/ci-5.x.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- os: windows-latest
1919
jdk: 11
2020
- os: ubuntu-latest
21-
jdk: 17
21+
jdk: 21
2222
uses: ./.github/workflows/ci.yml
2323
with:
2424
branch: ${{ github.event.pull_request.head.sha || github.ref_name }}

pom.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<parent>
2121
<groupId>io.vertx</groupId>
2222
<artifactId>vertx5-parent</artifactId>
23-
<version>4</version>
23+
<version>9</version>
2424
</parent>
2525

2626
<artifactId>vertx-http-proxy</artifactId>
@@ -36,7 +36,6 @@
3636

3737
<properties>
3838
<fatjar.dir>${project.build.directory}</fatjar.dir>
39-
<jar.manifest>${project.basedir}/src/main/resources/META-INF/MANIFEST.MF</jar.manifest>
4039
</properties>
4140

4241
<dependencyManagement>
@@ -134,4 +133,4 @@
134133
</plugin>
135134
</plugins>
136135
</build>
137-
</project>
136+
</project>

src/main/asciidoc/index.adoc

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ Likewise with the proxy response
120120
{@link examples.HttpProxyExamples#outboundInterceptor}
121121
----
122122

123-
Interceptors will not apply to WebSocket handshake packets by default. While in some use cases (e.g. changing the WebSocket request path), you can overwrite {@link io.vertx.httpproxy.ProxyInterceptor#allowApplyToWebSocket} method to allow interceptors apply to WebSocket.
124-
125123
==== Body filtering
126124

127125
You can filter body by simply replacing the original {@link io.vertx.httpproxy.Body} with a new one
@@ -143,27 +141,41 @@ You can change the control, e.g you can send a response immediately to the user-
143141
{@link examples.HttpProxyExamples#immediateResponse}
144142
----
145143

146-
==== Header interceptor
144+
==== Head interceptor
145+
146+
You can use the {@link io.vertx.httpproxy.interceptors.HeadInterceptor} to change HTTP request/response heads:
147+
148+
- request path
149+
- query params
150+
- request and response headers
151+
152+
A {@link io.vertx.httpproxy.interceptors.HeadInterceptor} is created and configured with a {@link io.vertx.httpproxy.interceptors.HeadInterceptorBuilder}.
147153

148-
You can apply header interceptors to change headers from the request and response with common operations:
154+
The builder methods can be invoked several times.
155+
Operations on the path will be invoked in the order of configuration.
156+
That goes for operations on query parameters, request headers and response headers.
157+
158+
===== Headers interception
159+
160+
You can apply the interceptor to change headers from the request and response with common operations:
149161

150162
[source,java]
151163
----
152164
{@link examples.HttpProxyExamples#headerInterceptorFilter}
153165
----
154166

155-
Check out the {@link io.vertx.httpproxy.interceptors.HeadersInterceptor} class for details about the available methods.
167+
Check out {@link io.vertx.httpproxy.interceptors.HeadInterceptorBuilder} for details about the available methods.
156168

157-
==== Query interceptor
169+
===== Query params interception
158170

159-
You can use query interceptors to update or remove the query parameters:
171+
You can apply the interceptor to update or remove query parameters:
160172

161173
[source,java]
162174
----
163175
{@link examples.HttpProxyExamples#queryInterceptorAdd}
164176
----
165177

166-
You can also refer to {@link io.vertx.httpproxy.interceptors.QueryInterceptor} for more information.
178+
You can also refer to {@link io.vertx.httpproxy.interceptors.HeadInterceptorBuilder} for more information.
167179

168180
==== Body interceptor
169181

@@ -176,9 +188,11 @@ You can use body interceptor to create body transformations for common data type
176188

177189
Please check the {@link io.vertx.httpproxy.interceptors.BodyTransformer} for other supported transformations.
178190

179-
==== WebSocket interceptor
191+
==== Interception and WebSocket upgrades
192+
193+
By default, interceptors are not invoked during WebSocket upgrades.
180194

181-
You can use WebSocket interceptor to wrap an interceptor to let it allow WebSocket handling:
195+
To make an interceptor available during the WebSocket handshake, use {@link io.vertx.httpproxy.HttpProxy#addInterceptor(io.vertx.httpproxy.ProxyInterceptor, boolean)}:
182196

183197
[source,java]
184198
----

src/main/generated/io/vertx/httpproxy/ProxyOptionsConverter.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
import io.vertx.core.json.JsonArray;
55
import java.time.Instant;
66
import java.time.format.DateTimeFormatter;
7-
import java.util.Base64;
87

98
/**
109
* Converter and mapper for {@link io.vertx.httpproxy.ProxyOptions}.
1110
* NOTE: This class has been automatically generated from the {@link io.vertx.httpproxy.ProxyOptions} original class using Vert.x codegen.
1211
*/
1312
public class ProxyOptionsConverter {
1413

15-
private static final Base64.Decoder BASE64_DECODER = Base64.getUrlDecoder();
16-
private static final Base64.Encoder BASE64_ENCODER = Base64.getUrlEncoder().withoutPadding();
17-
1814
static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, ProxyOptions obj) {
1915
for (java.util.Map.Entry<String, Object> member : json) {
2016
switch (member.getKey()) {

src/main/generated/io/vertx/httpproxy/cache/CacheOptionsConverter.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
import io.vertx.core.json.JsonArray;
55
import java.time.Instant;
66
import java.time.format.DateTimeFormatter;
7-
import java.util.Base64;
87

98
/**
109
* Converter and mapper for {@link io.vertx.httpproxy.cache.CacheOptions}.
1110
* NOTE: This class has been automatically generated from the {@link io.vertx.httpproxy.cache.CacheOptions} original class using Vert.x codegen.
1211
*/
1312
public class CacheOptionsConverter {
1413

15-
private static final Base64.Decoder BASE64_DECODER = Base64.getUrlDecoder();
16-
private static final Base64.Encoder BASE64_ENCODER = Base64.getUrlEncoder().withoutPadding();
17-
1814
static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, CacheOptions obj) {
1915
for (java.util.Map.Entry<String, Object> member : json) {
2016
switch (member.getKey()) {

src/main/java/examples/HttpProxyExamples.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
import io.vertx.core.net.SocketAddress;
1414
import io.vertx.httpproxy.*;
1515
import io.vertx.httpproxy.cache.CacheOptions;
16-
import io.vertx.httpproxy.interceptors.*;
16+
import io.vertx.httpproxy.interceptors.BodyInterceptor;
17+
import io.vertx.httpproxy.interceptors.BodyTransformer;
18+
import io.vertx.httpproxy.interceptors.HeadInterceptor;
1719

1820
import java.util.Set;
1921

@@ -109,12 +111,12 @@ public Future<Void> handleProxyResponse(ProxyContext context) {
109111
public void headerInterceptorFilter(HttpProxy proxy, Set<CharSequence> shouldRemove) {
110112
// remove a set of headers
111113
proxy.addInterceptor(
112-
HeadersInterceptor.filterResponseHeaders(shouldRemove));
114+
HeadInterceptor.builder().filteringResponseHeaders(shouldRemove).build());
113115
}
114116

115117
public void queryInterceptorAdd(HttpProxy proxy, String key, String value) {
116118
proxy.addInterceptor(
117-
QueryInterceptor.setQueryParam(key, value));
119+
HeadInterceptor.builder().settingQueryParam(key, value).build());
118120
}
119121

120122
public void bodyInterceptorJson(HttpProxy proxy) {
@@ -127,9 +129,10 @@ public void bodyInterceptorJson(HttpProxy proxy) {
127129
}
128130

129131
public void webSocketInterceptorPath(HttpProxy proxy) {
130-
proxy.addInterceptor(
131-
WebSocketInterceptor.allow(PathInterceptor.addPrefix("/api"))
132-
);
132+
HeadInterceptor interceptor = HeadInterceptor.builder()
133+
.addingPathPrefix("/api")
134+
.build();
135+
proxy.addInterceptor(interceptor, true);
133136
}
134137

135138
public void immediateResponse(HttpProxy proxy) {

src/main/java/io/vertx/httpproxy/HttpProxy.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,29 @@ default HttpProxy originSelector(Function<HttpServerRequest, Future<SocketAddres
102102

103103
/**
104104
* Add an interceptor to the interceptor chain.
105+
* <p>
106+
* Interceptors are invoked in order of configuration.
107+
* When added with this method, it is considered the interceptor doesn't support WebSocket upgrades.
105108
*
106-
* @param interceptor
109+
* @param interceptor the {@link ProxyInterceptor} to add
107110
* @return a reference to this, so the API can be used fluently
108111
*/
109112
@Fluent
110-
HttpProxy addInterceptor(ProxyInterceptor interceptor);
113+
default HttpProxy addInterceptor(ProxyInterceptor interceptor) {
114+
return addInterceptor(interceptor, false);
115+
}
116+
117+
/**
118+
* Add an interceptor to the interceptor chain.
119+
* <p>
120+
* Interceptors are invoked in order of configuration.
121+
*
122+
* @param interceptor the {@link ProxyInterceptor} to add
123+
* @param supportsWebSocketUpgrade whether the interceptor supports WebSocket upgrades
124+
* @return a reference to this, so the API can be used fluently
125+
*/
126+
@Fluent
127+
HttpProxy addInterceptor(ProxyInterceptor interceptor, boolean supportsWebSocketUpgrade);
111128

112129
/**
113130
* Handle the <i><b>outbound</b></i> {@code HttpServerRequest}.

src/main/java/io/vertx/httpproxy/ProxyInterceptor.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,4 @@ default Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
2828
default Future<Void> handleProxyResponse(ProxyContext context) {
2929
return context.sendResponse();
3030
}
31-
32-
/**
33-
* Used to set whether to apply the interceptor to the WebSocket
34-
* handshake packet. The default value is false.
35-
*
36-
* @return the boolean value
37-
*/
38-
default boolean allowApplyToWebSocket() {
39-
return false;
40-
}
4131
}

src/main/java/io/vertx/httpproxy/impl/ProxiedRequest.java

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
2+
* Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -10,9 +10,14 @@
1010
*/
1111
package io.vertx.httpproxy.impl;
1212

13-
import io.vertx.core.*;
13+
import io.vertx.core.Future;
14+
import io.vertx.core.MultiMap;
1415
import io.vertx.core.buffer.Buffer;
15-
import io.vertx.core.http.*;
16+
import io.vertx.core.http.HttpClientRequest;
17+
import io.vertx.core.http.HttpHeaders;
18+
import io.vertx.core.http.HttpMethod;
19+
import io.vertx.core.http.HttpServerRequest;
20+
import io.vertx.core.http.HttpVersion;
1621
import io.vertx.core.internal.ContextInternal;
1722
import io.vertx.core.internal.http.HttpServerRequestInternal;
1823
import io.vertx.core.net.HostAndPort;
@@ -24,19 +29,27 @@
2429
import java.util.Map;
2530
import java.util.Objects;
2631

32+
import static io.vertx.core.http.HttpHeaders.CONNECTION;
33+
import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH;
34+
import static io.vertx.core.http.HttpHeaders.KEEP_ALIVE;
35+
import static io.vertx.core.http.HttpHeaders.PROXY_AUTHENTICATE;
36+
import static io.vertx.core.http.HttpHeaders.PROXY_AUTHORIZATION;
37+
import static io.vertx.core.http.HttpHeaders.TRANSFER_ENCODING;
38+
import static io.vertx.core.http.HttpHeaders.UPGRADE;
39+
2740
public class ProxiedRequest implements ProxyRequest {
2841

2942
private static final CharSequence X_FORWARDED_HOST = HttpHeaders.createOptimized("x-forwarded-host");
3043

3144
private static final MultiMap HOP_BY_HOP_HEADERS = MultiMap.caseInsensitiveMultiMap()
32-
.add(HttpHeaders.CONNECTION, "whatever")
33-
.add(HttpHeaders.KEEP_ALIVE, "whatever")
34-
.add(HttpHeaders.PROXY_AUTHENTICATE, "whatever")
35-
.add(HttpHeaders.PROXY_AUTHORIZATION, "whatever")
45+
.add(CONNECTION, "whatever")
46+
.add(KEEP_ALIVE, "whatever")
47+
.add(PROXY_AUTHENTICATE, "whatever")
48+
.add(PROXY_AUTHORIZATION, "whatever")
3649
.add("te", "whatever")
3750
.add("trailer", "whatever")
38-
.add(HttpHeaders.TRANSFER_ENCODING, "whatever")
39-
.add(HttpHeaders.UPGRADE, "whatever");
51+
.add(TRANSFER_ENCODING, "whatever")
52+
.add(UPGRADE, "whatever");
4053

4154
final ContextInternal context;
4255
private HttpMethod method;
@@ -53,7 +66,7 @@ public ProxiedRequest(HttpServerRequest proxiedRequest) {
5366

5467
// Determine content length
5568
long contentLength = -1L;
56-
String contentLengthHeader = proxiedRequest.getHeader(HttpHeaders.CONTENT_LENGTH);
69+
String contentLengthHeader = proxiedRequest.getHeader(CONTENT_LENGTH);
5770
if (contentLengthHeader != null) {
5871
try {
5972
contentLength = Long.parseLong(contentLengthHeader);
@@ -170,24 +183,31 @@ Future<ProxyResponse> sendRequest() {
170183
}
171184
}
172185

173-
long len = body.length();
174-
if (len >= 0) {
175-
request.putHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(len));
186+
if (body == null) {
187+
if (proxiedRequest.headers().contains(CONTENT_LENGTH)) {
188+
request.putHeader(CONTENT_LENGTH, "0");
189+
}
190+
request.end();
176191
} else {
177-
Boolean isChunked = HttpUtils.isChunked(proxiedRequest.headers());
178-
request.setChunked(len == -1 && Boolean.TRUE == isChunked);
179-
}
180-
181-
Pipe<Buffer> pipe = body.stream().pipe();
182-
pipe.endOnComplete(true);
183-
pipe.endOnFailure(false);
184-
pipe.to(request).onComplete(ar -> {
185-
if (ar.failed()) {
186-
request.reset();
192+
long len = body.length();
193+
if (len >= 0) {
194+
request.putHeader(CONTENT_LENGTH, Long.toString(len));
195+
} else {
196+
Boolean isChunked = HttpUtils.isChunked(proxiedRequest.headers());
197+
request.setChunked(len == -1 && Boolean.TRUE == isChunked);
187198
}
188-
});
189199

190-
return request.response().<ProxyResponse>map(r -> {
200+
Pipe<Buffer> pipe = body.stream().pipe();
201+
pipe.endOnComplete(true);
202+
pipe.endOnFailure(false);
203+
pipe.to(request).onComplete(ar -> {
204+
if (ar.failed()) {
205+
request.reset();
206+
}
207+
});
208+
}
209+
210+
return request.response().map(r -> {
191211
r.pause(); // Pause it
192212
return new ProxiedResponse(this, proxiedRequest.response(), r);
193213
});

src/main/java/io/vertx/httpproxy/impl/ProxiedResponse.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
2+
* Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -10,11 +10,8 @@
1010
*/
1111
package io.vertx.httpproxy.impl;
1212

13-
import io.vertx.core.AsyncResult;
1413
import io.vertx.core.Future;
15-
import io.vertx.core.Handler;
1614
import io.vertx.core.MultiMap;
17-
import io.vertx.core.Promise;
1815
import io.vertx.core.buffer.Buffer;
1916
import io.vertx.core.http.HttpClientResponse;
2017
import io.vertx.core.http.HttpHeaders;
@@ -31,6 +28,8 @@
3128
import java.util.Iterator;
3229
import java.util.List;
3330

31+
import static io.vertx.core.http.HttpHeaders.CONTENT_LENGTH;
32+
3433
class ProxiedResponse implements ProxyResponse {
3534

3635
private final ProxiedRequest request;
@@ -214,8 +213,10 @@ public Future<Void> send() {
214213
}
215214
});
216215

217-
//
218216
if (body == null) {
217+
if (response != null && response.headers().contains(CONTENT_LENGTH)) {
218+
proxiedResponse.putHeader(CONTENT_LENGTH, "0");
219+
}
219220
return proxiedResponse.end();
220221
} else {
221222
long len = body.length();

0 commit comments

Comments
 (0)