Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public String toString() {
* @return the newly created copy.
*/
public static DefaultHttp3SettingsFrame copyOf(Http3SettingsFrame settingsFrame) {
DefaultHttp3SettingsFrame copy = new DefaultHttp3SettingsFrame();
Http3Settings settingsCopy = new Http3Settings(settingsFrame.settings().nonStandardSettingsValidator);
DefaultHttp3SettingsFrame copy = new DefaultHttp3SettingsFrame(settingsCopy);
if (settingsFrame instanceof DefaultHttp3SettingsFrame) {
copy.settings.putAll(((DefaultHttp3SettingsFrame) settingsFrame).settings);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,37 @@ public Http3ClientConnectionHandler(@Nullable ChannelHandler inboundControlStrea
@Nullable LongFunction<ChannelHandler> pushStreamHandlerFactory,
@Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable) {
this(inboundControlStreamHandler, pushStreamHandlerFactory, unknownInboundStreamHandlerFactory, localSettings,
disableQpackDynamicTable, null);
}

/**
* Create a new instance.
*
* @param inboundControlStreamHandler the {@link ChannelHandler} which will be notified about
* {@link Http3RequestStreamFrame}s or {@code null} if the user is not
* interested in these.
* @param pushStreamHandlerFactory the {@link LongFunction} that will provide a custom
* {@link ChannelHandler} for push streams {@code null} if no special
* handling should be done. When present, push ID will be passed as an
* argument to the {@link LongFunction}.
* @param unknownInboundStreamHandlerFactory the {@link LongFunction} that will provide a custom
* {@link ChannelHandler} for unknown inbound stream types or
* {@code null} if no special handling should be done.
* @param localSettings the local {@link Http3SettingsFrame} that should be sent to the
* remote peer or {@code null} if the default settings should be used.
* @param disableQpackDynamicTable If QPACK dynamic table should be disabled.
* @param nonStandardSettingsValidator the {@link Http3Settings.NonStandardHttp3SettingsValidator} to use
* when validating settings that are non-standard.
*/
public Http3ClientConnectionHandler(@Nullable ChannelHandler inboundControlStreamHandler,
@Nullable LongFunction<ChannelHandler> pushStreamHandlerFactory,
@Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable,
@Nullable Http3Settings.NonStandardHttp3SettingsValidator
nonStandardSettingsValidator) {
super(false, inboundControlStreamHandler, unknownInboundStreamHandlerFactory, localSettings,
disableQpackDynamicTable);
disableQpackDynamicTable, nonStandardSettingsValidator);
this.pushStreamHandlerFactory = pushStreamHandlerFactory;
}

Expand All @@ -70,7 +99,7 @@ void initBidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel channe
void initUnidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel) {
final long maxTableCapacity = maxTableCapacity();
streamChannel.pipeline().addLast(
new Http3UnidirectionalStreamInboundClientHandler(codecFactory,
new Http3UnidirectionalStreamInboundClientHandler(codecFactory, nonStandardSettingsValidator,
localControlStreamHandler, remoteControlStreamHandler,
unknownInboundStreamHandlerFactory, pushStreamHandlerFactory,
() -> new QpackEncoderHandler(maxTableCapacity, qpackDecoder),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapte
final Http3ControlStreamOutboundHandler remoteControlStreamHandler;
final QpackDecoder qpackDecoder;
final QpackEncoder qpackEncoder;
final Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator;
private boolean controlStreamCreationInProgress;

final long maxTableCapacity;
Expand All @@ -61,9 +62,15 @@ public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapte
*/
Http3ConnectionHandler(boolean server, @Nullable ChannelHandler inboundControlStreamHandler,
@Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable) {
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable,
@Nullable Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator) {
this.unknownInboundStreamHandlerFactory = unknownInboundStreamHandlerFactory;
this.disableQpackDynamicTable = disableQpackDynamicTable;
if (nonStandardSettingsValidator != null) {
this.nonStandardSettingsValidator = nonStandardSettingsValidator;
} else {
this.nonStandardSettingsValidator = (id, value) -> false;
}
if (localSettings == null) {
localSettings = new DefaultHttp3SettingsFrame();
} else {
Expand All @@ -81,7 +88,8 @@ public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapte
qpackEncoder = new QpackEncoder();
codecFactory = Http3FrameCodec.newFactory(qpackDecoder, maxFieldSectionSize, qpackEncoder);
remoteControlStreamHandler = new Http3ControlStreamOutboundHandler(server, localSettings,
codecFactory.newCodec(Http3FrameTypeValidator.NO_VALIDATION, NO_STATE, NO_STATE));
codecFactory.newCodec(Http3FrameTypeValidator.NO_VALIDATION, NO_STATE, NO_STATE,
this.nonStandardSettingsValidator));
localControlStreamHandler = new Http3ControlStreamInboundHandler(server, inboundControlStreamHandler,
qpackEncoder, remoteControlStreamHandler);
}
Expand Down Expand Up @@ -121,7 +129,8 @@ public final boolean isGoAwayReceived() {
*/
public final ChannelHandler newCodec(Http3RequestStreamCodecState encodeState,
Http3RequestStreamCodecState decodeState) {
return codecFactory.newCodec(Http3RequestStreamFrameTypeValidator.INSTANCE, encodeState, decodeState);
return codecFactory.newCodec(
Http3RequestStreamFrameTypeValidator.INSTANCE, encodeState, decodeState, nonStandardSettingsValidator);
}

final ChannelHandler newRequestStreamValidationHandler(
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/io/netty/incubator/codec/http3/Http3FrameCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ final class Http3FrameCodec extends ByteToMessageDecoder implements ChannelOutbo
private final QpackEncoder qpackEncoder;
private final Http3RequestStreamCodecState encodeState;
private final Http3RequestStreamCodecState decodeState;
private final Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator;

private boolean firstFrame = true;
private boolean error;
Expand All @@ -77,19 +78,22 @@ static Http3FrameCodecFactory newFactory(QpackDecoder qpackDecoder,
checkNotNull(qpackDecoder, "qpackDecoder");

// QPACK decoder and encoder are shared between streams in a connection.
return (validator, encodeState, decodeState) -> new Http3FrameCodec(validator, qpackDecoder,
maxHeaderListSize, qpackEncoder, encodeState, decodeState);
return (validator, encodeState, decodeState,
nonStandardSettingsValidator) -> new Http3FrameCodec(validator, qpackDecoder,
maxHeaderListSize, qpackEncoder, encodeState, decodeState, nonStandardSettingsValidator);
}

Http3FrameCodec(Http3FrameTypeValidator validator, QpackDecoder qpackDecoder,
long maxHeaderListSize, QpackEncoder qpackEncoder, Http3RequestStreamCodecState encodeState,
Http3RequestStreamCodecState decodeState) {
Http3RequestStreamCodecState decodeState,
Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator) {
this.validator = checkNotNull(validator, "validator");
this.qpackDecoder = checkNotNull(qpackDecoder, "qpackDecoder");
this.maxHeaderListSize = checkPositive(maxHeaderListSize, "maxHeaderListSize");
this.qpackEncoder = checkNotNull(qpackEncoder, "qpackEncoder");
this.encodeState = checkNotNull(encodeState, "encodeState");
this.decodeState = checkNotNull(decodeState, "decodeState");
this.nonStandardSettingsValidator = nonStandardSettingsValidator;
}

@Override
Expand Down Expand Up @@ -352,7 +356,8 @@ private boolean enforceMaxPayloadLength(

@Nullable
private Http3SettingsFrame decodeSettings(ChannelHandlerContext ctx, ByteBuf in, int payLoadLength) {
Http3SettingsFrame settingsFrame = new DefaultHttp3SettingsFrame();
Http3SettingsFrame settingsFrame = new DefaultHttp3SettingsFrame(
new Http3Settings(nonStandardSettingsValidator));
while (payLoadLength > 0) {
int keyLen = numBytesForVariableLengthInteger(in.getByte(in.readerIndex()));
long key = readVariableLengthInteger(in, keyLen);
Expand Down Expand Up @@ -812,6 +817,7 @@ interface Http3FrameCodecFactory {
* @return new codec instance for the passed {@code streamType}.
*/
ChannelHandler newCodec(Http3FrameTypeValidator validator, Http3RequestStreamCodecState encodeState,
Http3RequestStreamCodecState decodeState);
Http3RequestStreamCodecState decodeState,
Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,35 @@ public Http3ServerConnectionHandler(ChannelHandler requestStreamHandler,
@Nullable ChannelHandler inboundControlStreamHandler,
@Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable) {
this(requestStreamHandler, inboundControlStreamHandler, unknownInboundStreamHandlerFactory,
localSettings, disableQpackDynamicTable, null);
}

/**
* Create a new instance.
* @param requestStreamHandler the {@link ChannelHandler} that is used for each new request stream.
* This handler will receive {@link Http3HeadersFrame} and
* {@link Http3DataFrame}s.
* @param inboundControlStreamHandler the {@link ChannelHandler} which will be notified about
* {@link Http3RequestStreamFrame}s or {@code null} if the user is not
* interested in these.
* @param unknownInboundStreamHandlerFactory the {@link LongFunction} that will provide a custom
* {@link ChannelHandler} for unknown inbound stream types or
* {@code null} if no special handling should be done.
* @param localSettings the local {@link Http3SettingsFrame} that should be sent to the
* remote peer or {@code null} if the default settings should be used.
* @param disableQpackDynamicTable If QPACK dynamic table should be disabled.
* @param nonStandardSettingsValidator the {@link Http3Settings.NonStandardHttp3SettingsValidator} to
* use when validating settings that are non-standard.
*/
public Http3ServerConnectionHandler(ChannelHandler requestStreamHandler,
@Nullable ChannelHandler inboundControlStreamHandler,
@Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
@Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable,
@Nullable Http3Settings.NonStandardHttp3SettingsValidator
nonStandardSettingsValidator) {
super(true, inboundControlStreamHandler, unknownInboundStreamHandlerFactory, localSettings,
disableQpackDynamicTable);
disableQpackDynamicTable, nonStandardSettingsValidator);
this.requestStreamHandler = ObjectUtil.checkNotNull(requestStreamHandler, "requestStreamHandler");
}

Expand All @@ -84,7 +111,7 @@ void initBidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel stream
void initUnidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel) {
final long maxTableCapacity = maxTableCapacity();
streamChannel.pipeline().addLast(
new Http3UnidirectionalStreamInboundServerHandler(codecFactory,
new Http3UnidirectionalStreamInboundServerHandler(codecFactory, nonStandardSettingsValidator,
localControlStreamHandler, remoteControlStreamHandler,
unknownInboundStreamHandlerFactory,
() -> new QpackEncoderHandler(maxTableCapacity, qpackDecoder),
Expand Down
52 changes: 31 additions & 21 deletions src/main/java/io/netty/incubator/codec/http3/Http3Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
public final class Http3Settings implements Iterable<Map.Entry<Long, Long>> {

private final LongObjectMap<Long> settings;
final NonStandardHttp3SettingsValidator nonStandardSettingsValidator;

/**
* QPACK maximum table capacity setting identifier (<b>0x1</b>).
Expand Down Expand Up @@ -119,26 +120,18 @@ public final class Http3Settings implements Iterable<Map.Entry<Long, Long>> {
* Creates a new instance
*/
public Http3Settings() {
this.settings = new LongObjectHashMap<>(Http3SettingIdentifier.values().length);
}

/**
* Creates a new instance with the specified initial capacity.
*
* @param initialCapacity initial capacity of the underlying map
*/
Http3Settings(int initialCapacity) {
this.settings = new LongObjectHashMap<>(initialCapacity);
this((id, v) -> false);
}

/**
* Creates a new instance with the specified initial capacity and load factor.
* Creates a new instance
*
* @param initialCapacity initial capacity of the underlying map
* @param loadFactor load factor for the underlying map
* @param nonStandardSettingsValidator the {@link NonStandardHttp3SettingsValidator} to use to check if a specific
* setting that is non-standard should be supported or not.
*/
Http3Settings(int initialCapacity, float loadFactor) {
this.settings = new LongObjectHashMap<>(initialCapacity, loadFactor);
public Http3Settings(NonStandardHttp3SettingsValidator nonStandardSettingsValidator) {
this.settings = new LongObjectHashMap<>(Http3SettingIdentifier.values().length);
this.nonStandardSettingsValidator = checkNotNull(nonStandardSettingsValidator, "nonStandardSettingsValidator");
}

/**
Expand All @@ -162,14 +155,15 @@ public Long put(long key, Long value) {

Http3SettingIdentifier identifier = Http3SettingIdentifier.fromId(key);

// When Non-Standard/Unknown settings identifier identifier present - Ignore
if (identifier == null) {
return null;
// When Non-Standard/Unknown settings identifier present check if we should ignore it or not.
if (!nonStandardSettingsValidator.validate(key, value)) {
return null;
}
} else {
//Validation
verifyStandardSetting(identifier, value);
}

//Validation
verifyStandardSetting(identifier, value);

return settings.put(key, value);
}

Expand Down Expand Up @@ -434,4 +428,20 @@ private static void verifyStandardSetting(Http3SettingIdentifier identifier, Lon
}
}
}

/**
* Allows to handle non-standard settings. By default non-standard settings will be ignore as defined by the
* RFC.
*/
public interface NonStandardHttp3SettingsValidator {
/**
* Validate the setting with the given id and value.
*
* @param id the id of the setting
* @param value the value of the setting
* @return {@code true} if the settings is supported, {@code false} otherwise.
* @throws IllegalArgumentException if the given {@code value} is not supported for the id.
*/
boolean validate(long id, Long value) throws IllegalArgumentException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ final class Http3UnidirectionalStreamInboundClientHandler extends Http3Unidirect

Http3UnidirectionalStreamInboundClientHandler(
Http3FrameCodecFactory codecFactory,
Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator,
Http3ControlStreamInboundHandler localControlStreamHandler,
Http3ControlStreamOutboundHandler remoteControlStreamHandler,
@Nullable LongFunction<ChannelHandler> unknownStreamHandlerFactory,
@Nullable LongFunction<ChannelHandler> pushStreamHandlerFactory,
Supplier<ChannelHandler> qpackEncoderHandlerFactory, Supplier<ChannelHandler> qpackDecoderHandlerFactory) {
super(codecFactory, localControlStreamHandler, remoteControlStreamHandler, unknownStreamHandlerFactory,
super(codecFactory, nonStandardSettingsValidator,
localControlStreamHandler, remoteControlStreamHandler, unknownStreamHandlerFactory,
qpackEncoderHandlerFactory, qpackDecoderHandlerFactory);
this.pushStreamHandlerFactory = pushStreamHandlerFactory == null ? __ -> ReleaseHandler.INSTANCE :
pushStreamHandlerFactory;
Expand Down
Loading
Loading