Skip to content

Commit c41506d

Browse files
tsegismontvietj
authored andcommitted
Introduce OriginRequestProvider
Closes #35, closes #44 OriginRequestProvider is a Vert.x API type that unifies all the origin selection methods currently present as overloaded methods in HttpProxy. OriginRequestProvider being a FunctionalInterface, users can provide one in the form of a lambda. OriginRequestProvider is a little different from the existing contract. Instead of the proxied request and HTTP client as parameters, it has the ProxyContext (that exposes all the proxy data and the client). This allows implementations to make a decision on the modifications interceptors can make (including when the request is an upgrade to WebSocket). See #44 It also allows interceptor and provider to exchange data via the ProxyContext attachments. See #35 Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
1 parent 81f788a commit c41506d

File tree

8 files changed

+155
-96
lines changed

8 files changed

+155
-96
lines changed

src/main/asciidoc/index.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ All user-agent requests are forwarded to the *origin server* conveniently.
4848

4949
=== Origin server routing
5050

51-
You can create a proxy that forwards all the traffic to a single server like seen before
51+
You can create a proxy that forwards all the traffic to a single server like seen before.
5252

53-
You can set an origin selector to route the traffic to a given server
53+
You can set an origin selector to route the traffic to a given server:
5454

5555
[source,java]
5656
----
5757
{@link examples.HttpProxyExamples#originSelector}
5858
----
5959

60-
You can set a function to create the client request to the origin server for ultimate flexibility
60+
You can set a function to create the client request to the origin server for ultimate flexibility:
6161

6262
[source,java]
6363
----

src/main/java/examples/HttpProxyExamples.java

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import io.vertx.core.buffer.Buffer;
77
import io.vertx.core.http.HttpClient;
88
import io.vertx.core.http.HttpServer;
9-
import io.vertx.core.http.HttpServerRequest;
109
import io.vertx.core.http.RequestOptions;
1110
import io.vertx.core.json.JsonObject;
1211
import io.vertx.core.net.HostAndPort;
@@ -23,6 +22,7 @@
2322
* @author <a href="mailto:emad.albloushi@gmail.com">Emad Alblueshi</a>
2423
*/
2524

25+
@SuppressWarnings("unused")
2626
public class HttpProxyExamples {
2727

2828
public void origin(Vertx vertx) {
@@ -46,20 +46,20 @@ public void proxy(Vertx vertx) {
4646
proxyServer.requestHandler(proxy).listen(8080);
4747
}
4848

49-
private SocketAddress resolveOriginAddress(HttpServerRequest request) {
49+
private Future<SocketAddress> resolveOriginAddress(ProxyContext proxyContext) {
5050
return null;
5151
}
5252

5353
public void originSelector(HttpProxy proxy) {
54-
proxy.originSelector(request -> Future.succeededFuture(resolveOriginAddress(request)));
54+
proxy.origin(OriginRequestProvider.selector(proxyContext -> resolveOriginAddress(proxyContext)));
5555
}
5656

57-
private RequestOptions resolveOriginOptions(HttpServerRequest request) {
57+
private RequestOptions resolveOriginOptions(ProxyContext request) {
5858
return null;
5959
}
6060

6161
public void originRequestProvider(HttpProxy proxy) {
62-
proxy.originRequestProvider((request, client) -> client.request(resolveOriginOptions(request)));
62+
proxy.origin((proxyContext) -> proxyContext.client().request(resolveOriginOptions(proxyContext)));
6363
}
6464

6565
public void inboundInterceptor(HttpProxy proxy) {
@@ -168,36 +168,6 @@ private JsonObject removeSomeFields(JsonObject o) {
168168
return o;
169169
}
170170

171-
;
172-
173-
public void more(Vertx vertx, HttpClient proxyClient) {
174-
HttpProxy proxy = HttpProxy.reverseProxy(proxyClient).originSelector(
175-
address -> Future.succeededFuture(SocketAddress.inetSocketAddress(7070, "origin"))
176-
);
177-
}
178-
179-
public void lowLevel(Vertx vertx, HttpServer proxyServer, HttpClient proxyClient) {
180-
181-
proxyServer.requestHandler(request -> {
182-
ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);
183-
184-
proxyClient.request(proxyRequest.getMethod(), 8080, "origin", proxyRequest.getURI())
185-
.compose(proxyRequest::send)
186-
.onSuccess(proxyResponse -> {
187-
// Send the proxy response
188-
proxyResponse.send();
189-
})
190-
.onFailure(err -> {
191-
// Release the request
192-
proxyRequest.release();
193-
194-
// Send error
195-
request.response().setStatusCode(500)
196-
.send();
197-
});
198-
});
199-
}
200-
201171
public void overrideAuthority(HttpProxy proxy) {
202172
proxy.addInterceptor(new ProxyInterceptor() {
203173
@Override

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

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,13 @@
1111
package io.vertx.httpproxy;
1212

1313
import io.vertx.codegen.annotations.Fluent;
14-
import io.vertx.codegen.annotations.GenIgnore;
1514
import io.vertx.codegen.annotations.VertxGen;
16-
import io.vertx.core.Future;
1715
import io.vertx.core.Handler;
1816
import io.vertx.core.http.HttpClient;
19-
import io.vertx.core.http.HttpClientRequest;
2017
import io.vertx.core.http.HttpServerRequest;
21-
import io.vertx.core.http.RequestOptions;
2218
import io.vertx.core.net.SocketAddress;
2319
import io.vertx.httpproxy.impl.ReverseProxy;
2420

25-
import java.util.function.BiFunction;
26-
import java.util.function.Function;
27-
2821
/**
2922
* Handles the HTTP reverse proxy logic between the <i><b>user agent</b></i> and the <i><b>origin</b></i>.
3023
* <p>
@@ -61,7 +54,7 @@ static HttpProxy reverseProxy(ProxyOptions options, HttpClient client) {
6154
*/
6255
@Fluent
6356
default HttpProxy origin(SocketAddress address) {
64-
return originSelector(req -> Future.succeededFuture(address));
57+
return origin(OriginRequestProvider.fixedAddress(address));
6558
}
6659

6760
/**
@@ -73,32 +66,17 @@ default HttpProxy origin(SocketAddress address) {
7366
*/
7467
@Fluent
7568
default HttpProxy origin(int port, String host) {
76-
return origin(SocketAddress.inetSocketAddress(port, host));
77-
}
78-
79-
/**
80-
* Set a selector that resolves the <i><b>origin</b></i> address based on the incoming HTTP request.
81-
*
82-
* @param selector the selector
83-
* @return a reference to this, so the API can be used fluently
84-
*/
85-
@Fluent
86-
default HttpProxy originSelector(Function<HttpServerRequest, Future<SocketAddress>> selector) {
87-
return originRequestProvider((req, client) -> selector
88-
.apply(req)
89-
.flatMap(server -> client.request(new RequestOptions().setServer(server))));
69+
return origin(OriginRequestProvider.fixedAddress(port, host));
9070
}
9171

9272
/**
93-
* Set a provider that creates the request to the <i><b>origin</b></i> server based the incoming HTTP request.
94-
* Setting a provider overrides any origin selector previously set.
73+
* Set a provider that creates the request to the <i><b>origin</b></i> server based on {@link ProxyContext}.
9574
*
9675
* @param provider the provider
9776
* @return a reference to this, so the API can be used fluently
9877
*/
99-
@GenIgnore()
10078
@Fluent
101-
HttpProxy originRequestProvider(BiFunction<HttpServerRequest, HttpClient, Future<HttpClientRequest>> provider);
79+
HttpProxy origin(OriginRequestProvider provider);
10280

10381
/**
10482
* Add an interceptor to the interceptor chain.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.vertx.httpproxy;
2+
3+
import io.vertx.codegen.annotations.VertxGen;
4+
import io.vertx.core.Future;
5+
import io.vertx.core.http.HttpClientRequest;
6+
import io.vertx.core.http.RequestOptions;
7+
import io.vertx.core.net.SocketAddress;
8+
9+
import java.util.function.Function;
10+
11+
/**
12+
* A provider that creates the request to the <i><b>origin</b></i> server based on {@link ProxyContext}.
13+
*/
14+
@VertxGen
15+
@FunctionalInterface
16+
public interface OriginRequestProvider {
17+
18+
/**
19+
* Creates a simple provider for a fixed {@code port} and {@code host}.
20+
*/
21+
static OriginRequestProvider fixedAddress(int port, String host) {
22+
return fixedAddress(SocketAddress.inetSocketAddress(port, host));
23+
}
24+
25+
/**
26+
* Creates a simple provider for a fixed {@link SocketAddress}.
27+
*/
28+
static OriginRequestProvider fixedAddress(SocketAddress address) {
29+
return new OriginRequestProvider() {
30+
@Override
31+
public Future<HttpClientRequest> create(ProxyContext proxyContext) {
32+
return proxyContext.client().request(new RequestOptions().setServer(address));
33+
}
34+
};
35+
}
36+
37+
/**
38+
* Creates a provider that selects the <i><b>origin</b></i> server based on {@link ProxyContext}.
39+
*/
40+
static OriginRequestProvider selector(Function<ProxyContext, Future<SocketAddress>> selector) {
41+
return new OriginRequestProvider() {
42+
@Override
43+
public Future<HttpClientRequest> create(ProxyContext proxyContext) {
44+
return selector.apply(proxyContext).flatMap(server -> proxyContext.client().request(new RequestOptions().setServer(server)));
45+
}
46+
};
47+
}
48+
49+
/**
50+
* Create the {@link HttpClientRequest} to the origin server for a given {@link ProxyContext}.
51+
*
52+
* @param proxyContext the context of the proxied request and response
53+
* @return a future, completed with the {@link HttpClientRequest} or failed
54+
*/
55+
Future<HttpClientRequest> create(ProxyContext proxyContext);
56+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.vertx.codegen.annotations.VertxGen;
44
import io.vertx.core.Future;
5+
import io.vertx.core.http.HttpClient;
56

67
/**
78
* A controller for proxy interception.
@@ -50,4 +51,9 @@ public interface ProxyContext {
5051
* @return the attached payload
5152
*/
5253
<T> T get(String name, Class<T> type);
54+
55+
/**
56+
* @return the {@link HttpClient} use to interact with the origin server
57+
*/
58+
HttpClient client();
5359
}

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

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,19 @@
1313
import io.vertx.core.Future;
1414
import io.vertx.core.Vertx;
1515
import io.vertx.core.buffer.Buffer;
16-
import io.vertx.core.http.HttpClient;
17-
import io.vertx.core.http.HttpClientRequest;
18-
import io.vertx.core.http.HttpClientResponse;
19-
import io.vertx.core.http.HttpHeaders;
20-
import io.vertx.core.http.HttpServerRequest;
21-
import io.vertx.core.http.HttpServerResponse;
16+
import io.vertx.core.http.*;
2217
import io.vertx.core.internal.CloseFuture;
2318
import io.vertx.core.internal.VertxInternal;
2419
import io.vertx.core.internal.http.HttpClientInternal;
2520
import io.vertx.core.internal.logging.Logger;
2621
import io.vertx.core.internal.logging.LoggerFactory;
2722
import io.vertx.core.net.NetSocket;
2823
import io.vertx.core.streams.ReadStream;
29-
import io.vertx.httpproxy.HttpProxy;
30-
import io.vertx.httpproxy.ProxyContext;
31-
import io.vertx.httpproxy.ProxyInterceptor;
32-
import io.vertx.httpproxy.ProxyOptions;
33-
import io.vertx.httpproxy.ProxyRequest;
34-
import io.vertx.httpproxy.ProxyResponse;
24+
import io.vertx.httpproxy.*;
3525
import io.vertx.httpproxy.cache.CacheOptions;
3626
import io.vertx.httpproxy.spi.cache.Cache;
3727

38-
import java.util.ArrayList;
39-
import java.util.HashMap;
40-
import java.util.List;
41-
import java.util.ListIterator;
42-
import java.util.Map;
43-
import java.util.Objects;
44-
import java.util.function.BiFunction;
28+
import java.util.*;
4529

4630
import static io.vertx.core.http.HttpHeaders.CONNECTION;
4731
import static io.vertx.core.http.HttpHeaders.UPGRADE;
@@ -51,7 +35,7 @@ public class ReverseProxy implements HttpProxy {
5135
private final static Logger log = LoggerFactory.getLogger(ReverseProxy.class);
5236
private final HttpClient client;
5337
private final boolean supportWebSocket;
54-
private BiFunction<HttpServerRequest, HttpClient, Future<HttpClientRequest>> selector = (req, client) -> Future.failedFuture("No origin available");
38+
private OriginRequestProvider originRequestProvider = (pc) -> Future.failedFuture("No origin available");
5539
private final List<ProxyInterceptorEntry> interceptors = new ArrayList<>();
5640

5741
public ReverseProxy(ProxyOptions options, HttpClient client) {
@@ -75,8 +59,8 @@ public Cache newCache(CacheOptions options, Vertx vertx) {
7559
}
7660

7761
@Override
78-
public HttpProxy originRequestProvider(BiFunction<HttpServerRequest, HttpClient, Future<HttpClientRequest>> provider) {
79-
selector = provider;
62+
public HttpProxy origin(OriginRequestProvider provider) {
63+
originRequestProvider = Objects.requireNonNull(provider);
8064
return this;
8165
}
8266

@@ -121,8 +105,8 @@ private void end(ProxyRequest proxyRequest, int sc) {
121105
.send();
122106
}
123107

124-
private Future<HttpClientRequest> resolveOrigin(HttpServerRequest proxiedRequest) {
125-
return selector.apply(proxiedRequest, client);
108+
private Future<HttpClientRequest> resolveOrigin(ProxyContext proxyContext) {
109+
return originRequestProvider.create(proxyContext);
126110
}
127111

128112
private class Proxy implements ProxyContext {
@@ -155,6 +139,11 @@ public <T> T get(String name, Class<T> type) {
155139
return type.isInstance(o) ? type.cast(o) : null;
156140
}
157141

142+
@Override
143+
public HttpClient client() {
144+
return client;
145+
}
146+
158147
@Override
159148
public ProxyRequest request() {
160149
return request;
@@ -171,7 +160,7 @@ public Future<ProxyResponse> sendRequest() {
171160
} else {
172161
if (isWebSocket) {
173162
HttpServerRequest proxiedRequest = request().proxiedRequest();
174-
return resolveOrigin(proxiedRequest).compose(request -> {
163+
return resolveOrigin(this).compose(request -> {
175164
request.setMethod(request().getMethod());
176165
request.setURI(request().getURI());
177166
// Firefox is known to send an unexpected connection header value
@@ -237,7 +226,7 @@ public Future<Void> sendResponse() {
237226
}
238227

239228
private Future<ProxyResponse> sendProxyRequest(ProxyRequest proxyRequest) {
240-
return resolveOrigin(proxyRequest.proxiedRequest()).compose(proxyRequest::send);
229+
return resolveOrigin(this).compose(proxyRequest::send);
241230
}
242231

243232
private Future<Void> sendProxyResponse(ProxyResponse response) {

src/test/java/io/vertx/tests/ProxyTest.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
import io.vertx.core.http.HttpClient;
1515
import io.vertx.core.http.HttpClientResponse;
1616
import io.vertx.core.http.HttpMethod;
17+
import io.vertx.core.http.RequestOptions;
1718
import io.vertx.core.net.SocketAddress;
1819
import io.vertx.ext.unit.Async;
1920
import io.vertx.ext.unit.TestContext;
2021
import io.vertx.ext.unit.junit.VertxUnitRunnerWithParametersFactory;
21-
import io.vertx.httpproxy.ProxyContext;
22-
import io.vertx.httpproxy.ProxyInterceptor;
23-
import io.vertx.httpproxy.ProxyOptions;
24-
import io.vertx.httpproxy.ProxyResponse;
22+
import io.vertx.httpproxy.*;
2523
import org.junit.Test;
2624
import org.junit.runner.RunWith;
2725
import org.junit.runners.Parameterized;
@@ -61,7 +59,7 @@ public void testRoundRobinSelector(TestContext ctx) {
6159
backends[i] = startHttpBackend(ctx, 8081 + value, req -> req.response().end("" + value));
6260
}
6361
AtomicInteger count = new AtomicInteger();
64-
startProxy(proxy -> proxy.originSelector(req -> Future.succeededFuture(backends[count.getAndIncrement() % backends.length])));
62+
startProxy(proxy -> proxy.origin(OriginRequestProvider.selector(proxyContext -> Future.succeededFuture(backends[count.getAndIncrement() % backends.length]))));
6563
client = vertx.createHttpClient();
6664
Map<String, AtomicInteger> result = Collections.synchronizedMap(new HashMap<>());
6765
Async latch = ctx.async();
@@ -208,4 +206,29 @@ public Future<Void> handleProxyResponse(ProxyContext context) {
208206
}))
209207
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
210208
}
209+
210+
@Test
211+
public void testVariableFromInterceptor(TestContext ctx) {
212+
SocketAddress backend = startHttpBackend(ctx, 8081, req -> req.response().end("HOLA"));
213+
ProxyInterceptor interceptor = new ProxyInterceptor() {
214+
@Override
215+
public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
216+
context.set("foo", "bar");
217+
return context.sendRequest();
218+
}
219+
};
220+
OriginRequestProvider provider = (proxyContext) -> {
221+
ctx.assertEquals("bar", proxyContext.get("foo", String.class));
222+
return client.request(new RequestOptions().setServer(backend));
223+
};
224+
startProxy(proxy -> proxy.origin(provider).addInterceptor(interceptor));
225+
client = vertx.createHttpClient();
226+
client
227+
.request(HttpMethod.GET, 8080, "localhost", "/")
228+
.compose(req -> req
229+
.send()
230+
.compose(HttpClientResponse::body)
231+
)
232+
.onComplete(ctx.asyncAssertSuccess(buffer -> ctx.assertEquals("HOLA", buffer.toString())));
233+
}
211234
}

0 commit comments

Comments
 (0)