From 03564ed30f72100aeb6ad21eca9d80cbb87b1e20 Mon Sep 17 00:00:00 2001 From: mrogers Date: Tue, 23 Oct 2018 17:49:52 -0600 Subject: [PATCH 01/30] Made client details available to ChainedProxyManager --- .../proxy/ChainedProxyManager.java | 5 +- .../littleshoot/proxy/impl/ClientDetails.java | 35 +++++++++++ .../proxy/impl/ClientToProxyConnection.java | 8 +++ .../proxy/impl/ProxyToServerConnection.java | 2 +- .../AuthenticatingProxyWithChainingTest.java | 63 +++++++++++++++++++ .../proxy/BaseChainedProxyTest.java | 4 +- .../proxy/ChainedProxyWithFallbackTest.java | 4 +- ...ProxyWithFallbackToDirectDueToSSLTest.java | 4 +- ...llbackToOtherChainedProxyDueToSSLTest.java | 4 +- .../org/littleshoot/proxy/HttpFilterTest.java | 5 +- .../proxy/NoChainedProxiesTest.java | 4 +- 11 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/littleshoot/proxy/impl/ClientDetails.java create mode 100644 src/test/java/org/littleshoot/proxy/AuthenticatingProxyWithChainingTest.java diff --git a/src/main/java/org/littleshoot/proxy/ChainedProxyManager.java b/src/main/java/org/littleshoot/proxy/ChainedProxyManager.java index f73c69cb3..e7123d319 100644 --- a/src/main/java/org/littleshoot/proxy/ChainedProxyManager.java +++ b/src/main/java/org/littleshoot/proxy/ChainedProxyManager.java @@ -1,6 +1,7 @@ package org.littleshoot.proxy; import io.netty.handler.codec.http.HttpRequest; +import org.littleshoot.proxy.impl.ClientDetails; import java.util.Queue; @@ -32,7 +33,9 @@ public interface ChainedProxyManager { * * @param httpRequest * @param chainedProxies + * @param clientDetails */ void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies); + Queue chainedProxies, + ClientDetails clientDetails); } \ No newline at end of file diff --git a/src/main/java/org/littleshoot/proxy/impl/ClientDetails.java b/src/main/java/org/littleshoot/proxy/impl/ClientDetails.java new file mode 100644 index 000000000..aa47c2769 --- /dev/null +++ b/src/main/java/org/littleshoot/proxy/impl/ClientDetails.java @@ -0,0 +1,35 @@ +package org.littleshoot.proxy.impl; + +import java.net.InetSocketAddress; + +/** + * Contains information about the client. + */ +public class ClientDetails { + + /** + * The user name that was used for authentication, or null if authentication wasn't performed. + */ + private volatile String userName; + + /** + * The client's address + */ + private volatile InetSocketAddress clientAddress; + + public String getUserName() { + return userName; + } + + void setUserName(String userName) { + this.userName = userName; + } + + public InetSocketAddress getClientAddress() { + return clientAddress; + } + + void setClientAddress(InetSocketAddress clientAddress) { + this.clientAddress = clientAddress; + } +} diff --git a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java index 964858fbf..6ba7c06b8 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java @@ -141,6 +141,8 @@ public class ClientToProxyConnection extends ProxyConnection { */ private volatile HttpRequest currentRequest; + private final ClientDetails clientDetails = new ClientDetails(); + ClientToProxyConnection( final DefaultHttpProxyServer proxyServer, SslEngineSource sslEngineSource, @@ -994,6 +996,7 @@ private boolean authenticationRequired(HttpRequest request) { writeAuthenticationRequired(authenticator.getRealm()); return true; } + clientDetails.setUserName(userName); LOG.debug("Got proxy authorization!"); // We need to remove the header before sending the request on. @@ -1402,6 +1405,7 @@ protected void responseWritten(HttpResponse httpResponse) { private void recordClientConnected() { try { InetSocketAddress clientAddress = getClientAddress(); + clientDetails.setClientAddress(clientAddress); for (ActivityTracker tracker : proxyServer .getActivityTrackers()) { tracker.clientConnected(clientAddress); @@ -1452,4 +1456,8 @@ private FlowContext flowContext() { } } + public ClientDetails getClientDetails() { + return clientDetails; + } + } diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java index 2c9cece42..bffbdd2ff 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java @@ -161,7 +161,7 @@ static ProxyToServerConnection create(DefaultHttpProxyServer proxyServer, .getChainProxyManager(); if (chainedProxyManager != null) { chainedProxyManager.lookupChainedProxies(initialHttpRequest, - chainedProxies); + chainedProxies, clientConnection.getClientDetails()); if (chainedProxies.size() == 0) { // ChainedProxyManager returned no proxies, can't connect return null; diff --git a/src/test/java/org/littleshoot/proxy/AuthenticatingProxyWithChainingTest.java b/src/test/java/org/littleshoot/proxy/AuthenticatingProxyWithChainingTest.java new file mode 100644 index 000000000..1d7dfb159 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/AuthenticatingProxyWithChainingTest.java @@ -0,0 +1,63 @@ +package org.littleshoot.proxy; + +import io.netty.handler.codec.http.HttpRequest; +import org.junit.Assert; +import org.littleshoot.proxy.impl.ClientDetails; + +import java.util.Queue; + +/** + * Tests a single proxy that requires username/password authentication. + */ +public class AuthenticatingProxyWithChainingTest extends BaseProxyTest + implements ProxyAuthenticator, ChainedProxyManager { + + private ClientDetails savedClientDetails; + + @Override + protected void setUp() { + this.proxyServer = bootstrapProxy() + .withPort(0) + .withProxyAuthenticator(this) + .withChainProxyManager(this) + .start(); + } + + @Override + protected String getUsername() { + return "user1"; + } + + @Override + protected String getPassword() { + return "user2"; + } + + @Override + public boolean authenticate(String userName, String password) { + return getUsername().equals(userName) && getPassword().equals(password); + } + + @Override + protected boolean isAuthenticating() { + return true; + } + + @Override + public String getRealm() { + return null; + } + + @Override + public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies, ClientDetails clientDetails) { + savedClientDetails = clientDetails; + chainedProxies.add(ChainedProxyAdapter.FALLBACK_TO_DIRECT_CONNECTION); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + Assert.assertEquals(getUsername(), savedClientDetails.getUserName()); + Assert.assertTrue(savedClientDetails.getClientAddress().getAddress().isLoopbackAddress()); + } +} diff --git a/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java index bb327314f..a8f9cd761 100644 --- a/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseChainedProxyTest.java @@ -1,6 +1,7 @@ package org.littleshoot.proxy; import io.netty.handler.codec.http.HttpRequest; +import org.littleshoot.proxy.impl.ClientDetails; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import java.net.InetAddress; @@ -70,7 +71,8 @@ protected ChainedProxyManager chainedProxyManager() { return new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { + Queue chainedProxies, + ClientDetails clientDetails) { chainedProxies.add(newChainedProxy()); } }; diff --git a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackTest.java b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackTest.java index 8cb7efb8a..04d7c5381 100644 --- a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackTest.java +++ b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackTest.java @@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Assert; +import org.littleshoot.proxy.impl.ClientDetails; /** * Tests a proxy chained to a missing downstream proxy. When the downstream @@ -27,7 +28,8 @@ protected void setUp() { .withChainProxyManager(new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { + Queue chainedProxies, + ClientDetails clientDetails) { chainedProxies.add(new ChainedProxyAdapter() { @Override public InetSocketAddress getChainedProxyAddress() { diff --git a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToDirectDueToSSLTest.java b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToDirectDueToSSLTest.java index b01296eaa..92359acd5 100644 --- a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToDirectDueToSSLTest.java +++ b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToDirectDueToSSLTest.java @@ -1,6 +1,7 @@ package org.littleshoot.proxy; import io.netty.handler.codec.http.HttpRequest; +import org.littleshoot.proxy.impl.ClientDetails; import java.util.Queue; @@ -27,7 +28,8 @@ protected ChainedProxyManager chainedProxyManager() { return new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { + Queue chainedProxies, + ClientDetails clientDetails) { // This first one has a bad cert chainedProxies.add(newChainedProxy()); chainedProxies diff --git a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToOtherChainedProxyDueToSSLTest.java b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToOtherChainedProxyDueToSSLTest.java index c16ffa9d1..f2df4f4fb 100644 --- a/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToOtherChainedProxyDueToSSLTest.java +++ b/src/test/java/org/littleshoot/proxy/ChainedProxyWithFallbackToOtherChainedProxyDueToSSLTest.java @@ -1,6 +1,7 @@ package org.littleshoot.proxy; import io.netty.handler.codec.http.HttpRequest; +import org.littleshoot.proxy.impl.ClientDetails; import java.util.Queue; @@ -22,7 +23,8 @@ protected ChainedProxyManager chainedProxyManager() { return new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { + Queue chainedProxies, + ClientDetails clientDetails) { // This first one has a bad cert chainedProxies.add(newChainedProxy()); // This 2nd one should work diff --git a/src/test/java/org/littleshoot/proxy/HttpFilterTest.java b/src/test/java/org/littleshoot/proxy/HttpFilterTest.java index 56e3a229e..d50e8f365 100644 --- a/src/test/java/org/littleshoot/proxy/HttpFilterTest.java +++ b/src/test/java/org/littleshoot/proxy/HttpFilterTest.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.junit.Test; import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; +import org.littleshoot.proxy.impl.ClientDetails; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import org.littleshoot.proxy.test.HttpClientUtil; import org.mockserver.integration.ClientAndServer; @@ -548,7 +549,7 @@ public HttpFilters filterRequest(HttpRequest originalRequest) { .withFiltersSource(filtersSource) .withChainProxyManager(new ChainedProxyManager() { @Override - public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies) { + public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies, ClientDetails clientDetails) { chainedProxies.add(new ChainedProxyAdapter() { @Override public InetSocketAddress getChainedProxyAddress() { @@ -616,7 +617,7 @@ public HttpFilters filterRequest(HttpRequest originalRequest) { .withFiltersSource(filtersSource) .withChainProxyManager(new ChainedProxyManager() { @Override - public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies) { + public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies, ClientDetails clientDetails) { chainedProxies.add(new ChainedProxyAdapter() { @Override public InetSocketAddress getChainedProxyAddress() { diff --git a/src/test/java/org/littleshoot/proxy/NoChainedProxiesTest.java b/src/test/java/org/littleshoot/proxy/NoChainedProxiesTest.java index ec152e13e..3eef7c952 100644 --- a/src/test/java/org/littleshoot/proxy/NoChainedProxiesTest.java +++ b/src/test/java/org/littleshoot/proxy/NoChainedProxiesTest.java @@ -5,6 +5,7 @@ import java.util.Queue; import org.junit.Test; +import org.littleshoot.proxy.impl.ClientDetails; /** * Tests that when there are no chained proxies, we get a bad gateway. @@ -17,7 +18,8 @@ protected void setUp() { .withChainProxyManager(new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, - Queue chainedProxies) { + Queue chainedProxies, + ClientDetails clientDetails) { // Leave list empty } }) From 348229a2b4a5d6b8bf74784b8ea54321119e08f3 Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Wed, 6 Feb 2019 00:56:23 +0100 Subject: [PATCH 02/30] Bump libraries versions. Move to different groupid to allow Maven central release of this fork --- README.md | 9 ++++----- pom.xml | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d512855e6..2a8413fcc 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,21 @@ -[![Build Status](https://travis-ci.org/adamfisk/LittleProxy.png?branch=master)](https://travis-ci.org/adamfisk/LittleProxy) +This is an updated fork of adamfisk's LittleProxy with only some of dependencies updated to their most recent versions. It is published with all those tha cannot wait for it's 1.1.3 release, and yet need to have it in Maven central. LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. One option is to clone LittleProxy and run it from the command line. This is as simple as: ``` -$ git clone git://github.com/adamfisk/LittleProxy.git +$ git clone git@github.com:JockX/LittleProxy.git $ cd LittleProxy $ ./run.bash ``` You can embed LittleProxy in your own projects through Maven with the following: - ``` - org.littleshoot + net.jockx littleproxy - 1.1.2 + 1.1.3 ``` diff --git a/pom.xml b/pom.xml index 351b006d3..711ca2b35 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@ 4.0.0 - org.littleshoot + net.jockx littleproxy jar 1.1.3-SNAPSHOT @@ -8,15 +8,15 @@ LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework. - http://littleproxy.org + https://github.com/JockX/LittleProxy UTF-8 UTF-8 github - 4.0.44.Final - 1.7.24 - 1.7 + 4.0.54.Final + 1.7.25 + 1.8 @@ -43,13 +43,13 @@ github - https://github.com/adamfisk/LittleProxy/issues + https://github.com/JockX/LittleProxy/issues - scm:git:https://adamfisk@github.com/adamfisk/LittleProxy.git - scm:git:git@github.com:adamfisk/LittleProxy - scm:git:git@github.com:adamfisk/LittleProxy.git + scm:git:https://github.com/JockX/LittleProxy.git + scm:git:git@github.com:JockX/LittleProxy.git + scm:git:https://github.com/JockX/LittleProxy HEAD @@ -177,7 +177,7 @@ netty-4.1 - 4.1.8.Final + 4.1.33.Final @@ -186,13 +186,13 @@ com.google.guava guava - 23.0 + 27.0.1-jre commons-cli commons-cli - 1.3.1 + 1.4 true @@ -200,7 +200,7 @@ org.apache.commons commons-lang3 - 3.5 + 3.7 @@ -234,7 +234,7 @@ org.mockito mockito-core - 2.7.12 + 2.13.0 test @@ -274,7 +274,7 @@ org.apache.httpcomponents httpclient - 4.5.3 + 4.5.4 test From b80e1fb493526697b7d5f1b3fad098c965a74a6f Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Wed, 6 Feb 2019 01:18:13 +0100 Subject: [PATCH 03/30] [maven-release-plugin] prepare release littleproxy-1.1.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 711ca2b35..bdbfd7084 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ net.jockx littleproxy jar - 1.1.3-SNAPSHOT + 1.1.3 LittleProxy LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework. @@ -50,7 +50,7 @@ scm:git:https://github.com/JockX/LittleProxy.git scm:git:git@github.com:JockX/LittleProxy.git scm:git:https://github.com/JockX/LittleProxy - HEAD + littleproxy-1.1.3 From b90574f5e8cfb0f9c827cc7f988556db37d3813a Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Wed, 6 Feb 2019 01:18:21 +0100 Subject: [PATCH 04/30] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bdbfd7084..b31e6b398 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ net.jockx littleproxy jar - 1.1.3 + 1.1.4-SNAPSHOT LittleProxy LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework. @@ -50,7 +50,7 @@ scm:git:https://github.com/JockX/LittleProxy.git scm:git:git@github.com:JockX/LittleProxy.git scm:git:https://github.com/JockX/LittleProxy - littleproxy-1.1.3 + HEAD From 12a42d3aa687997bcbf1b221ff3a34e5fb4d767b Mon Sep 17 00:00:00 2001 From: mrogers Date: Mon, 11 Feb 2019 14:27:02 -0700 Subject: [PATCH 05/30] Updated repo info --- README.md | 8 ++++---- pom.xml | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2a8413fcc..e85f44891 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -This is an updated fork of adamfisk's LittleProxy with only some of dependencies updated to their most recent versions. It is published with all those tha cannot wait for it's 1.1.3 release, and yet need to have it in Maven central. +This is an updated fork of adamfisk's LittleProxy. The original project appears to have been abondoned. Because it's so incredibly useful, it's being brought back to life in this repository. LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. One option is to clone LittleProxy and run it from the command line. This is as simple as: ``` -$ git clone git@github.com:JockX/LittleProxy.git +$ git clone git@github.com:mrog/LittleProxy.git $ cd LittleProxy $ ./run.bash ``` @@ -13,9 +13,9 @@ $ ./run.bash You can embed LittleProxy in your own projects through Maven with the following: ``` - net.jockx + xyz.rogfam littleproxy - 1.1.3 + 2.0.0 ``` diff --git a/pom.xml b/pom.xml index b31e6b398..840587ea4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,14 +1,14 @@ 4.0.0 - net.jockx + xyz.rogfam littleproxy jar - 1.1.4-SNAPSHOT + 2.0.0-SNAPSHOT LittleProxy LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework. - https://github.com/JockX/LittleProxy + https://github.com/mrog/LittleProxy UTF-8 @@ -43,13 +43,13 @@ github - https://github.com/JockX/LittleProxy/issues + https://github.com/mrog/LittleProxy/issues - scm:git:https://github.com/JockX/LittleProxy.git - scm:git:git@github.com:JockX/LittleProxy.git - scm:git:https://github.com/JockX/LittleProxy + scm:git:https://github.com/mrog/LittleProxy.git + scm:git:git@github.com:mrog/LittleProxy.git + scm:git:https://github.com/mrog/LittleProxy HEAD From 6b60974a0002f774174f609342d69da7ed845388 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Mon, 18 Feb 2019 11:57:30 -0700 Subject: [PATCH 06/30] Updated dependency versions --- littleproxy_cert | Bin 1233 -> 1233 bytes pom.xml | 93 ++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/littleproxy_cert b/littleproxy_cert index f609f3ba6b38fc3ba467fc5d970a3e09d60121d1..d31898a2dce61d8886717eb4f8775b79a9f1ea84 100644 GIT binary patch delta 1152 zcmV-`1b_R{3DF4?FoFclFoFWLpaTK{0s;gP(dOZi6D%A#FfuVXF*r3aF*sTn4>B<^ zIWRFYH8D6fFfllhTRMNtO@T;1G>RKvPy13FZyqb|m@^cxmZ`psXt7`^G%s`6Kku_F zd*ZP4Xh+L0z)y(!%{lO>s~;V}M$~ko=UIrg2RpdDNR<~5GoG#!5Y(F1ideg(FB?zW z3&j^PMC6agk;R)tFk$`=Y<@ZqE|Ng|y77U&xEJI7YvO~~Yt?^0^WGd%v^?T9Z`E_$ zZ7;8Uqm*7Bc-6V1nx&`uHbF0;YXRwo4a=ffQ4O_AHKyuBW6gI zDLF}6rM#&jX#jt+6|NsY#de&tfi2^sY$dyQCuWzc-tM_G z8TpPaxK;G5VYd8c0cpvd-8^zCvK|W(|1PiEGHRG8dJd)?%Ur310~K_7&>{4%NTl3+ z-ZMO(yh=3ypPbU22sO?(NGoO)%Kvg_5-3?Yi}TqT_nUv6PrMxQdwUsTJGs3!=6V>W zlc=4TTLah2LiRT{?}CeC;RusAR%1u3)LB~~GeKfWZtU5V5|pV%fZsH~ixQ7B9GD5L z$N-~32?F0r&Da00iwxqZ(M z$+#uG4?C&Ttk`o86wh^=1aDhKuG8NxYLH;_J*|IQrqp^ySH4|%Bl%?!DPve?cjX>Wk+blZXZwL5#UO_${k$MPv# zDe8ZH!BNcrEXe@CcNXpDDS`vv#hP5&*!b`ZH|bpS_egNQ|eDZ6cO5F8~$-B<^ zG%z+XG%_DcpfO zU1hawLy^_o4Z#!R(o^=ZoyUfnRpQIGD6YW4GwGx@UL3+eQq#R`ltWMQ8GYX1$B3F; zYwYOsWPt=&FnZIWyF30lvv=lFmah%7G2;OvAx&6*d3XU=8DsIwcwT1)=de zsiDLp4m|-#@o;zA$#F<4Ps~|15&1Zltv-}_&p4ej=^*!G5~;RgnBbog%q`(#_k;#^ zgOh$)I#0$Uw(EZ;=Y^boA#7m8ELMTaMPyRL;|>8ms4P>S^HQ;t_tYJ9^9}2AO!f`l z^nj0p$>u~)IA9Lpvg%-tNWEtM3Z2o%w5DzW|6<9i6~V0r-KmH>Q8B0=n$U-PLMM)t z{imDz3(f?YZU&Zkhd9Ucobi9V3GrkKYo*-jrG3k`1Ejvm zA^f>ZtgfI1N$ zV+Sa~v%-IQc(Ts|0|5X5qaiRKFdYU1RUHll76cScjihAu<26uc-FvhuU-p@S!0TRUyzoCX# z`gqesw@I*?p=dL{n2VmxQ(r2ip<$<&Qj01$>(YM&W^0l=m|$b-!MvNm=CNS8c6twL zPaE$YIT|7R-^;YV8x@BnGedIxrIV54z;~~YYR(I}KGjubiAJjgIdT7&)ZeQv)XyM; z;~Y(3LrGO}1VZ+kePp{ua{=c9!FlzPjg6qu>I#0waKs74=G!{hXq%8BXLrO3?s{{T zNBMufd&MNKDV8N9E>p_3J(j}o$r{+j zU7%}qXFW)P?>?LD*D1ayWMv2#hVk*l(J2J?>6R2O-d<{blN(kNkjX^Alr?%kzR_z~ z8&S>809%~aYxnd*;pgErv;hWP@su|SqSSxS;pc>6)R+O)>XA^DTq~)An(7a#Tv&?* z!?9_vmn8@MQQVGjAYa>=l5eV7uEmBjzJ>3huB2Sl45xu;cVQBvUv~kne7L_0eQTfF zc++-EPX`1-*$1csR3Pa4nO;lo7L{cLB+?Hj&X%$*jA2F+7TXIuWlqP=0rHbw2U0}d zlNGMb1k3C|yRovKO{hby1Am5bo2JGV9CdfsEitO_yWH2o>oogUD{cqa#>gSPy~}p^ S#|=uiv%ZZYLTmfz9kee;`7ne4 diff --git a/pom.xml b/pom.xml index 840587ea4..9e109edb3 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ UTF-8 UTF-8 github - 4.0.54.Final + 4.1.33.Final 1.7.25 1.8 @@ -78,15 +78,6 @@ -Xdoclint:none - - doclint-java7-earlier - - [,1.8) - - - - - release @@ -96,7 +87,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 2.22.1 true @@ -118,7 +109,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.0.1 ${java.version} @@ -151,7 +142,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh @@ -173,13 +164,6 @@ - - - netty-4.1 - - 4.1.33.Final - - @@ -200,7 +184,7 @@ org.apache.commons commons-lang3 - 3.7 + 3.8.1 @@ -213,35 +197,35 @@ org.hamcrest hamcrest-core - 1.3 + 2.1 test org.hamcrest hamcrest-library - 1.3 + 2.1 test org.eclipse.jetty jetty-server - 8.1.17.v20150415 + 8.1.22.v20160922 test org.mockito mockito-core - 2.13.0 + 2.24.0 test org.mock-server mockserver-netty - 3.10.4 + 5.5.1 test @@ -254,7 +238,7 @@ org.seleniumhq.selenium selenium-java - 2.53.1 + 3.141.59 test @@ -265,16 +249,16 @@ - log4j - log4j - 1.2.17 + org.apache.logging.log4j + log4j-core + 2.11.2 true org.apache.httpcomponents httpclient - 4.5.4 + 4.5.7 test @@ -421,7 +405,7 @@ org.apache.maven.plugins maven-site-plugin - 3.6 + 3.7.1 @@ -433,13 +417,13 @@ org.apache.maven.plugins maven-dependency-plugin - 2.10 + 3.1.1 org.apache.maven.plugins maven-clean-plugin - 3.0.0 + 3.1.0 @@ -457,13 +441,13 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.1.1 org.apache.maven.plugins maven-resources-plugin - 3.0.2 + 3.1.0 @@ -472,7 +456,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 2.22.1 -Xmx1g -XX:MaxPermSize=256m @@ -481,7 +465,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.6.0 + 3.8.0 ${java.version} ${java.version} @@ -492,7 +476,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.0.1 private ${java.version} @@ -507,7 +491,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.4.3 + 3.2.1 package @@ -557,12 +541,12 @@ org.apache.maven.plugins maven-site-plugin - 3.4 + 3.7.1 maven-dependency-plugin - 2.10 + 3.1.1 @@ -574,7 +558,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 2.9 + 3.0.0 true true @@ -583,7 +567,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 + 3.0.1 private ${java.version} @@ -597,7 +581,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 2.19.1 + 2.22.1 false @@ -605,7 +589,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.17 + 3.0.0 org.apache.maven.plugins @@ -627,7 +611,7 @@ org.codehaus.mojo findbugs-maven-plugin - 3.0.4 + 3.0.5 @@ -635,12 +619,12 @@ org.apache.maven.plugins maven-jxr-plugin - 2.5 + 3.0.0 org.apache.maven.plugins maven-pmd-plugin - 3.7 + 3.11.0 true utf-8 @@ -656,7 +640,7 @@ org.codehaus.mojo versions-maven-plugin - 2.3 + 2.7 @@ -708,5 +692,14 @@ Developer -5 + + + mrogers + Mark Rogers + rogfam + rogfam.xyz + Developer + -7 + From 87c6beb9237eba20b0392831e36431cd1a579bf9 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 22 Feb 2019 15:05:31 -0700 Subject: [PATCH 07/30] Using autoclosing streams in a few places --- .../extras/SelfSignedSslEngineSource.java | 11 ++- .../java/org/littleshoot/proxy/TestUtils.java | 16 ++-- .../proxy/VariableSpeedClientServerTest.java | 79 +++++++++---------- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java index 31b671cdd..ff7906502 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java @@ -106,7 +106,9 @@ private void initializeSSLContext() { final KeyStore ks = KeyStore.getInstance("JKS"); // ks.load(new FileInputStream("keystore.jks"), // "changeit".toCharArray()); - ks.load(new FileInputStream(keyStoreFile), PASSWORD.toCharArray()); + try (InputStream is = new FileInputStream(keyStoreFile)) { + ks.load(is, PASSWORD.toCharArray()); + } // Set up key manager factory to use our key store final KeyManagerFactory kmf = @@ -164,9 +166,10 @@ private String nativeCall(final String... commands) { final ProcessBuilder pb = new ProcessBuilder(commands); try { final Process process = pb.start(); - final InputStream is = process.getInputStream(); - - byte[] data = ByteStreams.toByteArray(is); + byte[] data; + try (InputStream is = process.getInputStream()) { + data = ByteStreams.toByteArray(is); + } String dataAsString = new String(data); LOG.info("Completed native call: '{}'\nResponse: '" + dataAsString + "'", diff --git a/src/test/java/org/littleshoot/proxy/TestUtils.java b/src/test/java/org/littleshoot/proxy/TestUtils.java index 61cd3fce2..af9d4ec32 100644 --- a/src/test/java/org/littleshoot/proxy/TestUtils.java +++ b/src/test/java/org/littleshoot/proxy/TestUtils.java @@ -88,10 +88,10 @@ public void handle(String target, Request baseRequest, } long numberOfBytesRead = 0; - InputStream in = new BufferedInputStream(request - .getInputStream()); - while (in.read() != -1) { - numberOfBytesRead += 1; + try (InputStream in = new BufferedInputStream(request.getInputStream())) { + while (in.read() != -1) { + numberOfBytesRead += 1; + } } System.out.println("Done reading # of bytes: " + numberOfBytesRead); @@ -163,10 +163,10 @@ public void handle(String target, Request baseRequest, } long numberOfBytesRead = 0; - InputStream in = new BufferedInputStream(request - .getInputStream()); - while (in.read() != -1) { - numberOfBytesRead += 1; + try (InputStream in = new BufferedInputStream(request.getInputStream())) { + while (in.read() != -1) { + numberOfBytesRead += 1; + } } System.out.println("Done reading # of bytes: " + numberOfBytesRead); diff --git a/src/test/java/org/littleshoot/proxy/VariableSpeedClientServerTest.java b/src/test/java/org/littleshoot/proxy/VariableSpeedClientServerTest.java index 26f330c1c..5452ad8a1 100644 --- a/src/test/java/org/littleshoot/proxy/VariableSpeedClientServerTest.java +++ b/src/test/java/org/littleshoot/proxy/VariableSpeedClientServerTest.java @@ -93,22 +93,19 @@ public int available() throws IOException { final long cl = entity.getContentLength(); assertEquals(CONTENT_LENGTH, cl); - InputStream content = entity.getContent(); - if (!slowServer) { - content = new ThrottledInputStream(entity.getContent(), 10 * 1000); - } - final byte[] input = new byte[100000]; - int read = content.read(input); - int bytesRead = 0; - while (read != -1) { - bytesRead += read; - read = content.read(input); + try (InputStream content = slowServer ? new ThrottledInputStream(entity.getContent(), 10 * 1000) : entity.getContent()) { + final byte[] input = new byte[100000]; + int read = content.read(input); + + while (read != -1) { + bytesRead += read; + read = content.read(input); + } } assertEquals(CONTENT_LENGTH, bytesRead); // final String body = IOUtils.toString(entity.getContent()); EntityUtils.consume(entity); - content.close(); System.out .println("------------------ Memory Usage At Beginning ------------------"); TestUtils.getOpenFileDescriptorsAndPrintMemoryUsage(); @@ -138,39 +135,37 @@ private void startServerOnThread(int port, boolean slowReader) try { server.setSoTimeout(100000); final Socket sock = server.accept(); - InputStream is = sock.getInputStream(); - if (slowReader) { - is = new ThrottledInputStream(is, 10 * 1000); - } - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - while (br.read() != 0) { + try (InputStream is = slowReader ? new ThrottledInputStream(sock.getInputStream(), 10 * 1000) : sock.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is))) { + while (br.read() != 0) { + } } - final OutputStream os = sock.getOutputStream(); - final String responseHeaders = - "HTTP/1.1 200 OK\r\n" + - "Date: Sun, 20 Jan 2013 00:16:23 GMT\r\n" + - "Expires: -1\r\n" + - "Cache-Control: private, max-age=0\r\n" + - "Content-Type: text/html; charset=ISO-8859-1\r\n" + - "Server: gws\r\n" + - "Content-Length: " + CONTENT_LENGTH + "\r\n\r\n"; // 10 - // gigs - // or - // so. - - os.write(responseHeaders.getBytes(Charset.forName("UTF-8"))); - - int bufferSize = 100000; - final byte[] bytes = new byte[bufferSize]; - Arrays.fill(bytes, (byte) 77); - int remainingBytes = CONTENT_LENGTH; - - while (remainingBytes > 0) { - int numberOfBytesToWrite = Math.min(remainingBytes, bufferSize); - os.write(bytes, 0, numberOfBytesToWrite); - remainingBytes -= numberOfBytesToWrite; + try (OutputStream os = sock.getOutputStream()) { + final String responseHeaders = + "HTTP/1.1 200 OK\r\n" + + "Date: Sun, 20 Jan 2013 00:16:23 GMT\r\n" + + "Expires: -1\r\n" + + "Cache-Control: private, max-age=0\r\n" + + "Content-Type: text/html; charset=ISO-8859-1\r\n" + + "Server: gws\r\n" + + "Content-Length: " + CONTENT_LENGTH + "\r\n\r\n"; // 10 + // gigs + // or + // so. + + os.write(responseHeaders.getBytes(Charset.forName("UTF-8"))); + + int bufferSize = 100000; + final byte[] bytes = new byte[bufferSize]; + Arrays.fill(bytes, (byte) 77); + int remainingBytes = CONTENT_LENGTH; + + while (remainingBytes > 0) { + int numberOfBytesToWrite = Math.min(remainingBytes, bufferSize); + os.write(bytes, 0, numberOfBytesToWrite); + remainingBytes -= numberOfBytesToWrite; + } } - os.close(); } finally { server.close(); } From adeb66f7e13d4e7962f060687944f3816527347e Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 22 Feb 2019 22:53:33 -0700 Subject: [PATCH 08/30] Changed bad address in test code --- src/test/java/org/littleshoot/proxy/BaseProxyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java index b6af36b47..dc70aa9cb 100644 --- a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java @@ -49,7 +49,7 @@ public void testHeadRequestFollowedByGet() throws Exception { public void testProxyWithBadAddress() throws Exception { ResponseInfo response = - httpPostWithApacheClient(new HttpHost("test.localhost"), + httpPostWithApacheClient(new HttpHost("127.123.213.222"), DEFAULT_RESOURCE, true); assertReceivedBadGateway(response); } From 57af663b79fc57881dde221afed89fe76b86fbaf Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 22 Feb 2019 22:59:04 -0700 Subject: [PATCH 09/30] Revert "Changed bad address in test code" This reverts commit adeb66f7e13d4e7962f060687944f3816527347e. --- src/test/java/org/littleshoot/proxy/BaseProxyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java index dc70aa9cb..b6af36b47 100644 --- a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java @@ -49,7 +49,7 @@ public void testHeadRequestFollowedByGet() throws Exception { public void testProxyWithBadAddress() throws Exception { ResponseInfo response = - httpPostWithApacheClient(new HttpHost("127.123.213.222"), + httpPostWithApacheClient(new HttpHost("test.localhost"), DEFAULT_RESOURCE, true); assertReceivedBadGateway(response); } From 06e43edff7b810dfba5039a6a38a933f9806c4b2 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 22 Feb 2019 23:09:27 -0700 Subject: [PATCH 10/30] Another attempt at fixing bad address tests --- src/test/java/org/littleshoot/proxy/BaseProxyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java index b6af36b47..487358d08 100644 --- a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java @@ -49,7 +49,7 @@ public void testHeadRequestFollowedByGet() throws Exception { public void testProxyWithBadAddress() throws Exception { ResponseInfo response = - httpPostWithApacheClient(new HttpHost("test.localhost"), + httpPostWithApacheClient(new HttpHost("localhost", 17), DEFAULT_RESOURCE, true); assertReceivedBadGateway(response); } From 326628124636a4db841938340b466d3ea78f3ca8 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Sat, 23 Feb 2019 08:18:24 -0700 Subject: [PATCH 11/30] Added a comment --- src/test/java/org/littleshoot/proxy/BaseProxyTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java index 487358d08..7e052d88c 100644 --- a/src/test/java/org/littleshoot/proxy/BaseProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/BaseProxyTest.java @@ -48,6 +48,9 @@ public void testHeadRequestFollowedByGet() throws Exception { @Test public void testProxyWithBadAddress() throws Exception { + // This test used to try connecting to "test.localhost" and that worked for for local builds, but resulted in + // the wrong error (405 instead of 502) on the build server due to nginx. So, switched it to localhost:17, + // which should work as long as there's not a web server running on the QOTD port. ResponseInfo response = httpPostWithApacheClient(new HttpHost("localhost", 17), DEFAULT_RESOURCE, true); From 4b200ea5b488578388c3894fb62cb8204a4a79e1 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Sat, 23 Feb 2019 12:08:57 -0700 Subject: [PATCH 12/30] Skip IdleTest in Jenkins --- src/test/java/org/littleshoot/proxy/IdleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/littleshoot/proxy/IdleTest.java b/src/test/java/org/littleshoot/proxy/IdleTest.java index f3bee0d13..42c4c0e6c 100644 --- a/src/test/java/org/littleshoot/proxy/IdleTest.java +++ b/src/test/java/org/littleshoot/proxy/IdleTest.java @@ -30,7 +30,7 @@ public class IdleTest { public void setup() throws Exception { assumeTrue("Skipping due to non-Unix OS", TestUtils.isUnixManagementCapable()); - assumeFalse("Skipping for travis-ci build", "true".equals(System.getenv("TRAVIS"))); + assumeFalse("Skipping for Jenkins build", "true".equals(System.getenv("JENKINS"))); webServer = new Server(0); webServer.start(); From 8b8e31e362729e712cd4468cd0c84785d2e3631f Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Sat, 23 Feb 2019 17:55:04 -0700 Subject: [PATCH 13/30] Updated readme --- README.md | 11 +++-------- pom.xml | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e85f44891..07d4cbeca 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://ci.codemc.org/buildStatus/icon?job=mrog%2FLittleProxy)](https://ci.codemc.org/job/mrog/job/LittleProxy/) + This is an updated fork of adamfisk's LittleProxy. The original project appears to have been abondoned. Because it's so incredibly useful, it's being brought back to life in this repository. LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. @@ -10,14 +12,7 @@ $ cd LittleProxy $ ./run.bash ``` -You can embed LittleProxy in your own projects through Maven with the following: -``` - - xyz.rogfam - littleproxy - 2.0.0 - -``` +This project will soon be in maven central. That will make it easier to use. Once you've included LittleProxy, you can start the server with the following: diff --git a/pom.xml b/pom.xml index 9e109edb3..89d5567b4 100644 --- a/pom.xml +++ b/pom.xml @@ -696,6 +696,7 @@ mrogers Mark Rogers + mark.rogers@gmail.com rogfam rogfam.xyz Developer From 0b04f513fd7a29da9818580443bd80de051594a0 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Sun, 24 Feb 2019 16:58:13 -0700 Subject: [PATCH 14/30] Removed build status from README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 07d4cbeca..e657aafbd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Build Status](https://ci.codemc.org/buildStatus/icon?job=mrog%2FLittleProxy)](https://ci.codemc.org/job/mrog/job/LittleProxy/) - This is an updated fork of adamfisk's LittleProxy. The original project appears to have been abondoned. Because it's so incredibly useful, it's being brought back to life in this repository. LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. From 56a17c9bbcfe8c43c7d8115e6730f7cb46ee1b8c Mon Sep 17 00:00:00 2001 From: uarlousk Date: Mon, 25 Feb 2019 16:40:15 +0300 Subject: [PATCH 15/30] Refactor mitm manager to accept engine with user-defined parameters --- .../proxy/extras/SelfSignedMitmManager.java | 11 ++++- .../extras/SelfSignedSslEngineSource.java | 29 +++++++----- .../SelfSignedSslEngineChainedProxyTest.java | 34 ++++++++++++++ .../extras/SelfSignedMitmManagerTest.java | 47 +++++++++++++++++++ 4 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java create mode 100644 src/test/java/org/littleshoot/proxy/extras/SelfSignedMitmManagerTest.java diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java index 45c71fe69..190eacaa3 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedMitmManager.java @@ -10,8 +10,15 @@ * {@link MitmManager} that uses self-signed certs for everything. */ public class SelfSignedMitmManager implements MitmManager { - SelfSignedSslEngineSource selfSignedSslEngineSource = - new SelfSignedSslEngineSource(true); + private final SelfSignedSslEngineSource selfSignedSslEngineSource; + + public SelfSignedMitmManager() { + this.selfSignedSslEngineSource = new SelfSignedSslEngineSource(true); + } + + public SelfSignedMitmManager(SelfSignedSslEngineSource selfSignedSslEngineSource) { + this.selfSignedSslEngineSource = selfSignedSslEngineSource; + } @Override public SSLEngine serverSslEngine(String peerHost, int peerPort) { diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java index ff7906502..f2c6e736b 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java @@ -31,24 +31,31 @@ public class SelfSignedSslEngineSource implements SslEngineSource { private static final Logger LOG = LoggerFactory .getLogger(SelfSignedSslEngineSource.class); - private static final String ALIAS = "littleproxy"; - private static final String PASSWORD = "Be Your Own Lantern"; private static final String PROTOCOL = "TLS"; + + private final String alias; + private final String password; private final File keyStoreFile; private final boolean trustAllServers; private final boolean sendCerts; private SSLContext sslContext; - public SelfSignedSslEngineSource(String keyStorePath, - boolean trustAllServers, boolean sendCerts) { + public SelfSignedSslEngineSource(String keyStorePath, boolean trustAllServers, boolean sendCerts, + String alias, String password) { this.trustAllServers = trustAllServers; this.sendCerts = sendCerts; this.keyStoreFile = new File(keyStorePath); + this.alias = alias; + this.password = password; initializeKeyStore(); initializeSSLContext(); } + public SelfSignedSslEngineSource(String keyStorePath, boolean trustAllServers, boolean sendCerts) { + this(keyStorePath, trustAllServers, sendCerts, "littleproxy", "Be Your Own Lantern"); + } + public SelfSignedSslEngineSource(String keyStorePath) { this(keyStorePath, false, true); } @@ -85,13 +92,13 @@ private void initializeKeyStore() { return; } - nativeCall("keytool", "-genkey", "-alias", ALIAS, "-keysize", + nativeCall("keytool", "-genkey", "-alias", alias, "-keysize", "4096", "-validity", "36500", "-keyalg", "RSA", "-dname", - "CN=littleproxy", "-keypass", PASSWORD, "-storepass", - PASSWORD, "-keystore", keyStoreFile.getName()); + "CN=littleproxy", "-keypass", password, "-storepass", + password, "-keystore", keyStoreFile.getName()); - nativeCall("keytool", "-exportcert", "-alias", ALIAS, "-keystore", - keyStoreFile.getName(), "-storepass", PASSWORD, "-file", + nativeCall("keytool", "-exportcert", "-alias", alias, "-keystore", + keyStoreFile.getName(), "-storepass", password, "-file", "littleproxy_cert"); } @@ -107,13 +114,13 @@ private void initializeSSLContext() { // ks.load(new FileInputStream("keystore.jks"), // "changeit".toCharArray()); try (InputStream is = new FileInputStream(keyStoreFile)) { - ks.load(is, PASSWORD.toCharArray()); + ks.load(is, password.toCharArray()); } // Set up key manager factory to use our key store final KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); - kmf.init(ks, PASSWORD.toCharArray()); + kmf.init(ks, password.toCharArray()); // Set up a trust manager factory to use our key store TrustManagerFactory tmf = TrustManagerFactory diff --git a/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java new file mode 100644 index 000000000..88a888259 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java @@ -0,0 +1,34 @@ +package org.littleshoot.proxy; + +import org.littleshoot.proxy.*; +import org.littleshoot.proxy.extras.SelfSignedSslEngineSource; + +import javax.net.ssl.SSLEngine; + +import static org.littleshoot.proxy.TransportProtocol.TCP; + +public class SelfSignedSslEngineChainedProxyTest extends BaseChainedProxyTest { + private final SslEngineSource sslEngineSource = new SelfSignedSslEngineSource("chain_proxy_keystore_1.jks", + false, true, "littleproxy", "Be Your Own Lantern"); + + @Override + protected HttpProxyServerBootstrap upstreamProxy() { + return super.upstreamProxy() + .withSslEngineSource(sslEngineSource); + } + + @Override + protected ChainedProxy newChainedProxy() { + return new BaseChainedProxy() { + @Override + public boolean requiresEncryption() { + return true; + } + + @Override + public SSLEngine newSslEngine() { + return sslEngineSource.newSslEngine(); + } + }; + } +} diff --git a/src/test/java/org/littleshoot/proxy/extras/SelfSignedMitmManagerTest.java b/src/test/java/org/littleshoot/proxy/extras/SelfSignedMitmManagerTest.java new file mode 100644 index 000000000..9b6f8ebc2 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/extras/SelfSignedMitmManagerTest.java @@ -0,0 +1,47 @@ +package org.littleshoot.proxy.extras; + +import io.netty.handler.codec.http.HttpRequest; +import org.junit.Test; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; + +public class SelfSignedMitmManagerTest { + + @Test + public void testServerSslEnginePeerAndPort() { + String peer = "localhost"; + int port = 8090; + SelfSignedSslEngineSource source = mock(SelfSignedSslEngineSource.class); + SelfSignedMitmManager manager = new SelfSignedMitmManager(source); + SSLEngine engine = mock(SSLEngine.class); + when(source.newSslEngine(peer, port)).thenReturn(engine); + assertEquals(engine, manager.serverSslEngine(peer, port)); + } + + @Test + public void testServerSslEngine() { + SelfSignedSslEngineSource source = mock(SelfSignedSslEngineSource.class); + SelfSignedMitmManager manager = new SelfSignedMitmManager(source); + SSLEngine engine = mock(SSLEngine.class); + when(source.newSslEngine()).thenReturn(engine); + assertEquals(engine, manager.serverSslEngine()); + } + + @Test + public void testClientSslEngineFor() { + HttpRequest request = mock(HttpRequest.class); + SSLSession session = mock(SSLSession.class); + SelfSignedSslEngineSource source = mock(SelfSignedSslEngineSource.class); + SelfSignedMitmManager manager = new SelfSignedMitmManager(source); + SSLEngine engine = mock(SSLEngine.class); + when(source.newSslEngine()).thenReturn(engine); + assertEquals(engine, manager.clientSslEngineFor(request, session)); + verifyZeroInteractions(request, session); + } +} From 17028e893ba3bda8c629a2b1dce59d75f0028543 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Wed, 27 Feb 2019 23:30:54 -0700 Subject: [PATCH 16/30] Fix for newer maven javadoc plugin --- pom.xml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 89d5567b4..d25e1e223 100644 --- a/pom.xml +++ b/pom.xml @@ -69,16 +69,6 @@ - - doclint-java8-disable - - [1.8,) - - - -Xdoclint:none - - - release @@ -113,7 +103,7 @@ ${java.version} - ${javadoc.opts} + none @@ -484,7 +474,7 @@ http://netty.io/4.0/api/ - ${javadoc.opts} + none @@ -575,7 +565,7 @@ http://netty.io/4.0/api/ - ${javadoc.opts} + none From 7a2273c9d6f9a797dbe735476fcc8c0e1e912e5b Mon Sep 17 00:00:00 2001 From: uarlousk Date: Wed, 27 Feb 2019 16:48:27 +0300 Subject: [PATCH 17/30] Clean up --- .../org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java index f2c6e736b..2499143e9 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java @@ -111,8 +111,6 @@ private void initializeSSLContext() { try { final KeyStore ks = KeyStore.getInstance("JKS"); - // ks.load(new FileInputStream("keystore.jks"), - // "changeit".toCharArray()); try (InputStream is = new FileInputStream(keyStoreFile)) { ks.load(is, password.toCharArray()); } From 1769e97be109e2092bc81dc7132a74a3f382e38a Mon Sep 17 00:00:00 2001 From: uarlousk Date: Thu, 28 Feb 2019 12:19:37 +0300 Subject: [PATCH 18/30] Add ability to load keystore from classpath --- .../extras/SelfSignedSslEngineSource.java | 45 ++++++++++++------ .../SelfSignedSslEngineChainedProxyTest.java | 2 +- .../certificate/chain_proxy_keystore.jks | Bin 0 -> 3749 bytes 3 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 src/test/resources/certificate/chain_proxy_keystore.jks diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java index 2499143e9..d5b68e144 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java @@ -16,6 +16,9 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.Security; import java.security.cert.CertificateException; @@ -35,7 +38,7 @@ public class SelfSignedSslEngineSource implements SslEngineSource { private final String alias; private final String password; - private final File keyStoreFile; + private final String keyStoreFile; private final boolean trustAllServers; private final boolean sendCerts; @@ -45,10 +48,9 @@ public SelfSignedSslEngineSource(String keyStorePath, boolean trustAllServers, b String alias, String password) { this.trustAllServers = trustAllServers; this.sendCerts = sendCerts; - this.keyStoreFile = new File(keyStorePath); + this.keyStoreFile = keyStorePath; this.alias = alias; this.password = password; - initializeKeyStore(); initializeSSLContext(); } @@ -86,19 +88,14 @@ public SSLContext getSslContext() { return sslContext; } - private void initializeKeyStore() { - if (keyStoreFile.isFile()) { - LOG.info("Not deleting keystore"); - return; - } - + private void initializeKeyStore(String filename) { nativeCall("keytool", "-genkey", "-alias", alias, "-keysize", "4096", "-validity", "36500", "-keyalg", "RSA", "-dname", "CN=littleproxy", "-keypass", password, "-storepass", - password, "-keystore", keyStoreFile.getName()); + password, "-keystore", filename); nativeCall("keytool", "-exportcert", "-alias", alias, "-keystore", - keyStoreFile.getName(), "-storepass", password, "-file", + filename, "-storepass", password, "-file", "littleproxy_cert"); } @@ -110,10 +107,7 @@ private void initializeSSLContext() { } try { - final KeyStore ks = KeyStore.getInstance("JKS"); - try (InputStream is = new FileInputStream(keyStoreFile)) { - ks.load(is, password.toCharArray()); - } + final KeyStore ks = loadKeyStore(); // Set up key manager factory to use our key store final KeyManagerFactory kmf = @@ -166,6 +160,27 @@ public X509Certificate[] getAcceptedIssuers() { } } + private KeyStore loadKeyStore() throws IOException, GeneralSecurityException { + final KeyStore keyStore = KeyStore.getInstance("JKS"); + URL resourceUrl = getClass().getResource(keyStoreFile); + if(resourceUrl != null) { + loadKetStore(keyStore, resourceUrl); + } else { + File keyStoreLocalFile = new File(keyStoreFile); + if(!keyStoreLocalFile.isFile()) { + initializeKeyStore(keyStoreLocalFile.getName()); + } + loadKetStore(keyStore, keyStoreLocalFile.toURI().toURL()); + } + return keyStore; + } + + private void loadKetStore(KeyStore keyStore, URL url) throws IOException, GeneralSecurityException { + try(InputStream is = url.openStream()) { + keyStore.load(is, password.toCharArray()); + } + } + private String nativeCall(final String... commands) { LOG.info("Running '{}'", Arrays.asList(commands)); final ProcessBuilder pb = new ProcessBuilder(commands); diff --git a/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java b/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java index 88a888259..727a69161 100644 --- a/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java +++ b/src/test/java/org/littleshoot/proxy/SelfSignedSslEngineChainedProxyTest.java @@ -8,7 +8,7 @@ import static org.littleshoot.proxy.TransportProtocol.TCP; public class SelfSignedSslEngineChainedProxyTest extends BaseChainedProxyTest { - private final SslEngineSource sslEngineSource = new SelfSignedSslEngineSource("chain_proxy_keystore_1.jks", + private final SslEngineSource sslEngineSource = new SelfSignedSslEngineSource("/certificate/chain_proxy_keystore.jks", false, true, "littleproxy", "Be Your Own Lantern"); @Override diff --git a/src/test/resources/certificate/chain_proxy_keystore.jks b/src/test/resources/certificate/chain_proxy_keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..6028a70a9d205da5af573607a4ca79542a1e99b3 GIT binary patch literal 3749 zcmZ{mS2!CE--eTj5iM$u*sHY@sMO08!XzVJA(AuL`joMq4ma0u{ zwbe+C7PY^A@AV%1Pu_#)puYkBC)dz!SS;G900~c9?Ll*2UAHwMoMd6-H`Z+qgtOWhYw1% z91Sk%wCNiMtevawIC2L0g&FZ2LrTT#5J`fa&4<%vpH^3Hb!yS}Wa)q{sgl_TU;6_| z{G@xLrcG^Z7BwV3?jYz_s$zydH#Bz z-!fQkH6CWyX(EMw6_S}nMQHqpd^6v<;V~wbcnPXuIvwg}Ct-eU;iIxCmQ@Y_RnjUf1gA8{mr5mrz z1V_A5k7)6vCq2*9nmga!nfHBsMD|XRIq1<;A*};G3oP zR&f$F(gk~(%j(HG0pl!l=0BfpOPr1$Wh_G%if?-mc^WTw5xW(27m9pT_KDjv%-oke ziK;m6+f==ApLh`@5;XqhFQ>-92fFLi%Af!DlZ4-o8O-)1pc)Efte~AA>zq0om#j*Qh{b6#GXmvNGgZiwKV}&{ zlf^d2Dt6C-Dpm|PV9$|ym($n}Tl$32s;YYzsT%K7cqL6X`!mGcJ3sR%XcSaobGWt! z%Eu}~WE8>a9u=jY4lSJ2?pH2~em0gFz;3U~(>igl!w6M1?P#2R>%m8ZfX3<~#jXXM zrb^pP*i4dCOzBO}sHek8-y-%5VL z_lC8zxv1wb+3f^{YVfp7sHeUeK0vqG8JNHMZ&Y+r@RIJCdl3`^)5={8*{uM-(1)-)m_n+< zN4K+KfYE??*)G4_e?~3_&mB^07-LTGy5$a$TtsCrGq0(^~q*x+!a`c6^G~GPa@tN->wNiC;NYyi%3m!Z_5Ab=pD2&n$;FPtj zD$=lm)MsJ!(`JLF!_4c_iw#h0leG>pw<5|n%R`xG9c*Vh2yP;|sSaWoa<8;*ig>XR z0Lv%-`5w?=Z^r`3kr&qqz ziB|BVZdFCGvO03xvl-(u|Up z2}aEeH!sRiB)`vRJG{w{;l&22SHjkWRLnJsvYnF9=@`MQhR7YTytcC+VB*FIUZyaO z`ePrZGuq)yluKf=WIS>`t8J>Ua-vSxSQ-^%t!9JwR>~Bh7%h-V5TP3?y*=-=L&aN&XJvN9DnoxprG;UIF3+IS57z{Ff9T$FWi~qI{*W0YX zY;$MIoFy`jq{{-8;2CtR`iuRHvcXq~?~cA4(s6$H!l$hEbgxX9nw(}1p(Ohw&S*m` zo?}jqa)_~7{Ifyex-|DYe@Ep4O0xVEg&3q{gY7WcmTbLrpdFZaia{ z1>THtKIojLR%HL(fr}@nb+2&(kJR;QyW%`?6H-<_!AtRXVeL?&spDe_izc!o2)U_1 zAe)iZa`?IK*GBuzKZ>3DHtTctF-kRAPb+))bz$>kb=Rv)uwOi5)u6 zl-aKW);DrZuGiGl9K{J*8(rUis%xDoL=Dz45YXzubElG54=>NnW=(Ybs!JEXHHxx* zbZ(2ZU~wghe=1#3fHk~x*;o?|Kg>b*ySR4bsG24x)ms?r4!P$#3cU;dz~tzC-&3clg`z8%)rvkKc|J@; z!#Dax{PULal%qr%IZNy6jWi}I=&eTPr&t{z8hKE>wK#$5TKv5(L@5yDTA#Pee%S{P zxf+|rmx0Mi-+?3Ga&Y)VHacne{}(Xn{}%uA5)b14_sCxpB*TN~0Du3W z2H`1$)GK8z{raky7;4qodKNQJcbSk;ja;*g3($%3I~5r3aPBLt&3`VGDs{5)Uz zfF3#i!1T@RPGyBK@A2M=U9bCGqJ#bT%doFB9`g&oviD!$&DyRs(R%yH+3PI!s=b6O zd#D;OyF6U0gn1vaGM1@TR8A>MvipHsTX!ygQ#QAy)u{iF+>H;{(23aevPHp=leIuiC1Ujp5dx#P zS~}006;6itUY!lI+xc1TsKmwQ(0ke_loA==bCU*FbzFl>PhFaQD_bUR-oHg2L!gt%pC-s#G^GsUWh!<^Q8+@COvk6XvZa92*W5C49JrEEUMX>1SP z7;ZWI9$(X@HwSM#`NUo(&1*A|ZO+I41v1%9LnnsDGVH5?&xA!(D`*rEhZh?9>(g&n z2ARLl^73`me{kOva)xcipx07zr&K745^za0550r+%WRX4N2n5vsxjY!E!G-K)YZ?N z5cw#|H@Vu;SW@vw=S6Z4>{BHN2_WBC76W6IeJ> zfqd;Dfn%ILJcQ#)mm5~wSEX~%kE1Y2@vjQ#$4{ld9?8S?>xJ1}rVRk#lZc}^zJg}P GjsF9hOZXoE literal 0 HcmV?d00001 From 228adc2a7c0dfa7328f5e9cb8044885500f315bf Mon Sep 17 00:00:00 2001 From: uarlousk Date: Thu, 28 Feb 2019 19:33:29 +0300 Subject: [PATCH 19/30] Fix typo loadKetStore -> loadKeyStore --- .../littleshoot/proxy/extras/SelfSignedSslEngineSource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java index d5b68e144..7b9ec0947 100644 --- a/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java +++ b/src/main/java/org/littleshoot/proxy/extras/SelfSignedSslEngineSource.java @@ -164,18 +164,18 @@ private KeyStore loadKeyStore() throws IOException, GeneralSecurityException { final KeyStore keyStore = KeyStore.getInstance("JKS"); URL resourceUrl = getClass().getResource(keyStoreFile); if(resourceUrl != null) { - loadKetStore(keyStore, resourceUrl); + loadKeyStore(keyStore, resourceUrl); } else { File keyStoreLocalFile = new File(keyStoreFile); if(!keyStoreLocalFile.isFile()) { initializeKeyStore(keyStoreLocalFile.getName()); } - loadKetStore(keyStore, keyStoreLocalFile.toURI().toURL()); + loadKeyStore(keyStore, keyStoreLocalFile.toURI().toURL()); } return keyStore; } - private void loadKetStore(KeyStore keyStore, URL url) throws IOException, GeneralSecurityException { + private void loadKeyStore(KeyStore keyStore, URL url) throws IOException, GeneralSecurityException { try(InputStream is = url.openStream()) { keyStore.load(is, password.toCharArray()); } From d3faaca1f63d372d8e35c9cdfe3321bebc64bb1f Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Thu, 28 Feb 2019 09:38:10 -0700 Subject: [PATCH 20/30] Reverting test change for travis-ci --- src/test/java/org/littleshoot/proxy/IdleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/littleshoot/proxy/IdleTest.java b/src/test/java/org/littleshoot/proxy/IdleTest.java index 42c4c0e6c..f3bee0d13 100644 --- a/src/test/java/org/littleshoot/proxy/IdleTest.java +++ b/src/test/java/org/littleshoot/proxy/IdleTest.java @@ -30,7 +30,7 @@ public class IdleTest { public void setup() throws Exception { assumeTrue("Skipping due to non-Unix OS", TestUtils.isUnixManagementCapable()); - assumeFalse("Skipping for Jenkins build", "true".equals(System.getenv("JENKINS"))); + assumeFalse("Skipping for travis-ci build", "true".equals(System.getenv("TRAVIS"))); webServer = new Server(0); webServer.start(); From ffadef246730b3f43213ae59f57113d017b23978 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Thu, 28 Feb 2019 09:45:30 -0700 Subject: [PATCH 21/30] Removed Java 7 from Travis settings --- .travis.yml | 1 - pom.xml | 20 -------------------- 2 files changed, 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa369a5af..45201c819 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ sudo: false language: java jdk: - - oraclejdk7 - oraclejdk8 cache: diff --git a/pom.xml b/pom.xml index d25e1e223..84421c1e6 100644 --- a/pom.xml +++ b/pom.xml @@ -663,26 +663,6 @@ - - afisk - Adam fisk - a@littleshoot.org - LittleShoot - http://www.littleshoot.org/ - Developer - -5 - - - - ox.to.a.cart - Ox Cart - ox@getlantern.org - GetLantern - https://www.getlantern.org/ - Developer - -5 - - mrogers Mark Rogers From 9a3de04140b802a1ed2db2b14434dcfc6fda8ec7 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Thu, 28 Feb 2019 10:07:07 -0700 Subject: [PATCH 22/30] Added badges to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e657aafbd..f0d8db40c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Build Status](https://travis-ci.com/mrog/LittleProxy.svg?branch=master)](https://travis-ci.com/mrog/LittleProxy) +[![DepShield Badge](https://depshield.sonatype.org/badges/mrog/LittleProxy/depshield.svg)](https://depshield.github.io) + This is an updated fork of adamfisk's LittleProxy. The original project appears to have been abondoned. Because it's so incredibly useful, it's being brought back to life in this repository. LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. From e3a317fdd715878b4bfc8b87db4dae216f0e2f01 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 1 Mar 2019 20:53:29 -0700 Subject: [PATCH 23/30] 2.0.0 beta 1 release --- pom.xml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 84421c1e6..2aab7ec00 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xyz.rogfam littleproxy jar - 2.0.0-SNAPSHOT + 2.0.0-beta-1 LittleProxy LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework. @@ -71,6 +71,12 @@ release + + + performRelease + true + + @@ -126,6 +132,15 @@ sign + + ${gpg.keyname} + ${gpg.keyname} + gpg + + --pinentry-mode + loopback + + @@ -389,7 +404,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 1.4.1 + 3.0.0-M2 @@ -439,6 +454,12 @@ maven-resources-plugin 3.1.0 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + @@ -667,8 +688,8 @@ mrogers Mark Rogers mark.rogers@gmail.com - rogfam - rogfam.xyz + Mark Rogers + https://github.com/mrog Developer -7 From 7323389f0c76333fb109229a013190e977a10442 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 1 Mar 2019 21:03:19 -0700 Subject: [PATCH 24/30] Updated README --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0d8db40c..5bf9b749e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,14 @@ $ cd LittleProxy $ ./run.bash ``` -This project will soon be in maven central. That will make it easier to use. +You can embed LittleProxy in your own projects through Maven with the following: +``` + + xyz.rogfam + littleproxy + 2.0.0-beta-1 + +``` Once you've included LittleProxy, you can start the server with the following: From dc6b0578c4f3a3e8c7aac708a1998d8bd9f7a809 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Fri, 1 Mar 2019 23:15:33 -0700 Subject: [PATCH 25/30] Updated README and cleaned up pom.xml a bit --- README.md | 4 +++- pom.xml | 13 ++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5bf9b749e..10d5d5e98 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,9 @@ For examples of configuring logging, see [src/test/resources/log4j.xml](src/test If you have questions, please visit our Google Group here: -https://groups.google.com/forum/#!forum/littleproxy +https://groups.google.com/forum/#!forum/littleproxy2 + +(The original group at https://groups.google.com/forum/#!forum/littleproxy isn't accepting posts from new users. But it's still a great resource if you're searching for older answers.) To subscribe, send an E-Mail to mailto:LittleProxy+subscribe@googlegroups.com. Simply answering, don't clicking the button, bypasses Googles registration diff --git a/pom.xml b/pom.xml index 2aab7ec00..70db20a37 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,6 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 attach-sources @@ -105,7 +104,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 ${java.version} @@ -158,7 +156,6 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 true false @@ -460,6 +457,12 @@ maven-source-plugin 3.0.1 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.1 + @@ -487,7 +490,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 private ${java.version} @@ -552,12 +554,10 @@ org.apache.maven.plugins maven-site-plugin - 3.7.1 maven-dependency-plugin - 3.1.1 @@ -578,7 +578,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 private ${java.version} From 3e5c0f0c16efd04045db89add51336416cf0eaaa Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Sat, 2 Mar 2019 17:58:35 -0700 Subject: [PATCH 26/30] Added release notes --- RELEASE_NOTES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 RELEASE_NOTES.md diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 000000000..2d6af9af2 --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,10 @@ +# Release Notes + +- 2.0.0-beta-1 + - New Maven coordinates + - Moved from Java 7 to 8 + - Updated dependency versions + - Made client details available to ChainedProxyManager + - Refactored MITM manager to accept engine with user-defined parameters + - Added ability to load keystore from classpath + \ No newline at end of file From d5f0384300ad0ab94d270534d7baddb8db69408a Mon Sep 17 00:00:00 2001 From: mrogers Date: Mon, 4 Mar 2019 13:38:30 -0700 Subject: [PATCH 27/30] Updated README --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 10d5d5e98..e6d14992d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ [![Build Status](https://travis-ci.com/mrog/LittleProxy.svg?branch=master)](https://travis-ci.com/mrog/LittleProxy) [![DepShield Badge](https://depshield.sonatype.org/badges/mrog/LittleProxy/depshield.svg)](https://depshield.github.io) -This is an updated fork of adamfisk's LittleProxy. The original project appears to have been abondoned. Because it's so incredibly useful, it's being brought back to life in this repository. +This is an updated fork of adamfisk's LittleProxy. The original project appears +to have been abondoned. Because it's so incredibly useful, it's being brought +back to life in this repository. -LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's excellent [Netty](http://netty.io) event-based networking library. It's quite stable, performs well, and is easy to integrate into your projects. +LittleProxy is a high performance HTTP proxy written in Java atop Trustin Lee's +excellent [Netty](http://netty.io) event-based networking library. It's quite +stable, performs well, and is easy to integrate into your projects. One option is to clone LittleProxy and run it from the command line. This is as simple as: @@ -147,11 +151,11 @@ If you have questions, please visit our Google Group here: https://groups.google.com/forum/#!forum/littleproxy2 -(The original group at https://groups.google.com/forum/#!forum/littleproxy isn't accepting posts from new users. But it's still a great resource if you're searching for older answers.) +(The original group at https://groups.google.com/forum/#!forum/littleproxy isn't +accepting posts from new users. But it's still a great resource if you're +searching for older answers.) -To subscribe, send an E-Mail to mailto:LittleProxy+subscribe@googlegroups.com. -Simply answering, don't clicking the button, bypasses Googles registration -process. You will become a member. +To subscribe, send an E-Mail to mailto:LittleProxy2+subscribe@googlegroups.com. Benchmarking instructions and results can be found [here](performance). From 3e75b02f3c99c1d54703a803ebb010db00a40c33 Mon Sep 17 00:00:00 2001 From: mrogers Date: Mon, 4 Mar 2019 13:40:27 -0700 Subject: [PATCH 28/30] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6d14992d..2f39d65e5 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ https://groups.google.com/forum/#!forum/littleproxy2 accepting posts from new users. But it's still a great resource if you're searching for older answers.) -To subscribe, send an E-Mail to mailto:LittleProxy2+subscribe@googlegroups.com. +To subscribe, send an e-mail to [LittleProxy2+subscribe@googlegroups.com](mailto:LittleProxy2+subscribe@googlegroups.com). Benchmarking instructions and results can be found [here](performance). From 7f0e0791923f28e4d94a6c96ef5d8cc0720c2b42 Mon Sep 17 00:00:00 2001 From: myusername Date: Mon, 25 Mar 2019 04:00:16 +0530 Subject: [PATCH 29/30] Add support for Proxy Protocol. This is useful for Services that are fronted by little proxy, but do not want the client's ip address to be obscured because the server's business logic involves using the client's ip address. https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt The feature is controlled by two options in the DefaultHttpProxyServer class : 1) sendProxy - to send a proxy protocol header to server 2) acceptProxy - to accept one from client Based on these configurations, a proxy protocol message decoder and encoder is added in ClientToProxyConnection and ProxyToServerConnection classes respectively. The default values of these configurations is false. Tests : Existing tests passed. Unit tests added for send, accept and relay proxy header --- .../proxy/HttpProxyServerBootstrap.java | 14 ++ .../proxy/extras/HAProxyMessageEncoder.java | 24 +++ .../proxy/impl/ClientToProxyConnection.java | 35 +++++ .../proxy/impl/ConnectionFlow.java | 36 +++++ .../proxy/impl/DefaultHttpProxyServer.java | 34 ++++- .../proxy/impl/ProxyConnection.java | 10 ++ .../proxy/impl/ProxyToServerConnection.java | 11 ++ .../proxy/haproxy/BaseProxyProtocolTest.java | 142 ++++++++++++++++++ .../haproxy/ProxyProtocolClientHandler.java | 37 +++++ .../proxy/haproxy/ProxyProtocolHeader.java | 33 ++++ .../haproxy/ProxyProtocolServerHandler.java | 21 +++ .../proxy/haproxy/ProxyProtocolTest.java | 45 ++++++ .../haproxy/ProxyProtocolTestEncoder.java | 21 +++ 13 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/BaseProxyProtocolTest.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolClientHandler.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolHeader.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolServerHandler.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTest.java create mode 100644 src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTestEncoder.java diff --git a/src/main/java/org/littleshoot/proxy/HttpProxyServerBootstrap.java b/src/main/java/org/littleshoot/proxy/HttpProxyServerBootstrap.java index 367dc8dd1..62b0ee8b7 100644 --- a/src/main/java/org/littleshoot/proxy/HttpProxyServerBootstrap.java +++ b/src/main/java/org/littleshoot/proxy/HttpProxyServerBootstrap.java @@ -335,4 +335,18 @@ HttpProxyServerBootstrap withConnectTimeout( * @return proxy server bootstrap for chaining */ HttpProxyServerBootstrap withThreadPoolConfiguration(ThreadPoolConfiguration configuration); + + /** + * Specifies if the proxy server should accept a proxy protocol header. Once set it works with request that + * include a proxy protocol header. The proxy server reads an incoming proxy protocol header from the + * client. + * @param allowProxyProtocol when true, the proxy will accept a proxy protocol header + */ + HttpProxyServerBootstrap withAcceptProxyProtocol(boolean allowProxyProtocol); + + /** + * Specifies if the proxy server should send a proxy protocol header. + * @param sendProxyProtocol when true, the proxy will send a proxy protocol header + */ + HttpProxyServerBootstrap withSendProxyProtocol(boolean sendProxyProtocol); } \ No newline at end of file diff --git a/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java b/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java new file mode 100644 index 000000000..3a7a1ae00 --- /dev/null +++ b/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java @@ -0,0 +1,24 @@ +package org.littleshoot.proxy.extras; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.haproxy.HAProxyMessage; + +/** + * Encodes an HAProxy proxy protocol header + * + * @see Proxy Protocol Specification + */ +public class HAProxyMessageEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, HAProxyMessage msg, ByteBuf out) { + out.writeBytes(getHaProxyMessage(msg)); + } + + private byte [] getHaProxyMessage(HAProxyMessage msg) { + return String.format("%s %s %s %s %s %s\r\n", msg.command(), msg.proxiedProtocol(), msg.sourceAddress(), msg.destinationAddress(), msg.sourcePort(), msg.destinationPort()).getBytes(); + } + +} diff --git a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java index 964858fbf..95f026509 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java @@ -5,6 +5,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -102,6 +104,11 @@ public class ClientToProxyConnection extends ProxyConnection { private final AtomicInteger numberOfCurrentlyConnectingServers = new AtomicInteger( 0); + /** + * Keep track of proxy protocol header + */ + private HAProxyMessage haProxyMessage = null; + /** * Keep track of how many servers are currently connected. */ @@ -174,6 +181,11 @@ public void operationComplete( LOG.debug("Created ClientToProxyConnection"); } + @Override + protected void readHAProxyMessage(HAProxyMessage msg) { + haProxyMessage = msg; + } + /*************************************************************************** * Reading **************************************************************************/ @@ -783,6 +795,9 @@ private void initChannelPipeline(ChannelPipeline pipeline) { pipeline.addLast("bytesWrittenMonitor", bytesWrittenMonitor); pipeline.addLast("encoder", new HttpResponseEncoder()); + if (isAcceptProxyProtocol()) { + pipeline.addLast("proxy-protocol-decoder", new HAProxyMessageDecoder()); + } // We want to allow longer request lines, headers, and chunks // respectively. pipeline.addLast("decoder", new HttpRequestDecoder( @@ -808,6 +823,22 @@ private void initChannelPipeline(ChannelPipeline pipeline) { pipeline.addLast("handler", this); } + /** + * Is the proxy server set to accept a proxy protocol header + * @return True if the proxy server set to accept a proxy protocol header. False otherwise + */ + boolean isAcceptProxyProtocol() { + return proxyServer.isAcceptProxyProtocol(); + } + + /** + * Is the proxy server set to send a proxy protocol header + * @return True if the proxy server set to send a proxy protocol header. False otherwise + */ + boolean isSendProxyProtocol() { + return proxyServer.isSendProxyProtocol(); + } + /** * This method takes care of closing client to proxy and/or proxy to server * connections after finishing a write. @@ -1452,4 +1483,8 @@ private FlowContext flowContext() { } } + public HAProxyMessage getHaProxyMessage() { + return haProxyMessage; + } + } diff --git a/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java b/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java index 23a65f08e..7a95dd3c9 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java +++ b/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java @@ -1,8 +1,16 @@ package org.littleshoot.proxy.impl; +import io.netty.handler.codec.haproxy.HAProxyCommand; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; +import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.InetSocketAddress; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -166,10 +174,38 @@ void succeed() { serverConnection.getLOG().debug( "Connection flow completed successfully: {}", currentStep); serverConnection.connectionSucceeded(!suppressInitialRequest); + relayProxyInformation(); notifyThreadsWaitingForConnection(); } } + private void relayProxyInformation() { + if (clientConnection.isSendProxyProtocol()) { + HAProxyMessage haProxyMessage = getHAProxyMessage(clientConnection.getClientAddress(), serverConnection.getRemoteAddress()); + if ( haProxyMessage != null ){ + serverConnection.writeToChannel(haProxyMessage); + } + } + } + + private HAProxyMessage getHAProxyMessage(InetSocketAddress clientAddress, InetSocketAddress remoteAddress) { + HAProxyMessage haProxyMessage = clientConnection.getHaProxyMessage(); + if (haProxyMessage == null) { + // Hack : HAProxyMessage class provides no public constructors. + Constructor constructor = null; + try { + constructor = HAProxyMessage.class.getDeclaredConstructor(HAProxyProtocolVersion.class, HAProxyCommand.class, HAProxyProxiedProtocol.class, String.class, + String.class, String.class, String.class); + constructor.setAccessible(true); + haProxyMessage = constructor.newInstance(HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, clientAddress.getAddress().getHostAddress(), + remoteAddress.getAddress().getHostAddress(), String.valueOf(clientAddress.getPort()), String.valueOf(remoteAddress.getPort())); + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ignored) { + } + } + return haProxyMessage; + + } + /** * Called when the flow fails at some {@link ConnectionFlowStep}. * Disconnects the {@link ProxyToServerConnection} and informs the diff --git a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java index 1891532e4..2b64d1ac0 100644 --- a/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java +++ b/src/main/java/org/littleshoot/proxy/impl/DefaultHttpProxyServer.java @@ -117,6 +117,8 @@ public class DefaultHttpProxyServer implements HttpProxyServer { private final int maxHeaderSize; private final int maxChunkSize; private final boolean allowRequestsToOriginServer; + private final boolean acceptProxyProtocol; + private final boolean sendProxyProtocol; /** * The alias or pseudonym for this proxy, used when adding the Via header. @@ -230,6 +232,8 @@ public static HttpProxyServerBootstrap bootstrapFromFile(String path) { * @param maxChunkSize * @param allowRequestsToOriginServer * when true, allow the proxy to handle requests that contain an origin-form URI, as defined in RFC 7230 5.3.1 + * @param acceptProxyProtocol when true, the proxy will accept a proxy protocol header from client + * @param sendProxyProtocol when true, the proxy will send a proxy protocol header to the server */ private DefaultHttpProxyServer(ServerGroup serverGroup, TransportProtocol transportProtocol, @@ -252,7 +256,9 @@ private DefaultHttpProxyServer(ServerGroup serverGroup, int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, - boolean allowRequestsToOriginServer) { + boolean allowRequestsToOriginServer, + boolean acceptProxyProtocol, + boolean sendProxyProtocol) { this.serverGroup = serverGroup; this.transportProtocol = transportProtocol; this.requestedAddress = requestedAddress; @@ -291,6 +297,8 @@ private DefaultHttpProxyServer(ServerGroup serverGroup, this.maxHeaderSize = maxHeaderSize; this.maxChunkSize = maxChunkSize; this.allowRequestsToOriginServer = allowRequestsToOriginServer; + this.acceptProxyProtocol = acceptProxyProtocol; + this.sendProxyProtocol = sendProxyProtocol; } /** @@ -384,6 +392,14 @@ public boolean isAllowRequestsToOriginServer() { return allowRequestsToOriginServer; } + public boolean isAcceptProxyProtocol() { + return acceptProxyProtocol; + } + + public boolean isSendProxyProtocol() { + return sendProxyProtocol; + } + @Override public HttpProxyServerBootstrap clone() { return new DefaultHttpProxyServerBootstrap(serverGroup, @@ -624,6 +640,8 @@ private static class DefaultHttpProxyServerBootstrap implements HttpProxyServerB private int maxHeaderSize = MAX_HEADER_SIZE_DEFAULT; private int maxChunkSize = MAX_CHUNK_SIZE_DEFAULT; private boolean allowRequestToOriginServer = false; + private boolean acceptProxyProtocol = false; + private boolean sendProxyProtocol = false; private DefaultHttpProxyServerBootstrap() { } @@ -873,6 +891,18 @@ public HttpProxyServerBootstrap withAllowRequestToOriginServer(boolean allowRequ return this; } + @Override + public HttpProxyServerBootstrap withAcceptProxyProtocol(boolean acceptProxyProtocol) { + this.acceptProxyProtocol = acceptProxyProtocol; + return this; + } + + @Override + public HttpProxyServerBootstrap withSendProxyProtocol(boolean sendProxyProtocol) { + this.sendProxyProtocol = sendProxyProtocol; + return this; + } + @Override public HttpProxyServer start() { return build().start(); @@ -904,7 +934,7 @@ transportProtocol, determineListenAddress(), idleConnectionTimeout, activityTrackers, connectTimeout, serverResolver, readThrottleBytesPerSecond, writeThrottleBytesPerSecond, localAddress, proxyAlias, maxInitialLineLength, maxHeaderSize, maxChunkSize, - allowRequestToOriginServer); + allowRequestToOriginServer, acceptProxyProtocol, sendProxyProtocol); } private InetSocketAddress determineListenAddress() { diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java index 58c3eb240..f68797b39 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ProxyConnection.java @@ -3,6 +3,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; +import io.netty.handler.codec.haproxy.HAProxyMessage; import io.netty.handler.codec.http.*; import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.IdleStateEvent; @@ -115,12 +116,21 @@ protected void read(Object msg) { if (tunneling) { // In tunneling mode, this connection is simply shoveling bytes readRaw((ByteBuf) msg); + } else if ( msg instanceof HAProxyMessage) { + readHAProxyMessage((HAProxyMessage)msg); } else { // If not tunneling, then we are always dealing with HttpObjects. readHTTP((HttpObject) msg); } } + /** + * Read an {@link HAProxyMessage} + * @param msg {@link HAProxyMessage} + */ + protected abstract void readHAProxyMessage(HAProxyMessage msg); + + /** * Handles reading {@link HttpObject}s. * diff --git a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java index b612f5160..8bb00c824 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java +++ b/src/main/java/org/littleshoot/proxy/impl/ProxyToServerConnection.java @@ -13,6 +13,7 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.udt.nio.NioUdtProvider; +import io.netty.handler.codec.haproxy.HAProxyMessage; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; @@ -50,6 +51,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.RejectedExecutionException; +import org.littleshoot.proxy.extras.HAProxyMessageEncoder; import static org.littleshoot.proxy.impl.ConnectionState.AWAITING_CHUNK; import static org.littleshoot.proxy.impl.ConnectionState.AWAITING_CONNECT_OK; @@ -215,6 +217,12 @@ protected void read(Object msg) { } } + @Override + protected void readHAProxyMessage(HAProxyMessage msg) { + // NO-OP, + // We never expect server to send a proxy protocol message. + } + @Override protected ConnectionState readHTTPInitial(HttpResponse httpResponse) { LOG.debug("Received raw response: {}", httpResponse); @@ -876,6 +884,9 @@ private void initChannelPipeline(ChannelPipeline pipeline, pipeline.addLast("bytesReadMonitor", bytesReadMonitor); pipeline.addLast("bytesWrittenMonitor", bytesWrittenMonitor); + if ( proxyServer.isSendProxyProtocol()) { + pipeline.addLast("proxy-protocol-encoder", new HAProxyMessageEncoder()); + } pipeline.addLast("encoder", new HttpRequestEncoder()); pipeline.addLast("decoder", new HeadAwareHttpResponseDecoder( proxyServer.getMaxInitialLineLength(), diff --git a/src/test/java/org/littleshoot/proxy/haproxy/BaseProxyProtocolTest.java b/src/test/java/org/littleshoot/proxy/haproxy/BaseProxyProtocolTest.java new file mode 100644 index 000000000..29d416a7b --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/BaseProxyProtocolTest.java @@ -0,0 +1,142 @@ +package org.littleshoot.proxy.haproxy; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ChannelFactory; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.ServerChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpRequestEncoder; +import io.netty.handler.timeout.ReadTimeoutHandler; +import java.net.InetSocketAddress; +import org.junit.After; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +/** + * Base for running Proxy protocol tests. + * Proxy Protocol tests need special client and servers that are + * capable of emitting and consuming proxy protocol headers. + */ +public abstract class BaseProxyProtocolTest { + + private EventLoopGroup childGroup; + private EventLoopGroup parentGroup; + private EventLoopGroup clientWorkGroup; + private ProxyProtocolServerHandler proxyProtocolServerHandler; + private HttpProxyServer proxyServer; + private int proxyPort; + private boolean acceptProxy = true; + private boolean sendProxy = true; + int serverPort; + static final String SOURCE_ADDRESS = "192.168.0.153"; + static final String DESTINATION_ADDRESS = "192.168.0.154"; + static final String SOURCE_PORT = "123"; + static final String DESTINATION_PORT = "456"; + + + public void setup(boolean acceptProxy, boolean sendProxy) throws Exception { + this.acceptProxy = acceptProxy; + this.sendProxy = sendProxy; + startProxyServer(); + startServer(); + startClient(); + } + + void startServer() { + parentGroup = new NioEventLoopGroup(); + childGroup = new NioEventLoopGroup(); + ServerBootstrap b = new ServerBootstrap(); + b.group(parentGroup, childGroup) + .channelFactory(new ChannelFactory() { + public ServerChannel newChannel() { + return new NioServerSocketChannel(); + } + }) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + proxyProtocolServerHandler = new ProxyProtocolServerHandler(); + ch.pipeline().addLast(new HAProxyMessageDecoder()).addLast(new HttpRequestDecoder()).addLast(proxyProtocolServerHandler); + } + }).option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + ChannelFuture f = b.bind(0) + .awaitUninterruptibly(); + Throwable cause = f.cause(); + if (cause != null) { + throw new RuntimeException(cause); + } + serverPort = ((InetSocketAddress)f.channel().localAddress()).getPort(); + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + public void run() { + stopServer(); + } + }, "stopServerHook")); + } + + void startClient() throws Exception { + String host = "localhost"; + clientWorkGroup = new NioEventLoopGroup(); + Bootstrap b = new Bootstrap(); + b.group(clientWorkGroup); + b.channel(NioSocketChannel.class); + b.option(ChannelOption.SO_KEEPALIVE, true); + b.handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + ch.pipeline().addLast(new ReadTimeoutHandler(1)); + if (acceptProxy) { + ch.pipeline().addLast(new ProxyProtocolTestEncoder()); + } + ch.pipeline().addLast(new HttpRequestEncoder()).addLast(new ProxyProtocolClientHandler(serverPort, getProxyProtocolHeader())); + } + }); + ChannelFuture f = b.connect(host, proxyPort).sync(); + f.channel().closeFuture().sync(); + } + + HAProxyMessage getRelayedHaProxyMessage() { + return proxyProtocolServerHandler.getHaProxyMessage(); + } + + private void stopServer() { + childGroup.shutdownGracefully(); + parentGroup.shutdownGracefully(); + } + + private void stopProxyServer() { + proxyServer.abort(); + } + + private void startProxyServer() { + proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(0) + .withAcceptProxyProtocol(acceptProxy) + .withSendProxyProtocol(sendProxy) + .start(); + proxyPort = proxyServer.getListenAddress().getPort(); + + } + + private ProxyProtocolHeader getProxyProtocolHeader() { + return new ProxyProtocolHeader(SOURCE_ADDRESS, DESTINATION_ADDRESS, SOURCE_PORT, DESTINATION_PORT); + } + + @After + public void tearDown() { + stopServer(); + stopProxyServer(); + } + +} diff --git a/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolClientHandler.java b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolClientHandler.java new file mode 100644 index 000000000..288439bfb --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolClientHandler.java @@ -0,0 +1,37 @@ +package org.littleshoot.proxy.haproxy; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; + + +public class ProxyProtocolClientHandler extends ChannelInboundHandlerAdapter { + + private static final String HOST = "http://localhost"; + private int serverPort; + private ProxyProtocolHeader proxyProtocolHeader; + + ProxyProtocolClientHandler(int serverPort, ProxyProtocolHeader proxyProtocolHeader) { + this.serverPort = serverPort; + this.proxyProtocolHeader = proxyProtocolHeader; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.write(getHAProxyHeader()); + ctx.writeAndFlush(getConnectRequest()); + } + + private HttpRequest getConnectRequest() { + return new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, HOST + ":" + serverPort); + } + + + private String getHAProxyHeader() { + return String.format("PROXY TCP4 %s %s %s %s\r\n", proxyProtocolHeader.getSourceAddress(), proxyProtocolHeader.getDestinationAddress(), + proxyProtocolHeader.getSourcePort(), proxyProtocolHeader.getDestinationPort()); + } +} diff --git a/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolHeader.java b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolHeader.java new file mode 100644 index 000000000..d989139b1 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolHeader.java @@ -0,0 +1,33 @@ +package org.littleshoot.proxy.haproxy; + +class ProxyProtocolHeader { + + private String sourceAddress; + private String destinationAddress; + private String sourcePort; + private String destinationPort; + + ProxyProtocolHeader(String sourceAddress, String destinationAddress, String sourcePort, String destinationPort) { + this.sourceAddress = sourceAddress; + this.destinationAddress = destinationAddress; + this.sourcePort = sourcePort; + this.destinationPort = destinationPort; + } + + String getSourceAddress() { + return sourceAddress; + } + + String getDestinationAddress() { + return destinationAddress; + } + + String getSourcePort() { + return sourcePort; + } + + String getDestinationPort() { + return destinationPort; + } + +} diff --git a/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolServerHandler.java b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolServerHandler.java new file mode 100644 index 000000000..eec2e72d9 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolServerHandler.java @@ -0,0 +1,21 @@ +package org.littleshoot.proxy.haproxy; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.haproxy.HAProxyMessage; + +public class ProxyProtocolServerHandler extends ChannelInboundHandlerAdapter { + + private HAProxyMessage haProxyMessage; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if ( msg instanceof HAProxyMessage){ + this.haProxyMessage = (HAProxyMessage) msg; + } + } + + HAProxyMessage getHaProxyMessage() { + return haProxyMessage; + } +} diff --git a/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTest.java b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTest.java new file mode 100644 index 000000000..a0c6baee2 --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTest.java @@ -0,0 +1,45 @@ +package org.littleshoot.proxy.haproxy; + +import io.netty.handler.codec.haproxy.HAProxyMessage; +import org.junit.Assert; +import org.junit.Test; + +public class ProxyProtocolTest extends BaseProxyProtocolTest { + + + private static final String LOCALHOST = "127.0.0.1"; + private static final boolean ACCEPT_PROXY = true; + private static final boolean SEND_PROXY = true; + private static final boolean DO_NOT_ACCEPT_PROXY = false; + private static final boolean DO_NOT_SEND_PROXY = false; + + @Test + public void canRelayProxyProtocolHeader() throws Exception { + setup(ACCEPT_PROXY, SEND_PROXY); + HAProxyMessage haProxyMessage = getRelayedHaProxyMessage(); + Assert.assertNotNull(haProxyMessage); + Assert.assertEquals(SOURCE_ADDRESS, haProxyMessage.sourceAddress()); + Assert.assertEquals(DESTINATION_ADDRESS, haProxyMessage.destinationAddress()); + Assert.assertEquals(SOURCE_PORT, String.valueOf(haProxyMessage.sourcePort())); + Assert.assertEquals(DESTINATION_PORT, String.valueOf(haProxyMessage.destinationPort())); + } + + @Test + public void canSendProxyProtocolHeader() throws Exception { + setup(DO_NOT_ACCEPT_PROXY, SEND_PROXY); + HAProxyMessage haProxyMessage = getRelayedHaProxyMessage(); + Assert.assertNotNull(haProxyMessage); + Assert.assertEquals(LOCALHOST, haProxyMessage.sourceAddress()); + Assert.assertEquals(LOCALHOST, haProxyMessage.destinationAddress()); + Assert.assertEquals(String.valueOf(serverPort), String.valueOf(haProxyMessage.destinationPort())); + } + + @Test + public void canAcceptProxyProtocolHeader() throws Exception { + setup(ACCEPT_PROXY, DO_NOT_SEND_PROXY); + HAProxyMessage haProxyMessage = getRelayedHaProxyMessage(); + Assert.assertNull(haProxyMessage); + } + + +} diff --git a/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTestEncoder.java b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTestEncoder.java new file mode 100644 index 000000000..cc8bbe89c --- /dev/null +++ b/src/test/java/org/littleshoot/proxy/haproxy/ProxyProtocolTestEncoder.java @@ -0,0 +1,21 @@ +package org.littleshoot.proxy.haproxy; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.MessageToByteEncoder; + +public class ProxyProtocolTestEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) { + out.writeBytes(msg.getBytes()); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + super.write(ctx, msg, promise); + } + + +} From 7f283ef99174eeac1233646b0506197a656a223c Mon Sep 17 00:00:00 2001 From: myusername Date: Thu, 28 Mar 2019 17:38:36 +0530 Subject: [PATCH 30/30] Use ProxyProtocolMessage instead of HAProxyMessage --- .../proxy/extras/HAProxyMessageEncoder.java | 10 +-- .../proxy/extras/ProxyProtocolMessage.java | 66 +++++++++++++++++++ .../proxy/impl/ConnectionFlow.java | 29 +++----- 3 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/littleshoot/proxy/extras/ProxyProtocolMessage.java diff --git a/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java b/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java index 3a7a1ae00..a58fb928a 100644 --- a/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java +++ b/src/main/java/org/littleshoot/proxy/extras/HAProxyMessageEncoder.java @@ -3,22 +3,22 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.handler.codec.haproxy.HAProxyMessage; /** * Encodes an HAProxy proxy protocol header * * @see Proxy Protocol Specification */ -public class HAProxyMessageEncoder extends MessageToByteEncoder { +public class HAProxyMessageEncoder extends MessageToByteEncoder { @Override - protected void encode(ChannelHandlerContext ctx, HAProxyMessage msg, ByteBuf out) { + protected void encode(ChannelHandlerContext ctx, ProxyProtocolMessage msg, ByteBuf out) { out.writeBytes(getHaProxyMessage(msg)); } - private byte [] getHaProxyMessage(HAProxyMessage msg) { - return String.format("%s %s %s %s %s %s\r\n", msg.command(), msg.proxiedProtocol(), msg.sourceAddress(), msg.destinationAddress(), msg.sourcePort(), msg.destinationPort()).getBytes(); + private byte [] getHaProxyMessage(ProxyProtocolMessage msg) { + return String.format("%s %s %s %s %s %s\r\n", msg.getCommand(), msg.getProxiedProtocol(), msg.getSourceAddress(), msg.getDestinationAddress(), msg.getSourcePort(), + msg.getDestinationPort()).getBytes(); } } diff --git a/src/main/java/org/littleshoot/proxy/extras/ProxyProtocolMessage.java b/src/main/java/org/littleshoot/proxy/extras/ProxyProtocolMessage.java new file mode 100644 index 000000000..cee89ad06 --- /dev/null +++ b/src/main/java/org/littleshoot/proxy/extras/ProxyProtocolMessage.java @@ -0,0 +1,66 @@ +package org.littleshoot.proxy.extras; + +import io.netty.handler.codec.haproxy.HAProxyCommand; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; + +public class ProxyProtocolMessage { + + private HAProxyProtocolVersion protocolVersion; + private HAProxyCommand command; + private HAProxyProxiedProtocol proxiedProtocol; + private String sourceAddress; + private String destinationAddress; + private int sourcePort; + private int destinationPort; + + public ProxyProtocolMessage(HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, String sourceAddress, String destinationAddress + , int sourcePort, int destinationPort) { + this.protocolVersion = protocolVersion; + this.command = command; + this.proxiedProtocol = proxiedProtocol; + this.sourceAddress = sourceAddress; + this.destinationAddress = destinationAddress; + this.sourcePort = sourcePort; + this.destinationPort = destinationPort; + } + + public ProxyProtocolMessage(HAProxyMessage haProxyMessage) { + this.protocolVersion = haProxyMessage.protocolVersion(); + this.command = haProxyMessage.command(); + this.proxiedProtocol = haProxyMessage.proxiedProtocol(); + this.sourceAddress = haProxyMessage.sourceAddress(); + this.destinationAddress = haProxyMessage.destinationAddress(); + this.sourcePort = haProxyMessage.sourcePort(); + this.destinationPort = haProxyMessage.destinationPort(); + } + + public HAProxyProtocolVersion getProtocolVersion() { + return protocolVersion; + } + + public HAProxyCommand getCommand() { + return command; + } + + public HAProxyProxiedProtocol getProxiedProtocol() { + return proxiedProtocol; + } + + public String getSourceAddress() { + return sourceAddress; + } + + public String getDestinationAddress() { + return destinationAddress; + } + + public int getSourcePort() { + return sourcePort; + } + + public int getDestinationPort() { + return destinationPort; + } +} diff --git a/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java b/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java index 7a95dd3c9..6a667edd6 100644 --- a/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java +++ b/src/main/java/org/littleshoot/proxy/impl/ConnectionFlow.java @@ -2,17 +2,14 @@ import io.netty.handler.codec.haproxy.HAProxyCommand; import io.netty.handler.codec.haproxy.HAProxyMessage; -import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import org.littleshoot.proxy.extras.ProxyProtocolMessage; /** * Coordinates the various steps involved in establishing a connection, such as @@ -181,29 +178,19 @@ void succeed() { private void relayProxyInformation() { if (clientConnection.isSendProxyProtocol()) { - HAProxyMessage haProxyMessage = getHAProxyMessage(clientConnection.getClientAddress(), serverConnection.getRemoteAddress()); - if ( haProxyMessage != null ){ - serverConnection.writeToChannel(haProxyMessage); + ProxyProtocolMessage proxyProtocolMessage = getHAProxyMessage(clientConnection.getClientAddress(), serverConnection.getRemoteAddress()); + if ( proxyProtocolMessage != null ){ + serverConnection.writeToChannel(proxyProtocolMessage); } } } - private HAProxyMessage getHAProxyMessage(InetSocketAddress clientAddress, InetSocketAddress remoteAddress) { + private ProxyProtocolMessage getHAProxyMessage(InetSocketAddress clientAddress, InetSocketAddress remoteAddress) { HAProxyMessage haProxyMessage = clientConnection.getHaProxyMessage(); - if (haProxyMessage == null) { - // Hack : HAProxyMessage class provides no public constructors. - Constructor constructor = null; - try { - constructor = HAProxyMessage.class.getDeclaredConstructor(HAProxyProtocolVersion.class, HAProxyCommand.class, HAProxyProxiedProtocol.class, String.class, - String.class, String.class, String.class); - constructor.setAccessible(true); - haProxyMessage = constructor.newInstance(HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, clientAddress.getAddress().getHostAddress(), - remoteAddress.getAddress().getHostAddress(), String.valueOf(clientAddress.getPort()), String.valueOf(remoteAddress.getPort())); - } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ignored) { - } + if ( haProxyMessage != null ){ + return new ProxyProtocolMessage(haProxyMessage); } - return haProxyMessage; - + return new ProxyProtocolMessage(HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(), clientAddress.getPort(), remoteAddress.getPort()); } /**