Skip to content

Commit faed05d

Browse files
homioctsegismont
authored andcommitted
Assist in capturing and handling exceptions or errors within interceptors to prevent request hanging (#84)
* ### Assist in capturing and handling exceptions or errors within interceptors to prevent request hanging, which also might address issue #75. This may help solve #75. In addition, when interceptors fail to filter the request or response in the chain, this can return a response in time, preventing the request from hanging. * Release resources of the request before send the response * Log errors during filtering the request/response, simplify the code
1 parent 3f1621c commit faed05d

File tree

2 files changed

+66
-23
lines changed

2 files changed

+66
-23
lines changed

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

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
package io.vertx.httpproxy.impl;
1212

1313
import io.vertx.core.Future;
14-
import io.vertx.core.Promise;
1514
import io.vertx.core.http.*;
15+
import io.vertx.core.impl.logging.Logger;
16+
import io.vertx.core.impl.logging.LoggerFactory;
1617
import io.vertx.core.net.NetSocket;
1718
import io.vertx.httpproxy.*;
1819
import io.vertx.httpproxy.cache.CacheOptions;
@@ -23,6 +24,7 @@
2324

2425
public class ReverseProxy implements HttpProxy {
2526

27+
private final static Logger log = LoggerFactory.getLogger(ReverseProxy.class);
2628
private final HttpClient client;
2729
private final boolean supportWebSocket;
2830
private BiFunction<HttpServerRequest, HttpClient, Future<HttpClientRequest>> selector = (req, client) -> Future.failedFuture("No origin available");
@@ -70,7 +72,16 @@ public void handle(HttpServerRequest request) {
7072

7173
Proxy proxy = new Proxy(proxyRequest);
7274
proxy.filters = interceptors.listIterator();
73-
proxy.sendRequest().compose(proxy::sendProxyResponse);
75+
proxy.sendRequest()
76+
.recover(throwable -> {
77+
log.trace("Error in sending the request", throwable);
78+
return Future.succeededFuture(proxyRequest.release().response().setStatusCode(502));
79+
})
80+
.compose(proxy::sendProxyResponse)
81+
.recover(throwable -> {
82+
log.trace("Error in sending the response", throwable);
83+
return proxy.response().release().setStatusCode(502).send();
84+
});
7485
}
7586

7687
private void handleWebSocketUpgrade(ProxyRequest proxyRequest) {
@@ -191,27 +202,7 @@ public Future<Void> sendResponse() {
191202
}
192203

193204
private Future<ProxyResponse> sendProxyRequest(ProxyRequest proxyRequest) {
194-
Future<HttpClientRequest> f = resolveOrigin(proxyRequest.proxiedRequest());
195-
f.onFailure(err -> {
196-
// Should this be done here ? I don't think so
197-
HttpServerRequest proxiedRequest = proxyRequest.proxiedRequest();
198-
proxiedRequest.resume();
199-
Promise<Void> promise = Promise.promise();
200-
proxiedRequest.exceptionHandler(promise::tryFail);
201-
proxiedRequest.endHandler(promise::tryComplete);
202-
promise.future().onComplete(ar2 -> {
203-
end(proxyRequest, 502);
204-
});
205-
});
206-
return f.compose(a -> sendProxyRequest(proxyRequest, a));
207-
}
208-
209-
private Future<ProxyResponse> sendProxyRequest(ProxyRequest proxyRequest, HttpClientRequest request) {
210-
Future<ProxyResponse> fut = proxyRequest.send(request);
211-
fut.onFailure(err -> {
212-
proxyRequest.proxiedRequest().response().setStatusCode(502).end();
213-
});
214-
return fut;
205+
return resolveOrigin(proxyRequest.proxiedRequest()).compose(proxyRequest::send);
215206
}
216207

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

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,56 @@ public Future<Void> handleProxyResponse(ProxyContext context) {
102102
latch.countDown();
103103
});
104104
}
105+
106+
@Test
107+
public void testUpstreamRefuse(TestContext ctx) {
108+
SocketAddress backend = SocketAddress.inetSocketAddress(8081, "localhost");
109+
startProxy(proxy -> proxy.origin(backend));
110+
HttpClient client = vertx.createHttpClient();
111+
Async async = ctx.async();
112+
client.request(HttpMethod.GET, 8080, "localhost", "/")
113+
.compose(req -> req.send().compose(resp -> {
114+
ctx.assertEquals(502, resp.statusCode());
115+
return resp.body();
116+
}))
117+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
118+
}
119+
120+
@Test
121+
public void testFilterRequestFail(TestContext ctx) {
122+
SocketAddress backend = startHttpBackend(ctx, 8081, req -> req.response().end("HOLA"));
123+
startProxy(proxy -> proxy.origin(backend).addInterceptor(new ProxyInterceptor() {
124+
@Override
125+
public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
126+
return Future.failedFuture(new RuntimeException("Some error"));
127+
}
128+
}));
129+
HttpClient client = vertx.createHttpClient();
130+
Async async = ctx.async();
131+
client.request(HttpMethod.GET, 8080, "localhost", "/")
132+
.compose(req -> req.send().compose(resp -> {
133+
ctx.assertEquals(502, resp.statusCode());
134+
return resp.body();
135+
}))
136+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
137+
}
138+
139+
@Test
140+
public void testFilterResponseFail(TestContext ctx) {
141+
SocketAddress backend = startHttpBackend(ctx, 8081, req -> req.response().end("HOLA"));
142+
startProxy(proxy -> proxy.origin(backend).addInterceptor(new ProxyInterceptor() {
143+
@Override
144+
public Future<Void> handleProxyResponse(ProxyContext context) {
145+
return Future.failedFuture(new RuntimeException("Some error"));
146+
}
147+
}));
148+
HttpClient client = vertx.createHttpClient();
149+
Async async = ctx.async();
150+
client.request(HttpMethod.GET, 8080, "localhost", "/")
151+
.compose(req -> req.send().compose(resp -> {
152+
ctx.assertEquals(502, resp.statusCode());
153+
return resp.body();
154+
}))
155+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
156+
}
105157
}

0 commit comments

Comments
 (0)