Save This Page
Home » openjdk-7 » sun » net » httpserver » [javadoc | source]
    1   /*
    2    * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.net.httpserver;
   27   
   28   import java.net;
   29   import java.io;
   30   import java.nio;
   31   import java.security;
   32   import java.nio.channels;
   33   import java.util;
   34   import java.util.concurrent;
   35   import java.util.logging.Logger;
   36   import java.util.logging.Level;
   37   import javax.net.ssl;
   38   import com.sun.net.httpserver;
   39   import com.sun.net.httpserver.spi;
   40   import sun.net.httpserver.HttpConnection.State;
   41   
   42   /**
   43    * Provides implementation for both HTTP and HTTPS
   44    */
   45   class ServerImpl implements TimeSource {
   46   
   47       private String protocol;
   48       private boolean https;
   49       private Executor executor;
   50       private HttpsConfigurator httpsConfig;
   51       private SSLContext sslContext;
   52       private ContextList contexts;
   53       private InetSocketAddress address;
   54       private ServerSocketChannel schan;
   55       private Selector selector;
   56       private SelectionKey listenerKey;
   57       private Set<HttpConnection> idleConnections;
   58       private Set<HttpConnection> allConnections;
   59       /* following two are used to keep track of the times
   60        * when a connection/request is first received
   61        * and when we start to send the response
   62        */
   63       private Set<HttpConnection> reqConnections;
   64       private Set<HttpConnection> rspConnections;
   65       private List<Event> events;
   66       private Object lolock = new Object();
   67       private volatile boolean finished = false;
   68       private volatile boolean terminating = false;
   69       private boolean bound = false;
   70       private boolean started = false;
   71       private volatile long time;  /* current time */
   72       private volatile long subticks = 0;
   73       private volatile long ticks; /* number of clock ticks since server started */
   74       private HttpServer wrapper;
   75   
   76       final static int CLOCK_TICK = ServerConfig.getClockTick();
   77       final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
   78       final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
   79       final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
   80       final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());
   81       final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
   82       final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
   83   
   84       private Timer timer, timer1;
   85       private Logger logger;
   86   
   87       ServerImpl (
   88           HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
   89       ) throws IOException {
   90   
   91           this.protocol = protocol;
   92           this.wrapper = wrapper;
   93           this.logger = Logger.getLogger ("com.sun.net.httpserver");
   94           ServerConfig.checkLegacyProperties (logger);
   95           https = protocol.equalsIgnoreCase ("https");
   96           this.address = addr;
   97           contexts = new ContextList();
   98           schan = ServerSocketChannel.open();
   99           if (addr != null) {
  100               ServerSocket socket = schan.socket();
  101               socket.bind (addr, backlog);
  102               bound = true;
  103           }
  104           selector = Selector.open ();
  105           schan.configureBlocking (false);
  106           listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
  107           dispatcher = new Dispatcher();
  108           idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
  109           allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
  110           reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
  111           rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
  112           time = System.currentTimeMillis();
  113           timer = new Timer ("server-timer", true);
  114           timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
  115           if (timer1Enabled) {
  116               timer1 = new Timer ("server-timer1", true);
  117               timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
  118               logger.config ("HttpServer timer1 enabled period in ms:  "+TIMER_MILLIS);
  119               logger.config ("MAX_REQ_TIME:  "+MAX_REQ_TIME);
  120               logger.config ("MAX_RSP_TIME:  "+MAX_RSP_TIME);
  121           }
  122           events = new LinkedList<Event>();
  123           logger.config ("HttpServer created "+protocol+" "+ addr);
  124       }
  125   
  126       public void bind (InetSocketAddress addr, int backlog) throws IOException {
  127           if (bound) {
  128               throw new BindException ("HttpServer already bound");
  129           }
  130           if (addr == null) {
  131               throw new NullPointerException ("null address");
  132           }
  133           ServerSocket socket = schan.socket();
  134           socket.bind (addr, backlog);
  135           bound = true;
  136       }
  137   
  138       public void start () {
  139           if (!bound || started || finished) {
  140               throw new IllegalStateException ("server in wrong state");
  141           }
  142           if (executor == null) {
  143               executor = new DefaultExecutor();
  144           }
  145           Thread t = new Thread (dispatcher);
  146           started = true;
  147           t.start();
  148       }
  149   
  150       public void setExecutor (Executor executor) {
  151           if (started) {
  152               throw new IllegalStateException ("server already started");
  153           }
  154           this.executor = executor;
  155       }
  156   
  157       private static class DefaultExecutor implements Executor {
  158           public void execute (Runnable task) {
  159               task.run();
  160           }
  161       }
  162   
  163       public Executor getExecutor () {
  164           return executor;
  165       }
  166   
  167       public void setHttpsConfigurator (HttpsConfigurator config) {
  168           if (config == null) {
  169               throw new NullPointerException ("null HttpsConfigurator");
  170           }
  171           if (started) {
  172               throw new IllegalStateException ("server already started");
  173           }
  174           this.httpsConfig = config;
  175           sslContext = config.getSSLContext();
  176       }
  177   
  178       public HttpsConfigurator getHttpsConfigurator () {
  179           return httpsConfig;
  180       }
  181   
  182       public void stop (int delay) {
  183           if (delay < 0) {
  184               throw new IllegalArgumentException ("negative delay parameter");
  185           }
  186           terminating = true;
  187           try { schan.close(); } catch (IOException e) {}
  188           selector.wakeup();
  189           long latest = System.currentTimeMillis() + delay * 1000;
  190           while (System.currentTimeMillis() < latest) {
  191               delay();
  192               if (finished) {
  193                   break;
  194               }
  195           }
  196           finished = true;
  197           selector.wakeup();
  198           synchronized (allConnections) {
  199               for (HttpConnection c : allConnections) {
  200                   c.close();
  201               }
  202           }
  203           allConnections.clear();
  204           idleConnections.clear();
  205           timer.cancel();
  206           if (timer1Enabled) {
  207               timer1.cancel();
  208           }
  209       }
  210   
  211       Dispatcher dispatcher;
  212   
  213       public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
  214           if (handler == null || path == null) {
  215               throw new NullPointerException ("null handler, or path parameter");
  216           }
  217           HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
  218           contexts.add (context);
  219           logger.config ("context created: " + path);
  220           return context;
  221       }
  222   
  223       public synchronized HttpContextImpl createContext (String path) {
  224           if (path == null) {
  225               throw new NullPointerException ("null path parameter");
  226           }
  227           HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
  228           contexts.add (context);
  229           logger.config ("context created: " + path);
  230           return context;
  231       }
  232   
  233       public synchronized void removeContext (String path) throws IllegalArgumentException {
  234           if (path == null) {
  235               throw new NullPointerException ("null path parameter");
  236           }
  237           contexts.remove (protocol, path);
  238           logger.config ("context removed: " + path);
  239       }
  240   
  241       public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {
  242           if (!(context instanceof HttpContextImpl)) {
  243               throw new IllegalArgumentException ("wrong HttpContext type");
  244           }
  245           contexts.remove ((HttpContextImpl)context);
  246           logger.config ("context removed: " + context.getPath());
  247       }
  248   
  249       public InetSocketAddress getAddress() {
  250           return (InetSocketAddress)schan.socket().getLocalSocketAddress();
  251       }
  252   
  253       Selector getSelector () {
  254           return selector;
  255       }
  256   
  257       void addEvent (Event r) {
  258           synchronized (lolock) {
  259               events.add (r);
  260               selector.wakeup();
  261           }
  262       }
  263   
  264       /* main server listener task */
  265   
  266       class Dispatcher implements Runnable {
  267   
  268           private void handleEvent (Event r) {
  269               ExchangeImpl t = r.exchange;
  270               HttpConnection c = t.getConnection();
  271               try {
  272                   if (r instanceof WriteFinishedEvent) {
  273   
  274                       int exchanges = endExchange();
  275                       if (terminating && exchanges == 0) {
  276                           finished = true;
  277                       }
  278                       responseCompleted (c);
  279                       LeftOverInputStream is = t.getOriginalInputStream();
  280                       if (!is.isEOF()) {
  281                           t.close = true;
  282                       }
  283                       if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {
  284                           c.close();
  285                           allConnections.remove (c);
  286                       } else {
  287                           if (is.isDataBuffered()) {
  288                               /* don't re-enable the interestops, just handle it */
  289                               requestStarted (c);
  290                               handle (c.getChannel(), c);
  291                           } else {
  292                               connsToRegister.add (c);
  293                           }
  294                       }
  295                   }
  296               } catch (IOException e) {
  297                   logger.log (
  298                       Level.FINER, "Dispatcher (1)", e
  299                   );
  300                   c.close();
  301               }
  302           }
  303   
  304           final LinkedList<HttpConnection> connsToRegister =
  305                   new LinkedList<HttpConnection>();
  306   
  307           void reRegister (HttpConnection c) {
  308               /* re-register with selector */
  309               try {
  310                   SocketChannel chan = c.getChannel();
  311                   chan.configureBlocking (false);
  312                   SelectionKey key = chan.register (selector, SelectionKey.OP_READ);
  313                   key.attach (c);
  314                   c.selectionKey = key;
  315                   c.time = getTime() + IDLE_INTERVAL;
  316                   idleConnections.add (c);
  317               } catch (IOException e) {
  318                   dprint(e);
  319                   logger.log(Level.FINER, "Dispatcher(8)", e);
  320                   c.close();
  321               }
  322           }
  323   
  324           public void run() {
  325               while (!finished) {
  326                   try {
  327                       ListIterator<HttpConnection> li =
  328                           connsToRegister.listIterator();
  329                       for (HttpConnection c : connsToRegister) {
  330                           reRegister(c);
  331                       }
  332                       connsToRegister.clear();
  333   
  334                       List<Event> list = null;
  335                       selector.select(1000);
  336                       synchronized (lolock) {
  337                           if (events.size() > 0) {
  338                               list = events;
  339                               events = new LinkedList<Event>();
  340                           }
  341                       }
  342   
  343                       if (list != null) {
  344                           for (Event r: list) {
  345                               handleEvent (r);
  346                           }
  347                       }
  348   
  349                       /* process the selected list now  */
  350   
  351                       Set<SelectionKey> selected = selector.selectedKeys();
  352                       Iterator<SelectionKey> iter = selected.iterator();
  353                       while (iter.hasNext()) {
  354                           SelectionKey key = iter.next();
  355                           iter.remove ();
  356                           if (key.equals (listenerKey)) {
  357                               if (terminating) {
  358                                   continue;
  359                               }
  360                               SocketChannel chan = schan.accept();
  361                               if (chan == null) {
  362                                   continue; /* cancel something ? */
  363                               }
  364                               chan.configureBlocking (false);
  365                               SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ);
  366                               HttpConnection c = new HttpConnection ();
  367                               c.selectionKey = newkey;
  368                               c.setChannel (chan);
  369                               newkey.attach (c);
  370                               requestStarted (c);
  371                               allConnections.add (c);
  372                           } else {
  373                               try {
  374                                   if (key.isReadable()) {
  375                                       boolean closed;
  376                                       SocketChannel chan = (SocketChannel)key.channel();
  377                                       HttpConnection conn = (HttpConnection)key.attachment();
  378   
  379                                       key.cancel();
  380                                       chan.configureBlocking (true);
  381                                       if (idleConnections.remove(conn)) {
  382                                           // was an idle connection so add it
  383                                           // to reqConnections set.
  384                                           requestStarted (conn);
  385                                       }
  386                                       handle (chan, conn);
  387                                   } else {
  388                                       assert false;
  389                                   }
  390                               } catch (CancelledKeyException e) {
  391                                   handleException(key, null);
  392                               } catch (IOException e) {
  393                                   handleException(key, e);
  394                               }
  395                           }
  396                       }
  397                       // call the selector just to process the cancelled keys
  398                       selector.selectNow();
  399                   } catch (IOException e) {
  400                       logger.log (Level.FINER, "Dispatcher (4)", e);
  401                   } catch (Exception e) {
  402                       e.printStackTrace();
  403                       logger.log (Level.FINER, "Dispatcher (7)", e);
  404                   }
  405               }
  406           }
  407   
  408           private void handleException (SelectionKey key, Exception e) {
  409               HttpConnection conn = (HttpConnection)key.attachment();
  410               if (e != null) {
  411                   logger.log (Level.FINER, "Dispatcher (2)", e);
  412               }
  413               closeConnection(conn);
  414           }
  415   
  416           public void handle (SocketChannel chan, HttpConnection conn)
  417           throws IOException
  418           {
  419               try {
  420                   Exchange t = new Exchange (chan, protocol, conn);
  421                   executor.execute (t);
  422               } catch (HttpError e1) {
  423                   logger.log (Level.FINER, "Dispatcher (4)", e1);
  424                   closeConnection(conn);
  425               } catch (IOException e) {
  426                   logger.log (Level.FINER, "Dispatcher (5)", e);
  427                   closeConnection(conn);
  428               }
  429           }
  430       }
  431   
  432       static boolean debug = ServerConfig.debugEnabled ();
  433   
  434       static synchronized void dprint (String s) {
  435           if (debug) {
  436               System.out.println (s);
  437           }
  438       }
  439   
  440       static synchronized void dprint (Exception e) {
  441           if (debug) {
  442               System.out.println (e);
  443               e.printStackTrace();
  444           }
  445       }
  446   
  447       Logger getLogger () {
  448           return logger;
  449       }
  450   
  451       private void closeConnection(HttpConnection conn) {
  452           conn.close();
  453           allConnections.remove(conn);
  454           switch (conn.getState()) {
  455           case REQUEST:
  456               reqConnections.remove(conn);
  457               break;
  458           case RESPONSE:
  459               rspConnections.remove(conn);
  460               break;
  461           case IDLE:
  462               idleConnections.remove(conn);
  463               break;
  464           }
  465           assert !reqConnections.remove(conn);
  466           assert !rspConnections.remove(conn);
  467           assert !idleConnections.remove(conn);
  468       }
  469   
  470           /* per exchange task */
  471   
  472       class Exchange implements Runnable {
  473           SocketChannel chan;
  474           HttpConnection connection;
  475           HttpContextImpl context;
  476           InputStream rawin;
  477           OutputStream rawout;
  478           String protocol;
  479           ExchangeImpl tx;
  480           HttpContextImpl ctx;
  481           boolean rejected = false;
  482   
  483           Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {
  484               this.chan = chan;
  485               this.connection = conn;
  486               this.protocol = protocol;
  487           }
  488   
  489           public void run () {
  490               /* context will be null for new connections */
  491               context = connection.getHttpContext();
  492               boolean newconnection;
  493               SSLEngine engine = null;
  494               String requestLine = null;
  495               SSLStreams sslStreams = null;
  496               try {
  497                   if (context != null ) {
  498                       this.rawin = connection.getInputStream();
  499                       this.rawout = connection.getRawOutputStream();
  500                       newconnection = false;
  501                   } else {
  502                       /* figure out what kind of connection this is */
  503                       newconnection = true;
  504                       if (https) {
  505                           if (sslContext == null) {
  506                               logger.warning ("SSL connection received. No https contxt created");
  507                               throw new HttpError ("No SSL context established");
  508                           }
  509                           sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan);
  510                           rawin = sslStreams.getInputStream();
  511                           rawout = sslStreams.getOutputStream();
  512                           engine = sslStreams.getSSLEngine();
  513                           connection.sslStreams = sslStreams;
  514                       } else {
  515                           rawin = new BufferedInputStream(
  516                               new Request.ReadStream (
  517                                   ServerImpl.this, chan
  518                           ));
  519                           rawout = new Request.WriteStream (
  520                               ServerImpl.this, chan
  521                           );
  522                       }
  523                       connection.raw = rawin;
  524                       connection.rawout = rawout;
  525                   }
  526                   Request req = new Request (rawin, rawout);
  527                   requestLine = req.requestLine();
  528                   if (requestLine == null) {
  529                       /* connection closed */
  530                       closeConnection(connection);
  531                       return;
  532                   }
  533                   int space = requestLine.indexOf (' ');
  534                   if (space == -1) {
  535                       reject (Code.HTTP_BAD_REQUEST,
  536                               requestLine, "Bad request line");
  537                       return;
  538                   }
  539                   String method = requestLine.substring (0, space);
  540                   int start = space+1;
  541                   space = requestLine.indexOf(' ', start);
  542                   if (space == -1) {
  543                       reject (Code.HTTP_BAD_REQUEST,
  544                               requestLine, "Bad request line");
  545                       return;
  546                   }
  547                   String uriStr = requestLine.substring (start, space);
  548                   URI uri = new URI (uriStr);
  549                   start = space+1;
  550                   String version = requestLine.substring (start);
  551                   Headers headers = req.headers();
  552                   String s = headers.getFirst ("Transfer-encoding");
  553                   long clen = 0L;
  554                   if (s !=null && s.equalsIgnoreCase ("chunked")) {
  555                       clen = -1L;
  556                   } else {
  557                       s = headers.getFirst ("Content-Length");
  558                       if (s != null) {
  559                           clen = Long.parseLong(s);
  560                       }
  561                       if (clen == 0) {
  562                           requestCompleted (connection);
  563                       }
  564                   }
  565                   ctx = contexts.findContext (protocol, uri.getPath());
  566                   if (ctx == null) {
  567                       reject (Code.HTTP_NOT_FOUND,
  568                               requestLine, "No context found for request");
  569                       return;
  570                   }
  571                   connection.setContext (ctx);
  572                   if (ctx.getHandler() == null) {
  573                       reject (Code.HTTP_INTERNAL_ERROR,
  574                               requestLine, "No handler for context");
  575                       return;
  576                   }
  577                   tx = new ExchangeImpl (
  578                       method, uri, req, clen, connection
  579                   );
  580                   String chdr = headers.getFirst("Connection");
  581                   Headers rheaders = tx.getResponseHeaders();
  582   
  583                   if (chdr != null && chdr.equalsIgnoreCase ("close")) {
  584                       tx.close = true;
  585                   }
  586                   if (version.equalsIgnoreCase ("http/1.0")) {
  587                       tx.http10 = true;
  588                       if (chdr == null) {
  589                           tx.close = true;
  590                           rheaders.set ("Connection", "close");
  591                       } else if (chdr.equalsIgnoreCase ("keep-alive")) {
  592                           rheaders.set ("Connection", "keep-alive");
  593                           int idle=(int)ServerConfig.getIdleInterval()/1000;
  594                           int max=(int)ServerConfig.getMaxIdleConnections();
  595                           String val = "timeout="+idle+", max="+max;
  596                           rheaders.set ("Keep-Alive", val);
  597                       }
  598                   }
  599   
  600                   if (newconnection) {
  601                       connection.setParameters (
  602                           rawin, rawout, chan, engine, sslStreams,
  603                           sslContext, protocol, ctx, rawin
  604                       );
  605                   }
  606                   /* check if client sent an Expect 100 Continue.
  607                    * In that case, need to send an interim response.
  608                    * In future API may be modified to allow app to
  609                    * be involved in this process.
  610                    */
  611                   String exp = headers.getFirst("Expect");
  612                   if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
  613                       logReply (100, requestLine, null);
  614                       sendReply (
  615                           Code.HTTP_CONTINUE, false, null
  616                       );
  617                   }
  618                   /* uf is the list of filters seen/set by the user.
  619                    * sf is the list of filters established internally
  620                    * and which are not visible to the user. uc and sc
  621                    * are the corresponding Filter.Chains.
  622                    * They are linked together by a LinkHandler
  623                    * so that they can both be invoked in one call.
  624                    */
  625                   List<Filter> sf = ctx.getSystemFilters();
  626                   List<Filter> uf = ctx.getFilters();
  627   
  628                   Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
  629                   Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc));
  630   
  631                   /* set up the two stream references */
  632                   tx.getRequestBody();
  633                   tx.getResponseBody();
  634                   if (https) {
  635                       uc.doFilter (new HttpsExchangeImpl (tx));
  636                   } else {
  637                       uc.doFilter (new HttpExchangeImpl (tx));
  638                   }
  639   
  640               } catch (IOException e1) {
  641                   logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1);
  642                   closeConnection(connection);
  643               } catch (NumberFormatException e3) {
  644                   reject (Code.HTTP_BAD_REQUEST,
  645                           requestLine, "NumberFormatException thrown");
  646               } catch (URISyntaxException e) {
  647                   reject (Code.HTTP_BAD_REQUEST,
  648                           requestLine, "URISyntaxException thrown");
  649               } catch (Exception e4) {
  650                   logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4);
  651                   closeConnection(connection);
  652               }
  653           }
  654   
  655           /* used to link to 2 or more Filter.Chains together */
  656   
  657           class LinkHandler implements HttpHandler {
  658               Filter.Chain nextChain;
  659   
  660               LinkHandler (Filter.Chain nextChain) {
  661                   this.nextChain = nextChain;
  662               }
  663   
  664               public void handle (HttpExchange exchange) throws IOException {
  665                   nextChain.doFilter (exchange);
  666               }
  667           }
  668   
  669           void reject (int code, String requestStr, String message) {
  670               rejected = true;
  671               logReply (code, requestStr, message);
  672               sendReply (
  673                   code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
  674               );
  675               closeConnection(connection);
  676           }
  677   
  678           void sendReply (
  679               int code, boolean closeNow, String text)
  680           {
  681               try {
  682                   StringBuilder builder = new StringBuilder (512);
  683                   builder.append ("HTTP/1.1 ")
  684                       .append (code).append (Code.msg(code)).append ("\r\n");
  685   
  686                   if (text != null && text.length() != 0) {
  687                       builder.append ("Content-Length: ")
  688                           .append (text.length()).append ("\r\n")
  689                           .append ("Content-Type: text/html\r\n");
  690                   } else {
  691                       builder.append ("Content-Length: 0\r\n");
  692                       text = "";
  693                   }
  694                   if (closeNow) {
  695                       builder.append ("Connection: close\r\n");
  696                   }
  697                   builder.append ("\r\n").append (text);
  698                   String s = builder.toString();
  699                   byte[] b = s.getBytes("ISO8859_1");
  700                   rawout.write (b);
  701                   rawout.flush();
  702                   if (closeNow) {
  703                       closeConnection(connection);
  704                   }
  705               } catch (IOException e) {
  706                   logger.log (Level.FINER, "ServerImpl.sendReply", e);
  707                   closeConnection(connection);
  708               }
  709           }
  710   
  711       }
  712   
  713       void logReply (int code, String requestStr, String text) {
  714           if (!logger.isLoggable(Level.FINE)) {
  715               return;
  716           }
  717           if (text == null) {
  718               text = "";
  719           }
  720           String r;
  721           if (requestStr.length() > 80) {
  722              r = requestStr.substring (0, 80) + "<TRUNCATED>";
  723           } else {
  724              r = requestStr;
  725           }
  726           String message = r + " [" + code + " " +
  727                       Code.msg(code) + "] ("+text+")";
  728           logger.fine (message);
  729       }
  730   
  731       long getTicks() {
  732           return ticks;
  733       }
  734   
  735       public long getTime() {
  736           return time;
  737       }
  738   
  739       void delay () {
  740           Thread.yield();
  741           try {
  742               Thread.sleep (200);
  743           } catch (InterruptedException e) {}
  744       }
  745   
  746       private int exchangeCount = 0;
  747   
  748       synchronized void startExchange () {
  749           exchangeCount ++;
  750       }
  751   
  752       synchronized int endExchange () {
  753           exchangeCount --;
  754           assert exchangeCount >= 0;
  755           return exchangeCount;
  756       }
  757   
  758       HttpServer getWrapper () {
  759           return wrapper;
  760       }
  761   
  762       void requestStarted (HttpConnection c) {
  763           c.creationTime = getTime();
  764           c.setState (State.REQUEST);
  765           reqConnections.add (c);
  766       }
  767   
  768       // called after a request has been completely read
  769       // by the server. This stops the timer which would
  770       // close the connection if the request doesn't arrive
  771       // quickly enough. It then starts the timer
  772       // that ensures the client reads the response in a timely
  773       // fashion.
  774   
  775       void requestCompleted (HttpConnection c) {
  776           assert c.getState() == State.REQUEST;
  777           reqConnections.remove (c);
  778           c.rspStartedTime = getTime();
  779           rspConnections.add (c);
  780           c.setState (State.RESPONSE);
  781       }
  782   
  783       // called after response has been sent
  784       void responseCompleted (HttpConnection c) {
  785           assert c.getState() == State.RESPONSE;
  786           rspConnections.remove (c);
  787           c.setState (State.IDLE);
  788       }
  789   
  790       /**
  791        * TimerTask run every CLOCK_TICK ms
  792        */
  793       class ServerTimerTask extends TimerTask {
  794           public void run () {
  795               LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
  796               time = System.currentTimeMillis();
  797               ticks ++;
  798               synchronized (idleConnections) {
  799                   for (HttpConnection c : idleConnections) {
  800                       if (c.time <= time) {
  801                           toClose.add (c);
  802                       }
  803                   }
  804                   for (HttpConnection c : toClose) {
  805                       idleConnections.remove (c);
  806                       allConnections.remove (c);
  807                       c.close();
  808                   }
  809               }
  810           }
  811       }
  812   
  813       class ServerTimerTask1 extends TimerTask {
  814   
  815           // runs every TIMER_MILLIS
  816           public void run () {
  817               LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
  818               time = System.currentTimeMillis();
  819               synchronized (reqConnections) {
  820                   if (MAX_REQ_TIME != -1) {
  821                       for (HttpConnection c : reqConnections) {
  822                           if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
  823                               toClose.add (c);
  824                           }
  825                       }
  826                       for (HttpConnection c : toClose) {
  827                           logger.log (Level.FINE, "closing: no request: " + c);
  828                           reqConnections.remove (c);
  829                           allConnections.remove (c);
  830                           c.close();
  831                       }
  832                   }
  833               }
  834               toClose = new LinkedList<HttpConnection>();
  835               synchronized (rspConnections) {
  836                   if (MAX_RSP_TIME != -1) {
  837                       for (HttpConnection c : rspConnections) {
  838                           if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
  839                               toClose.add (c);
  840                           }
  841                       }
  842                       for (HttpConnection c : toClose) {
  843                           logger.log (Level.FINE, "closing: no response: " + c);
  844                           rspConnections.remove (c);
  845                           allConnections.remove (c);
  846                           c.close();
  847                       }
  848                   }
  849               }
  850           }
  851       }
  852   
  853       void logStackTrace (String s) {
  854           logger.finest (s);
  855           StringBuilder b = new StringBuilder ();
  856           StackTraceElement[] e = Thread.currentThread().getStackTrace();
  857           for (int i=0; i<e.length; i++) {
  858               b.append (e[i].toString()).append("\n");
  859           }
  860           logger.finest (b.toString());
  861       }
  862   
  863       static long getTimeMillis(long secs) {
  864           if (secs == -1) {
  865               return -1;
  866           } else {
  867               return secs * 1000;
  868           }
  869       }
  870   }

Save This Page
Home » openjdk-7 » sun » net » httpserver » [javadoc | source]