io.tourniquet.junit.http.rules.HttpServer Maven / Gradle / Ivy
/*
* Copyright 2015-2016 DevCon5 GmbH, [email protected]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.tourniquet.junit.http.rules;
import static java.nio.file.FileSystems.newFileSystem;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import io.tourniquet.junit.net.NetworkUtils;
import io.tourniquet.junit.rules.ExternalResource;
import io.tourniquet.junit.rules.TemporaryFile;
import io.tourniquet.junit.rules.TemporaryZipFile;
import io.undertow.Undertow;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.resource.PathResourceManager;
import io.undertow.server.handlers.resource.ResourceHandler;
import io.undertow.server.handlers.resource.ResourceManager;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
/**
* Server rule that starts an embedded http server, that serves static content. The server may be instantiated directly
* or by using the {@link HttpServerBuilder}. Content may be provided as generated zip, using the {@link
* io.tourniquet.junit.rules.TemporaryFile} rule or as predefinded zip from the classpath.
*/
public class HttpServer extends ExternalResource {
private static final Logger LOG = getLogger(HttpServer.class);
private final String hostname;
private final int port;
private final Map resources;
private Undertow server;
private PathHandler pathHandler;
/**
* Creates a http server on localhost, running on an available tcp port. The server won't server any static
* content.
*/
public HttpServer() {
this("localhost", NetworkUtils.findAvailablePort());
}
/**
* Creates a http server for the specified hostname and tcp port. The server won't server any static content.
*
* @param hostname
* the hostname the server listens on.
* @param port
* the tcp port the server is accepting incoming connections.
*/
public HttpServer(String hostname, int port) {
this(hostname, port, Collections.emptyMap());
}
/**
* Creates a http server for the specified hostname and tcp port. The server serves the content on the context paths
* provided in the resource map.
*
* @param hostname
* the hostname the server listens on.
* @param port
* the tcp port the server is accepting incoming connections.
* @param resources
*/
public HttpServer(final String hostname, final int port, final Map resources) {
this.hostname = hostname;
this.port = port;
this.resources = resources;
}
@Override
protected void beforeClass() throws Throwable {
before();
}
@Override
protected void afterClass() {
after();
}
@Override
protected void before() throws Throwable {
LOG.info("Creating http server {}:{}", getHostname(), getPort());
this.pathHandler = new PathHandler();
for (Map.Entry entry : this.resources.entrySet()) {
final String path = entry.getKey();
final Object resource = entry.getValue();
addResource(path, resource);
}
this.server = Undertow.builder().addHttpListener(this.port, this.hostname).setHandler(pathHandler).build();
LOG.info("Starting HTTP server");
this.server.start();
LOG.info("HTTP Server running");
}
/**
* Adds a resource to the path handler under the specified context path. Resources may be of various types:
* - {@link io.tourniquet.junit.rules.TemporaryZipFile} - zip file that is created for test execution. All files
* in the zip are hosted on the specified path as root folder.
- {@link java.net.URL} pointing to a zip
* resource, same as the TemporaryZipFile but the zip has to be predined
*
* @param path
* the path to the resource
* @param resource
* a resource to add. The method can handle various types of resources.
*
* @throws IOException
* @throws URISyntaxException
*/
void addResource(final String path, final Object resource) {
try {
if (resource instanceof TemporaryZipFile) {
final URL url = ((TemporaryZipFile) resource).getFile().toURI().toURL();
this.pathHandler.addPrefixPath(path, createZipResourceHandler(url));
} else if (resource instanceof TemporaryFolder) {
final Path resourcePath = ((TemporaryFolder) resource).getRoot().toPath();
this.pathHandler.addPrefixPath(path, new ResourceHandler(new PathResourceManager(resourcePath, 1024)));
} else if (resource instanceof TemporaryFile) {
final Path resourcePath = ((TemporaryFile) resource).getFile().toPath();
this.pathHandler.addExactPath(path, new PathResourceHandler(resourcePath));
} else if (resource instanceof URL) {
final URL url = (URL) resource;
if (url.getPath().endsWith(".zip")) {
this.pathHandler.addPrefixPath(path, createZipResourceHandler(url));
} else {
this.pathHandler.addExactPath(path, new UrlResourceHandler(url));
}
} else if (resource instanceof byte[]) {
this.pathHandler.addExactPath(path, new ByteArrayHandler((byte[]) resource));
}
} catch (IOException e) {
throw new AssertionError("Could not add Resource", e);
}
}
/**
* Creates the resource handle for a zip file, specified by the URL.
*
* @param zipFile
* url to a zip file
*
* @return the resource handler to handle requests to files in the zip
*
* @throws IOException
*/
private ResourceHandler createZipResourceHandler(final URL zipFile) throws IOException {
final FileSystem fileSystem = newFileSystem(URI.create("jar:" + zipFile),
Collections.emptyMap());
final ResourceManager resMgr = new FileSystemResourceManager(fileSystem);
return new ResourceHandler(resMgr);
}
@Override
protected void after() {
LOG.info("Stopping HTTP server");
this.server.stop();
LOG.info("HTTP Server stopped");
}
/**
* Provides the hostname of the http server. The server always runs on localhost, but possibly under another alias
* of it.
*
* @return the hostname of the server
*/
public String getHostname() {
return hostname;
}
/**
* The tcp port the server accepts incoming requests.
*
* @return the tcp port.
*/
public int getPort() {
return port;
}
/**
* Entry point for fluently defining response for http GET requests.
*
* @param resource
* the resource that should be retrieved via http GET.
*
* @return a stubbing defining what to respond on a get request on the specified resource.
*/
public GetResponseStubbing onGet(final String resource) {
return new GetResponseStubbing(this).resource(resource);
}
/**
* Creates an URL to the root path of the http server, i.e. 'http://localhost:8080/'
*
* @return the base URL to the http server
*/
public URL getBaseUrl() {
try {
return new URL("http", getHostname(), getPort(), "/");
} catch (MalformedURLException e) {
throw new AssertionError("Invalid base URL", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy