/* * 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. Load more
/* * 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;