Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
66646 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
/* check key for illegal characters */
586
for (var k : headers.keySet()) {
587
if (!isValidHeaderKey(k)) {
588
reject(Code.HTTP_BAD_REQUEST, requestLine,
589
"Header key contains illegal characters");
590
return;
591
}
592
}
593
/* checks for unsupported combinations of lengths and encodings */
594
if (headers.containsKey("Content-Length") &&
595
(headers.containsKey("Transfer-encoding") || headers.get("Content-Length").size() > 1)) {
596
reject(Code.HTTP_BAD_REQUEST, requestLine,
597
"Conflicting or malformed headers detected");
598
return;
599
}
600
long clen = 0L;
601
String headerValue = null;
602
List<String> teValueList = headers.get("Transfer-encoding");
603
if (teValueList != null && !teValueList.isEmpty()) {
604
headerValue = teValueList.get(0);
605
}
606
if (headerValue != null) {
607
if (headerValue.equalsIgnoreCase("chunked") && teValueList.size() == 1) {
608
clen = -1L;
609
} else {
610
reject(Code.HTTP_NOT_IMPLEMENTED,
611
requestLine, "Unsupported Transfer-Encoding value");
612
return;
613
}
614
} else {
615
headerValue = headers.getFirst("Content-Length");
616
if (headerValue != null) {
617
clen = Long.parseLong(headerValue);
618
if (clen < 0) {
619
reject(Code.HTTP_BAD_REQUEST, requestLine,
620
"Illegal Content-Length value");
621
return;
622
}
623
}
624
if (clen == 0) {
625
requestCompleted(connection);
626
}
627
}
628
ctx = contexts.findContext (protocol, uri.getPath());
629
if (ctx == null) {
630
reject (Code.HTTP_NOT_FOUND,
631
requestLine, "No context found for request");
632
return;
633
}
634
connection.setContext (ctx);
635
if (ctx.getHandler() == null) {
636
reject (Code.HTTP_INTERNAL_ERROR,
637
requestLine, "No handler for context");
638
return;
639
}
640
tx = new ExchangeImpl (
641
method, uri, req, clen, connection
642
);
643
String chdr = headers.getFirst("Connection");
644
Headers rheaders = tx.getResponseHeaders();
645
646
if (chdr != null && chdr.equalsIgnoreCase ("close")) {
647
tx.close = true;
648
}
649
if (version.equalsIgnoreCase ("http/1.0")) {
650
tx.http10 = true;
651
if (chdr == null) {
652
tx.close = true;
653
rheaders.set ("Connection", "close");
654
} else if (chdr.equalsIgnoreCase ("keep-alive")) {
655
rheaders.set ("Connection", "keep-alive");
656
int idle=(int)(ServerConfig.getIdleInterval()/1000);
657
int max=ServerConfig.getMaxIdleConnections();
658
String val = "timeout="+idle+", max="+max;
659
rheaders.set ("Keep-Alive", val);
660
}
661
}
662
663
if (newconnection) {
664
connection.setParameters (
665
rawin, rawout, chan, engine, sslStreams,
666
sslContext, protocol, ctx, rawin
667
);
668
}
669
/* check if client sent an Expect 100 Continue.
670
* In that case, need to send an interim response.
671
* In future API may be modified to allow app to
672
* be involved in this process.
673
*/
674
String exp = headers.getFirst("Expect");
675
if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
676
logReply (100, requestLine, null);
677
sendReply (
678
Code.HTTP_CONTINUE, false, null
679
);
680
}
681
/* uf is the list of filters seen/set by the user.
682
* sf is the list of filters established internally
683
* and which are not visible to the user. uc and sc
684
* are the corresponding Filter.Chains.
685
* They are linked together by a LinkHandler
686
* so that they can both be invoked in one call.
687
*/
688
final List<Filter> sf = ctx.getSystemFilters();
689
final List<Filter> uf = ctx.getFilters();
690
691
final Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
692
final Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc));
693
694
/* set up the two stream references */
695
tx.getRequestBody();
696
tx.getResponseBody();
697
if (https) {
698
uc.doFilter (new HttpsExchangeImpl (tx));
699
} else {
700
uc.doFilter (new HttpExchangeImpl (tx));
701
}
702
703
} catch (IOException e1) {
704
logger.log (Level.TRACE, "ServerImpl.Exchange (1)", e1);
705
closeConnection(connection);
706
} catch (NumberFormatException e2) {
707
logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e2);
708
reject (Code.HTTP_BAD_REQUEST,
709
requestLine, "NumberFormatException thrown");
710
} catch (URISyntaxException e3) {
711
logger.log (Level.TRACE, "ServerImpl.Exchange (3)", e3);
712
reject (Code.HTTP_BAD_REQUEST,
713
requestLine, "URISyntaxException thrown");
714
} catch (Exception e4) {
715
logger.log (Level.TRACE, "ServerImpl.Exchange (4)", e4);
716
closeConnection(connection);
717
} catch (Throwable t) {
718
logger.log(Level.TRACE, "ServerImpl.Exchange (5)", t);
719
throw t;
720
}
721
}
722
723
/* used to link to 2 or more Filter.Chains together */
724
725
class LinkHandler implements HttpHandler {
726
Filter.Chain nextChain;
727
728
LinkHandler (Filter.Chain nextChain) {
729
this.nextChain = nextChain;
730
}
731
732
public void handle (HttpExchange exchange) throws IOException {
733
nextChain.doFilter (exchange);
734
}
735
}
736
737
void reject (int code, String requestStr, String message) {
738
rejected = true;
739
logReply (code, requestStr, message);
740
sendReply (
741
code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
742
);
743
closeConnection(connection);
744
}
745
746
void sendReply (
747
int code, boolean closeNow, String text)
748
{
749
try {
750
StringBuilder builder = new StringBuilder (512);
751
builder.append ("HTTP/1.1 ")
752
.append (code).append (Code.msg(code)).append ("\r\n");
753
754
if (text != null && text.length() != 0) {
755
builder.append ("Content-Length: ")
756
.append (text.length()).append ("\r\n")
757
.append ("Content-Type: text/html\r\n");
758
} else {
759
builder.append ("Content-Length: 0\r\n");
760
text = "";
761
}
762
if (closeNow) {
763
builder.append ("Connection: close\r\n");
764
}
765
builder.append ("\r\n").append (text);
766
String s = builder.toString();
767
byte[] b = s.getBytes("ISO8859_1");
768
rawout.write (b);
769
rawout.flush();
770
if (closeNow) {
771
closeConnection(connection);
772
}
773
} catch (IOException e) {
774
logger.log (Level.TRACE, "ServerImpl.sendReply", e);
775
closeConnection(connection);
776
}
777
}
778
779
}
780
781
void logReply (int code, String requestStr, String text) {
782
if (!logger.isLoggable(Level.DEBUG)) {
783
return;
784
}
785
if (text == null) {
786
text = "";
787
}
788
String r;
789
if (requestStr.length() > 80) {
790
r = requestStr.substring (0, 80) + "<TRUNCATED>";
791
} else {
792
r = requestStr;
793
}
794
String message = r + " [" + code + " " +
795
Code.msg(code) + "] ("+text+")";
796
logger.log (Level.DEBUG, message);
797
}
798
799
long getTicks() {
800
return ticks;
801
}
802
803
public long getTime() {
804
return time;
805
}
806
807
void delay () {
808
Thread.yield();
809
try {
810
Thread.sleep (200);
811
} catch (InterruptedException e) {}
812
}
813
814
private int exchangeCount = 0;
815
816
synchronized void startExchange () {
817
exchangeCount ++;
818
}
819
820
synchronized int endExchange () {
821
exchangeCount --;
822
assert exchangeCount >= 0;
823
return exchangeCount;
824
}
825
826
HttpServer getWrapper () {
827
return wrapper;
828
}
829
830
void requestStarted (HttpConnection c) {
831
c.creationTime = getTime();
832
c.setState (State.REQUEST);
833
reqConnections.add (c);
834
}
835
836
// called after a request has been completely read
837
// by the server. This stops the timer which would
838
// close the connection if the request doesn't arrive
839
// quickly enough. It then starts the timer
840
// that ensures the client reads the response in a timely
841
// fashion.
842
843
void requestCompleted (HttpConnection c) {
844
State s = c.getState();
845
assert s == State.REQUEST : "State is not REQUEST ("+s+")";
846
reqConnections.remove (c);
847
c.rspStartedTime = getTime();
848
rspConnections.add (c);
849
c.setState (State.RESPONSE);
850
}
851
852
// called after response has been sent
853
void responseCompleted (HttpConnection c) {
854
State s = c.getState();
855
assert s == State.RESPONSE : "State is not RESPONSE ("+s+")";
856
rspConnections.remove (c);
857
c.setState (State.IDLE);
858
}
859
860
/**
861
* TimerTask run every CLOCK_TICK ms
862
*/
863
class ServerTimerTask extends TimerTask {
864
public void run () {
865
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
866
time = System.currentTimeMillis();
867
ticks ++;
868
synchronized (idleConnections) {
869
for (HttpConnection c : idleConnections) {
870
if (c.time <= time) {
871
toClose.add (c);
872
}
873
}
874
for (HttpConnection c : toClose) {
875
idleConnections.remove (c);
876
allConnections.remove (c);
877
c.close();
878
}
879
}
880
}
881
}
882
883
class ServerTimerTask1 extends TimerTask {
884
885
// runs every TIMER_MILLIS
886
public void run () {
887
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
888
time = System.currentTimeMillis();
889
synchronized (reqConnections) {
890
if (MAX_REQ_TIME != -1) {
891
for (HttpConnection c : reqConnections) {
892
if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
893
toClose.add (c);
894
}
895
}
896
for (HttpConnection c : toClose) {
897
logger.log (Level.DEBUG, "closing: no request: " + c);
898
reqConnections.remove (c);
899
allConnections.remove (c);
900
c.close();
901
}
902
}
903
}
904
toClose = new LinkedList<HttpConnection>();
905
synchronized (rspConnections) {
906
if (MAX_RSP_TIME != -1) {
907
for (HttpConnection c : rspConnections) {
908
if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
909
toClose.add (c);
910
}
911
}
912
for (HttpConnection c : toClose) {
913
logger.log (Level.DEBUG, "closing: no response: " + c);
914
rspConnections.remove (c);
915
allConnections.remove (c);
916
c.close();
917
}
918
}
919
}
920
}
921
}
922
923
void logStackTrace (String s) {
924
logger.log (Level.TRACE, s);
925
StringBuilder b = new StringBuilder ();
926
StackTraceElement[] e = Thread.currentThread().getStackTrace();
927
for (int i=0; i<e.length; i++) {
928
b.append (e[i].toString()).append("\n");
929
}
930
logger.log (Level.TRACE, b.toString());
931
}
932
933
static long getTimeMillis(long secs) {
934
if (secs == -1) {
935
return -1;
936
} else {
937
return secs * 1000;
938
}
939
}
940
941
/*
942
* Validates a RFC 7230 header-key.
943
*/
944
static boolean isValidHeaderKey(String token) {
945
if (token == null || token.isEmpty()) return false;
946
947
boolean isValidChar;
948
char[] chars = token.toCharArray();
949
String validSpecialChars = "!#$%&'*+-.^_`|~";
950
for (char c : chars) {
951
isValidChar = ((c >= 'a') && (c <= 'z')) ||
952
((c >= 'A') && (c <= 'Z')) ||
953
((c >= '0') && (c <= '9'));
954
if (!isValidChar && validSpecialChars.indexOf(c) == -1) {
955
return false;
956
}
957
}
958
return true;
959
}
960
}
961
962