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);
}
}