Skip to content

Commit

Permalink
CONTINUATION frames must be for the correct stream. (apple#66)
Browse files Browse the repository at this point in the history
Motivation:

We mustn't allow CONTINUATION frames to be sent on invalid stream IDs.

Modifications:

- Police that the CONTINUATION frame raw stream ID matches the previous
    frame's raw stream ID.

Result:

Better conformance to the RFC
  • Loading branch information
Lukasa authored Mar 15, 2019
1 parent d9f1131 commit 1138881
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Sources/NIOHTTP2/HTTP2FrameParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,11 @@ struct HTTP2FrameDecoder {
throw InternalError.codecError(code: .protocolError)
}

// This must be for the stream we're buffering header block fragments for, or this is an error.
guard header.rawStreamID == state.header.rawStreamID else {
throw InternalError.codecError(code: .protocolError)
}

self.state = .accumulatingContinuationPayload(AccumulatingContinuationPayloadParserState(fromAccumulatingHeaderBlockFragments: state, continuationHeader: header))
}

Expand Down
2 changes: 2 additions & 0 deletions Tests/NIOHTTP2Tests/HTTP2FrameParserTests+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ extension HTTP2FrameParserTests {
("testHeadersContinuationFrameDecoding", testHeadersContinuationFrameDecoding),
("testPushPromiseContinuationFrameDecoding", testPushPromiseContinuationFrameDecoding),
("testUnsolicitedContinuationFrame", testUnsolicitedContinuationFrame),
("testContinuationFrameStreamZero", testContinuationFrameStreamZero),
("testContinuationFrameWrongStream", testContinuationFrameWrongStream),
("testAltServiceFrameDecoding", testAltServiceFrameDecoding),
("testAltServiceFrameDecodingFailure", testAltServiceFrameDecodingFailure),
("testAltServiceFrameEncoding", testAltServiceFrameEncoding),
Expand Down
78 changes: 78 additions & 0 deletions Tests/NIOHTTP2Tests/HTTP2FrameParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,84 @@ class HTTP2FrameParserTests: XCTestCase {
XCTAssertEqual(error as? InternalError, .codecError(code: .protocolError))
}
}

func testContinuationFrameStreamZero() throws {
var headers2 = self.byteBuffer(withBytes: self.simpleHeadersEncoded)
var headers1 = headers2.readSlice(length: 10)!

let frameBytes: [UInt8] = [
0x00, 0x00, 0x0a, // 3-byte payload length (10 bytes)
0x01, // 1-byte frame type (HEADERS)
0x00, // 1-byte flags (none)
0x00, 0x00, 0x00, 0x01, // 4-byte stream identifier
]
var buf = byteBuffer(withBytes: frameBytes, extraCapacity: 10)
buf.writeBuffer(&headers1)

var decoder = HTTP2FrameDecoder(allocator: self.allocator, expectClientMagic: false)

// should return nothing thus far and wait for CONTINUATION frames and an END_HEADERS flag
decoder.append(bytes: &buf)
XCTAssertNil(try decoder.nextFrame())

// should consume all the bytes
XCTAssertEqual(buf.readableBytes, 0)
buf.clear()

let continuationFrameBytes: [UInt8] = [
0x00, 0x00, 0x07, // 3-byte payload length (7 bytes)
0x09, // 1-byte frame type (CONTINUATION)
0x04, // 1-byte flags (END_HEADERS)
0x00, 0x00, 0x00, 0x00, // 4-byte stream identifier, stream 0
]
buf.writeBytes(continuationFrameBytes)
buf.writeBuffer(&headers2)

// This should fail
decoder.append(bytes: &buf)
XCTAssertThrowsError(try decoder.nextFrame()) { error in
XCTAssertEqual(error as? InternalError, .codecError(code: .protocolError))
}
}

func testContinuationFrameWrongStream() throws {
var headers2 = self.byteBuffer(withBytes: self.simpleHeadersEncoded)
var headers1 = headers2.readSlice(length: 10)!

let frameBytes: [UInt8] = [
0x00, 0x00, 0x0a, // 3-byte payload length (10 bytes)
0x01, // 1-byte frame type (HEADERS)
0x00, // 1-byte flags (none)
0x00, 0x00, 0x00, 0x01, // 4-byte stream identifier
]
var buf = byteBuffer(withBytes: frameBytes, extraCapacity: 10)
buf.writeBuffer(&headers1)

var decoder = HTTP2FrameDecoder(allocator: self.allocator, expectClientMagic: false)

// should return nothing thus far and wait for CONTINUATION frames and an END_HEADERS flag
decoder.append(bytes: &buf)
XCTAssertNil(try decoder.nextFrame())

// should consume all the bytes
XCTAssertEqual(buf.readableBytes, 0)
buf.clear()

let continuationFrameBytes: [UInt8] = [
0x00, 0x00, 0x07, // 3-byte payload length (7 bytes)
0x09, // 1-byte frame type (CONTINUATION)
0x04, // 1-byte flags (END_HEADERS)
0x00, 0x00, 0x00, 0x03, // 4-byte stream identifier, stream 3
]
buf.writeBytes(continuationFrameBytes)
buf.writeBuffer(&headers2)

// This should fail
decoder.append(bytes: &buf)
XCTAssertThrowsError(try decoder.nextFrame()) { error in
XCTAssertEqual(error as? InternalError, .codecError(code: .protocolError))
}
}

// MARK: - ALTSVC frames

Expand Down

0 comments on commit 1138881

Please sign in to comment.