package aQute.bnd.deployer.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLHandshakeException;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import aQute.bnd.service.url.TaggedData;
import aQute.lib.io.IO;
import junit.framework.TestCase;
import test.http.ETaggingResourceHandler;
public class HttpConnectorTest extends TestCase {
private static final String LOCALHOST = "127.0.0.1";
private static int HTTP_PORT = 0;
private static int HTTPS_PORT = 0;
private static final String RESOURCE_BASE = "testdata/http";
private static final String SECURED_PATH = "/securebundles/*";
private static final String USER_ROLE_FILE = "testdata/jetty-users.properties";
private static final String KEYSTORE_PATH = "testdata/example.keystore";
private static final String KEYSTORE_PASS = "opensesame";
private static final String EXPECTED_ETAG = "64035a95";
private static Server jetty;
private static String getUrl(boolean http) {
if (http) {
return "http://127.0.0.1:" + HTTP_PORT + "/";
}
return "https://127.0.0.1:" + HTTPS_PORT + "/";
}
@Override
protected void setUp() throws Exception {
File tmpFile = File.createTempFile("cache", ".tmp");
tmpFile.deleteOnExit();
jetty = startJetty();
}
@Override
protected void tearDown() throws Exception {
jetty.stop();
}
private static Server startJetty() throws Exception {
Server server = new Server();
// Create the login service
String REQUIRED_ROLE = "users";
HashLoginService loginSvc = new HashLoginService(REQUIRED_ROLE, USER_ROLE_FILE);
server.addBean(loginSvc);
// Start HTTP and HTTPS connectors
SelectChannelConnector httpConnector = new SelectChannelConnector();
httpConnector.setPort(0);
httpConnector.setHost(LOCALHOST);
server.addConnector(httpConnector);
SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
sslConnector.setPort(0);
sslConnector.setHost(LOCALHOST);
SslContextFactory contextFactory = sslConnector.getSslContextFactory();
contextFactory.setKeyStorePath(KEYSTORE_PATH);
contextFactory.setKeyStorePassword(KEYSTORE_PASS);
server.addConnector(sslConnector);
// Create the resource handler to serve files
ResourceHandler resourceHandler = new ETaggingResourceHandler();
resourceHandler.setResourceBase(RESOURCE_BASE);
resourceHandler.setDirectoriesListed(true);
// Setup user role constraints
Constraint constraint = new Constraint();
constraint.setName(Constraint.__BASIC_AUTH);
constraint.setRoles(new String[] {
REQUIRED_ROLE
});
constraint.setAuthenticate(true);
// Map constraints to the secured directory
ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec(SECURED_PATH);
// Setup the constraint handler
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setAuthMethod("BASIC");
securityHandler.setHandler(resourceHandler);
securityHandler.setLoginService(loginSvc);
securityHandler.setConstraintMappings(new ConstraintMapping[] {
cm
});
// Finally!! Start the server
server.setHandler(securityHandler);
server.start();
while (!server.isRunning()) {
Thread.sleep(10);
}
HTTP_PORT = httpConnector.getLocalPort();
HTTPS_PORT = sslConnector.getLocalPort();
assertNotSame(Integer.valueOf(0), Integer.valueOf(HTTP_PORT));
assertNotSame(Integer.valueOf(-1), Integer.valueOf(HTTP_PORT));
assertNotSame(Integer.valueOf(0), Integer.valueOf(HTTPS_PORT));
assertNotSame(Integer.valueOf(-1), Integer.valueOf(HTTPS_PORT));
assertNotSame(Integer.valueOf(HTTP_PORT), Integer.valueOf(HTTPS_PORT));
return server;
}
public static void testConnectTagged() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
TaggedData data = connector.connectTagged(new URL(getUrl(true) + "bundles/dummybundle.jar"));
assertNotNull("Data should be non-null because ETag not provided", data);
data.getInputStream().close();
assertEquals("ETag is incorrect", EXPECTED_ETAG, data.getTag());
}
public static void testConnectKnownTag() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
TaggedData data = connector.connectTagged(new URL(getUrl(true) + "bundles/dummybundle.jar"), EXPECTED_ETAG);
assertNull("Data should be null since ETag not modified.", data);
}
public static void testConnectTagModified() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
TaggedData data = connector.connectTagged(new URL(getUrl(true) + "bundles/dummybundle.jar"), "00000000");
assertNotNull("Data should be non-null because ETag was different", data);
data.getInputStream().close();
assertEquals("ETag is incorrect", EXPECTED_ETAG, data.getTag());
}
public static void testConnectHTTPS() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put(HttpsUtil.PROP_DISABLE_SERVER_CERT_VERIFY, "true");
connector.setProperties(config);
InputStream stream = connector.connect(new URL(getUrl(false) + "bundles/dummybundle.jar"));
assertNotNull(stream);
stream.close();
}
public static void testConnectHTTPSBadCerficate() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
InputStream stream = null;
try {
stream = connector.connect(new URL(getUrl(false) + "bundles/dummybundle.jar"));
fail("Expected SSLHandsakeException");
} catch (SSLHandshakeException e) {
// expected
} finally {
if (stream != null)
IO.close(stream);
}
}
public static void testConnectTaggedHTTPS() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put(HttpsUtil.PROP_DISABLE_SERVER_CERT_VERIFY, "true");
connector.setProperties(config);
TaggedData data = connector.connectTagged(new URL(getUrl(false) + "bundles/dummybundle.jar"));
assertNotNull(data);
data.getInputStream().close();
}
public static void testConnectTaggedHTTPSBadCerficate() throws Exception {
DefaultURLConnector connector = new DefaultURLConnector();
InputStream stream = null;
try {
connector.connectTagged(new URL(getUrl(false) + "bundles/dummybundle.jar"));
fail("Expected SSLHandsakeException");
} catch (SSLHandshakeException e) {
// expected
} finally {
if (stream != null)
IO.close(stream);
}
}
public static void testConnectNoUserPass() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "");
connector.setProperties(config);
try {
connector.connect(new URL(getUrl(true) + "securebundles/dummybundle.jar"));
fail("Should have thrown IOException due to missing auth");
} catch (IOException e) {
// expected
assertTrue(e.getMessage().startsWith("Server returned HTTP response code: 401"));
}
}
public static void testConnectWithUserPass() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth.properties");
connector.setProperties(config);
InputStream stream = connector.connect(new URL(getUrl(true) + "securebundles/dummybundle.jar"));
assertNotNull(stream);
stream.close();
}
public static void testConnectHTTPSBadCertificate() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth.properties");
connector.setProperties(config);
try {
connector.connect(new URL(getUrl(false) + "securebundles/dummybundle.jar"));
fail("Should have thrown error: invalid server certificate");
} catch (IOException e) {
// expected
assertTrue(e instanceof SSLHandshakeException);
}
}
public static void testConnectWithUserPassHTTPS() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth.properties");
config.put(HttpsUtil.PROP_DISABLE_SERVER_CERT_VERIFY, "true");
connector.setProperties(config);
InputStream stream = connector.connect(new URL(getUrl(false) + "securebundles/dummybundle.jar"));
assertNotNull(stream);
stream.close();
}
public static void testConnectWithWrongUserPass() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth_wrong.properties");
connector.setProperties(config);
try {
connector.connect(new URL(getUrl(true) + "securebundles/dummybundle.jar"));
fail("Should have thrown IOException due to incorrect auth");
} catch (IOException e) {
// expected
assertTrue(e.getMessage().startsWith("Server returned HTTP response code: 401"));
}
}
public static void testConnectWithWrongUserPassHTTPS() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth_wrong.properties");
config.put(HttpsUtil.PROP_DISABLE_SERVER_CERT_VERIFY, "true");
connector.setProperties(config);
try {
connector.connect(new URL(getUrl(false) + "securebundles/dummybundle.jar"));
fail("Should have thrown IOException due to incorrect auth");
} catch (IOException e) {
// expected
assertTrue(e.getMessage().startsWith("Server returned HTTP response code: 401"));
}
}
public static void testConnectWithUserPassAndTag() throws Exception {
HttpBasicAuthURLConnector connector = new HttpBasicAuthURLConnector();
Map<String,String> config = new HashMap<String,String>();
config.put("configs", "testdata/http_auth.properties");
connector.setProperties(config);
TaggedData data = connector.connectTagged(new URL(getUrl(true) + "securebundles/dummybundle.jar"),
EXPECTED_ETAG);
assertNull("Data should be null because resource not modified", data);
}
}