Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit f569c11

Browse files
committed
Fix - Http3FrameCodec decode fail during unknown settings
**Motivation:** The change in [https://github.com/netty/netty/pull/15909](https://github.com/netty/netty/pull/15909) is merged but not yet released. While working on HTTP/3 connections I noticed a logical issue: when an unknown settings key is received, the current implementation returns a value, which causes Http3FrameCodec to treat it as an error (because settings.put(key, value) returns the previous value for known keys and null for new ones). For unknown keys we should ignore them, so we now always return null to achieve the expected behavior. **Modification:** * [x] settings.put(key, value) now returns null for unknown keys (non null triggers an error in Http3FrameCodec decoding). * [x] Added unit tests to verify this. > Note: Http3CodecTest didn’t catch the issue previously because it used frames produced by the same put logic, so both encoding and decoding behaved consistently and the issue was hidden. Port of netty/netty#15998
1 parent c8d683c commit f569c11

3 files changed

Lines changed: 45 additions & 8 deletions

File tree

src/main/java/io/netty/incubator/codec/http3/Http3SettingIdentifier.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,7 @@ public enum Http3SettingIdentifier {
9090
private final long id;
9191

9292
private static final Map<Long, Http3SettingIdentifier> LOOKUP = Collections.unmodifiableMap(
93-
Arrays.stream(values())
94-
.collect(Collectors.toMap(
95-
Http3SettingIdentifier::id,
96-
Function.identity()
97-
))
93+
Arrays.stream(values()).collect(Collectors.toMap(Http3SettingIdentifier::id, Function.identity()))
9894
);
9995

10096
Http3SettingIdentifier(long id) {

src/main/java/io/netty/incubator/codec/http3/Http3Settings.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public final class Http3Settings implements Iterable<Map.Entry<Long, Long>> {
119119
* Creates a new instance
120120
*/
121121
public Http3Settings() {
122-
this.settings = new LongObjectHashMap<>(4);
122+
this.settings = new LongObjectHashMap<>(Http3SettingIdentifier.values().length);
123123
}
124124

125125
/**
@@ -164,7 +164,7 @@ public Long put(long key, Long value) {
164164

165165
// When Non-Standard/Unknown settings identifier identifier present - Ignore
166166
if (identifier == null) {
167-
return value;
167+
return null;
168168
}
169169

170170
//Validation

src/test/java/io/netty/incubator/codec/http3/Http3SettingsTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import static org.junit.jupiter.api.Assertions.assertNull;
3333
import static org.junit.jupiter.api.Assertions.assertNotSame;
3434
import static org.junit.jupiter.api.Assertions.assertNotNull;
35-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
3635

3736
/**
3837
* unit tests for {@link Http3Settings}.
@@ -264,4 +263,46 @@ void testCopyFromNullThrows() {
264263
Http3Settings settings = new Http3Settings();
265264
assertThrows(NullPointerException.class, () -> settings.putAll(null));
266265
}
266+
267+
@Test
268+
void duplicateSettingsValuesInsideFrameTest() {
269+
Http3SettingsFrame settingsFrame = new DefaultHttp3SettingsFrame();
270+
assertNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY.id(), 100L));
271+
assertNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS.id(), 1L));
272+
assertNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE.id(), 128L));
273+
assertNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_ENABLE_CONNECT_PROTOCOL.id(), 0L));
274+
assertNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_H3_DATAGRAM.id(), 1L));
275+
//known headers should not contain duplicate so give non-null
276+
// which is used in http3framecodec to throw error
277+
assertNotNull(settingsFrame.put(Http3SettingIdentifier.HTTP3_SETTINGS_H3_DATAGRAM.id(), 1L));
278+
// Ensure we can encode and decode all sizes correctly.
279+
// unknown settings id/key will be ignored
280+
assertNull(settingsFrame.put(63, 63L));
281+
assertNull(settingsFrame.put(16383, 16383L));
282+
assertNull(settingsFrame.put(1073741823, 1073741823L));
283+
assertNull(settingsFrame.put(4611686018427387903L, 4611686018427387903L));
284+
//even duplicates of unknown ignored as we ignore unknown
285+
assertNull(settingsFrame.put(4611686018427387903L, 4611686018427387903L));
286+
}
287+
288+
@Test
289+
void duplicateSettingsValuesInsideHttp3SettingsTest() {
290+
Http3Settings http3Settings = new Http3Settings();
291+
assertNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY.id(), 100L));
292+
assertNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS.id(), 1L));
293+
assertNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE.id(), 128L));
294+
assertNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_ENABLE_CONNECT_PROTOCOL.id(), 0L));
295+
assertNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_H3_DATAGRAM.id(), 1L));
296+
//known headers should not contain duplicate so give non-null
297+
// which is used in http3framecodec to throw error
298+
assertNotNull(http3Settings.put(Http3SettingIdentifier.HTTP3_SETTINGS_H3_DATAGRAM.id(), 1L));
299+
// Ensure we can encode and decode all sizes correctly.
300+
// unknown settings id/key will be ignored
301+
assertNull(http3Settings.put(63, 63L));
302+
assertNull(http3Settings.put(16383, 16383L));
303+
assertNull(http3Settings.put(1073741823, 1073741823L));
304+
assertNull(http3Settings.put(4611686018427387903L, 4611686018427387903L));
305+
//even duplicates of unknown ignored as we ignore unknown
306+
assertNull(http3Settings.put(4611686018427387903L, 4611686018427387903L));
307+
}
267308
}

0 commit comments

Comments
 (0)