Skip to content

Commit 70a79cd

Browse files
committed
Improved H2 connection termination in case of the opposite endpoint failing to send GOAWAY frame
1 parent 170e576 commit 70a79cd

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,11 @@ public final void onInput(final ByteBuffer src) throws HttpException, IOExceptio
442442
for (;;) {
443443
final RawFrame frame = inputBuffer.read(src, ioSession);
444444
if (frame == null) {
445+
if (inputBuffer.isEndOfStream() && connState == ConnectionHandshake.ACTIVE) {
446+
connState = ConnectionHandshake.SHUTDOWN;
447+
final RawFrame goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.NO_ERROR, "Unexpected end of stream");
448+
commitFrame(goAway);
449+
}
445450
break;
446451
}
447452
if (streamListener != null) {

httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/FrameInputBuffer.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import java.nio.ByteBuffer;
3131
import java.nio.channels.ReadableByteChannel;
3232

33-
import org.apache.hc.core5.http.ConnectionClosedException;
33+
import org.apache.hc.core5.annotation.Internal;
3434
import org.apache.hc.core5.http2.H2ConnectionException;
3535
import org.apache.hc.core5.http2.H2CorruptFrameException;
3636
import org.apache.hc.core5.http2.H2Error;
@@ -61,6 +61,8 @@ enum State { HEAD_EXPECTED, PAYLOAD_EXPECTED }
6161
private int flags;
6262
private int streamId;
6363

64+
private boolean endOfStream;
65+
6466
FrameInputBuffer(final BasicH2TransportMetrics metrics, final int bufferLen, final int maxFramePayloadSize) {
6567
Args.notNull(metrics, "HTTP2 transport metrics");
6668
Args.positive(maxFramePayloadSize, "Maximum payload size");
@@ -70,6 +72,7 @@ enum State { HEAD_EXPECTED, PAYLOAD_EXPECTED }
7072
this.buffer = ByteBuffer.wrap(bytes);
7173
this.buffer.flip();
7274
this.state = State.HEAD_EXPECTED;
75+
this.endOfStream = false;
7376
}
7477

7578
public FrameInputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) {
@@ -174,11 +177,13 @@ public RawFrame read(final ByteBuffer src, final ReadableByteChannel channel) th
174177
}
175178
if (bytesRead == 0) {
176179
break;
177-
} else if (bytesRead < 0) {
180+
}
181+
if (bytesRead == -1) {
178182
if (state != State.HEAD_EXPECTED || buffer.hasRemaining()) {
179183
throw new H2CorruptFrameException("Corrupt or incomplete HTTP2 frame");
180184
} else {
181-
throw new ConnectionClosedException();
185+
endOfStream = true;
186+
break;
182187
}
183188
}
184189
}
@@ -199,10 +204,16 @@ public RawFrame read(final ReadableByteChannel channel) throws IOException {
199204
public void reset() {
200205
buffer.compact();
201206
state = State.HEAD_EXPECTED;
207+
endOfStream = false;
202208
}
203209

204210
public H2TransportMetrics getMetrics() {
205211
return metrics;
206212
}
207213

214+
@Internal
215+
public boolean isEndOfStream() {
216+
return endOfStream;
217+
}
218+
208219
}

httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestFrameInOutBuffers.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
import java.nio.ByteBuffer;
3131

32-
import org.apache.hc.core5.http.ConnectionClosedException;
3332
import org.apache.hc.core5.http2.H2ConnectionException;
3433
import org.apache.hc.core5.http2.H2CorruptFrameException;
3534
import org.apache.hc.core5.http2.ReadableByteChannelMock;
@@ -255,8 +254,11 @@ void testReadFrameConnectionClosed() throws Exception {
255254
final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(new byte[] {});
256255

257256
Assertions.assertNull(inBuffer.read(readableChannel));
258-
Assertions.assertThrows(ConnectionClosedException.class, () ->
259-
inBuffer.read(readableChannel));
257+
Assertions.assertFalse(inBuffer.isEndOfStream());
258+
Assertions.assertNull(inBuffer.read(readableChannel));
259+
Assertions.assertTrue(inBuffer.isEndOfStream());
260+
Assertions.assertNull(inBuffer.read(readableChannel));
261+
Assertions.assertTrue(inBuffer.isEndOfStream());
260262
}
261263

262264
@Test

0 commit comments

Comments
 (0)