package cz.cuni.mff.d3s.been.swrepository.httpserver;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import org.apache.http.HttpException;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of listener thread for http sockets used by {@link HttpServer}
* .
*/
class HttpListener extends Thread {
private static final Logger log = LoggerFactory.getLogger(HttpListener.class);
private final HttpService service;
private final HttpContext context;
private final SocketAddress sockAddr;
private final HttpParams params;
private ServerSocket serverSocket;
/**
* Creates new http socket listener for given service on given address with
* given params.
*
* @param service
* service on which this listener will invoke request handling on
* socket receive
* @param sockAddr
* address on which the listener will be listening
* @param params
* parameters for connection which will be created on given socket
*/
HttpListener(HttpService service, SocketAddress sockAddr, HttpParams params) {
this.service = service;
this.sockAddr = sockAddr;
this.context = new BasicHttpContext();
this.params = params;
}
/**
* Creates new socket from socket address given in constructor and binds this
* listener to it.
*
* @throws HttpServerException
* when socket cannot be created from some reason
*/
public void bind() throws HttpServerException {
try {
serverSocket = new ServerSocket();
serverSocket.bind(sockAddr);
} catch (IOException e) {
throw new HttpServerException(String.format("Failed to bind HTTP listener thread on %s.", sockAddr.toString()), e);
}
log.info(String.format("Listener thread bound to socket %s", serverSocket.toString()));
}
@Override
public void run() {
setName(getClass().getSimpleName());
super.run();
while (!Thread.interrupted()) {
log.debug("Listener thread waiting for connection...");
Socket socket = acceptSocket();
if (socket == null) {
continue;
}
DefaultHttpServerConnection connection = createConnection(socket);
if (connection == null) {
continue;
}
handleRequest(connection);
closeConnection(connection);
log.debug("Request processed.");
}
try {
serverSocket.close();
log.info(String.format("Socket %s released, listener is down.", serverSocket.toString()));
} catch (IOException e) {
log.error(String.format("Failed to close listener socket - %s", e.getMessage()));
}
}
private Socket acceptSocket() {
try {
return serverSocket.accept();
} catch (IOException e) {
log.error(String.format("Failed to accept incoming socket connection - %s", e.getMessage()));
}
return null;
}
private DefaultHttpServerConnection createConnection(Socket socket) {
DefaultHttpServerConnection connection = new DefaultHttpServerConnection();
try {
connection.bind(socket, params);
} catch (IOException e) {
log.error(String.format("Failed to bind incoming connection %s - %s", connection.toString(), e.getMessage()));
return null;
}
return connection;
}
private void closeConnection(DefaultHttpServerConnection connection) {
try {
connection.close();
} catch (IOException e) {
log.error(String.format(
"Leaked connection %s when attempting release after handling request",
connection.toString()));
}
}
private void handleRequest(DefaultHttpServerConnection connection) {
try {
service.handleRequest(connection, context);
} catch (HttpException e) {
log.error("Could not process incoming connection %s - %s", connection.toString(), e.getMessage());
} catch (IOException e) {
log.error(String.format(
"I/O error when processing incoming connection %s - %s",
connection.toString(),
e.getMessage()));
}
}
}