1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http3;
17
18 import io.netty.channel.ChannelHandler;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelInboundHandlerAdapter;
21 import io.netty.handler.codec.http3.Http3FrameCodec.Http3FrameCodecFactory;
22 import io.netty.handler.codec.quic.QuicChannel;
23 import io.netty.handler.codec.quic.QuicStreamChannel;
24 import io.netty.handler.codec.quic.QuicStreamType;
25 import org.jetbrains.annotations.Nullable;
26
27 import java.util.function.LongFunction;
28
29 import static io.netty.handler.codec.http3.Http3RequestStreamCodecState.NO_STATE;
30 import static java.lang.Math.toIntExact;
31
32
33
34
35 public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapter {
36
37 final Http3FrameCodecFactory codecFactory;
38 final LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory;
39 final boolean disableQpackDynamicTable;
40 final Http3ControlStreamInboundHandler localControlStreamHandler;
41 final Http3ControlStreamOutboundHandler remoteControlStreamHandler;
42 final QpackDecoder qpackDecoder;
43 final QpackEncoder qpackEncoder;
44 final Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator;
45 private boolean controlStreamCreationInProgress;
46
47 final long maxTableCapacity;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 Http3ConnectionHandler(boolean server, @Nullable ChannelHandler inboundControlStreamHandler,
64 @Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
65 @Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable,
66 @Nullable Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator,
67 int maxUnknownFramePayloadLength) {
68 this(server, inboundControlStreamHandler, unknownInboundStreamHandlerFactory, localSettings,
69 disableQpackDynamicTable, nonStandardSettingsValidator, null, maxUnknownFramePayloadLength);
70 }
71
72 Http3ConnectionHandler(boolean server, @Nullable ChannelHandler inboundControlStreamHandler,
73 @Nullable LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory,
74 @Nullable Http3SettingsFrame localSettings, boolean disableQpackDynamicTable,
75 @Nullable Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator,
76 @Nullable QpackSensitivityDetector sensitivityDetector,
77 int maxUnknownFramePayloadLength) {
78 this.unknownInboundStreamHandlerFactory = unknownInboundStreamHandlerFactory;
79 this.disableQpackDynamicTable = disableQpackDynamicTable;
80 if (nonStandardSettingsValidator != null) {
81 this.nonStandardSettingsValidator = nonStandardSettingsValidator;
82 } else {
83 this.nonStandardSettingsValidator = (id, value) -> false;
84 }
85 if (localSettings == null) {
86 localSettings = new DefaultHttp3SettingsFrame(Http3Settings.defaultSettings());
87 } else {
88 localSettings = DefaultHttp3SettingsFrame.copyOf(localSettings);
89 }
90 Long maxFieldSectionSize = localSettings
91 .settings()
92 .getOrDefault(
93 Http3SettingIdentifier.HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE.id(),
94 Http3CodecUtils.DEFAULT_MAX_FIELD_SECTION_SIZE
95 );
96 this.maxTableCapacity = localSettings
97 .settings()
98 .getOrDefault(
99 Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY.id(),
100 0L
101 );
102 int maxBlockedStreams = toIntExact(localSettings
103 .settings()
104 .getOrDefault(
105 Http3SettingIdentifier.HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS.id(),
106 0L)
107 );
108 qpackDecoder = new QpackDecoder(maxTableCapacity, maxBlockedStreams);
109 qpackEncoder = new QpackEncoder(sensitivityDetector);
110 codecFactory = Http3FrameCodec.newFactory(
111 qpackDecoder, maxFieldSectionSize, maxUnknownFramePayloadLength, qpackEncoder);
112 remoteControlStreamHandler = new Http3ControlStreamOutboundHandler(server, localSettings,
113 codecFactory.newCodec(Http3FrameTypeValidator.NO_VALIDATION, NO_STATE, NO_STATE,
114 this.nonStandardSettingsValidator));
115 localControlStreamHandler = new Http3ControlStreamInboundHandler(server, inboundControlStreamHandler,
116 qpackEncoder, remoteControlStreamHandler);
117 }
118
119 private void createControlStreamIfNeeded(ChannelHandlerContext ctx) {
120 if (!controlStreamCreationInProgress && Http3.getLocalControlStream(ctx.channel()) == null) {
121 controlStreamCreationInProgress = true;
122 QuicChannel channel = (QuicChannel) ctx.channel();
123
124
125
126 channel.createStream(QuicStreamType.UNIDIRECTIONAL, remoteControlStreamHandler)
127 .addListener(f -> {
128 if (!f.isSuccess()) {
129 ctx.fireExceptionCaught(new Http3Exception(Http3ErrorCode.H3_STREAM_CREATION_ERROR,
130 "Unable to open control stream", f.cause()));
131 ctx.close();
132 } else {
133 Http3.setLocalControlStream(channel, (QuicStreamChannel) f.getNow());
134 }
135 });
136 }
137 }
138
139
140
141
142
143 public final boolean isGoAwayReceived() {
144 return localControlStreamHandler.isGoAwayReceived();
145 }
146
147
148
149
150
151
152 final ChannelHandler newCodec(Http3RequestStreamCodecState encodeState,
153 Http3RequestStreamCodecState decodeState) {
154 return codecFactory.newCodec(
155 Http3RequestStreamFrameTypeValidator.INSTANCE, encodeState, decodeState, nonStandardSettingsValidator);
156 }
157
158 final ChannelHandler newRequestStreamValidationHandler(
159 QuicStreamChannel forStream, Http3RequestStreamCodecState encodeState,
160 Http3RequestStreamCodecState decodeState) {
161 final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
162 assert qpackAttributes != null;
163 if (localControlStreamHandler.isServer()) {
164 return Http3RequestStreamValidationHandler.newServerValidator(qpackAttributes, qpackDecoder,
165 encodeState, decodeState);
166 }
167 return Http3RequestStreamValidationHandler.newClientValidator(localControlStreamHandler::isGoAwayReceived,
168 qpackAttributes, qpackDecoder, encodeState, decodeState);
169 }
170
171 final ChannelHandler newPushStreamValidationHandler(QuicStreamChannel forStream,
172 Http3RequestStreamCodecState decodeState) {
173 if (localControlStreamHandler.isServer()) {
174 return Http3PushStreamServerValidationHandler.INSTANCE;
175 }
176 final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
177 assert qpackAttributes != null;
178 return new Http3PushStreamClientValidationHandler(qpackAttributes, qpackDecoder, decodeState);
179 }
180
181 @Override
182 public void handlerAdded(ChannelHandlerContext ctx) {
183 QuicChannel channel = (QuicChannel) ctx.channel();
184 Http3.setQpackAttributes(channel, new QpackAttributes(channel, disableQpackDynamicTable));
185 if (ctx.channel().isActive()) {
186 createControlStreamIfNeeded(ctx);
187 }
188 }
189
190 @Override
191 public void channelActive(ChannelHandlerContext ctx) {
192 createControlStreamIfNeeded(ctx);
193
194 ctx.fireChannelActive();
195 }
196
197 @Override
198 public void channelRead(ChannelHandlerContext ctx, Object msg) {
199 if (msg instanceof QuicStreamChannel) {
200 QuicStreamChannel channel = (QuicStreamChannel) msg;
201 switch (channel.type()) {
202 case BIDIRECTIONAL:
203 initBidirectionalStream(ctx, channel);
204 break;
205 case UNIDIRECTIONAL:
206 initUnidirectionalStream(ctx, channel);
207 break;
208 default:
209 throw new Error("Unexpected channel type: " + channel.type());
210 }
211 }
212 ctx.fireChannelRead(msg);
213 }
214
215
216
217
218
219
220
221 abstract void initBidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel);
222
223
224
225
226
227
228
229 abstract void initUnidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel);
230
231 long maxTableCapacity() {
232 return maxTableCapacity;
233 }
234
235
236
237
238 @Override
239 public boolean isSharable() {
240 return false;
241 }
242 }