Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
41159 views
1
/*
2
* Copyright (c) 2005, 2021, 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.channels.*;
31
import java.util.*;
32
import java.util.concurrent.*;
33
import java.lang.System.Logger;
34
import java.lang.System.Logger.Level;
35
import javax.net.ssl.*;
36
import com.sun.net.httpserver.*;
37
import java.security.AccessController;
38
import java.security.PrivilegedAction;
39
import sun.net.httpserver.HttpConnection.State;
40
41
/**
42
* Provides implementation for both HTTP and HTTPS
43
*/
44
class ServerImpl implements TimeSource {
45
46
private String protocol;
47
private boolean https;
48
private Executor executor;
49
private HttpsConfigurator httpsConfig;
50
private SSLContext sslContext;
51
private ContextList contexts;
52
private InetSocketAddress address;
53
private ServerSocketChannel schan;
54
private Selector selector;
55
private SelectionKey listenerKey;
56
private Set<HttpConnection> idleConnections;
57
private Set<HttpConnection> allConnections;
58
/* following two are used to keep track of the times
59
* when a connection/request is first received
60
* and when we start to send the response
61
*/
62
private Set<HttpConnection> reqConnections;
63
private Set<HttpConnection> rspConnections;
64
private List<Event> events;
65
private Object lolock = new Object();
66
private volatile boolean finished = false;
67
private volatile boolean terminating = false;
68
private boolean bound = false;
69
private boolean started = false;
70
private volatile long time; /* current time */
71
private volatile long subticks = 0;
72
private volatile long ticks; /* number of clock ticks since server started */
73
private HttpServer wrapper;
74
75
final static int CLOCK_TICK = ServerConfig.getClockTick();
76
final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
77
final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
78
final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
79
final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());
80
final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
81
final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
82
83
private Timer timer, timer1;
84
private final Logger logger;
85
private Thread dispatcherThread;
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 = System.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.log (Level.DEBUG, "HttpServer timer1 enabled period in ms: ", TIMER_MILLIS);
119
logger.log (Level.DEBUG, "MAX_REQ_TIME: "+MAX_REQ_TIME);
120
logger.log (Level.DEBUG, "MAX_RSP_TIME: "+MAX_RSP_TIME);
121
}
122
events = new LinkedList<Event>();
123
logger.log (Level.DEBUG, "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
dispatcherThread = new Thread(null, dispatcher, "HTTP-Dispatcher", 0, false);
146
started = true;
147
dispatcherThread.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 final boolean isFinishing() {
183
return finished;
184
}
185
186
public void stop (int delay) {
187
if (delay < 0) {
188
throw new IllegalArgumentException ("negative delay parameter");
189
}
190
terminating = true;
191
try { schan.close(); } catch (IOException e) {}
192
selector.wakeup();
193
long latest = System.currentTimeMillis() + delay * 1000;
194
while (System.currentTimeMillis() < latest) {
195
delay();
196
if (finished) {
197
break;
198
}
199
}
200
finished = true;
201
selector.wakeup();
202
synchronized (allConnections) {
203
for (HttpConnection c : allConnections) {
204
c.close();
205
}
206
}
207
allConnections.clear();
208
idleConnections.clear();
209
timer.cancel();
210
if (timer1Enabled) {
211
timer1.cancel();
212
}
213
if (dispatcherThread != null && dispatcherThread != Thread.currentThread()) {
214
try {
215
dispatcherThread.join();
216
} catch (InterruptedException e) {
217
Thread.currentThread().interrupt();
218
logger.log (Level.TRACE, "ServerImpl.stop: ", e);
219
}
220
}
221
}
222
223
Dispatcher dispatcher;
224
225
public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
226
if (handler == null || path == null) {
227
throw new NullPointerException ("null handler, or path parameter");
228
}
229
HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
230
contexts.add (context);
231
logger.log (Level.DEBUG, "context created: " + path);
232
return context;
233
}
234
235
public synchronized HttpContextImpl createContext (String path) {
236
if (path == null) {
237
throw new NullPointerException ("null path parameter");
238
}
239
HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
240
contexts.add (context);
241
logger.log (Level.DEBUG, "context created: " + path);
242
return context;
243
}
244
245
public synchronized void removeContext (String path) throws IllegalArgumentException {
246
if (path == null) {
247
throw new NullPointerException ("null path parameter");
248
}
249
contexts.remove (protocol, path);
250
logger.log (Level.DEBUG, "context removed: " + path);
251
}
252
253
public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {
254
if (!(context instanceof HttpContextImpl)) {
255
throw new IllegalArgumentException ("wrong HttpContext type");
256
}
257
contexts.remove ((HttpContextImpl)context);
258
logger.log (Level.DEBUG, "context removed: " + context.getPath());
259
}
260
261
@SuppressWarnings("removal")
262
public InetSocketAddress getAddress() {
263
return AccessController.doPrivileged(
264
new PrivilegedAction<InetSocketAddress>() {
265
public InetSocketAddress run() {
266
return
267
(InetSocketAddress)schan.socket()
268
.getLocalSocketAddress();
269
}
270
});
271
}
272
273
Selector getSelector () {
274
return selector;
275
}
276
277
void addEvent (Event r) {
278
synchronized (lolock) {
279
events.add (r);
280
selector.wakeup();
281
}
282
}
283
284
/* main server listener task */
285
286
class Dispatcher implements Runnable {
287
288
private void handleEvent (Event r) {
289
ExchangeImpl t = r.exchange;
290
HttpConnection c = t.getConnection();
291
try {
292
if (r instanceof WriteFinishedEvent) {
293
294
logger.log(Level.TRACE, "Write Finished");
295
int exchanges = endExchange();
296
if (terminating && exchanges == 0) {
297
finished = true;
298
}
299
LeftOverInputStream is = t.getOriginalInputStream();
300
if (!is.isEOF()) {
301
t.close = true;
302
if (c.getState() == State.REQUEST) {
303
requestCompleted(c);
304
}
305
}
306
responseCompleted (c);
307
if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {
308
c.close();
309
allConnections.remove (c);
310
} else {
311
if (is.isDataBuffered()) {
312
/* don't re-enable the interestops, just handle it */
313
requestStarted (c);
314
handle (c.getChannel(), c);
315
} else {
316
connsToRegister.add (c);
317
}
318
}
319
}
320
} catch (IOException e) {
321
logger.log (
322
Level.TRACE, "Dispatcher (1)", e
323
);
324
c.close();
325
}
326
}
327
328
final LinkedList<HttpConnection> connsToRegister =
329
new LinkedList<HttpConnection>();
330
331
void reRegister (HttpConnection c) {
332
/* re-register with selector */
333
try {
334
SocketChannel chan = c.getChannel();
335
chan.configureBlocking (false);
336
SelectionKey key = chan.register (selector, SelectionKey.OP_READ);
337
key.attach (c);
338
c.selectionKey = key;
339
c.time = getTime() + IDLE_INTERVAL;
340
idleConnections.add (c);
341
} catch (IOException e) {
342
dprint(e);
343
logger.log (Level.TRACE, "Dispatcher(8)", e);
344
c.close();
345
}
346
}
347
348
public void run() {
349
while (!finished) {
350
try {
351
List<Event> list = null;
352
synchronized (lolock) {
353
if (events.size() > 0) {
354
list = events;
355
events = new LinkedList<Event>();
356
}
357
}
358
359
if (list != null) {
360
for (Event r: list) {
361
handleEvent (r);
362
}
363
}
364
365
for (HttpConnection c : connsToRegister) {
366
reRegister(c);
367
}
368
connsToRegister.clear();
369
370
selector.select(1000);
371
372
/* process the selected list now */
373
Set<SelectionKey> selected = selector.selectedKeys();
374
Iterator<SelectionKey> iter = selected.iterator();
375
while (iter.hasNext()) {
376
SelectionKey key = iter.next();
377
iter.remove ();
378
if (key.equals (listenerKey)) {
379
if (terminating) {
380
continue;
381
}
382
SocketChannel chan = schan.accept();
383
384
// optimist there's a channel
385
if (chan != null) {
386
// Set TCP_NODELAY, if appropriate
387
if (ServerConfig.noDelay()) {
388
chan.socket().setTcpNoDelay(true);
389
}
390
chan.configureBlocking (false);
391
SelectionKey newkey =
392
chan.register (selector, SelectionKey.OP_READ);
393
HttpConnection c = new HttpConnection ();
394
c.selectionKey = newkey;
395
c.setChannel (chan);
396
newkey.attach (c);
397
requestStarted (c);
398
allConnections.add (c);
399
}
400
} else {
401
try {
402
if (key.isReadable()) {
403
SocketChannel chan = (SocketChannel)key.channel();
404
HttpConnection conn = (HttpConnection)key.attachment();
405
406
key.cancel();
407
chan.configureBlocking (true);
408
if (idleConnections.remove(conn)) {
409
// was an idle connection so add it
410
// to reqConnections set.
411
requestStarted (conn);
412
}
413
handle (chan, conn);
414
} else {
415
assert false : "Unexpected non-readable key:" + key;
416
}
417
} catch (CancelledKeyException e) {
418
handleException(key, null);
419
} catch (IOException e) {
420
handleException(key, e);
421
}
422
}
423
}
424
// call the selector just to process the cancelled keys
425
selector.selectNow();
426
} catch (IOException e) {
427
logger.log (Level.TRACE, "Dispatcher (4)", e);
428
} catch (Exception e) {
429
logger.log (Level.TRACE, "Dispatcher (7)", e);
430
}
431
}
432
try {selector.close(); } catch (Exception e) {}
433
}
434
435
private void handleException (SelectionKey key, Exception e) {
436
HttpConnection conn = (HttpConnection)key.attachment();
437
if (e != null) {
438
logger.log (Level.TRACE, "Dispatcher (2)", e);
439
}
440
closeConnection(conn);
441
}
442
443
public void handle (SocketChannel chan, HttpConnection conn)
444
{
445
try {
446
Exchange t = new Exchange (chan, protocol, conn);
447
executor.execute (t);
448
} catch (HttpError e1) {
449
logger.log (Level.TRACE, "Dispatcher (4)", e1);
450
closeConnection(conn);
451
} catch (IOException e) {
452
logger.log (Level.TRACE, "Dispatcher (5)", e);
453
closeConnection(conn);
454
} catch (Throwable e) {
455
logger.log (Level.TRACE, "Dispatcher (6)", e);
456
closeConnection(conn);
457
}
458
}
459
}
460
461
static boolean debug = ServerConfig.debugEnabled ();
462
463
static synchronized void dprint (String s) {
464
if (debug) {
465
System.out.println (s);
466
}
467
}
468
469
static synchronized void dprint (Exception e) {
470
if (debug) {
471
System.out.println (e);
472
e.printStackTrace();
473
}
474
}
475
476
Logger getLogger () {
477
return logger;
478
}
479
480
private void closeConnection(HttpConnection conn) {
481
conn.close();
482
allConnections.remove(conn);
483
switch (conn.getState()) {
484
case REQUEST:
485
reqConnections.remove(conn);
486
break;
487
case RESPONSE:
488
rspConnections.remove(conn);
489
break;
490
case IDLE:
491
idleConnections.remove(conn);
492
break;
493
}
494
assert !reqConnections.remove(conn);
495
assert !rspConnections.remove(conn);
496
assert !idleConnections.remove(conn);
497
}
498
499
/* per exchange task */
500
501
class Exchange implements Runnable {
502
SocketChannel chan;
503
HttpConnection connection;
504
HttpContextImpl context;
505
InputStream rawin;
506
OutputStream rawout;
507
String protocol;
508
ExchangeImpl tx;
509
HttpContextImpl ctx;
510
boolean rejected = false;
511
512
Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {
513
this.chan = chan;
514
this.connection = conn;
515
this.protocol = protocol;
516
}
517
518
public void run () {
519
/* context will be null for new connections */
520
logger.log(Level.TRACE, "exchange started");
521
context = connection.getHttpContext();
522
boolean newconnection;
523
SSLEngine engine = null;
524
String requestLine = null;
525
SSLStreams sslStreams = null;
526
try {
527
if (context != null ) {
528
this.rawin = connection.getInputStream();
529
this.rawout = connection.getRawOutputStream();
530
newconnection = false;
531
} else {
532
/* figure out what kind of connection this is */
533
newconnection = true;
534
if (https) {
535
if (sslContext == null) {
536
logger.log (Level.WARNING,
537
"SSL connection received. No https context created");
538
throw new HttpError ("No SSL context established");
539
}
540
sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan);
541
rawin = sslStreams.getInputStream();
542
rawout = sslStreams.getOutputStream();
543
engine = sslStreams.getSSLEngine();
544
connection.sslStreams = sslStreams;
545
} else {
546
rawin = new BufferedInputStream(
547
new Request.ReadStream (
548
ServerImpl.this, chan
549
));
550
rawout = new Request.WriteStream (
551
ServerImpl.this, chan
552
);
553
}
554
connection.raw = rawin;
555
connection.rawout = rawout;
556
}
557
Request req = new Request (rawin, rawout);
558
requestLine = req.requestLine();
559
if (requestLine == null) {
560
/* connection closed */
561
logger.log(Level.DEBUG, "no request line: closing");
562
closeConnection(connection);
563
return;
564
}
565
logger.log(Level.DEBUG, "Exchange request line: {0}", requestLine);
566
int space = requestLine.indexOf (' ');
567
if (space == -1) {
568
reject (Code.HTTP_BAD_REQUEST,
569
requestLine, "Bad request line");
570
return;
571
}
572
String method = requestLine.substring (0, space);
573
int start = space+1;
574
space = requestLine.indexOf(' ', start);
575
if (space == -1) {
576
reject (Code.HTTP_BAD_REQUEST,
577
requestLine, "Bad request line");
578
return;
579
}
580
String uriStr = requestLine.substring (start, space);
581
URI uri = new URI (uriStr);
582
start = space+1;
583
String version = requestLine.substring (start);
584
Headers headers = req.headers();
585
String s = headers.getFirst ("Transfer-encoding");
586
long clen = 0L;
587
if (s !=null && s.equalsIgnoreCase ("chunked")) {
588
clen = -1L;
589
} else {
590
s = headers.getFirst ("Content-Length");
591
if (s != null) {
592
clen = Long.parseLong(s);
593
}
594
if (clen == 0) {
595
requestCompleted (connection);
596
}
597
}
598
ctx = contexts.findContext (protocol, uri.getPath());
599
if (ctx == null) {
600
reject (Code.HTTP_NOT_FOUND,
601
requestLine, "No context found for request");
602
return;
603
}
604
connection.setContext (ctx);
605
if (ctx.getHandler() == null) {
606
reject (Code.HTTP_INTERNAL_ERROR,
607
requestLine, "No handler for context");
608
return;
609
}
610
tx = new ExchangeImpl (
611
method, uri, req, clen, connection
612
);
613
String chdr = headers.getFirst("Connection");
614
Headers rheaders = tx.getResponseHeaders();
615
616
if (chdr != null && chdr.equalsIgnoreCase ("close")) {
617
tx.close = true;
618
}
619
if (version.equalsIgnoreCase ("http/1.0")) {
620
tx.http10 = true;
621
if (chdr == null) {
622
tx.close = true;
623
rheaders.set ("Connection", "close");
624
} else if (chdr.equalsIgnoreCase ("keep-alive")) {
625
rheaders.set ("Connection", "keep-alive");
626
int idle=(int)(ServerConfig.getIdleInterval()/1000);
627
int max=ServerConfig.getMaxIdleConnections();
628
String val = "timeout="+idle+", max="+max;
629
rheaders.set ("Keep-Alive", val);
630
}
631
}
632
633
if (newconnection) {
634
connection.setParameters (
635
rawin, rawout, chan, engine, sslStreams,
636
sslContext, protocol, ctx, rawin
637
);
638
}
639
/* check if client sent an Expect 100 Continue.
640
* In that case, need to send an interim response.
641
* In future API may be modified to allow app to
642
* be involved in this process.
643
*/
644
String exp = headers.getFirst("Expect");
645
if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
646
logReply (100, requestLine, null);
647
sendReply (
648
Code.HTTP_CONTINUE, false, null
649
);
650
}
651
/* uf is the list of filters seen/set by the user.
652
* sf is the list of filters established internally
653
* and which are not visible to the user. uc and sc
654
* are the corresponding Filter.Chains.
655
* They are linked together by a LinkHandler
656
* so that they can both be invoked in one call.
657
*/
658
final List<Filter> sf = ctx.getSystemFilters();
659
final List<Filter> uf = ctx.getFilters();
660
661
final Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
662
final Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc));
663
664
/* set up the two stream references */
665
tx.getRequestBody();
666
tx.getResponseBody();
667
if (https) {
668
uc.doFilter (new HttpsExchangeImpl (tx));
669
} else {
670
uc.doFilter (new HttpExchangeImpl (tx));
671
}
672
673
} catch (IOException e1) {
674
logger.log (Level.TRACE, "ServerImpl.Exchange (1)", e1);
675
closeConnection(connection);
676
} catch (NumberFormatException e2) {
677
logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e2);
678
reject (Code.HTTP_BAD_REQUEST,
679
requestLine, "NumberFormatException thrown");
680
} catch (URISyntaxException e3) {
681
logger.log (Level.TRACE, "ServerImpl.Exchange (3)", e3);
682
reject (Code.HTTP_BAD_REQUEST,
683
requestLine, "URISyntaxException thrown");
684
} catch (Exception e4) {
685
logger.log (Level.TRACE, "ServerImpl.Exchange (4)", e4);
686
closeConnection(connection);
687
} catch (Throwable t) {
688
logger.log(Level.TRACE, "ServerImpl.Exchange (5)", t);
689
throw t;
690
}
691
}
692
693
/* used to link to 2 or more Filter.Chains together */
694
695
class LinkHandler implements HttpHandler {
696
Filter.Chain nextChain;
697
698
LinkHandler (Filter.Chain nextChain) {
699
this.nextChain = nextChain;
700
}
701
702
public void handle (HttpExchange exchange) throws IOException {
703
nextChain.doFilter (exchange);
704
}
705
}
706
707
void reject (int code, String requestStr, String message) {
708
rejected = true;
709
logReply (code, requestStr, message);
710
sendReply (
711
code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
712
);
713
closeConnection(connection);
714
}
715
716
void sendReply (
717
int code, boolean closeNow, String text)
718
{
719
try {
720
StringBuilder builder = new StringBuilder (512);
721
builder.append ("HTTP/1.1 ")
722
.append (code).append (Code.msg(code)).append ("\r\n");
723
724
if (text != null && text.length() != 0) {
725
builder.append ("Content-Length: ")
726
.append (text.length()).append ("\r\n")
727
.append ("Content-Type: text/html\r\n");
728
} else {
729
builder.append ("Content-Length: 0\r\n");
730
text = "";
731
}
732
if (closeNow) {
733
builder.append ("Connection: close\r\n");
734
}
735
builder.append ("\r\n").append (text);
736
String s = builder.toString();
737
byte[] b = s.getBytes("ISO8859_1");
738
rawout.write (b);
739
rawout.flush();
740
if (closeNow) {
741
closeConnection(connection);
742
}
743
} catch (IOException e) {
744
logger.log (Level.TRACE, "ServerImpl.sendReply", e);
745
closeConnection(connection);
746
}
747
}
748
749
}
750
751
void logReply (int code, String requestStr, String text) {
752
if (!logger.isLoggable(Level.DEBUG)) {
753
return;
754
}
755
if (text == null) {
756
text = "";
757
}
758
String r;
759
if (requestStr.length() > 80) {
760
r = requestStr.substring (0, 80) + "<TRUNCATED>";
761
} else {
762
r = requestStr;
763
}
764
String message = r + " [" + code + " " +
765
Code.msg(code) + "] ("+text+")";
766
logger.log (Level.DEBUG, message);
767
}
768
769
long getTicks() {
770
return ticks;
771
}
772
773
public long getTime() {
774
return time;
775
}
776
777
void delay () {
778
Thread.yield();
779
try {
780
Thread.sleep (200);
781
} catch (InterruptedException e) {}
782
}
783
784
private int exchangeCount = 0;
785
786
synchronized void startExchange () {
787
exchangeCount ++;
788
}
789
790
synchronized int endExchange () {
791
exchangeCount --;
792
assert exchangeCount >= 0;
793
return exchangeCount;
794
}
795
796
HttpServer getWrapper () {
797
return wrapper;
798
}
799
800
void requestStarted (HttpConnection c) {
801
c.creationTime = getTime();
802
c.setState (State.REQUEST);
803
reqConnections.add (c);
804
}
805
806
// called after a request has been completely read
807
// by the server. This stops the timer which would
808
// close the connection if the request doesn't arrive
809
// quickly enough. It then starts the timer
810
// that ensures the client reads the response in a timely
811
// fashion.
812
813
void requestCompleted (HttpConnection c) {
814
State s = c.getState();
815
assert s == State.REQUEST : "State is not REQUEST ("+s+")";
816
reqConnections.remove (c);
817
c.rspStartedTime = getTime();
818
rspConnections.add (c);
819
c.setState (State.RESPONSE);
820
}
821
822
// called after response has been sent
823
void responseCompleted (HttpConnection c) {
824
State s = c.getState();
825
assert s == State.RESPONSE : "State is not RESPONSE ("+s+")";
826
rspConnections.remove (c);
827
c.setState (State.IDLE);
828
}
829
830
/**
831
* TimerTask run every CLOCK_TICK ms
832
*/
833
class ServerTimerTask extends TimerTask {
834
public void run () {
835
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
836
time = System.currentTimeMillis();
837
ticks ++;
838
synchronized (idleConnections) {
839
for (HttpConnection c : idleConnections) {
840
if (c.time <= time) {
841
toClose.add (c);
842
}
843
}
844
for (HttpConnection c : toClose) {
845
idleConnections.remove (c);
846
allConnections.remove (c);
847
c.close();
848
}
849
}
850
}
851
}
852
853
class ServerTimerTask1 extends TimerTask {
854
855
// runs every TIMER_MILLIS
856
public void run () {
857
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
858
time = System.currentTimeMillis();
859
synchronized (reqConnections) {
860
if (MAX_REQ_TIME != -1) {
861
for (HttpConnection c : reqConnections) {
862
if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
863
toClose.add (c);
864
}
865
}
866
for (HttpConnection c : toClose) {
867
logger.log (Level.DEBUG, "closing: no request: " + c);
868
reqConnections.remove (c);
869
allConnections.remove (c);
870
c.close();
871
}
872
}
873
}
874
toClose = new LinkedList<HttpConnection>();
875
synchronized (rspConnections) {
876
if (MAX_RSP_TIME != -1) {
877
for (HttpConnection c : rspConnections) {
878
if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
879
toClose.add (c);
880
}
881
}
882
for (HttpConnection c : toClose) {
883
logger.log (Level.DEBUG, "closing: no response: " + c);
884
rspConnections.remove (c);
885
allConnections.remove (c);
886
c.close();
887
}
888
}
889
}
890
}
891
}
892
893
void logStackTrace (String s) {
894
logger.log (Level.TRACE, s);
895
StringBuilder b = new StringBuilder ();
896
StackTraceElement[] e = Thread.currentThread().getStackTrace();
897
for (int i=0; i<e.length; i++) {
898
b.append (e[i].toString()).append("\n");
899
}
900
logger.log (Level.TRACE, b.toString());
901
}
902
903
static long getTimeMillis(long secs) {
904
if (secs == -1) {
905
return -1;
906
} else {
907
return secs * 1000;
908
}
909
}
910
}
911
912