Skip to content

Commit c1c2e42

Browse files
committed
Backport 041a12e65530b5832b4a500180c97a2a60e0dc51
1 parent 2dc6bac commit c1c2e42

File tree

3 files changed

+101
-33
lines changed

3 files changed

+101
-33
lines changed

src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,10 @@ void deleteConnection(Http2Connection c) {
213213

214214
private EOFException STOPPED;
215215
void stop() {
216-
synchronized (this) {stopping = true;}
217216
if (debug.on()) debug.log("stopping");
218217
STOPPED = new EOFException("HTTP/2 client stopped");
219218
STOPPED.setStackTrace(new StackTraceElement[0]);
219+
synchronized (this) {stopping = true;}
220220
do {
221221
connections.values().forEach(this::close);
222222
} while (!connections.isEmpty());

src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import java.io.EOFException;
2929
import java.io.IOException;
3030
import java.io.UncheckedIOException;
31+
import java.lang.invoke.MethodHandles;
32+
import java.lang.invoke.VarHandle;
3133
import java.net.InetSocketAddress;
3234
import java.net.ProtocolException;
3335
import java.net.URI;
@@ -287,7 +289,10 @@ public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws
287289
}
288290
}
289291

290-
volatile boolean closed;
292+
private static final int HALF_CLOSED_LOCAL = 1;
293+
private static final int HALF_CLOSED_REMOTE = 2;
294+
private static final int SHUTDOWN_REQUESTED = 4;
295+
volatile int closedState;
291296

292297
//-------------------------------------
293298
final HttpConnection connection;
@@ -703,13 +708,15 @@ final int maxConcurrentServerInitiatedStreams() {
703708
}
704709

705710
void close() {
706-
Log.logTrace("Closing HTTP/2 connection: to {0}", connection.address());
707-
if (connection.channel().isOpen()) {
708-
GoAwayFrame f = new GoAwayFrame(0,
709-
ErrorFrame.NO_ERROR,
710-
"Requested by user".getBytes(UTF_8));
711-
// TODO: set last stream. For now zero ok.
712-
sendFrame(f);
711+
if (markHalfClosedLocal()) {
712+
if (connection.channel().isOpen()) {
713+
Log.logTrace("Closing HTTP/2 connection: to {0}", connection.address());
714+
GoAwayFrame f = new GoAwayFrame(0,
715+
ErrorFrame.NO_ERROR,
716+
"Requested by user".getBytes(UTF_8));
717+
// TODO: set last stream. For now zero ok.
718+
sendFrame(f);
719+
}
713720
}
714721
}
715722

@@ -771,18 +778,17 @@ Throwable getRecordedCause() {
771778
}
772779

773780
void shutdown(Throwable t) {
774-
if (debug.on()) debug.log(() -> "Shutting down h2c (closed="+closed+"): " + t);
775-
if (closed == true) return;
776-
synchronized (this) {
777-
if (closed == true) return;
778-
closed = true;
779-
}
781+
int state = closedState;
782+
if (debug.on()) debug.log(() -> "Shutting down h2c (state="+describeClosedState(state)+"): " + t);
783+
if (!markShutdownRequested()) return;
780784
cause.compareAndSet(null, t);
781785
if (Log.errors()) {
782-
if (!(t instanceof EOFException) || isActive()) {
786+
if (t!= null && (!(t instanceof EOFException) || isActive())) {
783787
Log.logError(t);
784788
} else if (t != null) {
785789
Log.logError("Shutting down connection: {0}", t.getMessage());
790+
} else {
791+
Log.logError("Shutting down connection");
786792
}
787793
}
788794
client2.deleteConnection(this);
@@ -966,7 +972,7 @@ private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) {
966972
}
967973

968974
final void dropDataFrame(DataFrame df) {
969-
if (closed) return;
975+
if (isMarked(closedState, SHUTDOWN_REQUESTED)) return;
970976
if (debug.on()) {
971977
debug.log("Dropping data frame for stream %d (%d payload bytes)",
972978
df.streamid(), df.payloadLength());
@@ -976,7 +982,7 @@ final void dropDataFrame(DataFrame df) {
976982

977983
final void ensureWindowUpdated(DataFrame df) {
978984
try {
979-
if (closed) return;
985+
if (isMarked(closedState, SHUTDOWN_REQUESTED)) return;
980986
int length = df.payloadLength();
981987
if (length > 0) {
982988
windowUpdater.update(length);
@@ -1076,7 +1082,8 @@ private void handleConnectionFrame(Http2Frame frame)
10761082
}
10771083

10781084
boolean isOpen() {
1079-
return !closed && connection.channel().isOpen();
1085+
return !isMarked(closedState, SHUTDOWN_REQUESTED)
1086+
&& connection.channel().isOpen();
10801087
}
10811088

10821089
void resetStream(int streamid, int code) {
@@ -1189,11 +1196,13 @@ private void protocolError(int errorCode, String msg)
11891196
String protocolError = "protocol error" + (msg == null?"":(": " + msg));
11901197
ProtocolException protocolException =
11911198
new ProtocolException(protocolError);
1192-
framesDecoder.close(protocolError);
1193-
subscriber.stop(protocolException);
1194-
if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException);
1195-
GoAwayFrame frame = new GoAwayFrame(0, errorCode);
1196-
sendFrame(frame);
1199+
if (markHalfClosedLocal()) {
1200+
framesDecoder.close(protocolError);
1201+
subscriber.stop(protocolException);
1202+
if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException);
1203+
GoAwayFrame frame = new GoAwayFrame(0, errorCode);
1204+
sendFrame(frame);
1205+
}
11971206
shutdown(protocolException);
11981207
}
11991208

@@ -1226,9 +1235,11 @@ private void handlePing(PingFrame frame)
12261235
private void handleGoAway(GoAwayFrame frame)
12271236
throws IOException
12281237
{
1229-
shutdown(new IOException(
1230-
connection.channel().getLocalAddress()
1231-
+": GOAWAY received"));
1238+
if (markHalfClosedLRemote()) {
1239+
shutdown(new IOException(
1240+
connection.channel().getLocalAddress()
1241+
+ ": GOAWAY received"));
1242+
}
12321243
}
12331244

12341245
/**
@@ -1342,7 +1353,7 @@ <T> void putStream(Stream<T> stream, int streamid) {
13421353
// to prevent the SelectorManager thread from exiting until
13431354
// the stream is closed.
13441355
synchronized (this) {
1345-
if (!closed) {
1356+
if (!isMarked(closedState, SHUTDOWN_REQUESTED)) {
13461357
if (debug.on()) {
13471358
debug.log("Opened stream %d", streamid);
13481359
}
@@ -1353,7 +1364,6 @@ <T> void putStream(Stream<T> stream, int streamid) {
13531364
}
13541365
if (debug.on()) debug.log("connection closed: closing stream %d", stream);
13551366
stream.cancel();
1356-
13571367
}
13581368

13591369
/**
@@ -1491,7 +1501,7 @@ void sendFrame(Http2Frame frame) {
14911501
}
14921502
publisher.signalEnqueued();
14931503
} catch (IOException e) {
1494-
if (!closed) {
1504+
if (!isMarked(closedState, SHUTDOWN_REQUESTED)) {
14951505
Log.logError(e);
14961506
shutdown(e);
14971507
}
@@ -1509,7 +1519,7 @@ void sendDataFrame(DataFrame frame) {
15091519
publisher.enqueue(encodeFrame(frame));
15101520
publisher.signalEnqueued();
15111521
} catch (IOException e) {
1512-
if (!closed) {
1522+
if (!isMarked(closedState, SHUTDOWN_REQUESTED)) {
15131523
Log.logError(e);
15141524
shutdown(e);
15151525
}
@@ -1527,7 +1537,7 @@ void sendUnorderedFrame(Http2Frame frame) {
15271537
publisher.enqueueUnordered(encodeFrame(frame));
15281538
publisher.signalEnqueued();
15291539
} catch (IOException e) {
1530-
if (!closed) {
1540+
if (!isMarked(closedState, SHUTDOWN_REQUESTED)) {
15311541
Log.logError(e);
15321542
shutdown(e);
15331543
}
@@ -1693,4 +1703,60 @@ AbstractAsyncSSLConnection getConnection() {
16931703
return connection;
16941704
}
16951705
}
1706+
1707+
private boolean isMarked(int state, int mask) {
1708+
return (state & mask) == mask;
1709+
}
1710+
1711+
private boolean markShutdownRequested() {
1712+
return markClosedState(SHUTDOWN_REQUESTED);
1713+
}
1714+
1715+
private boolean markHalfClosedLocal() {
1716+
return markClosedState(HALF_CLOSED_LOCAL);
1717+
}
1718+
1719+
private boolean markHalfClosedLRemote() {
1720+
return markClosedState(HALF_CLOSED_REMOTE);
1721+
}
1722+
1723+
private boolean markClosedState(int flag) {
1724+
int state, desired;
1725+
do {
1726+
state = desired = closedState;
1727+
if ((state & flag) == flag) return false;
1728+
desired = state | flag;
1729+
} while (!CLOSED_STATE.compareAndSet(this, state, desired));
1730+
return true;
1731+
}
1732+
1733+
String describeClosedState(int state) {
1734+
if (state == 0) return "active";
1735+
String desc = null;
1736+
if (isMarked(state, SHUTDOWN_REQUESTED)) {
1737+
desc = "shutdown";
1738+
}
1739+
if (isMarked(state, HALF_CLOSED_LOCAL | HALF_CLOSED_REMOTE)) {
1740+
if (desc == null) return "closed";
1741+
else return desc + "+closed";
1742+
}
1743+
if (isMarked(state, HALF_CLOSED_LOCAL)) {
1744+
if (desc == null) return "half-closed-local";
1745+
else return desc + "+half-closed-local";
1746+
}
1747+
if (isMarked(state, HALF_CLOSED_REMOTE)) {
1748+
if (desc == null) return "half-closed-remote";
1749+
else return desc + "+half-closed-remote";
1750+
}
1751+
return "0x" + Integer.toString(state, 16);
1752+
}
1753+
1754+
private static final VarHandle CLOSED_STATE;
1755+
static {
1756+
try {
1757+
CLOSED_STATE = MethodHandles.lookup().findVarHandle(Http2Connection.class, "closedState", int.class);
1758+
} catch (Exception x) {
1759+
throw new ExceptionInInitializerError(x);
1760+
}
1761+
}
16961762
}

test/jdk/java/net/httpclient/http2/NoBodyTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
* @bug 8087112
2727
* @library /test/lib /test/jdk/java/net/httpclient/lib
2828
* @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer
29-
* @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors NoBodyTest
29+
* @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors
30+
* -Djdk.internal.httpclient.debug=true
31+
* NoBodyTest
3032
*/
3133

3234
import java.io.IOException;

0 commit comments

Comments
 (0)