package org.deftserver.web;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.util.List;
import org.deftserver.io.IOLoop;
import org.deftserver.util.Closeables;
import org.deftserver.web.http.HttpProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
public class HttpServer {
private final Logger logger = LoggerFactory.getLogger(HttpServer.class);
private static final int MIN_PORT_NUMBER = 1;
private static final int MAX_PORT_NUMBER = 65535;
private ServerSocketChannel serverChannel;
private final List<IOLoop> ioLoops = Lists.newLinkedList();
private final Application application;
public HttpServer(Application application) {
this.application = application;
}
/**
* If you want to run Deft on multiple threads first invoke {@link #bind(int)} then {@link #start(int)}
* instead of {@link #listen(int)} (listen starts Deft http server on a single thread with the default IOLoop
* instance: {@code IOLoop.INSTANCE}).
*
* @return this for chaining purposes
*/
public void listen(int port) {
bind(port);
ioLoops.add(IOLoop.INSTANCE);
registerHandler(IOLoop.INSTANCE, new HttpProtocol(application));
}
public void bind(int port) {
if (port <= MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
throw new IllegalArgumentException("Invalid port number. Valid range: [" +
MIN_PORT_NUMBER + ", " + MAX_PORT_NUMBER + ")");
}
try {
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
} catch (IOException e) {
logger.error("Error creating ServerSocketChannel: {}", e);
}
InetSocketAddress endpoint = new InetSocketAddress(port); // use "any" address
try {
serverChannel.socket().bind(endpoint);
} catch (IOException e) {
logger.error("Could not bind socket: {}", e);
}
}
public void start(int numThreads) {
for (int i = 0; i < numThreads; i++) {
final IOLoop ioLoop = new IOLoop();
ioLoops.add(ioLoop);
final HttpProtocol protocol = new HttpProtocol(ioLoop, application);
new Thread(new Runnable() {
@Override public void run() {
registerHandler(ioLoop, protocol);
ioLoop.start();
}
}).start();
}
}
/**
* Unbinds the port and shutdown the HTTP server
*/
public void stop() {
logger.debug("Stopping HTTP server");
for (IOLoop ioLoop : ioLoops) {
// TODO RS 110527 Should probably do this in each IOLoop through an AsyncCallback
// (hint: ioloop.addCallback(..))
Closeables.closeQuietly(ioLoop, serverChannel);
}
}
private void registerHandler(IOLoop ioLoop, HttpProtocol protocol) {
ioLoop.addHandler(
serverChannel,
protocol,
SelectionKey.OP_ACCEPT,
null /*attachment*/
);
}
}