Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions java/org/apache/coyote/http2/StreamStateMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ private enum State {
Http2Error.STREAM_CLOSED, FrameType.PRIORITY,
FrameType.RST,
FrameType.WINDOW_UPDATE),
CLOSED_RST_RX (false, false, false, false,
Http2Error.STREAM_CLOSED, FrameType.PRIORITY),
CLOSED_RST_RX (false, false, false, true,
Http2Error.STREAM_CLOSED, FrameType.PRIORITY,
FrameType.RST,
FrameType.WINDOW_UPDATE),
CLOSED_RST_TX (false, false, false, false,
Http2Error.STREAM_CLOSED, FrameType.DATA,
FrameType.HEADERS,
Expand Down
68 changes: 52 additions & 16 deletions test/org/apache/coyote/http2/TestHttp2Section_5_1.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,29 +156,65 @@ private void doTestHalfClosedRemoteHeadersMaintainsHpackState(boolean useContinu

@Test
public void testClosedInvalidFrame01() throws Exception {
// HTTP2 upgrade
// RFC 9113, section 5.1 permits an endpoint to treat receipt of any frame other than
// PRIORITY, WINDOW_UPDATE, or RST_STREAM on a closed stream as a connection error of
// type STREAM_CLOSED. Tomcat adopts this stricter policy for the CLOSED_RST_RX state,
// so receiving DATA after RST_STREAM produces a GOAWAY rather than a RST_STREAM.

http2Connect();

// Build the simple request
byte[] frameHeader = new byte[9];
ByteBuffer headersPayload = ByteBuffer.allocate(128);
buildSimpleGetRequest(frameHeader, headersPayload, null, 3);
// Complete a full request/response on stream 3 to drive it to CLOSED_TX,
// then send RST_STREAM so the server transitions to CLOSED_RST_RX.
sendSimpleGetRequest(3);
readSimpleGetResponse();
output.clearTrace();

// Remove the end of stream and end of headers flags
frameHeader[4] = 0;
sendRst(3, Http2Error.CANCEL.getCode());

// Process the request
writeFrame(frameHeader, headersPayload);
// DATA on a CLOSED_RST_RX stream must trigger a connection error.
sendData(3, new byte[] {});

// Send a rst
sendRst(3, Http2Error.INTERNAL_ERROR.getCode());
handleGoAwayResponse(3, Http2Error.STREAM_CLOSED);
}

// Then try sending some data (which should fail)
sendData(3, new byte[] {});
parser.readFrame();

Assert.assertTrue(output.getTrace(),
output.getTrace().startsWith("3-RST-[" + Http2Error.STREAM_CLOSED.getCode() + "]"));
@Test
public void testRstStreamAfterRstStreamReceived() throws Exception {
// RFC 9113, section 5.1: RST_STREAM on a closed stream must be silently ignored.
// A second RST_STREAM after the stream has entered CLOSED_RST_RX must not produce
// a connection error; the connection must remain open.
http2Connect();

sendSimpleGetRequest(3);
readSimpleGetResponse();
output.clearTrace();

sendRst(3, Http2Error.CANCEL.getCode());
sendRst(3, Http2Error.CANCEL.getCode());

sendSimpleGetRequest(5);
readSimpleGetResponse();
Assert.assertTrue(output.getTrace(), output.getTrace().contains(getSimpleResponseTrace(5)));
}


@Test
public void testWindowUpdateAfterRstStreamReceived() throws Exception {
// RFC 9113, section 5.1: WINDOW_UPDATE on a closed stream must be silently ignored.
// Receiving WINDOW_UPDATE after the stream has entered CLOSED_RST_RX must not produce
// a connection error; the connection must remain open.
http2Connect();

sendSimpleGetRequest(3);
readSimpleGetResponse();
output.clearTrace();

sendRst(3, Http2Error.CANCEL.getCode());
sendWindowUpdate(3, 100);

sendSimpleGetRequest(5);
readSimpleGetResponse();
Assert.assertTrue(output.getTrace(), output.getTrace().contains(getSimpleResponseTrace(5)));
}


Expand Down
Loading