+ * Specify an {@link ExceptionHandler} to handle client to proxy errors + *
+ * + *+ * Default = null + *
+ * + * @param clientToProxyExHandler + * @return exception handler + */ + HttpProxyServerBootstrap withClientToProxyExHandler( + ExceptionHandler clientToProxyExHandler); + + /** + *+ * Specify an {@link ExceptionHandler} to handle proxy to server errors + *
+ * + *+ * Default = null + *
+ * + * @param proxyToServerExHandler + * @return exception handler + */ + HttpProxyServerBootstrap withProxyToServerExHandler( + ExceptionHandler proxyToServerExHandler); + /** *
* Specify a {@link HttpFiltersSource} to use for filtering requests and/or
diff --git a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
index f5e6a908b..afef50a11 100644
--- a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
+++ b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
@@ -26,6 +26,7 @@
import org.apache.commons.lang3.StringUtils;
import org.littleshoot.proxy.ActivityTracker;
import org.littleshoot.proxy.BadGatewayFailureHttpResponseComposer;
+import org.littleshoot.proxy.ExceptionHandler;
import org.littleshoot.proxy.FailureHttpResponseComposer;
import org.littleshoot.proxy.FlowContext;
import org.littleshoot.proxy.FullFlowContext;
@@ -755,7 +756,13 @@ protected void exceptionCaught(Throwable cause) {
LOG.info("An executor rejected a read or write operation on the ClientToProxyConnection (this is normal if the proxy is shutting down). Message: " + cause.getMessage());
LOG.debug("A RejectedExecutionException occurred on ClientToProxyConnection", cause);
} else {
- LOG.error("Caught an exception on ClientToProxyConnection", cause);
+ ExceptionHandler exHandler = proxyServer.getClientToProxyExHandler();
+ if (exHandler != null) {
+ LOG.debug("Custom exception handler '" + exHandler.toString() + "' invoked", cause);
+ exHandler.handle(cause);
+ } else {
+ LOG.error("Caught an exception on ClientToProxyConnection", cause);
+ }
}
} finally {
// always disconnect the client when an exception occurs on the channel
diff --git a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java
index 64bb78cbe..91a2b710e 100644
--- a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java
+++ b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java
@@ -31,6 +31,7 @@
import org.littleshoot.proxy.MitmManager;
import org.littleshoot.proxy.MitmManagerFactory;
import org.littleshoot.proxy.ProxyAuthenticator;
+import org.littleshoot.proxy.ExceptionHandler;
import org.littleshoot.proxy.SslEngineSource;
import org.littleshoot.proxy.TransportProtocol;
import org.littleshoot.proxy.UnknownTransportProtocolException;
@@ -110,6 +111,8 @@ public class DefaultHttpProxyServer implements HttpProxyServer {
private final ProxyAuthenticator proxyAuthenticator;
private final ChainedProxyManager chainProxyManager;
private final MitmManagerFactory mitmManagerFactory;
+ private final ExceptionHandler clientToProxyExHandler;
+ private final ExceptionHandler proxyToServerExHandler;
private final HttpFiltersSource filtersSource;
private final FailureHttpResponseComposer unrecoverableFailureHttpResponseComposer;
private final boolean transparent;
@@ -243,6 +246,8 @@ private DefaultHttpProxyServer(ServerGroup serverGroup,
ProxyAuthenticator proxyAuthenticator,
ChainedProxyManager chainProxyManager,
MitmManagerFactory mitmManagerFactory,
+ ExceptionHandler clientToProxyExHandler,
+ ExceptionHandler proxyToServerExHandler,
HttpFiltersSource filtersSource,
FailureHttpResponseComposer unrecoverableFailureHttpResponseComposer,
boolean transparent,
@@ -266,6 +271,8 @@ private DefaultHttpProxyServer(ServerGroup serverGroup,
this.proxyAuthenticator = proxyAuthenticator;
this.chainProxyManager = chainProxyManager;
this.mitmManagerFactory = mitmManagerFactory;
+ this.clientToProxyExHandler = clientToProxyExHandler;
+ this.proxyToServerExHandler = proxyToServerExHandler;
this.filtersSource = filtersSource;
this.unrecoverableFailureHttpResponseComposer = unrecoverableFailureHttpResponseComposer;
this.transparent = transparent;
@@ -401,6 +408,8 @@ public HttpProxyServerBootstrap clone() {
proxyAuthenticator,
chainProxyManager,
mitmManagerFactory,
+ clientToProxyExHandler,
+ proxyToServerExHandler,
filtersSource,
unrecoverableFailureHttpResponseComposer,
transparent,
@@ -579,6 +588,14 @@ protected MitmManager getMitmManager(Channel channel) {
return null;
}
+ protected ExceptionHandler getClientToProxyExHandler() {
+ return clientToProxyExHandler;
+ }
+
+ protected ExceptionHandler getProxyToServerExHandler() {
+ return proxyToServerExHandler;
+ }
+
protected SslEngineSource getSslEngineSource() {
return sslEngineSource;
}
@@ -621,6 +638,8 @@ private static class DefaultHttpProxyServerBootstrap implements HttpProxyServerB
private ProxyAuthenticator proxyAuthenticator = null;
private ChainedProxyManager chainProxyManager = null;
private MitmManagerFactory mitmManagerFactory = null;
+ private ExceptionHandler clientToProxyExHandler = null;
+ private ExceptionHandler proxyToServerExHandler = null;
private HttpFiltersSource filtersSource = new HttpFiltersSourceAdapter();
private FailureHttpResponseComposer unrecoverableFailureHttpResponseComposer = new BadGatewayFailureHttpResponseComposer();
private boolean transparent = false;
@@ -652,6 +671,8 @@ private DefaultHttpProxyServerBootstrap(
ProxyAuthenticator proxyAuthenticator,
ChainedProxyManager chainProxyManager,
MitmManagerFactory mitmManagerFactory,
+ ExceptionHandler clientToProxyExHandler,
+ ExceptionHandler proxyToServerExHandler,
HttpFiltersSource filtersSource,
FailureHttpResponseComposer unrecoverableFailureHttpResponseComposer,
boolean transparent, int idleConnectionTimeout,
@@ -674,6 +695,8 @@ private DefaultHttpProxyServerBootstrap(
this.proxyAuthenticator = proxyAuthenticator;
this.chainProxyManager = chainProxyManager;
this.mitmManagerFactory = mitmManagerFactory;
+ this.clientToProxyExHandler = clientToProxyExHandler;
+ this.proxyToServerExHandler = proxyToServerExHandler;
this.filtersSource = filtersSource;
this.unrecoverableFailureHttpResponseComposer = unrecoverableFailureHttpResponseComposer;
this.transparent = transparent;
@@ -807,6 +830,20 @@ public HttpProxyServerBootstrap withManInTheMiddle(
return this;
}
+ @Override
+ public HttpProxyServerBootstrap withProxyToServerExHandler(
+ ExceptionHandler proxyToServerExHandler) {
+ this.proxyToServerExHandler = proxyToServerExHandler;
+ return this;
+ }
+
+ @Override
+ public HttpProxyServerBootstrap withClientToProxyExHandler(
+ ExceptionHandler clientToProxyExHandler) {
+ this.clientToProxyExHandler = clientToProxyExHandler;
+ return this;
+ }
+
@Override
public HttpProxyServerBootstrap withFiltersSource(
HttpFiltersSource filtersSource) {
@@ -923,6 +960,7 @@ private DefaultHttpProxyServer build() {
transportProtocol, determineListenAddress(),
sslEngineSource, authenticateSslClients,
proxyAuthenticator, chainProxyManager, mitmManagerFactory,
+ clientToProxyExHandler, proxyToServerExHandler,
filtersSource, unrecoverableFailureHttpResponseComposer, transparent,
idleConnectionTimeout, activityTrackers, connectTimeout,
serverResolver, readThrottleBytesPerSecond, writeThrottleBytesPerSecond,
diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java
index bb5e09d5e..3bd22224a 100644
--- a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java
+++ b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java
@@ -39,6 +39,7 @@
import org.littleshoot.proxy.FullFlowContext;
import org.littleshoot.proxy.HttpFilters;
import org.littleshoot.proxy.MitmManager;
+import org.littleshoot.proxy.ExceptionHandler;
import org.littleshoot.proxy.TransportProtocol;
import org.littleshoot.proxy.UnknownTransportProtocolException;
@@ -439,7 +440,13 @@ protected void exceptionCaught(Throwable cause) {
LOG.info("An executor rejected a read or write operation on the ProxyToServerConnection (this is normal if the proxy is shutting down). Message: " + cause.getMessage());
LOG.debug("A RejectedExecutionException occurred on ProxyToServerConnection", cause);
} else {
- LOG.error("Caught an exception on ProxyToServerConnection", cause);
+ ExceptionHandler exHandler = proxyServer.getProxyToServerExHandler();
+ if (exHandler != null) {
+ LOG.debug("Custom exception handler '" + exHandler.toString() + "' invoked", cause);
+ exHandler.handle(cause);
+ } else {
+ LOG.error("Caught an exception on ProxyToServerConnection", cause);
+ }
}
} finally {
if (!is(DISCONNECTED)) {
diff --git a/src/test/java/org/littleshoot/proxy/CustomClientToProxyExHandlerTest.java b/src/test/java/org/littleshoot/proxy/CustomClientToProxyExHandlerTest.java
new file mode 100644
index 000000000..3f07896b8
--- /dev/null
+++ b/src/test/java/org/littleshoot/proxy/CustomClientToProxyExHandlerTest.java
@@ -0,0 +1,51 @@
+package org.littleshoot.proxy;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.HttpRequest;
+import org.apache.http.NoHttpResponseException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.littleshoot.proxy.extras.SelfSignedMitmManagerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomClientToProxyExHandlerTest extends AbstractProxyTest {
+
+ private final List