Skip to content

Commit b6d4556

Browse files
authored
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 61bce84 commit b6d4556

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.internal.logging.Logger;
16+
import io.vertx.core.internal.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
@@ -110,4 +110,56 @@ public Future<Void> handleProxyResponse(ProxyContext context) {
110110
latch.countDown();
111111
});
112112
}
113+
114+
@Test
115+
public void testUpstreamRefuse(TestContext ctx) {
116+
SocketAddress backend = SocketAddress.inetSocketAddress(8081, "localhost");
117+
startProxy(proxy -> proxy.origin(backend));
118+
client = vertx.createHttpClient();
119+
Async async = ctx.async();
120+
client.request(HttpMethod.GET, 8080, "localhost", "/")
121+
.compose(req -> req.send().compose(resp -> {
122+
ctx.assertEquals(502, resp.statusCode());
123+
return resp.body();
124+
}))
125+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
126+
}
127+
128+
@Test
129+
public void testFilterRequestFail(TestContext ctx) {
130+
SocketAddress backend = startHttpBackend(ctx, 8081, req -> req.response().end("HOLA"));
131+
startProxy(proxy -> proxy.origin(backend).addInterceptor(new ProxyInterceptor() {
132+
@Override
133+
public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
134+
return Future.failedFuture(new RuntimeException("Some error"));
135+
}
136+
}));
137+
client = vertx.createHttpClient();
138+
Async async = ctx.async();
139+
client.request(HttpMethod.GET, 8080, "localhost", "/")
140+
.compose(req -> req.send().compose(resp -> {
141+
ctx.assertEquals(502, resp.statusCode());
142+
return resp.body();
143+
}))
144+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
145+
}
146+
147+
@Test
148+
public void testFilterResponseFail(TestContext ctx) {
149+
SocketAddress backend = startHttpBackend(ctx, 8081, req -> req.response().end("HOLA"));
150+
startProxy(proxy -> proxy.origin(backend).addInterceptor(new ProxyInterceptor() {
151+
@Override
152+
public Future<Void> handleProxyResponse(ProxyContext context) {
153+
return Future.failedFuture(new RuntimeException("Some error"));
154+
}
155+
}));
156+
client = vertx.createHttpClient();
157+
Async async = ctx.async();
158+
client.request(HttpMethod.GET, 8080, "localhost", "/")
159+
.compose(req -> req.send().compose(resp -> {
160+
ctx.assertEquals(502, resp.statusCode());
161+
return resp.body();
162+
}))
163+
.onComplete(ctx.asyncAssertSuccess(body -> async.complete()));
164+
}
113165
}

0 commit comments

Comments
 (0)