History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/httpserver/ServerImpl.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/httpserver/ServerImpl.java+870−0
@@ -0,0 +1,870 @@
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+}