package org.mockserver.integration.proxy.socks; import com.google.common.base.Charsets; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.junit.*; import org.mockserver.client.proxy.ProxyClient; import org.mockserver.echo.http.EchoServer; import org.mockserver.model.HttpStatusCode; import org.mockserver.proxy.Proxy; import org.mockserver.proxy.ProxyBuilder; import org.mockserver.socket.PortFactory; import org.mockserver.socket.SSLFactory; import org.mockserver.streams.IOStreamUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.net.*; import java.util.Collections; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.test.Assert.assertContains; import static org.mockserver.verify.VerificationTimes.exactly; /** * @author jamesdbloom */ public class NettyHttpProxySOCKSIntegrationTest { private static final Logger logger = LoggerFactory.getLogger(NettyHttpProxySOCKSIntegrationTest.class); private final static Integer SERVER_HTTP_PORT = PortFactory.findFreePort(); private final static Integer SERVER_HTTPS_PORT = PortFactory.findFreePort(); private final static Integer PROXY_HTTP_PORT = PortFactory.findFreePort(); private static EchoServer insecureEchoServer; private static EchoServer secureEchoServer; private static Proxy httpProxy; private static ProxyClient proxyClient; @BeforeClass public static void setupFixture() throws Exception { logger.debug("SERVER_HTTPS_PORT = " + SERVER_HTTPS_PORT); logger.debug("PROXY_HTTP_PORT = " + PROXY_HTTP_PORT); // start server insecureEchoServer = new EchoServer(SERVER_HTTP_PORT, false); secureEchoServer = new EchoServer(SERVER_HTTPS_PORT, true); // start proxy httpProxy = new ProxyBuilder() .withLocalPort(PROXY_HTTP_PORT) .build(); // start client proxyClient = new ProxyClient("localhost", PROXY_HTTP_PORT); } @AfterClass public static void shutdownFixture() { // stop server insecureEchoServer.stop(); secureEchoServer.stop(); // stop proxy httpProxy.stop(); } @Before public void resetProxy() { if (proxyClient != null) { proxyClient.reset(); } } @Test public void shouldProxyRequestsUsingHttpClientViaSOCKSConfiguredForJVM() throws Exception { ProxySelector defaultProxySelector = ProxySelector.getDefault(); try { // given - SOCKS proxy JVM settings ProxySelector.setDefault(new ProxySelector() { @Override public List<java.net.Proxy> select(URI uri) { return Collections.singletonList( new java.net.Proxy( java.net.Proxy.Type.SOCKS, new InetSocketAddress( System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort")) ) ) ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { System.out.println("Connection could not be established to proxy at socket [" + sa + "]"); ioe.printStackTrace(); } }); // and - an HTTP client HttpClient httpClient = HttpClientBuilder.create().setSslcontext(SSLFactory.getInstance().sslContext()).build(); // when HttpResponse response = httpClient.execute(new HttpHost("127.0.0.1", SERVER_HTTPS_PORT, "https"), new HttpGet("/")); // then assertThat(response.getStatusLine().getStatusCode(), is(200)); proxyClient.verify(request().withHeader("Host", "127.0.0.1" + ":" + SERVER_HTTPS_PORT)); } finally { ProxySelector.setDefault(defaultProxySelector); } } @Test public void shouldProxyRequestsUsingHttpClientViaSOCKS() throws Exception { // given Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", new ConnectionSocketFactory() { public Socket createSocket(final HttpContext context) throws IOException { return new Socket(new java.net.Proxy( java.net.Proxy.Type.SOCKS, new InetSocketAddress( System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort")) ))); } public Socket connectSocket( final int connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { Socket sock; if (socket != null) { sock = socket; } else { sock = createSocket(context); } if (localAddress != null) { sock.bind(localAddress); } try { sock.connect(remoteAddress, connectTimeout); } catch (SocketTimeoutException ex) { throw new ConnectTimeoutException(ex, host, remoteAddress.getAddress()); } return sock; } }) .build(); PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); HttpClient httpClient = HttpClients.custom() .setConnectionManager(clientConnectionManager) .build(); // when HttpPost request = new HttpPost( new URIBuilder() .setScheme("http") .setHost("localhost") .setPort(SERVER_HTTP_PORT) .setPath("/test_headers_and_body") .build() ); request.setEntity(new StringEntity("an_example_body")); HttpResponse response = httpClient.execute(request); // then assertEquals(HttpStatusCode.OK_200.code(), response.getStatusLine().getStatusCode()); assertEquals("an_example_body", new String(EntityUtils.toByteArray(response.getEntity()), Charsets.UTF_8)); // and proxyClient.verify( request() .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } @Test public void shouldProxyRequestsUsingRawSocketViaSOCKS() throws Exception { Socket socket = null; ProxySelector proxySelector = ProxySelector.getDefault(); try { ProxySelector.setDefault(new ProxySelector() { @Override public List<java.net.Proxy> select(URI uri) { return Collections.singletonList( new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", PROXY_HTTP_PORT)) ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { logger.error("Connection could not be established to proxy at socket [" + sa + "]", ioe); } }); socket = new Socket("localhost", SERVER_HTTP_PORT); // given OutputStream output = socket.getOutputStream(); // - send GET request for headers and body output.write(("" + "GET /test_headers_and_body HTTP/1.1\r\n" + "Host: localhost:" + SERVER_HTTP_PORT + "\r\n" + "X-Test: test_headers_and_body\r\n" + "Content-Length:" + "an_example_body".getBytes(Charsets.UTF_8).length + "\r\n" + "\r\n" + "an_example_body" + "\r\n" ).getBytes(Charsets.UTF_8)); output.flush(); // then // assertThat(socket.getInputStream().available(), greaterThan(0)); String response = IOStreamUtils.readInputStreamToString(socket); assertContains(response, "X-Test: test_headers_and_body"); assertContains(response, "an_example_body"); // and proxyClient.verify( request() .withMethod("GET") .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } finally { ProxySelector.setDefault(proxySelector); if (socket != null) { socket.close(); } } } @Test public void shouldProxyRequestsUsingRawSecureSocketViaSOCKS() throws Exception { Socket socket = null; ProxySelector proxySelector = ProxySelector.getDefault(); try { ProxySelector.setDefault(new ProxySelector() { @Override public List<java.net.Proxy> select(URI uri) { return Collections.singletonList( new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", PROXY_HTTP_PORT)) ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { logger.error("Connection could not be established to proxy at socket [" + sa + "]", ioe); } }); socket = SSLFactory.getInstance().wrapSocket(new Socket("localhost", SERVER_HTTP_PORT)); // given OutputStream output = socket.getOutputStream(); // - send GET request for headers and body output.write(("" + "GET /test_headers_and_body HTTP/1.1\r\n" + "Host: localhost:" + SERVER_HTTP_PORT + "\r\n" + "X-Test: test_headers_and_body\r\n" + "Content-Length:" + "an_example_body".getBytes(Charsets.UTF_8).length + "\r\n" + "\r\n" + "an_example_body" + "\r\n" ).getBytes(Charsets.UTF_8)); output.flush(); // then // assertThat(socket.getInputStream().available(), greaterThan(0)); String response = IOStreamUtils.readInputStreamToString(socket); assertContains(response, "X-Test: test_headers_and_body"); assertContains(response, "an_example_body"); // and proxyClient.verify( request() .withMethod("GET") .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } finally { ProxySelector.setDefault(proxySelector); if (socket != null) { socket.close(); } } } @Test public void shouldProxyRequestsUsingHttpClientViaSOCKSToSecureServerPort() throws Exception { // given Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create() .register("https", new ConnectionSocketFactory() { public Socket createSocket(final HttpContext context) throws IOException { return new Socket(new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))))); } public Socket connectSocket( final int connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { Socket sock; if (socket != null) { sock = socket; } else { sock = createSocket(context); } if (localAddress != null) { sock.bind(localAddress); } try { sock.connect(remoteAddress, connectTimeout); } catch (SocketTimeoutException ex) { throw new ConnectTimeoutException(ex, host, remoteAddress.getAddress()); } return sock; } }) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); HttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); // when HttpPost request = new HttpPost( new URIBuilder() .setScheme("https") .setHost("localhost") .setPort(SERVER_HTTPS_PORT) .setPath("/test_headers_and_body") .build() ); request.setEntity(new StringEntity("an_example_body")); request.addHeader("Secure", "true"); HttpResponse response = httpClient.execute(request); // then assertEquals(HttpStatusCode.OK_200.code(), response.getStatusLine().getStatusCode()); assertEquals("an_example_body", new String(EntityUtils.toByteArray(response.getEntity()), Charsets.UTF_8)); // and proxyClient.verify( request() .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } @Test public void shouldProxyRequestsUsingRawSocketViaSOCKSToSecureServerPort() throws Exception { Socket socket = null; ProxySelector proxySelector = ProxySelector.getDefault(); try { ProxySelector.setDefault(new ProxySelector() { @Override public List<java.net.Proxy> select(URI uri) { return Collections.singletonList( new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", PROXY_HTTP_PORT)) ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { logger.error("Connection could not be established to proxy at socket [" + sa + "]", ioe); } }); socket = new Socket("localhost", SERVER_HTTPS_PORT); // given OutputStream output = socket.getOutputStream(); // - send GET request for headers and body output.write(("" + "GET /test_headers_and_body HTTP/1.1\r\n" + "Host: localhost:" + SERVER_HTTPS_PORT + "\r\n" + "X-Test: test_headers_and_body\r\n" + "Content-Length:" + "an_example_body".getBytes(Charsets.UTF_8).length + "\r\n" + "\r\n" + "an_example_body" + "\r\n" ).getBytes(Charsets.UTF_8)); output.flush(); // then // assertThat(socket.getInputStream().available(), greaterThan(0)); String response = IOStreamUtils.readInputStreamToString(socket); assertContains(response, "X-Test: test_headers_and_body"); assertContains(response, "an_example_body"); // and proxyClient.verify( request() .withMethod("GET") .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } finally { ProxySelector.setDefault(proxySelector); if (socket != null) { socket.close(); } } } @Test public void shouldProxyRequestsUsingRawSecureSocketViaSOCKSToSecureServerPort() throws Exception { Socket socket = null; ProxySelector proxySelector = ProxySelector.getDefault(); try { ProxySelector.setDefault(new ProxySelector() { @Override public List<java.net.Proxy> select(URI uri) { return Collections.singletonList( new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", PROXY_HTTP_PORT)) ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { logger.error("Connection could not be established to proxy at socket [" + sa + "]", ioe); } }); socket = SSLFactory.getInstance().wrapSocket(new Socket("localhost", SERVER_HTTPS_PORT)); // given OutputStream output = socket.getOutputStream(); // - send GET request for headers and body output.write(("" + "GET /test_headers_and_body HTTP/1.1\r\n" + "Host: localhost:" + SERVER_HTTPS_PORT + "\r\n" + "X-Test: test_headers_and_body\r\n" + "Content-Length:" + "an_example_body".getBytes(Charsets.UTF_8).length + "\r\n" + "\r\n" + "an_example_body" + "\r\n" ).getBytes(Charsets.UTF_8)); output.flush(); // then // assertThat(socket.getInputStream().available(), greaterThan(0)); String response = IOStreamUtils.readInputStreamToString(socket); assertContains(response, "X-Test: test_headers_and_body"); assertContains(response, "an_example_body"); // and proxyClient.verify( request() .withMethod("GET") .withPath("/test_headers_and_body") .withBody("an_example_body"), exactly(1) ); } finally { ProxySelector.setDefault(proxySelector); if (socket != null) { socket.close(); } } } }