/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.communication.protocol.http.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import org.ws4d.java.DPWSFramework;
import org.ws4d.java.communication.ProtocolData;
import org.ws4d.java.communication.ProtocolException;
import org.ws4d.java.communication.connection.ip.IPAddress;
import org.ws4d.java.communication.connection.tcp.TCPConnection;
import org.ws4d.java.communication.connection.tcp.TCPConnectionHandler;
import org.ws4d.java.communication.connection.tcp.TCPListener;
import org.ws4d.java.communication.connection.tcp.TCPServer;
import org.ws4d.java.communication.monitor.MonitorStreamFactory;
import org.ws4d.java.communication.monitor.MonitoringContext;
import org.ws4d.java.communication.protocol.http.ChunkedOutputStream;
import org.ws4d.java.communication.protocol.http.HTTPGroup;
import org.ws4d.java.communication.protocol.http.HTTPInputStream;
import org.ws4d.java.communication.protocol.http.HTTPOutputStream;
import org.ws4d.java.communication.protocol.http.HTTPRequestUtil;
import org.ws4d.java.communication.protocol.http.HTTPResponse;
import org.ws4d.java.communication.protocol.http.HTTPResponseUtil;
import org.ws4d.java.communication.protocol.http.HTTPUser;
import org.ws4d.java.communication.protocol.http.header.HTTPRequestHeader;
import org.ws4d.java.communication.protocol.http.header.HTTPResponseHeader;
import org.ws4d.java.communication.protocol.http.server.responses.DefaultNotFoundResponse;
import org.ws4d.java.communication.protocol.http.server.responses.DefaultUnauthorizedResponse;
import org.ws4d.java.configuration.DPWSProperties;
import org.ws4d.java.constants.HTTPConstants;
import org.ws4d.java.message.Message;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.structures.Iterator;
import org.ws4d.java.structures.LinkedList;
import org.ws4d.java.structures.List;
import org.ws4d.java.types.InternetMediaType;
import org.ws4d.java.types.URI;
import org.ws4d.java.util.Log;
import org.ws4d.java.util.StringUtil;
import org.ws4d.java.util.TimedEntry;
import org.ws4d.java.util.WatchDog;
/**
* This class allows the creation of an HTTP server to handle incoming HTTP
* requests.
*/
public class HTTPServer {
/**
* This is a fall back for HTTP path search.
* <p>
* If <code>true</code> the used handler search will be changed. Usually we
* try to match the request directly to a registered handler. If no handler
* were found, the {@link DefaultHTTPNotFoundHandler} will be used to handle
* the request. Setting {@link #BACKTRACK} <code>true</code> implies that
* the handlers above the given request will also be searched.
* </p>
* <h4>Example</h4>
* <p>
* If no handler is set for <strong>/home/johndoe</strong>. The request for
* this path will fail. With {@link #BACKTRACK} <code>true</code>, the look
* up will be done at <strong>/home</strong> and <strong>/</strong> too.
* </p>
*/
private static final boolean BACKTRACK = false;
/**
* This allows to <i>eat</i> the bytes inside the HTTP request body if the
* handler does not read them.
*/
private static final boolean EAT = true;
/**
* This is the root path of the HTTP server.
*/
private URI base = null;
/**
* The host address of this HTTP server.
*/
private IPAddress ipAddress = null;
/**
* The host port of this HTTP server.
*/
private int port = -1;
/**
* A TCP connection handler which will handle the incoming HTTP requests.
*/
private HTTPConnectionHandler handler = new HTTPConnectionHandler();
/**
* This table contains path and handler.
*/
private HashMap handlers = new HashMap();
/**
* Indicates whether this server is running or not.
*/
private boolean running = false;
/**
* List of active timeouts. Necessary for correct {@link #stop()}.
*/
private List timeouts = new LinkedList();
/**
* Indicates whether this server should keep the connection or not.
*/
private boolean keepalive = true;
/**
* Simple counter representing the number of handlers handling incoming
* requests at the moment.
*/
private static int hand = 0;
/**
* Simple request timeout value.
*/
private static long REQUEST_TIMEOUT = 20000;
/**
* This table contains the created HTTP servers.
*/
private static HashMap servers = new HashMap();
/**
* Allows the shutdown of the underlying TCP client if all registrations are
* removed.
*/
private static boolean UNREGISTER_SHUTDOWN = false;
/**
* ws-security
*/
private boolean isSecure = false;
private String alias = null;
/**
* HTTP Authentication
*/
private HashMap authentication = new HashMap();
public void setAuthentication(URI resource, HTTPGroup group) {
authentication.put(resource, group);
}
public HTTPGroup getAuthenticatedUser(URI resource) {
return (HTTPGroup) authentication.get(resource);
}
public synchronized static HTTPServer get(IPAddress ipAddress, int port) throws IOException {
return get(ipAddress, port, false, null);
}
/**
* Returns a HTTP server for the given address and port. If no such server
* exists, a new server will be created.
* <p>
* The HTTP server is started at creation time.
* </p>
*
* @param address the address of the HTTP server.
* @param port the port for the server.
* @return a new HTTP server.
* @throws IOException Throws exception if the port could not be opened at
* the given address.
*/
public synchronized static HTTPServer get(IPAddress ipAddress, int port, boolean secure, String alias) throws IOException {
String key;
HTTPServer server;
if (port == 0) {
server = new HTTPServer(ipAddress, port, secure, alias);
key = ipAddress.getAddress() + "@" + server.port;
} else {
key = ipAddress.getAddress() + "@" + port;
server = (HTTPServer) servers.get(key);
if (server != null) return server;
server = new HTTPServer(ipAddress, port, secure, alias);
}
servers.put(key, server);
return server;
}
public synchronized static void unregisterAndStop(HTTPServer server) throws IOException {
String key = server.ipAddress.getAddress() + "@" + server.port;
servers.remove(key);
server.stop();
}
private HTTPServer(IPAddress ipAddress, int port, boolean secure, String alias) throws IOException {
DPWSProperties properies = DPWSProperties.getInstance();
keepalive = properies.getHTTPServerKeepAlive();
this.ipAddress = ipAddress;
this.port = port;
this.isSecure = secure;
this.alias = alias;
String httpSchema = (secure ? HTTPConstants.HTTPS_SCHEMA : HTTPConstants.HTTP_SCHEMA);
start();
// generate base URI after actual port has been assigned
base = new URI(httpSchema + "://" + ipAddress.getAddressWithoutNicId() + ":" + this.port);
}
/**
* Registers a relative HTTP path with a given {@link HTTPRequestHandler}.
*
* @param path the HTTP path.
* @param handler the HTTP handler which should handle the request.
*/
public void register(String path, HTTPRequestHandler handler, HTTPGroup user) {
URI registerURI = new URI(path, base);
handlers.put(registerURI, handler);
// TODO: unregister
if (user != null) {
setAuthentication(registerURI, user);
}
}
/**
* Registers a relative HTTP path and a content type with a given
* {@link HTTPRequestHandler}.
*
* @param path the HTTP path.
* @param type the HTTP content type.
* @param handler the HTTP handler which should handle the request.
*/
public void register(String path, InternetMediaType type, HTTPRequestHandler handler, HTTPGroup user) {
URI registerURI = new URI(path, base);
MappingEntry entry = new MappingEntry(registerURI, type);
handlers.put(entry, handler);
// TODO: unregister
if (user != null) {
setAuthentication(registerURI, user);
}
}
/**
* Removes registration of a relative HTTP path for a
* {@link HTTPRequestHandler}.
*
* @param path the HTTP path.
* @return the removed {@link HTTPRequestHandler}.
*/
public HTTPRequestHandler unregister(String path) {
URI registerURI = new URI(path, base);
HTTPRequestHandler handler = (HTTPRequestHandler) handlers.remove(registerURI);
if (UNREGISTER_SHUTDOWN && handlers.isEmpty()) {
try {
TCPServer.close(ipAddress, port);
} catch (IOException e) {
Log.error("Cannot shutdown TCP server after all registrations removed. " + e.getMessage());
}
}
return handler;
}
/**
* Removes registration of a relative HTTP path and content type for a HTTP
* handler.
*
* @param path the HTTP path.
* @param type the HTTP content type.
* @return the removed {@link HTTPRequestHandler}.
*/
public HTTPRequestHandler unregister(String path, InternetMediaType type) {
URI registerURI = new URI(path, base);
MappingEntry entry = new MappingEntry(registerURI, type);
HTTPRequestHandler handler = (HTTPRequestHandler) handlers.remove(entry);
if (UNREGISTER_SHUTDOWN && handlers.isEmpty()) {
try {
TCPServer.close(ipAddress, port);
} catch (IOException e) {
Log.error("Cannot shutdown TCP server after all registrations removed. " + e.getMessage());
}
}
return handler;
}
/**
* Starts the HTTP server.
*
* @throws IOException
*/
public synchronized void start() throws IOException {
if (running) return;
TCPListener listener;
if (!isSecure)
listener = TCPServer.open(ipAddress, port, handler);
else
listener = TCPServer.open(ipAddress, port, handler, true, this.alias);
if (port == 0) {
port = listener.getPort();
}
running = true;
}
/**
* Stops the HTTP server.
*
* @throws IOException
*/
public synchronized void stop() throws IOException {
if (!running) return;
TCPServer.close(ipAddress, port);
/*
* Unregister all timeouts.
*/
Iterator it = timeouts.iterator();
while (it.hasNext()) {
HandlerTimeOut timeout = (HandlerTimeOut) it.next();
WatchDog.getInstance().unregister(timeout);
it.remove();
}
running = false;
}
/**
* Returns <code>true</code> if the HTTP server is running,
* <code>false</code> otherwise.
*
* @return <code>true</code> if the HTTP server is running,
* <code>false</code> otherwise.
*/
public synchronized boolean isRunning() {
return running;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* TCP handler which handles the incoming HTTP requests.
*/
private class HTTPConnectionHandler implements TCPConnectionHandler {
public void handle(TCPConnection connection) throws IOException {
/*
* Default HTTP 1.1 behavior.
*/
hand++;
boolean firstRequest = true;
HandlerTimeOut timeout = new HandlerTimeOut(connection, keepalive);
ProtocolData protocolData = connection.getProtocolData();
MonitorStreamFactory monFac = DPWSFramework.getMonitorStreamFactory();
/*
* Keep persistent HTTP connection.
*/
while (timeout.keepAlive() || firstRequest) {
firstRequest = false;
MonitoringContext context = null;
if (monFac != null) {
context = monFac.getNewMonitoringContextIn(protocolData);
}
InputStream in = connection.getInputStream();
OutputStream out = connection.getOutputStream();
HTTPRequestHeader requestHeader = null;
try {
WatchDog.getInstance().register(timeout, REQUEST_TIMEOUT);
timeouts.add(timeout);
requestHeader = HTTPRequestUtil.handleRequest(in);
WatchDog.getInstance().unregister(timeout);
} catch (ProtocolException e) {
/*
* Something wrong in the shiny HTTP wonderland?! Send
* internal server error response and close the connection.
*/
WatchDog.getInstance().unregister(timeout);
HTTPResponseHeader responseHeader = HTTPResponseUtil.getResponseHeader(400);
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION, HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE);
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, "0");
String note = "Invalid HTTP request: " + e.getMessage();
responseHeader.toStream(out);
out.write(note.getBytes());
Log.warn("Closing HTTP connection. " + note + ".");
break;
}
/*
* No header? This happens if the input stream reaches the end.
*/
if (requestHeader == null) {
break;
}
if (Log.isDebug()) {
Log.debug("<I> " + requestHeader + " from " + protocolData.getSourceAddress() + ", " + connection, Log.DEBUG_LAYER_COMMUNICATION);
}
/*
* Get some parameters from the HTTP request.
*/
String path = requestHeader.getRequest();
/*
* Check for absolute path.
*/
if (path.startsWith(HTTPConstants.HTTP_SCHEMA)) {
URI absoluteURI = new URI(path);
path = absoluteURI.getPath();
}
String method = requestHeader.getMethod();
String encodingRequest = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_TRANSFER_ENCODING);
String bodyLength = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH);
int size = -1;
if (bodyLength != null) {
size = Integer.parseInt(bodyLength.trim());
}
// Add the Path to ProtocolData
connection.getProtocolData().setTransportAddress(new URI(base.toString(), path));
/*
* Check for necessary length.
*/
if (!HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED.equals(encodingRequest) && size < 0 && HTTPConstants.HTTP_METHOD_POST.equals(method)) {
HTTPResponseHeader response = HTTPResponseUtil.getResponseHeader(400);
response.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION, HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE);
String note = "Neither content length nor chunked encoding found. Cannot determinate content length.";
response.toStream(out);
out.write(note.getBytes());
break;
}
String mediaType = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_TYPE);
InternetMediaType type = new InternetMediaType(mediaType);
/*
* Does the client wish to close the connection? Disable
* keep-alive if necessary.
*/
String con = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION);
if (HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE.equals(con)) {
timeout.setKeepAlive(false);
}
/*
* Get TE (RFC2616 14.39)
*/
boolean responseChunkedTrailer = false;
String te = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_TE);
if (te != null) {
String[] tes = StringUtil.split(te, ',');
for (int t = 0; t < tes.length; t++) {
if (tes[t].indexOf(HTTPConstants.HTTP_HEADERVALUE_TE_TRAILERS) >= 0) {
responseChunkedTrailer = true;
}
}
}
/*
* Wrap the HTTP body inside a new stream.
*/
in = new HTTPInputStream(in, encodingRequest, size);
/*
* The requested URI
*/
URI requestedURI = new URI(base, requestHeader.getRequest());
/*
* Try to find the HTTP handler for this request.
*/
HTTPRequestHandler handler = getHTTPHandler(path, type);
/*
* This object will contain the HTTP response from the handler.
*/
HTTPResponse response = null;
/*
* Handle request (HTTP exchange) if possible. Send 404
* "Not found" if no handler found.
*/
if (handler != null) {
if (authentication.get(requestedURI) != null) {
// TODO: Authentication
HTTPUser requestedUser = null;
HTTPGroup authGroup = null;
String basicCredentials;
authGroup = (HTTPGroup) authentication.get(requestedURI);
basicCredentials = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_AUTHORIZATION);
if (basicCredentials != null) {
requestedUser = HTTPUser.createUserFromBase64String(basicCredentials.substring("Basic".length()));
}
if (requestedUser == null || !authGroup.inList(requestedUser)) {
/*
* Default 401 Unauthorized.
*/
response = new DefaultUnauthorizedResponse(requestHeader);
}
}
if (response == null) {
try {
response = handler.handle(requestedURI, requestHeader, in, connection.getProtocolData(), context);
} catch (IOException e) {
/*
* The handler got an exception. Shit happens... We
* should send a HTTP 500 internal server error.
* This can only happen while reading the input
* stream.
*/
String note = "The registered HTTP handler (" + handler.getClass().getName() + ") got an exception. " + e.getMessage();
Log.error(note);
HTTPResponseHeader responseHeader = HTTPResponseUtil.getResponseHeader(500);
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, "0");
responseHeader.addHeaderFieldValue("JMEDS-Debug", requestHeader.getRequest());
responseHeader.toStream(out);
out.write(note.getBytes());
Log.warn("Closing HTTP connection. " + note + ".");
break;
}
}
}
if (response == null || response.getResponseHeader() == null) {
/*
* Default 404 Not found.
*/
response = new DefaultNotFoundResponse(requestHeader);
}
// TODO: Authorization????
/*
* Analyze and serialize the HTTP response header and create a
* output stream to write the HTTP response body.
*/
HTTPResponseHeader responseHeader = response.getResponseHeader();
/*
* Does the server (the generated response) contain a "Date"
* field?
*/
String date = responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_DATE);
if (date == null) {
Date d = new Date();
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_DATE, StringUtil.getHTTPDate(d.getTime()));
}
/*
* Does the server (the generated response) contain a
* "Last-Modified" field?
*/
String ifModSince = requestHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_IF_MODIFIED_SINCE);
long ifModSinceL = -1;
if (ifModSince != null) {
ifModSinceL = StringUtil.getHTTPDateAsLong(ifModSince);
}
String lastMod = responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_LAST_MODIFIED);
long lastModL = -1;
if (lastMod != null) {
lastModL = StringUtil.getHTTPDateAsLong(lastMod);
}
if (ifModSinceL != -1 && lastModL != -1 && lastModL <= ifModSinceL) {
/*
* Resource was not modified...
*/
responseHeader = HTTPResponseUtil.getResponseHeader(304);
Date d = new Date();
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_DATE, StringUtil.getHTTPDate(d.getTime()));
responseHeader.toStream(out);
if (Log.isDebug()) {
Log.debug("Resource at " + requestedURI + " not modified since " + ifModSince + ".");
}
break;
}
/*
* Does the server (the generated response) wish to close the
* connection? Disable keep-alive if necessary.
*/
con = responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION);
if (HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE.equals(con)) {
timeout.setKeepAlive(false);
}
/*
* Does the global property prohibit the keep alive function?
*/
if (!keepalive) {
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION, HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE);
timeout.setKeepAlive(false);
}
String encodingResponse = responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_TRANSFER_ENCODING);
int contentLengthResponse = (responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH) != null) ? Integer.parseInt(responseHeader.getHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH).trim()) : -1;
/*
* Change context from incoming to outgoing.
*/
ProtocolData pOut = null;
if (monFac != null) {
pOut = protocolData.createSwappedProtocolData();
context = monFac.getNewMonitoringContextOut(pOut);
}
/*
* Header has chunked encoding set, but we should avoid chunks
* ...
*/
ByteArrayOutputStream buffer = null;
boolean buffered = false;
if (!HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED.equals(encodingResponse) && contentLengthResponse == -1) {
buffer = new ByteArrayOutputStream();
response.serializeResponseBody(requestedURI, requestHeader, buffer, connection.getProtocolData() == null ? null : connection.getProtocolData().createSwappedProtocolData(), context);
// response.serializeResponseBody(requestedURI,
// requestHeader, buffer,
// ProtocolData.swap(connection.getProtocolData()),
// context);
contentLengthResponse = buffer.size();
responseHeader.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, Integer.toString(contentLengthResponse));
buffered = true;
}
if (Log.isDebug()) {
Log.debug("<O> " + responseHeader + " to " + protocolData.getSourceAddress() + ", " + connection, Log.DEBUG_LAYER_COMMUNICATION);
}
responseHeader.toStream(out);
/*
* Serialize the HTTP response body.
*/
if (HTTPConstants.HTTP_METHOD_HEAD.equals(requestHeader.getMethod())) {
out = new HTTPOutputStream(out, 0);
} else {
if (HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED.equals(encodingResponse)) {
out = new ChunkedOutputStream(out, responseChunkedTrailer);
} else {
out = new HTTPOutputStream(out, contentLengthResponse);
}
}
if (!buffered) {
// response.serializeResponseBody(requestedURI,
// requestHeader, out,
// ProtocolData.swap(connection.getProtocolData()),
// context);
response.serializeResponseBody(requestedURI, requestHeader, out, connection.getProtocolData() == null ? null : connection.getProtocolData().createSwappedProtocolData(), context);
} else {
out.write(buffer.toByteArray());
}
/*
* Was this a chunked response? Write lust chunk!
*/
if (HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED.equals(encodingResponse)) {
ChunkedOutputStream.writeLastChunk((ChunkedOutputStream) out);
}
out.flush();
response.waitFor();
if (monFac != null) {
Message m = context.getMessage();
if (m != null) {
monFac.send(pOut, context, m);
}
}
/*
* Should we eat the omitted bytes or not?
*/
consumeStream(in);
}
hand--;
}
/**
* @param in
* @throws IOException
*/
private void consumeStream(InputStream in) throws IOException {
if (EAT) {
int n = -1;
while (in.read() != -1) {
/*
* Eat the omitted bytes from stream...
*/
n++;
}
if (n > -1) {
Log.warn("The registered handler has not consumed the HTTP body from the request. Eating " + n + " bytes.");
}
}
}
}
/**
* Returns the HTTP handler for the given path and content type.
* <p>
* This method will search for the HTTP handler depending on the value of
* the {@link HTTPServer#BACKTRACK} field.
* </p>
*
* @param path the path.
* @param type the content type.
* @return the HTTP handler which match path and content type.
*/
private HTTPRequestHandler getHTTPHandler(String path, InternetMediaType type) {
URI requestURI = new URI(path, base);
MappingEntry entry = new MappingEntry(requestURI, type);
HTTPRequestHandler handler = null;
/*
* Tries to get specific handler for the given type.
*/
handler = (HTTPRequestHandler) handlers.get(entry);
/*
* No specific handler found? Tries to find an handler which accepts
* every type for this address.
*/
if (handler == null) {
handler = (HTTPRequestHandler) handlers.get(requestURI);
}
if (BACKTRACK) {
/*
* No handler found? Does some backtracking... Looks up along the
* path, maybe some handler is there.
*/
if (handler == null && requestURI.getPathDeepness() > 0) {
URI backtrackURI = requestURI;
while (backtrackURI.getPathDeepness() > 0) {
int deepness = backtrackURI.getPathDeepness();
deepness--;
String backtrackPath = backtrackURI.getPath(deepness);
backtrackURI = new URI(backtrackURI, backtrackPath);
entry = new MappingEntry(requestURI, type);
handler = (HTTPRequestHandler) handlers.get(entry);
if (handler == null) {
handler = (HTTPRequestHandler) handlers.get(backtrackURI);
}
if (handler != null) {
break;
}
}
}
}
return handler;
}
/**
* HTTP timeout.
*/
private class HandlerTimeOut extends TimedEntry {
private TCPConnection connection = null;
private boolean keepalive = true;
private HandlerTimeOut(TCPConnection connection, boolean keepalive) {
this.connection = connection;
this.keepalive = keepalive;
}
protected void timedOut() {
keepalive = false;
if (Log.isDebug()) {
Log.debug("<I> Incoming TCP connection (" + connection.getIdentifier() + ") timeout after " + REQUEST_TIMEOUT + "ms.", Log.DEBUG_LAYER_COMMUNICATION);
}
try {
connection.close();
} catch (IOException e) {
Log.error("Cannot close server connection. " + e.getMessage());
}
}
public boolean keepAlive() {
return keepalive;
}
public void setKeepAlive(boolean keepalive) {
this.keepalive = keepalive;
}
}
/**
* This entry contains a URI and content type.
*/
private class MappingEntry {
private URI uri = null;
private InternetMediaType type = null;
MappingEntry(URI uri, InternetMediaType type) {
this.uri = uri;
this.type = type;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((uri == null) ? 0 : uri.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MappingEntry other = (MappingEntry) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (type == null) {
if (other.type != null) return false;
} else if (!type.equals(other.type)) return false;
if (uri == null) {
if (other.uri != null) return false;
} else if (!uri.equals(other.uri)) return false;
return true;
}
private HTTPServer getOuterType() {
return HTTPServer.this;
}
}
}