870 lines · 31.4 KB · Java
RawDownload
/* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.httpserver; import java.net.*;import java.io.*;import java.nio.*;import java.security.*;import java.nio.channels.*;import java.util.*;import java.util.concurrent.*;import java.util.logging.Logger;import java.util.logging.Level;import javax.net.ssl.*;import com.sun.net.httpserver.*;import com.sun.net.httpserver.spi.*;import sun.net.httpserver.HttpConnection.State; /** * Provides implementation for both HTTP and HTTPS */class ServerImpl implements TimeSource {     private String protocol;    private boolean https;    private Executor executor;    private HttpsConfigurator httpsConfig;    private SSLContext sslContext;    private ContextList contexts;    private InetSocketAddress address;    private ServerSocketChannel schan;    private Selector selector;    private SelectionKey listenerKey;    private Set<HttpConnection> idleConnections;    private Set<HttpConnection> allConnections;    /* following two are used to keep track of the times     * when a connection/request is first received     * and when we start to send the response     */    private Set<HttpConnection> reqConnections;    private Set<HttpConnection> rspConnections;    private List<Event> events;    private Object lolock = new Object();    private volatile boolean finished = false;    private volatile boolean terminating = false;    private boolean bound = false;    private boolean started = false;    private volatile long time;  /* current time */    private volatile long subticks = 0;    private volatile long ticks; /* number of clock ticks since server started */    private HttpServer wrapper;     final static int CLOCK_TICK = ServerConfig.getClockTick();    final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();    final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();    final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();    final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());    final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());    final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;     private Timer timer, timer1;    private Logger logger;     ServerImpl (        HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog    ) throws IOException {         this.protocol = protocol;        this.wrapper = wrapper;        this.logger = Logger.getLogger ("com.sun.net.httpserver");        ServerConfig.checkLegacyProperties (logger);        https = protocol.equalsIgnoreCase ("https");        this.address = addr;        contexts = new ContextList();        schan = ServerSocketChannel.open();        if (addr != null) {            ServerSocket socket = schan.socket();            socket.bind (addr, backlog);            bound = true;        }        selector = Selector.open ();        schan.configureBlocking (false);        listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);        dispatcher = new Dispatcher();        idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());        allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());        reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());        rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());        time = System.currentTimeMillis();        timer = new Timer ("server-timer", true);        timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);        if (timer1Enabled) {            timer1 = new Timer ("server-timer1", true);            timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);            logger.config ("HttpServer timer1 enabled period in ms:  "+TIMER_MILLIS);            logger.config ("MAX_REQ_TIME:  "+MAX_REQ_TIME);            logger.config ("MAX_RSP_TIME:  "+MAX_RSP_TIME);        }        events = new LinkedList<Event>();        logger.config ("HttpServer created "+protocol+" "+ addr);    }     public void bind (InetSocketAddress addr, int backlog) throws IOException {        if (bound) {            throw new BindException ("HttpServer already bound");        }        if (addr == null) {            throw new NullPointerException ("null address");        }        ServerSocket socket = schan.socket();        socket.bind (addr, backlog);        bound = true;    }     public void start () {        if (!bound || started || finished) {            throw new IllegalStateException ("server in wrong state");        }        if (executor == null) {            executor = new DefaultExecutor();        }        Thread t = new Thread (dispatcher);        started = true;        t.start();    }     public void setExecutor (Executor executor) {        if (started) {            throw new IllegalStateException ("server already started");        }        this.executor = executor;    }     private static class DefaultExecutor implements Executor {        public void execute (Runnable task) {            task.run();        }    }     public Executor getExecutor () {        return executor;    }     public void setHttpsConfigurator (HttpsConfigurator config) {        if (config == null) {            throw new NullPointerException ("null HttpsConfigurator");        }        if (started) {            throw new IllegalStateException ("server already started");        }        this.httpsConfig = config;        sslContext = config.getSSLContext();    }     public HttpsConfigurator getHttpsConfigurator () {        return httpsConfig;    }     public void stop (int delay) {        if (delay < 0) {            throw new IllegalArgumentException ("negative delay parameter");        }        terminating = true;        try { schan.close(); } catch (IOException e) {}        selector.wakeup();        long latest = System.currentTimeMillis() + delay * 1000;        while (System.currentTimeMillis() < latest) {            delay();            if (finished) {                break;            }        }        finished = true;        selector.wakeup();        synchronized (allConnections) {            for (HttpConnection c : allConnections) {                c.close();            }        }        allConnections.clear();        idleConnections.clear();        timer.cancel();        if (timer1Enabled) {            timer1.cancel();        }    }     Dispatcher dispatcher;     public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {        if (handler == null || path == null) {            throw new NullPointerException ("null handler, or path parameter");        }        HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);        contexts.add (context);        logger.config ("context created: " + path);        return context;    }     public synchronized HttpContextImpl createContext (String path) {        if (path == null) {            throw new NullPointerException ("null path parameter");        }        HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);        contexts.add (context);        logger.config ("context created: " + path);        return context;    }     public synchronized void removeContext (String path) throws IllegalArgumentException {        if (path == null) {            throw new NullPointerException ("null path parameter");        }        contexts.remove (protocol, path);        logger.config ("context removed: " + path);    }     public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {        if (!(context instanceof HttpContextImpl)) {            throw new IllegalArgumentException ("wrong HttpContext type");        }        contexts.remove ((HttpContextImpl)context);        logger.config ("context removed: " + context.getPath());    }     public InetSocketAddress getAddress() {        return (InetSocketAddress)schan.socket().getLocalSocketAddress();    }     Selector getSelector () {        return selector;    }     void addEvent (Event r) {        synchronized (lolock) {            events.add (r);            selector.wakeup();        }    }     /* main server listener task */     class Dispatcher implements Runnable {         private void handleEvent (Event r) {            ExchangeImpl t = r.exchange;            HttpConnection c = t.getConnection();            try {                if (r instanceof WriteFinishedEvent) {                     int exchanges = endExchange();                    if (terminating && exchanges == 0) {                        finished = true;                    }                    responseCompleted (c);                    LeftOverInputStream is = t.getOriginalInputStream();                    if (!is.isEOF()) {                        t.close = true;                    }                    if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {                        c.close();                        allConnections.remove (c);                    } else {                        if (is.isDataBuffered()) {                            /* don't re-enable the interestops, just handle it */                            requestStarted (c);                            handle (c.getChannel(), c);                        } else {                            connsToRegister.add (c);                        }                    }                }            } catch (IOException e) {                logger.log (                    Level.FINER, "Dispatcher (1)", e                );                c.close();            }        }         final LinkedList<HttpConnection> connsToRegister =                new LinkedList<HttpConnection>();         void reRegister (HttpConnection c) {            /* re-register with selector */            try {                SocketChannel chan = c.getChannel();                chan.configureBlocking (false);                SelectionKey key = chan.register (selector, SelectionKey.OP_READ);                key.attach (c);                c.selectionKey = key;                c.time = getTime() + IDLE_INTERVAL;                idleConnections.add (c);            } catch (IOException e) {                dprint(e);                logger.log(Level.FINER, "Dispatcher(8)", e);                c.close();            }        }         public void run() {            while (!finished) {                try {                    ListIterator<HttpConnection> li =                        connsToRegister.listIterator();                    for (HttpConnection c : connsToRegister) {                        reRegister(c);                    }                    connsToRegister.clear();                     List<Event> list = null;                    selector.select(1000);                    synchronized (lolock) {                        if (events.size() > 0) {                            list = events;                            events = new LinkedList<Event>();                        }                    }                     if (list != null) {                        for (Event r: list) {                            handleEvent (r);                        }                    }                     /* process the selected list now  */                     Set<SelectionKey> selected = selector.selectedKeys();                    Iterator<SelectionKey> iter = selected.iterator();                    while (iter.hasNext()) {                        SelectionKey key = iter.next();                        iter.remove ();                        if (key.equals (listenerKey)) {                            if (terminating) {                                continue;                            }                            SocketChannel chan = schan.accept();                            if (chan == null) {                                continue; /* cancel something ? */                            }                            chan.configureBlocking (false);                            SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ);                            HttpConnection c = new HttpConnection ();                            c.selectionKey = newkey;                            c.setChannel (chan);                            newkey.attach (c);                            requestStarted (c);                            allConnections.add (c);                        } else {                            try {                                if (key.isReadable()) {                                    boolean closed;                                    SocketChannel chan = (SocketChannel)key.channel();                                    HttpConnection conn = (HttpConnection)key.attachment();                                     key.cancel();                                    chan.configureBlocking (true);                                    if (idleConnections.remove(conn)) {                                        // was an idle connection so add it                                        // to reqConnections set.                                        requestStarted (conn);                                    }                                    handle (chan, conn);                                } else {                                    assert false;                                }                            } catch (CancelledKeyException e) {                                handleException(key, null);                            } catch (IOException e) {                                handleException(key, e);                            }                        }                    }                    // call the selector just to process the cancelled keys                    selector.selectNow();                } catch (IOException e) {                    logger.log (Level.FINER, "Dispatcher (4)", e);                } catch (Exception e) {                    e.printStackTrace();                    logger.log (Level.FINER, "Dispatcher (7)", e);                }            }        }         private void handleException (SelectionKey key, Exception e) {            HttpConnection conn = (HttpConnection)key.attachment();            if (e != null) {                logger.log (Level.FINER, "Dispatcher (2)", e);            }            closeConnection(conn);        }         public void handle (SocketChannel chan, HttpConnection conn)        throws IOException        {            try {                Exchange t = new Exchange (chan, protocol, conn);                executor.execute (t);            } catch (HttpError e1) {                logger.log (Level.FINER, "Dispatcher (4)", e1);                closeConnection(conn);            } catch (IOException e) {                logger.log (Level.FINER, "Dispatcher (5)", e);                closeConnection(conn);            }        }    }     static boolean debug = ServerConfig.debugEnabled ();     static synchronized void dprint (String s) {        if (debug) {            System.out.println (s);        }    }     static synchronized void dprint (Exception e) {        if (debug) {            System.out.println (e);            e.printStackTrace();        }    }     Logger getLogger () {        return logger;    }     private void closeConnection(HttpConnection conn) {        conn.close();        allConnections.remove(conn);        switch (conn.getState()) {        case REQUEST:            reqConnections.remove(conn);            break;        case RESPONSE:            rspConnections.remove(conn);            break;        case IDLE:            idleConnections.remove(conn);            break;        }        assert !reqConnections.remove(conn);        assert !rspConnections.remove(conn);        assert !idleConnections.remove(conn);    }         /* per exchange task */     class Exchange implements Runnable {        SocketChannel chan;        HttpConnection connection;        HttpContextImpl context;        InputStream rawin;        OutputStream rawout;        String protocol;        ExchangeImpl tx;        HttpContextImpl ctx;        boolean rejected = false;         Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {            this.chan = chan;            this.connection = conn;            this.protocol = protocol;        }         public void run () {            /* context will be null for new connections */            context = connection.getHttpContext();            boolean newconnection;            SSLEngine engine = null;            String requestLine = null;            SSLStreams sslStreams = null;            try {                if (context != null ) {                    this.rawin = connection.getInputStream();                    this.rawout = connection.getRawOutputStream();                    newconnection = false;

Lines 1–500 of 870.