Skip to content

Commit fdc38b1

Browse files
committed
backport 627ef34498c31b5d16f9da423cfe0a5fe46a3562
1 parent 50537ae commit fdc38b1

File tree

6 files changed

+134
-110
lines changed

6 files changed

+134
-110
lines changed

src/jdk.httpserver/share/classes/sun/net/httpserver/ChunkedOutputStream.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,8 @@
2626
package sun.net.httpserver;
2727

2828
import java.io.*;
29-
import java.net.*;
3029
import java.util.Objects;
3130

32-
import com.sun.net.httpserver.*;
33-
import com.sun.net.httpserver.spi.*;
34-
3531
/**
3632
* a class which allows the caller to write an arbitrary
3733
* number of bytes to an underlying stream.
@@ -153,7 +149,7 @@ public void close () throws IOException {
153149
closed = true;
154150
}
155151

156-
WriteFinishedEvent e = new WriteFinishedEvent (t);
152+
Event e = new Event.WriteFinished(t);
157153
t.getHttpContext().getServerImpl().addEvent (e);
158154
}
159155

src/jdk.httpserver/share/classes/sun/net/httpserver/Event.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,35 @@
2525

2626
package sun.net.httpserver;
2727

28-
import com.sun.net.httpserver.*;
29-
import com.sun.net.httpserver.spi.*;
28+
import java.util.Objects;
3029

31-
class Event {
30+
abstract sealed class Event {
3231

33-
ExchangeImpl exchange;
32+
final ExchangeImpl exchange;
3433

35-
protected Event (ExchangeImpl t) {
34+
protected Event(ExchangeImpl t) {
3635
this.exchange = t;
3736
}
37+
38+
/**
39+
* Stopping event for the http server.
40+
* The event applies to the whole server and is not tied to any particular
41+
* exchange.
42+
*/
43+
static final class StopRequested extends Event {
44+
StopRequested() {
45+
super(null);
46+
}
47+
}
48+
49+
/**
50+
* Event indicating that writing is finished for a given exchange.
51+
*/
52+
static final class WriteFinished extends Event {
53+
WriteFinished(ExchangeImpl t) {
54+
super(Objects.requireNonNull(t));
55+
assert !t.writefinished;
56+
t.writefinished = true;
57+
}
58+
}
3859
}

src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,8 @@
2626
package sun.net.httpserver;
2727

2828
import java.io.*;
29-
import java.net.*;
3029
import java.util.Objects;
3130

32-
import com.sun.net.httpserver.*;
33-
import com.sun.net.httpserver.spi.*;
34-
3531
/**
3632
* a class which allows the caller to write up to a defined
3733
* number of bytes to an underlying stream. The caller *must*
@@ -98,7 +94,7 @@ public void close () throws IOException {
9894
is.close();
9995
} catch (IOException e) {}
10096
}
101-
WriteFinishedEvent e = new WriteFinishedEvent (t);
97+
Event e = new Event.WriteFinished(t);
10298
t.getHttpContext().getServerImpl().addEvent (e);
10399
}
104100

src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java

Lines changed: 102 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -61,7 +61,9 @@
6161
import java.util.Set;
6262
import java.util.Timer;
6363
import java.util.TimerTask;
64+
import java.util.concurrent.CountDownLatch;
6465
import java.util.concurrent.Executor;
66+
import java.util.concurrent.TimeUnit;
6567

6668
import static java.nio.charset.StandardCharsets.ISO_8859_1;
6769
import static sun.net.httpserver.Utils.isValidName;
@@ -94,7 +96,7 @@ class ServerImpl {
9496
private final Set<HttpConnection> rspConnections;
9597
private List<Event> events;
9698
private final Object lolock = new Object();
97-
private volatile boolean finished = false;
99+
private final CountDownLatch finishedLatch = new CountDownLatch(1);
98100
private volatile boolean terminating = false;
99101
private boolean bound = false;
100102
private boolean started = false;
@@ -180,7 +182,7 @@ public void bind (InetSocketAddress addr, int backlog) throws IOException {
180182
}
181183

182184
public void start () {
183-
if (!bound || started || finished) {
185+
if (!bound || started || finished()) {
184186
throw new IllegalStateException ("server in wrong state");
185187
}
186188
if (executor == null) {
@@ -223,45 +225,75 @@ public HttpsConfigurator getHttpsConfigurator () {
223225
return httpsConfig;
224226
}
225227

228+
private final boolean finished(){
229+
// if the latch is 0, the server is finished
230+
return finishedLatch.getCount() == 0;
231+
}
232+
226233
public final boolean isFinishing() {
227-
return finished;
234+
return finished();
228235
}
229236

237+
/**
238+
* This method stops the server by adding a stop request event and
239+
* waiting for the server until the event is triggered or until the maximum delay is triggered.
240+
* <p>
241+
* This ensures that the server is stopped immediately after all exchanges are complete. HttpConnections will be forcefully closed if active exchanges do not
242+
* complete within the imparted delay.
243+
*
244+
* @param delay maximum delay to wait for exchanges completion, in seconds
245+
*/
230246
public void stop (int delay) {
231247
if (delay < 0) {
232248
throw new IllegalArgumentException ("negative delay parameter");
233249
}
250+
251+
logger.log(Level.TRACE, "stopping");
252+
// posting a stop event, which will flip finished flag if it finishes
253+
// before the timeout in this method
234254
terminating = true;
255+
256+
addEvent(new Event.StopRequested());
257+
235258
try { schan.close(); } catch (IOException e) {}
236259
selector.wakeup();
237-
long latest = System.currentTimeMillis() + delay * 1000;
238-
while (System.currentTimeMillis() < latest) {
239-
delay();
240-
if (finished) {
241-
break;
260+
261+
try {
262+
// waiting for the duration of the delay, unless released before
263+
finishedLatch.await(delay, TimeUnit.SECONDS);
264+
265+
} catch (InterruptedException e) {
266+
logger.log(Level.TRACE, "Error in awaiting the delay");
267+
268+
} finally {
269+
270+
logger.log(Level.TRACE, "closing connections");
271+
finishedLatch.countDown();
272+
selector.wakeup();
273+
synchronized (allConnections) {
274+
for (HttpConnection c : allConnections) {
275+
c.close();
276+
}
242277
}
243-
}
244-
finished = true;
245-
selector.wakeup();
246-
synchronized (allConnections) {
247-
for (HttpConnection c : allConnections) {
248-
c.close();
278+
allConnections.clear();
279+
idleConnections.clear();
280+
newlyAcceptedConnections.clear();
281+
timer.cancel();
282+
if (reqRspTimeoutEnabled) {
283+
timer1.cancel();
249284
}
250-
}
251-
allConnections.clear();
252-
idleConnections.clear();
253-
newlyAcceptedConnections.clear();
254-
timer.cancel();
255-
if (reqRspTimeoutEnabled) {
256-
timer1.cancel();
257-
}
258-
if (dispatcherThread != null && dispatcherThread != Thread.currentThread()) {
259-
try {
260-
dispatcherThread.join();
261-
} catch (InterruptedException e) {
262-
Thread.currentThread().interrupt();
263-
logger.log (Level.TRACE, "ServerImpl.stop: ", e);
285+
logger.log(Level.TRACE, "connections closed");
286+
287+
if (dispatcherThread != null && dispatcherThread != Thread.currentThread()) {
288+
logger.log(Level.TRACE, "waiting for dispatcher thread");
289+
try {
290+
dispatcherThread.join();
291+
} catch (InterruptedException e) {
292+
Thread.currentThread().interrupt();
293+
logger.log(Level.TRACE, "ServerImpl.stop: ", e);
294+
}
264295
}
296+
logger.log(Level.TRACE, "server stopped");
265297
}
266298
}
267299

@@ -391,15 +423,34 @@ void addEvent (Event r) {
391423
class Dispatcher implements Runnable {
392424

393425
private void handleEvent (Event r) {
426+
427+
// Stopping marking the state as finished if stop is requested,
428+
// termination is in progress and exchange count is 0
429+
if (r instanceof Event.StopRequested) {
430+
logger.log(Level.TRACE, "Handling Stop Requested Event");
431+
432+
// checking if terminating is set to true
433+
final boolean terminatingCopy = terminating;
434+
assert terminatingCopy;
435+
436+
if (getExchangeCount() == 0 && reqConnections.isEmpty()) {
437+
finishedLatch.countDown();
438+
} else {
439+
logger.log(Level.TRACE, "Some requests are still pending");
440+
}
441+
return;
442+
}
443+
394444
ExchangeImpl t = r.exchange;
395445
HttpConnection c = t.getConnection();
446+
396447
try {
397-
if (r instanceof WriteFinishedEvent) {
448+
if (r instanceof Event.WriteFinished) {
398449

399450
logger.log(Level.TRACE, "Write Finished");
400451
int exchanges = endExchange();
401-
if (terminating && exchanges == 0) {
402-
finished = true;
452+
if (terminating && exchanges == 0 && reqConnections.isEmpty()) {
453+
finishedLatch.countDown();
403454
}
404455
LeftOverInputStream is = t.getOriginalInputStream();
405456
if (!is.isEOF()) {
@@ -449,11 +500,12 @@ void reRegister (HttpConnection c) {
449500
}
450501

451502
public void run() {
452-
while (!finished) {
503+
// finished() will be true when there are no active exchange after terminating
504+
while (!finished()) {
453505
try {
454506
List<Event> list = null;
455507
synchronized (lolock) {
456-
if (events.size() > 0) {
508+
if (!events.isEmpty()) {
457509
list = events;
458510
events = new ArrayList<>();
459511
}
@@ -600,18 +652,18 @@ private void closeConnection(HttpConnection conn) {
600652
conn.close();
601653
allConnections.remove(conn);
602654
switch (conn.getState()) {
603-
case REQUEST:
604-
reqConnections.remove(conn);
605-
break;
606-
case RESPONSE:
607-
rspConnections.remove(conn);
608-
break;
609-
case IDLE:
610-
idleConnections.remove(conn);
611-
break;
612-
case NEWLY_ACCEPTED:
613-
newlyAcceptedConnections.remove(conn);
614-
break;
655+
case REQUEST:
656+
reqConnections.remove(conn);
657+
break;
658+
case RESPONSE:
659+
rspConnections.remove(conn);
660+
break;
661+
case IDLE:
662+
idleConnections.remove(conn);
663+
break;
664+
case NEWLY_ACCEPTED:
665+
newlyAcceptedConnections.remove(conn);
666+
break;
615667
}
616668
assert !reqConnections.remove(conn);
617669
assert !rspConnections.remove(conn);
@@ -933,19 +985,16 @@ void logReply (int code, String requestStr, String text) {
933985
logger.log (Level.DEBUG, message);
934986
}
935987

936-
void delay () {
937-
Thread.yield();
938-
try {
939-
Thread.sleep (200);
940-
} catch (InterruptedException e) {}
941-
}
942-
943988
private int exchangeCount = 0;
944989

945990
synchronized void startExchange () {
946991
exchangeCount ++;
947992
}
948993

994+
synchronized int getExchangeCount() {
995+
return exchangeCount;
996+
}
997+
949998
synchronized int endExchange () {
950999
exchangeCount --;
9511000
assert exchangeCount >= 0;

src/jdk.httpserver/share/classes/sun/net/httpserver/UndefLengthOutputStream.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,8 @@
2626
package sun.net.httpserver;
2727

2828
import java.io.*;
29-
import java.net.*;
3029
import java.util.Objects;
3130

32-
import com.sun.net.httpserver.*;
33-
import com.sun.net.httpserver.spi.*;
34-
3531
/**
3632
* a class which allows the caller to write an indefinite
3733
* number of bytes to an underlying stream , but without using
@@ -79,7 +75,7 @@ public void close () throws IOException {
7975
is.close();
8076
} catch (IOException e) {}
8177
}
82-
WriteFinishedEvent e = new WriteFinishedEvent (t);
78+
Event e = new Event.WriteFinished(t);
8379
t.getHttpContext().getServerImpl().addEvent (e);
8480
}
8581

0 commit comments

Comments
 (0)