package aQute.bnd.comm.tests; import java.io.IOException; import java.net.MalformedURLException; import java.net.Proxy.Type; import java.net.URL; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.HttpProxyServerBootstrap; import org.littleshoot.proxy.ProxyAuthenticator; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import aQute.bnd.connection.settings.ConnectionSettings; import aQute.bnd.connection.settings.ProxyDTO; import aQute.bnd.connection.settings.ServerDTO; import aQute.bnd.http.HttpClient; import aQute.bnd.osgi.Processor; import aQute.bnd.service.url.State; import aQute.bnd.service.url.TaggedData; import aQute.http.testservers.HttpTestServer; import aQute.http.testservers.Httpbin; import aQute.lib.io.IO; import aQute.lib.strings.Strings; import junit.framework.TestCase; import sockslib.common.AuthenticationException; import sockslib.common.Credentials; import sockslib.common.methods.UsernamePasswordMethod; import sockslib.server.Session; import sockslib.server.SocksProxyServer; import sockslib.server.SocksServerBuilder; import sockslib.server.UsernamePasswordAuthenticator; import sockslib.server.listener.CloseSessionException; import sockslib.server.listener.SessionListener; import sockslib.server.manager.MemoryBasedUserManager; import sockslib.server.manager.User; import sockslib.server.manager.UserManager; import sockslib.server.msg.CommandMessage; /** * This test verifies the HttpClient against a Http Proxy and a SOCKS 5 proxy. * Different combinations are tried out with a secure and unsecure server. See * https://github.com/fengyouchao/sockslib for socks */ public class HttpClientProxyTest extends TestCase { public void testPromiscuousProxyWithNoUser() throws Exception { createPromiscuousHttpProxy(); createUnsecureServer(); assertHttpProxy(null, false); } public void testPromiscuousProxyWithBadUser() throws Exception { createPromiscuousHttpProxy(); createUnsecureServer(); assertHttpProxy("bad", false); } public void testAuthenticatingProxyWithGoodUser() throws Exception { createAuthenticationHttpProxy(); createUnsecureServer(); assertHttpProxy("good", true); } public void testPromiscuousProxyWithGoodUser() throws Exception { createPromiscuousHttpProxy(); createUnsecureServer(); assertHttpProxy("good", false); } public void testNoProxyUnsecure() throws Exception { createUnsecureServer(); assertHttpProxy("good", false); } public void testNoProxySecure() throws Exception { createUnsecureServer(); assertHttpProxy("good", false); } public void testPromiscuousProxyWithGoodUserSecure() throws Exception { createPromiscuousHttpProxy(); createSecureServer(); assertHttpProxy("good", false); } public void testAuthenticatingProxyNoUser() throws Exception { try { createAuthenticationHttpProxy(); createUnsecureServer(); assertHttpProxy(null, true); fail("Expected authentication failure"); } catch (Exception e) { // ok } } public void testAuthenticatingProxyBadUser() throws Exception { try { createAuthenticationHttpProxy(); createUnsecureServer(); assertHttpProxy("bad", true); fail("Expected authentication failure"); } catch (Exception e) { // ok } } public void testSecureSocksAuthenticatingWithGoodUserSecure() throws Exception { createSecureSocks5(); createSecureServer(); assertSocks5Proxy("good", true); } public void testSecureSocksAuthenticatingWithBadUserSecure() throws Exception { try { createSecureSocks5(); createSecureServer(); assertSocks5Proxy("bad", true); fail("Expected the transfer to fail"); } catch (Exception e) { // ok } } public void testSecureSocksAuthenticatingWithGoodUser() throws Exception { createSecureSocks5(); createUnsecureServer(); assertSocks5Proxy("good", true); } public void testSecureSocksAuthenticatingWithBadUser() throws Exception { try { createSecureSocks5(); createUnsecureServer(); assertSocks5Proxy("bad", true); fail("Expected the transfer to fail"); } catch (Exception e) { // ok } } /* * */ private HttpProxyServer httpProxy; private AtomicBoolean authenticationCalled = new AtomicBoolean(); private HttpTestServer httpTestServer; private SocksProxyServer socks5Proxy; private static int httpProxyPort = 2080; private static int socksProxyPort = 3080; private AtomicReference<Throwable> exception = new AtomicReference<>(); private AtomicInteger created = new AtomicInteger(); // we use different ports because the servers seem to linger void createAuthenticationHttpProxy() { HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap().withPort(++httpProxyPort); bootstrap.withProxyAuthenticator(new ProxyAuthenticator() { @Override public boolean authenticate(String user, String password) { System.out.println("Authenticating " + user + " : " + password); authenticationCalled.set(true); return "proxyuser".equals(user) && "good".equals(password); } }); httpProxy = bootstrap.start(); } void createPromiscuousHttpProxy() { HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap().withPort(++httpProxyPort); httpProxy = bootstrap.start(); } void createSecureServer() throws Exception { Httpbin.Config config = new Httpbin.Config(); config.https = true; httpTestServer = new Httpbin(config); httpTestServer.start(); } void createUnsecureServer() throws Exception { Httpbin.Config config = new Httpbin.Config(); config.https = false; httpTestServer = new Httpbin(config); httpTestServer.start(); } private void createSecureSocks5() throws IOException, InterruptedException { UserManager userManager = new MemoryBasedUserManager(); userManager.create(new User("proxyuser", "good")); SocksServerBuilder builder = SocksServerBuilder.newSocks5ServerBuilder(); builder.setBindPort(++socksProxyPort); builder.setUserManager(userManager); builder.addSocksMethods(new UsernamePasswordMethod(new UsernamePasswordAuthenticator() { @Override public void doAuthenticate(Credentials arg0, Session arg1) throws AuthenticationException { System.out.println("Authenticating"); // does not get called? super.doAuthenticate(arg0, arg1); authenticationCalled.set(true); } })); socks5Proxy = builder.build(); socks5Proxy.getSessionManager().addSessionListener("abc", new SessionListener() { @Override public void onException(Session arg0, Exception arg1) { System.err.println("Exception " + arg0 + " " + arg1); arg1.printStackTrace(); exception.set(arg1); } @Override public void onCommand(Session arg0, CommandMessage arg1) throws CloseSessionException { System.err.println("Command " + arg0 + " " + arg1); } @Override public void onClose(Session arg0) { System.err.println("Close " + arg0); } @Override public void onCreate(Session arg0) throws CloseSessionException { System.err.println("Create " + arg0); created.incrementAndGet(); } }); socks5Proxy.start(); } @Override protected void tearDown() throws Exception { super.tearDown(); if (httpProxy != null) httpProxy.abort(); if (httpTestServer != null) httpTestServer.close(); if (socks5Proxy != null) socks5Proxy.shutdown(); } @SuppressWarnings("resource") void assertHttpProxy(String password, boolean authenticationCalled) throws MalformedURLException, Exception { Processor p = new Processor(); p.setProperty("-connectionsettings", "" + false); HttpClient hc = new HttpClient(); ConnectionSettings cs = new ConnectionSettings(p, hc); if (httpProxy != null) { ProxyDTO proxy = new ProxyDTO(); proxy.active = true; proxy.host = "localhost"; proxy.port = httpProxyPort; proxy.protocol = Type.HTTP; if (password != null) { proxy.username = "proxyuser"; proxy.password = password; } hc.addProxyHandler(ConnectionSettings.createProxyHandler(proxy)); } ServerDTO server = new ServerDTO(); server.id = httpTestServer.getBaseURI().toString(); server.verify = false; server.trust = Strings.join(httpTestServer.getTrustedCertificateFiles(IO.getFile("generated"))); cs.add(server); URL url = new URL(httpTestServer.getBaseURI() + "/get-tag/ABCDEFGH"); TaggedData tag = hc.connectTagged(url); assertNotNull(tag); if (tag.getState() != State.OTHER) assertEquals("ABCDEFGH", tag.getTag()); String s = IO.collect(tag.getInputStream()); assertNotNull(s); assertTrue(s.trim().startsWith("{")); assertTrue(this.authenticationCalled.get() == authenticationCalled); } @SuppressWarnings("resource") void assertSocks5Proxy(String password, boolean authenticationCalled) throws MalformedURLException, Exception { Processor p = new Processor(); p.setProperty("-connectionsettings", "" + false); HttpClient hc = new HttpClient(); ConnectionSettings cs = new ConnectionSettings(p, hc); if (socks5Proxy != null) { ProxyDTO proxy = new ProxyDTO(); proxy.active = true; proxy.host = "localhost"; proxy.port = socksProxyPort; proxy.protocol = Type.SOCKS; if (password != null) { proxy.username = "proxyuser"; proxy.password = password; } hc.addProxyHandler(ConnectionSettings.createProxyHandler(proxy)); } ServerDTO server = new ServerDTO(); server.id = httpTestServer.getBaseURI().toString(); server.verify = false; server.trust = Strings.join(httpTestServer.getTrustedCertificateFiles(IO.getFile("generated"))); cs.add(server); URL url = new URL(httpTestServer.getBaseURI() + "/get-tag/ABCDEFGH"); TaggedData tag = hc.connectTagged(url); assertNotNull(tag); assertEquals("ABCDEFGH", tag.getTag()); String s = IO.collect(tag.getInputStream()); assertNotNull(s); assertTrue(s.trim().startsWith("{")); // assertTrue(this.authenticationCalled.get() == authenticationCalled); } }