diff --git a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java index 2087a39ee..86a401040 100644 --- a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java +++ b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java @@ -709,8 +709,7 @@ public HttpProxyServerBootstrap withChainProxyManager( ChainedProxyManager chainProxyManager) { this.chainProxyManager = chainProxyManager; if (this.mitmManager != null) { - LOG.warn("Enabled proxy chaining with man in the middle. These are mutually exclusive - man in the middle will be disabled."); - this.mitmManager = null; + LOG.info("Enabled proxy chaining with man in the middle."); } return this; } @@ -720,8 +719,7 @@ public HttpProxyServerBootstrap withManInTheMiddle( MitmManager mitmManager) { this.mitmManager = mitmManager; if (this.chainProxyManager != null) { - LOG.warn("Enabled man in the middle along with proxy chaining. These are mutually exclusive - proxy chaining will be disabled."); - this.chainProxyManager = null; + LOG.info("Enabled man in the middle along with proxy chaining."); } return this; } diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java index f90f235a5..58c3eb240 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java @@ -377,7 +377,15 @@ protected Future encrypt(ChannelPipeline pipeline, channel.config().setAutoRead(true); } SslHandler handler = new SslHandler(sslEngine); - pipeline.addFirst("ssl", handler); + if(pipeline.get("ssl") == null) { + pipeline.addFirst("ssl", handler); + } else { + // The second SSL handler is added to handle the case + // where the proxy (running as MITM) has to chain with + // another SSL enabled proxy. The second SSL handler + // is to perform SSL with the server. + pipeline.addAfter("ssl", "sslWithServer", handler); + } return handler.handshakeFuture(); } diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java index 9e38287b7..97351cc7e 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java @@ -5,6 +5,8 @@ import io.netty.bootstrap.ChannelFactory; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -267,8 +269,16 @@ protected boolean isContentAlwaysEmpty(HttpMessage httpMessage) { identifyCurrentRequest(); } - return HttpMethod.HEAD.equals(currentHttpRequest.getMethod()) ? - true : super.isContentAlwaysEmpty(httpMessage); + // The current HTTP Request can be null when this proxy is + // negotiating a CONNECT request with a chained proxy + // while it is running as a MITM. Since the response to a + // CONNECT request does not have any content, we return true. + if(currentHttpRequest == null) { + return true; + } else { + return HttpMethod.HEAD.equals(currentHttpRequest.getMethod()) ? + true : super.isContentAlwaysEmpty(httpMessage); + } } }; @@ -550,23 +560,36 @@ private void initializeConnectionFlow() { } if (ProxyUtils.isCONNECT(initialRequest)) { + + // If we're chaining, forward the CONNECT request + if (hasUpstreamChainedProxy()) { + connectionFlow.then( + serverConnection.HTTPCONNECTWithChainedProxy); + } + MitmManager mitmManager = proxyServer.getMitmManager(); boolean isMitmEnabled = mitmManager != null; - if (isMitmEnabled) { - connectionFlow - .then(serverConnection.EncryptChannel(mitmManager - .serverSslEngine(remoteAddress.getHostName(), - remoteAddress.getPort()))) + if (isMitmEnabled) { + if(hasUpstreamChainedProxy()){ + // When MITM is enabled and when chained proxy is set up, remoteAddress + // will be the chained proxy's address. So we use serverHostAndPort + // which is the end server's address. + HostAndPort parsedHostAndPort = HostAndPort.fromString(serverHostAndPort); + + connectionFlow.then(serverConnection.EncryptChannel(proxyServer.getMitmManager() + .serverSslEngine(parsedHostAndPort.getHostText(), + parsedHostAndPort.getPort()))); + } else { + connectionFlow.then(serverConnection.EncryptChannel(proxyServer.getMitmManager() + .serverSslEngine(remoteAddress.getHostName(), + remoteAddress.getPort()))); + } + + connectionFlow .then(clientConnection.RespondCONNECTSuccessful) .then(serverConnection.MitmEncryptClientChannel); } else { - // If we're chaining, forward the CONNECT request - if (hasUpstreamChainedProxy()) { - connectionFlow.then( - serverConnection.HTTPCONNECTWithChainedProxy); - } - connectionFlow.then(serverConnection.StartTunneling) .then(clientConnection.RespondCONNECTSuccessful) .then(clientConnection.StartTunneling); @@ -631,7 +654,32 @@ protected void initChannel(Channel ch) throws Exception { protected Future execute() { LOG.debug("Handling CONNECT request through Chained Proxy"); chainedProxy.filterRequest(initialRequest); - return writeToChannel(initialRequest); + MitmManager mitmManager = proxyServer.getMitmManager(); + boolean isMitmEnabled = mitmManager != null; + /* + * We ignore the LastHttpContent which we read from the client + * connection when we are negotiating connect (see readHttp() + * in ProxyConnection). This cannot be ignored while we are + * doing MITM + Chained Proxy because the HttpRequestEncoder + * of the ProxyToServerConnection will be in an invalid state + * when the next request is written. Writing the EmptyLastContent + * resets its state. + */ + if(isMitmEnabled){ + ChannelFuture future = writeToChannel(initialRequest); + future.addListener(new ChannelFutureListener() { + + @Override + public void operationComplete(ChannelFuture arg0) throws Exception { + if(arg0.isSuccess()){ + writeToChannel(LastHttpContent.EMPTY_LAST_CONTENT); + } + } + }); + return future; + } else { + return writeToChannel(initialRequest); + } } void onSuccess(ConnectionFlow flow) { diff --git a/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java index a444f2e91..bb327314f 100644 --- a/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java @@ -20,13 +20,13 @@ * by the downstream proxy was received by the upstream proxy. */ public abstract class BaseChainedProxyTest extends BaseProxyTest { - private final AtomicLong REQUESTS_SENT_BY_DOWNSTREAM = new AtomicLong( + protected final AtomicLong REQUESTS_SENT_BY_DOWNSTREAM = new AtomicLong( 0l); - private final AtomicLong REQUESTS_RECEIVED_BY_UPSTREAM = new AtomicLong( + protected final AtomicLong REQUESTS_RECEIVED_BY_UPSTREAM = new AtomicLong( 0l); - private final ConcurrentSkipListSet TRANSPORTS_USED = new ConcurrentSkipListSet(); + protected final ConcurrentSkipListSet TRANSPORTS_USED = new ConcurrentSkipListSet(); - private final ActivityTracker DOWNSTREAM_TRACKER = new ActivityTrackerAdapter() { + protected final ActivityTracker DOWNSTREAM_TRACKER = new ActivityTrackerAdapter() { @Override public void requestSentToServer(FullFlowContext flowContext, io.netty.handler.codec.http.HttpRequest httpRequest) { @@ -36,7 +36,7 @@ public void requestSentToServer(FullFlowContext flowContext, } }; - private final ActivityTracker UPSTREAM_TRACKER = new ActivityTrackerAdapter() { + protected final ActivityTracker UPSTREAM_TRACKER = new ActivityTrackerAdapter() { @Override public void requestReceivedFromClient(FlowContext flowContext, HttpRequest httpRequest) { @@ -44,7 +44,7 @@ public void requestReceivedFromClient(FlowContext flowContext, } }; - private HttpProxyServer upstreamProxy; + protected HttpProxyServer upstreamProxy; @Override protected void setUp() { diff --git a/src/test/java/org/littleshoot/proxy/MitmProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmProxyTest.java index 1d73d83e7..7b2853a19 100644 --- a/src/test/java/org/littleshoot/proxy/MitmProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/MitmProxyTest.java @@ -31,14 +31,6 @@ public class MitmProxyTest extends BaseProxyTest { protected void setUp() { this.proxyServer = bootstrapProxy() .withPort(0) - // Include a ChainedProxyManager to make sure that MITM setting - // overrides this - .withChainProxyManager(new ChainedProxyManager() { - @Override - public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { - } - }) .withManInTheMiddle(new SelfSignedMitmManager()) .withFiltersSource(new HttpFiltersSourceAdapter() { @Override diff --git a/src/test/java/org/littleshoot/proxy/MitmWithBadClientAuthenticationTCPChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithBadClientAuthenticationTCPChainedProxyTest.java new file mode 100644 index 000000000..c45d5ff08 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithBadClientAuthenticationTCPChainedProxyTest.java @@ -0,0 +1,54 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +import javax.net.ssl.SSLEngine; + +import org.junit.Ignore; +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +/** + * Tests that clients are authenticated and that if they're missing certs, we + * get an error. + */ +@Ignore +public class MitmWithBadClientAuthenticationTCPChainedProxyTest extends + MitmWithChainedProxyTest { + private final SslEngineSource serverSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks"); + + private final SslEngineSource clientSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks", false, false); + + @Override + protected boolean expectBadGatewayForEverything() { + return true; + } + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(TCP) + .withSslEngineSource(serverSslEngineSource); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.TCP; + } + + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return clientSslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithBadServerAuthenticationTCPChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithBadServerAuthenticationTCPChainedProxyTest.java new file mode 100644 index 000000000..1cf317dc8 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithBadServerAuthenticationTCPChainedProxyTest.java @@ -0,0 +1,54 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +import javax.net.ssl.SSLEngine; + +import org.junit.Ignore; +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +/** + * Tests that servers are authenticated and that if they're missing certs, we + * get an error. + */ +@Ignore +public class MitmWithBadServerAuthenticationTCPChainedProxyTest extends + BaseChainedProxyTest { + protected final SslEngineSource serverSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks"); + + protected final SslEngineSource clientSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_2.jks"); + + @Override + protected boolean expectBadGatewayForEverything() { + return true; + } + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(TCP) + .withSslEngineSource(serverSslEngineSource); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.TCP; + } + + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return clientSslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithChainedProxyTest.java new file mode 100644 index 000000000..528d48537 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithChainedProxyTest.java @@ -0,0 +1,175 @@ +package org.littleshoot.proxy; + +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.Set; + +import org.littleshoot.proxy.extras.SelfSignedMitmManager; + +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; + +/** + * Tests a proxy that runs as a MITM and which is chained with + * another proxy. + */ +public class MitmWithChainedProxyTest extends BaseChainedProxyTest { + private Set requestPreMethodsSeen = new HashSet(); + private Set requestPostMethodsSeen = new HashSet(); + private StringBuilder responsePreBody = new StringBuilder(); + private StringBuilder responsePostBody = new StringBuilder(); + private Set responsePreOriginalRequestMethodsSeen = new HashSet(); + private Set responsePostOriginalRequestMethodsSeen = new HashSet(); + + @Override + protected void setUp() { + + REQUESTS_SENT_BY_DOWNSTREAM.set(0); + REQUESTS_RECEIVED_BY_UPSTREAM.set(0); + TRANSPORTS_USED.clear(); + this.upstreamProxy = upstreamProxy().start(); + + this.proxyServer = bootstrapProxy() + .withPort(0) + .withChainProxyManager(chainedProxyManager()) + .plusActivityTracker(DOWNSTREAM_TRACKER) + .withManInTheMiddle(new SelfSignedMitmManager()) + .withFiltersSource(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new HttpFiltersAdapter(originalRequest) { + @Override + public HttpResponse clientToProxyRequest( + HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + requestPreMethodsSeen + .add(((HttpRequest) httpObject) + .getMethod()); + } + return null; + } + + @Override + public HttpResponse proxyToServerRequest( + HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + requestPostMethodsSeen + .add(((HttpRequest) httpObject) + .getMethod()); + } + return null; + } + + @Override + public HttpObject serverToProxyResponse( + HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + responsePreOriginalRequestMethodsSeen + .add(originalRequest.getMethod()); + } else if (httpObject instanceof HttpContent) { + responsePreBody.append(((HttpContent) httpObject) + .content().toString( + Charset.forName("UTF-8"))); + } + return httpObject; + } + + @Override + public HttpObject proxyToClientResponse( + HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + responsePostOriginalRequestMethodsSeen + .add(originalRequest.getMethod()); + } else if (httpObject instanceof HttpContent) { + responsePostBody.append(((HttpContent) httpObject) + .content().toString( + Charset.forName("UTF-8"))); + } + return httpObject; + } + }; + } + }) + .start(); + } + + @Override + protected boolean isMITM() { + return true; + } + + @Override + public void testSimpleGetRequest() throws Exception { + super.testSimpleGetRequest(); + assertMethodSeenInRequestFilters(HttpMethod.GET); + assertMethodSeenInResponseFilters(HttpMethod.GET); + assertResponseFromFiltersMatchesActualResponse(); + } + + @Override + public void testSimpleGetRequestOverHTTPS() throws Exception { + super.testSimpleGetRequestOverHTTPS(); + assertMethodSeenInRequestFilters(HttpMethod.CONNECT); + assertMethodSeenInRequestFilters(HttpMethod.GET); + assertMethodSeenInResponseFilters(HttpMethod.GET); + assertResponseFromFiltersMatchesActualResponse(); + } + + @Override + public void testSimplePostRequest() throws Exception { + super.testSimplePostRequest(); + assertMethodSeenInRequestFilters(HttpMethod.POST); + assertMethodSeenInResponseFilters(HttpMethod.POST); + assertResponseFromFiltersMatchesActualResponse(); + } + + @Override + public void testSimplePostRequestOverHTTPS() throws Exception { + super.testSimplePostRequestOverHTTPS(); + assertMethodSeenInRequestFilters(HttpMethod.CONNECT); + assertMethodSeenInRequestFilters(HttpMethod.POST); + assertMethodSeenInResponseFilters(HttpMethod.POST); + assertResponseFromFiltersMatchesActualResponse(); + } + + private void assertMethodSeenInRequestFilters(HttpMethod method) { + assertThat(method + + " should have been seen in clientToProxyRequest filter", + requestPreMethodsSeen, hasItem(method)); + assertThat(method + + " should have been seen in proxyToServerRequest filter", + requestPostMethodsSeen, hasItem(method)); + } + + private void assertMethodSeenInResponseFilters(HttpMethod method) { + assertThat( + method + + " should have been seen as the original requests's method in serverToProxyResponse filter", + responsePreOriginalRequestMethodsSeen, hasItem(method)); + assertThat( + method + + " should have been seen as the original requests's method in proxyToClientResponse filter", + responsePostOriginalRequestMethodsSeen, hasItem(method)); + } + + private void assertResponseFromFiltersMatchesActualResponse() { + assertEquals( + "Data received through HttpFilters.serverToProxyResponse should match response", + lastResponse, responsePreBody.toString()); + assertEquals( + "Data received through HttpFilters.proxyToClientResponse should match response", + lastResponse, responsePostBody.toString()); + } + + @Override + protected void tearDown() throws Exception { + this.upstreamProxy.abort(); + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithClientAuthenticationNotRequiredTCPChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithClientAuthenticationNotRequiredTCPChainedProxyTest.java new file mode 100644 index 000000000..f748dddb7 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithClientAuthenticationNotRequiredTCPChainedProxyTest.java @@ -0,0 +1,48 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +import javax.net.ssl.SSLEngine; + +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +/** + * Tests that when client authentication is not required, it doesn't matter what + * certs the client sends. + */ +public class MitmWithClientAuthenticationNotRequiredTCPChainedProxyTest extends + MitmWithChainedProxyTest { + private final SslEngineSource serverSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks"); + + private final SslEngineSource clientSslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks", false, false); + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(TCP) + .withSslEngineSource(serverSslEngineSource) + .withAuthenticateSslClients(false); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.TCP; + } + + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return clientSslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithEncryptedTCPChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithEncryptedTCPChainedProxyTest.java new file mode 100644 index 000000000..418e7e8d6 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithEncryptedTCPChainedProxyTest.java @@ -0,0 +1,39 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +import javax.net.ssl.SSLEngine; + +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +public class MitmWithEncryptedTCPChainedProxyTest extends MitmWithChainedProxyTest { + private final SslEngineSource sslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks"); + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(TCP) + .withSslEngineSource(sslEngineSource); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.TCP; + } + + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return sslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithEncryptedUDTChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithEncryptedUDTChainedProxyTest.java new file mode 100644 index 000000000..0630149d6 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithEncryptedUDTChainedProxyTest.java @@ -0,0 +1,39 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +import javax.net.ssl.SSLEngine; + +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +public class MitmWithEncryptedUDTChainedProxyTest extends MitmWithChainedProxyTest { + private final SslEngineSource sslEngineSource = new SelfSignedSslEngineSource( + "chain_proxy_keystore_1.jks"); + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(UDT) + .withSslEngineSource(sslEngineSource); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.UDT; + } + + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return sslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedTCPChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedTCPChainedProxyTest.java new file mode 100644 index 000000000..6cfb1757d --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedTCPChainedProxyTest.java @@ -0,0 +1,26 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +public class MitmWithUnencryptedTCPChainedProxyTest extends MitmWithChainedProxyTest { + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(TCP); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.TCP; + } + + @Override + public boolean requiresEncryption() { + return false; + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedUDTChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedUDTChainedProxyTest.java new file mode 100644 index 000000000..3c32a4ee9 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/MitmWithUnencryptedUDTChainedProxyTest.java @@ -0,0 +1,26 @@ +package org.littleshoot.proxy; + +import static org.littleshoot.proxy.TransportProtocol.*; + +public class MitmWithUnencryptedUDTChainedProxyTest extends MitmWithChainedProxyTest { + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withTransportProtocol(UDT); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public TransportProtocol getTransportProtocol() { + return TransportProtocol.UDT; + } + + @Override + public boolean requiresEncryption() { + return false; + } + }; + } +}