package org.limewire.net; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import org.limewire.concurrent.ThreadExecutor; import org.limewire.io.IOUtils; import org.limewire.io.NetworkUtils; import org.limewire.nio.SocketFactory; import org.limewire.nio.channel.NIOMultiplexor; import org.limewire.nio.observer.AcceptObserver; /** * Listens on an HTTP port, accepts incoming connections, and dispatches threads * to handle requests. This allows simple HTTP requests. */ public class SocketAcceptor { private final ConnectionDispatcher dispatcher; /** * The socket that listens for incoming connections. * <p> * Note: Obtain <code>this</code> lock before accessing. */ private ServerSocket listeningSocket = null; private volatile int listeningPort = -1; private volatile boolean localOnly; public SocketAcceptor(ConnectionDispatcher connectionDispatcher) { this.dispatcher = connectionDispatcher; } public boolean isLocalOnly() { return localOnly; } public void setLocalOnly(boolean localOnly) { this.localOnly = localOnly; } /** * @requires only one thread is calling this method at a time * @modifies this * @effects sets the port on which the ConnectionManager is listening. If * that fails, this is <i>not</i> modified and IOException is * thrown. If port==0, tells this to stop listening to incoming * connections. This is properly synchronized and can be called * even while run() is being called. */ public synchronized void bind(int port) throws IOException { // if unchanged, do nothing. if (this.listeningSocket != null && this.listeningPort == port) { return; } // try new port. ServerSocket newSocket = SocketFactory.newServerSocket(port, new SocketListener()); // close old socket if (listeningSocket != null) { IOUtils.close(listeningSocket); } // replace with new sock. this.listeningSocket = newSocket; this.listeningPort = port; } public synchronized void unbind() { if (this.listeningSocket == null) { return; } IOUtils.close(this.listeningSocket); this.listeningSocket = null; this.listeningPort = -1; } /** * Return the listening port. */ public int getPort() { return listeningPort; } /** * Returns the dispatcher that dispatches incoming requests to a handler. */ public ConnectionDispatcher getDispatcher() { return dispatcher; } /** Dispatches sockets to a thread that'll handle them. */ private class SocketListener implements AcceptObserver { public void handleIOException(IOException ignored) { } public void shutdown() { } public void handleAccept(Socket client) { // only allow local connections if (isLocalOnly() && !NetworkUtils.isLocalHost(client)) { IOUtils.close(client); return; } // dispatch asynchronously if possible if (client instanceof NIOMultiplexor) { // TODO: This always allows TLS right now -- should conform to NetworkManager.isIncomingTLSEnabled ((NIOMultiplexor) client).setReadObserver(new AsyncConnectionDispatcher(dispatcher, client, null, true)); } else { ThreadExecutor.startThread(new BlockingConnectionDispatcher( dispatcher, client, null), "BlockingConnectionDispatchRunner"); } } } }