/* * This file is part of the OWASP Proxy, a free intercepting proxy library. * Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.owasp.proxy.daemon; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.logging.Logger; /** * This class implements a TCP server. The user is required to provide an implementation of {@link ConnectionHandler} * implementing the desired protocol. * * The most basic (echo) server might look like: * * <code> * InetSocketAddress address = new InetSocketAddress("localhost", 8008); * Server echo = new Server(address, new ConnectionHandler() { * protected void handleConnection(Socket socket) throws IOException { * InputStream in = socket.getInputStream(); * OutputStream out = socket.getOutputStream(); * byte[] buff = new byte[1024]; * int got; * while ((got = in.read(buff)) > -1) * out.write(buff, 0, got); * } * }); * echo.start(); * * <wait for signal to stop> * * if (!echo.stop()) { * // error stopping server * } * </code> * * @author Rogan Dawes * */ public class Server { private final static Logger logger = Logger.getLogger(Server.class .getName()); private ServerSocket socket; private int socketTimeout = 0; private ConnectionHandler connectionHandler; private Executor executor; public Server(InetSocketAddress listen, ConnectionHandler connectionHandler) throws IOException { this(listen, null, connectionHandler); } public Server(final InetSocketAddress listen, Executor executor, ConnectionHandler connectionHandler) throws IOException { if (listen == null) throw new NullPointerException("listen may not be null"); if (executor == null) executor = Executors.newCachedThreadPool(new ThreadFactory() { private int threadCount = 0; private String prefix = listen + "-"; private synchronized String getName() { return prefix + (threadCount++); } /* * (non-Javadoc) * * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable ) */ public Thread newThread(Runnable r) { Thread t = new Thread(r, getName()); t.setDaemon(true); return t; } }); if (connectionHandler == null) throw new NullPointerException("connectionHandler may not be null"); socket = new ServerSocket(listen.getPort(), 20, listen.getAddress()); socket.setReuseAddress(true); this.executor = executor; this.connectionHandler = connectionHandler; } /** * @return the socketTimeout */ public int getSocketTimeout() { return socketTimeout; } /** * @param socketTimeout * the socketTimeout to set */ public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } private void handleConnection(final Socket socket) throws IOException { executor.execute(new SocketHandler(socket)); } private AcceptThread acceptThread = null; private class AcceptThread extends Thread { public AcceptThread() { super("Accept: " + socket.getLocalSocketAddress()); } public void run() { try { do { handleConnection(socket.accept()); } while (!socket.isClosed()); } catch (IOException ioe) { if (!socket.isClosed()) { ioe.printStackTrace(); logger.warning("Exception listening for connections: " + ioe.getMessage()); } } try { if (socket != null && !socket.isClosed()) socket.close(); } catch (IOException ioe) { logger.warning("Exception closing socket: " + ioe.getMessage()); } synchronized (Server.this) { Server.this.notifyAll(); } } } public synchronized void start() { if (acceptThread == null) { acceptThread = new AcceptThread(); acceptThread.setDaemon(true); } else if (acceptThread.isAlive()) { throw new IllegalStateException( "Already running in another thread!"); } acceptThread.start(); } public synchronized boolean stop() { if (!isStopped()) { try { socket.close(); } catch (IOException ioe) { ioe.printStackTrace(); } while (!isStopped()) { int loop = 0; try { wait(1000); } catch (InterruptedException ie) { loop++; if (loop > 10) return false; } } } return true; } public synchronized boolean isStopped() { return acceptThread == null || !acceptThread.isAlive(); } private class SocketHandler implements Runnable { private Socket socket; public SocketHandler(Socket socket) { this.socket = socket; } public void run() { try { socket.setSoTimeout(socketTimeout); connectionHandler.handleConnection(socket); } catch (Exception ignore) { ignore.printStackTrace(); } finally { try { if (!socket.isClosed()) socket.close(); } catch (IOException ignore) { } } } } }