You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ab...@apache.org on 2026/03/13 14:59:36 UTC
(httpcomponents-core) branch master updated: Validate HEADERS priority self-dependency (#652)
This is an automated email from the ASF dual-hosted git repository.
abernal pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
The following commit(s) were added to refs/heads/master by this push:
new e1ebe30b7 Validate HEADERS priority self-dependency (#652)
e1ebe30b7 is described below
commit e1ebe30b796a52e3b839ddc3a1de79444a9145be
Author: Arturo Bernal <ab...@apache.org>
AuthorDate: Fri Mar 13 14:59:30 2026 +0000
Validate HEADERS priority self-dependency (#652)
Treat HEADERS with PRIORITY that depends on the same stream as a stream-level PROTOCOL_ERROR
---
.../impl/nio/AbstractH2StreamMultiplexer.java | 7 ++-
.../impl/nio/TestAbstractH2StreamMultiplexer.java | 52 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
index 11c5c39f7..8e9171b57 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
@@ -1185,8 +1185,11 @@ private void consumeHeaderFrame(final RawFrame frame, final H2Stream stream) thr
if (payload == null || payload.remaining() < 5) {
throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid HEADERS priority payload");
}
- payload.getInt();
- payload.get();
+ final int dependency = payload.getInt() & 0x7fffffff;
+ if (dependency == streamId) {
+ throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, "Stream cannot depend on itself");
+ }
+ payload.get(); // weight
}
if (continuation == null) {
final List<Header> headers = decodeHeaders(payload);
diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
index e792f0405..6c9e88e4f 100644
--- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
+++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
@@ -1965,5 +1965,57 @@ void testInputRstStreamWithInvalidLengthOnUnseenStreamRejected() throws Exceptio
Assertions.assertEquals(H2Error.FRAME_SIZE_ERROR, H2Error.getByCode(ex.getCode()));
}
+ @Test
+ void testHeadersWithPrioritySelfDependencyIsStreamProtocolError() throws Exception {
+ final H2Config h2Config = H2Config.custom().build();
+
+ final AbstractH2StreamMultiplexer mux = new H2StreamMultiplexerImpl(
+ protocolIOSession,
+ FRAME_FACTORY,
+ StreamIdGenerator.ODD,
+ httpProcessor,
+ CharCodingConfig.DEFAULT,
+ h2Config,
+ h2StreamListener,
+ () -> streamHandler);
+
+ final ByteArrayBuffer headerBuf = new ByteArrayBuffer(128);
+ final HPackEncoder encoder = new HPackEncoder(
+ h2Config.getHeaderTableSize(),
+ CharCodingSupport.createEncoder(CharCodingConfig.DEFAULT));
+
+ final List<Header> headers = Arrays.asList(
+ new BasicHeader(":method", "GET"),
+ new BasicHeader(":scheme", "https"),
+ new BasicHeader(":path", "/"),
+ new BasicHeader(":authority", "example.test"));
+
+ encoder.encodeHeaders(headerBuf, headers, h2Config.isCompressionEnabled());
+
+ final ByteBuffer payload = ByteBuffer.allocate(5 + headerBuf.length());
+ payload.putInt(0x80000002); // exclusive bit set, dependency = stream 2 (self-dependency)
+ payload.put((byte) 16); // weight
+ payload.put(headerBuf.array(), 0, headerBuf.length());
+ payload.flip();
+
+ final RawFrame headersFrame = new RawFrame(
+ FrameType.HEADERS.getValue(),
+ FrameFlag.PRIORITY.getValue() | FrameFlag.END_HEADERS.getValue(),
+ 2,
+ payload);
+
+ Assertions.assertDoesNotThrow(() -> mux.onInput(ByteBuffer.wrap(encodeFrame(headersFrame))));
+
+ Mockito.verify(streamHandler).failed(exceptionCaptor.capture());
+ final Exception cause = exceptionCaptor.getValue();
+ Assertions.assertInstanceOf(H2StreamResetException.class, cause);
+ Assertions.assertEquals(
+ H2Error.PROTOCOL_ERROR,
+ H2Error.getByCode(((H2StreamResetException) cause).getCode()));
+
+ Mockito.verify(streamHandler, Mockito.never())
+ .consumeHeader(ArgumentMatchers.anyList(), ArgumentMatchers.anyBoolean());
+ }
+
}
\ No newline at end of file