nl.hsac.fitnesse.fixture.util.HttpServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hsac-fitnesse-fixtures Show documentation
Show all versions of hsac-fitnesse-fixtures Show documentation
Fixtures to assist in testing via FitNesse
package nl.hsac.fitnesse.fixture.util;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpHandler;
import org.apache.commons.io.IOUtils;
import org.apache.http.ParseException;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Receiver for a callback from application being tested.
*/
public class HttpServer {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpServer.class);
private static final Charset UTF8 = ContentType.parse(XmlHttpResponse.CONTENT_TYPE_XML_TEXT_UTF8).getCharset();
public static final int MAX_PORT = 65535;
private final T response;
private final com.sun.net.httpserver.HttpServer server;
private final InetSocketAddress address;
private final String path;
private final AtomicInteger requestsReceived = new AtomicInteger(0);
private final Object lock = new Object();
/**
* Creates new.
* @param anAddress address to bind on.
* @param aPath context the server will serve (must start with '/').
* @param aResponse response to send when request is received, request will
* be added to it when this server receives one.
*/
public HttpServer(InetSocketAddress anAddress, String aPath, T aResponse) {
this(anAddress.getAddress(), anAddress.getPort(), aPath, aResponse);
}
/**
* Creates new.
* @param anAddress address to bind on.
* @param port port to bind on.
* @param aPath context the server will serve (must start with '/').
* @param aResponse response to send when request is received, request will
* be added to it when this server receives one.
*/
public HttpServer(InetAddress anAddress, int port, String aPath, T aResponse) {
this(anAddress, port, port, aPath, aResponse);
}
/**
* Creates new.
* @param anAddress address to bind on.
* @param startPort lowest port to bind on.
* @param maxPort highest number to bind on.
* @param aPath context the server will serve (must start with '/').
* @param aResponse response to send when request is received, request will
* be added to it when this server receives one.
*/
public HttpServer(InetAddress anAddress, int startPort, int maxPort, String aPath, T aResponse) {
if (startPort > MAX_PORT) {
throw new IllegalArgumentException("port must be below: " + MAX_PORT);
}
response = aResponse;
path = aPath;
server = bind(anAddress, startPort, maxPort);
InetSocketAddress serverAddress = server.getAddress();
int port = serverAddress.getPort();
address = new InetSocketAddress(anAddress, port);
server.createContext(aPath, getHandler(aResponse));
server.start();
}
/**
* @return address the server listens on.
*/
public InetSocketAddress getAddress() {
return address;
}
/**
* @return URL for requests to the server.
*/
public String getURL() {
InetSocketAddress address = getAddress();
InetAddress hostAddress = address.getAddress();
return "http://" + hostAddress.getHostAddress() + ":" + address.getPort() + path;
}
/**
* @return response, with request filled (with last received request) if getRequestsReceived() is larger than 0.
*/
public T getResponse() {
return response;
}
/**
* @return number of requests this server received.
*/
public int getRequestsReceived() {
return requestsReceived.get();
}
/**
* Waits until at least one request is received, and then stops the server.
* @param maxWait ms to wait at most.
* @return response with last request filled, if at least one was received.
*/
public T waitForRequest(long maxWait) {
long start = System.currentTimeMillis();
try {
while (requestsReceived.get() < 1
&& (System.currentTimeMillis() - start) < maxWait) {
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
} finally {
stopServer();
}
return getResponse();
}
/**
* Stops server from listening.
*/
public void stopServer() {
synchronized (lock) {
server.stop(0);
}
}
/**
* Finds free port number and binds a server to it.
* @param address address to listen on.
* @param startPort lowest allowed port.
* @param maxPort highest (inclusive) port.
* @return server created.
*/
public static com.sun.net.httpserver.HttpServer bind(InetAddress address, int startPort, int maxPort) {
com.sun.net.httpserver.HttpServer aServer = createServer();
int port = -1;
for (int possiblePort = startPort; port == -1 && possiblePort <= maxPort; possiblePort++) {
try {
InetSocketAddress s = new InetSocketAddress(address, possiblePort);
aServer.bind(s, 1);
port = possiblePort;
} catch (IOException e) {
// try next number
continue;
}
}
if (aServer.getAddress() == null) {
throw new RuntimeException(new BindException("Unable to bind to: " + address.getHostAddress()));
}
return aServer;
}
private static com.sun.net.httpserver.HttpServer createServer() {
try {
return com.sun.net.httpserver.HttpServer.create();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
protected HttpHandler getHandler(final T aResponse) {
return he -> {
// ensure we never handle multiple requests at the same time
synchronized (lock) {
String method = he.getRequestMethod();
String uri = he.getRequestURI().toString();
LOGGER.debug("Handling {} request for {}", method, uri);
OutputStream os = null;
try {
String request;
InputStream is = he.getRequestBody();
String body = IOUtils.toString(is, Charset.defaultCharset());
if ("POST".equals(method)
|| "PUT".equals(method)
|| ("DELETE".equals(method) && !"".equals(body))
|| "PATCH".equals(method)
) {
request = body;
} else {
request = String.format("%s: %s", method, uri);
}
aResponse.setRequest(request);
aResponse.setMethod(method);
Headers heHeaders = he.getResponseHeaders();
Map responseHeaders = aResponse.getResponseHeaders();
for (Map.Entry headerEntry : responseHeaders.entrySet()) {
String headerName = headerEntry.getKey();
Object headerEntryValue = headerEntry.getValue();
heHeaders.add(headerName, headerEntryValue.toString());
}
byte[] responseBytes;
if (aResponse.getResponse() == null) {
responseBytes = new byte[0];
} else {
Charset charset = getCharSet(heHeaders);
responseBytes = aResponse.getResponse().getBytes(charset);
}
he.sendResponseHeaders(aResponse.getStatusCode(), responseBytes.length);
os = he.getResponseBody();
os.write(responseBytes);
os.flush();
} catch (RuntimeException e) {
LOGGER.error("Error handling {} request for {}", method, uri, e);
} finally {
incrementRequestsReceived();
if (os != null) {
os.close();
}
}
}
};
}
protected Charset getCharSet(Headers heHeaders) {
Charset charset = UTF8;
String contentTypeHeader = heHeaders.getFirst(HTTP.CONTENT_TYPE);
if (contentTypeHeader != null) {
try {
ContentType contentType = ContentType.parse(contentTypeHeader);
Charset contentTypeCharset = contentType.getCharset();
if (contentTypeCharset != null) {
charset = contentTypeCharset;
}
} catch (ParseException | UnsupportedCharsetException e) {
// ignore, use default charset UTF8
}
}
return charset;
}
protected int incrementRequestsReceived() {
return requestsReceived.incrementAndGet();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy