Skip to content

Commit d7931f0

Browse files
authored
WebSocket interception API enhancement (#108)
Closes #103 Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
1 parent 5e44cae commit d7931f0

File tree

8 files changed

+58
-95
lines changed

8 files changed

+58
-95
lines changed

src/main/asciidoc/index.adoc

Lines changed: 4 additions & 4 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
@@ -190,9 +188,11 @@ You can use body interceptor to create body transformations for common data type
190188

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

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

195-
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):
196196

197197
[source,java]
198198
----

src/main/java/examples/HttpProxyExamples.java

Lines changed: 7 additions & 4 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

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

129131
public void webSocketInterceptorPath(HttpProxy proxy) {
130-
proxy.addInterceptor(
131-
WebSocketInterceptor.allow(HeadInterceptor.builder().addingPathPrefix("/api").build())
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 & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +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-
* @return the boolean value
36-
*/
37-
default boolean allowApplyToWebSocket() {
38-
return false;
39-
}
4031
}

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

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class ReverseProxy implements HttpProxy {
3333
private final HttpClient client;
3434
private final boolean supportWebSocket;
3535
private BiFunction<HttpServerRequest, HttpClient, Future<HttpClientRequest>> selector = (req, client) -> Future.failedFuture("No origin available");
36-
private final List<ProxyInterceptor> interceptors = new ArrayList<>();
36+
private final List<ProxyInterceptorEntry> interceptors = new ArrayList<>();
3737

3838
public ReverseProxy(ProxyOptions options, HttpClient client) {
3939
CacheOptions cacheOptions = options.getCacheOptions();
@@ -52,12 +52,11 @@ public HttpProxy originRequestProvider(BiFunction<HttpServerRequest, HttpClient,
5252
}
5353

5454
@Override
55-
public HttpProxy addInterceptor(ProxyInterceptor interceptor) {
56-
interceptors.add(interceptor);
55+
public HttpProxy addInterceptor(ProxyInterceptor interceptor, boolean supportsWebSocketUpgrade) {
56+
interceptors.add(new ProxyInterceptorEntry(Objects.requireNonNull(interceptor), supportsWebSocketUpgrade));
5757
return this;
5858
}
5959

60-
6160
@Override
6261
public void handle(HttpServerRequest request) {
6362
ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);
@@ -71,7 +70,6 @@ public void handle(HttpServerRequest request) {
7170

7271
boolean isWebSocket = supportWebSocket && request.canUpgradeToWebSocket();
7372
Proxy proxy = new Proxy(proxyRequest, isWebSocket);
74-
proxy.filters = interceptors.listIterator();
7573
proxy.sendRequest()
7674
.recover(throwable -> {
7775
log.trace("Error in sending the request", throwable);
@@ -103,12 +101,13 @@ private class Proxy implements ProxyContext {
103101
private final ProxyRequest request;
104102
private ProxyResponse response;
105103
private final Map<String, Object> attachments = new HashMap<>();
106-
private ListIterator<ProxyInterceptor> filters;
104+
private final ListIterator<ProxyInterceptorEntry> filters;
107105
private final boolean isWebSocket;
108106

109107
private Proxy(ProxyRequest request, boolean isWebSocket) {
110108
this.request = request;
111109
this.isWebSocket = isWebSocket;
110+
this.filters = interceptors.listIterator();
112111
}
113112

114113
@Override
@@ -135,11 +134,11 @@ public ProxyRequest request() {
135134
@Override
136135
public Future<ProxyResponse> sendRequest() {
137136
if (filters.hasNext()) {
138-
ProxyInterceptor next = filters.next();
139-
if (isWebSocket && !next.allowApplyToWebSocket()) {
137+
ProxyInterceptorEntry next = filters.next();
138+
if (isWebSocket && !next.supportsWebSocketUpgrade) {
140139
return sendRequest();
141140
}
142-
return next.handleProxyRequest(this);
141+
return next.interceptor.handleProxyRequest(this);
143142
} else {
144143
if (isWebSocket) {
145144
HttpServerRequest proxiedRequest = request().proxiedRequest();
@@ -171,11 +170,11 @@ public ProxyResponse response() {
171170
@Override
172171
public Future<Void> sendResponse() {
173172
if (filters.hasPrevious()) {
174-
ProxyInterceptor filter = filters.previous();
175-
if (isWebSocket && !filter.allowApplyToWebSocket()) {
173+
ProxyInterceptorEntry previous = filters.previous();
174+
if (isWebSocket && !previous.supportsWebSocketUpgrade) {
176175
return sendResponse();
177176
}
178-
return filter.handleProxyResponse(this);
177+
return previous.interceptor.handleProxyResponse(this);
179178
} else {
180179
if (isWebSocket) {
181180
HttpClientResponse proxiedResponse = response().proxiedResponse();
@@ -227,4 +226,15 @@ private Future<Void> sendProxyResponse(ProxyResponse response) {
227226
return sendResponse();
228227
}
229228
}
229+
230+
private static class ProxyInterceptorEntry {
231+
232+
final ProxyInterceptor interceptor;
233+
final boolean supportsWebSocketUpgrade;
234+
235+
ProxyInterceptorEntry(ProxyInterceptor interceptor, boolean supportsWebSocketUpgrade) {
236+
this.interceptor = interceptor;
237+
this.supportsWebSocketUpgrade = supportsWebSocketUpgrade;
238+
}
239+
}
230240
}

src/main/java/io/vertx/httpproxy/interceptors/WebSocketInterceptor.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/main/java/io/vertx/httpproxy/interceptors/impl/WebSocketInterceptorImpl.java

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/test/java/io/vertx/tests/interceptors/WebSocketInterceptorTest.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import io.vertx.core.net.SocketAddress;
77
import io.vertx.ext.unit.Async;
88
import io.vertx.ext.unit.TestContext;
9-
import io.vertx.httpproxy.*;
9+
import io.vertx.httpproxy.ProxyContext;
10+
import io.vertx.httpproxy.ProxyInterceptor;
11+
import io.vertx.httpproxy.ProxyOptions;
12+
import io.vertx.httpproxy.ProxyResponse;
1013
import io.vertx.httpproxy.interceptors.HeadInterceptor;
11-
import io.vertx.httpproxy.interceptors.WebSocketInterceptor;
1214
import io.vertx.tests.ProxyTestBase;
1315
import org.junit.Test;
1416

15-
16-
1717
public class WebSocketInterceptorTest extends ProxyTestBase {
1818

1919
public WebSocketInterceptorTest(ProxyOptions options) {
@@ -53,7 +53,7 @@ private void testWithInterceptor(TestContext ctx, ProxyInterceptor interceptor,
5353
startProxy(proxy -> {
5454
proxy.origin(backend);
5555
if (interceptor != null) {
56-
proxy.addInterceptor(interceptor);
56+
proxy.addInterceptor(interceptor, wsHit);
5757
};
5858
});
5959

@@ -93,7 +93,7 @@ public void testNotApplySocket(TestContext ctx) {
9393
@Test
9494
public void testWithSocketInterceptor(TestContext ctx) {
9595
// this interceptor applies to both regular HTTP traffic and WebSocket handshake
96-
ProxyInterceptor interceptor = WebSocketInterceptor.allow(HeadInterceptor.builder().updatingPath(x -> x + "/updated").build());
96+
ProxyInterceptor interceptor = HeadInterceptor.builder().updatingPath(x -> x + "/updated").build();
9797
testWithInterceptor(ctx, interceptor, true, true);
9898
}
9999

@@ -108,11 +108,6 @@ public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
108108
}
109109
return context.sendRequest();
110110
}
111-
112-
@Override
113-
public boolean allowApplyToWebSocket() {
114-
return true;
115-
}
116111
};
117112
testWithInterceptor(ctx, interceptor, false, true);
118113
}

0 commit comments

Comments
 (0)