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 io.netty.handler.codec.http3.Http3SettingsFrame.HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS;
31 import static io.netty.handler.codec.http3.Http3SettingsFrame.HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY;
32 import static java.lang.Math.toIntExact;
33
34
35
36
37 public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapter {
38 final Http3FrameCodecFactory codecFactory;
39 final LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory;
40 final boolean disableQpackDynamicTable;
41 final Http3ControlStreamInboundHandler localControlStreamHandler;
42 final Http3ControlStreamOutboundHandler remoteControlStreamHandler;
43 final QpackDecoder qpackDecoder;
44 final QpackEncoder qpackEncoder;
45 final Http3Settings.NonStandardHttp3SettingsValidator nonStandardSettingsValidator;
46 private boolean controlStreamCreationInProgress;
47
48 final long maxTableCapacity;
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 this.unknownInboundStreamHandlerFactory = unknownInboundStreamHandlerFactory;
68 this.disableQpackDynamicTable = disableQpackDynamicTable;
69 if (nonStandardSettingsValidator != null) {
70 this.nonStandardSettingsValidator = nonStandardSettingsValidator;
71 } else {
72 this.nonStandardSettingsValidator = (id, value) -> false;
73 }
74 if (localSettings == null) {
75 localSettings = new DefaultHttp3SettingsFrame();
76 } else {
77 localSettings = DefaultHttp3SettingsFrame.copyOf(localSettings);
78 }
79 Long maxFieldSectionSize = localSettings.get(Http3SettingsFrame.HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE);
80 if (maxFieldSectionSize == null) {
81
82
83 maxFieldSectionSize = (1L << 62) - 1;
84 }
85 this.maxTableCapacity = localSettings.getOrDefault(HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0);
86 int maxBlockedStreams = toIntExact(localSettings.getOrDefault(HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS, 0));
87 qpackDecoder = new QpackDecoder(maxTableCapacity, maxBlockedStreams);
88 qpackEncoder = new QpackEncoder();
89 codecFactory = Http3FrameCodec.newFactory(qpackDecoder, maxFieldSectionSize, qpackEncoder);
90 remoteControlStreamHandler = new Http3ControlStreamOutboundHandler(server, localSettings,
91 codecFactory.newCodec(Http3FrameTypeValidator.NO_VALIDATION, NO_STATE, NO_STATE,
92 this.nonStandardSettingsValidator));
93 localControlStreamHandler = new Http3ControlStreamInboundHandler(server, inboundControlStreamHandler,
94 qpackEncoder, remoteControlStreamHandler);
95 }
96
97 private void createControlStreamIfNeeded(ChannelHandlerContext ctx) {
98 if (!controlStreamCreationInProgress && Http3.getLocalControlStream(ctx.channel()) == null) {
99 controlStreamCreationInProgress = true;
100 QuicChannel channel = (QuicChannel) ctx.channel();
101
102
103
104 channel.createStream(QuicStreamType.UNIDIRECTIONAL, remoteControlStreamHandler)
105 .addListener(f -> {
106 if (!f.isSuccess()) {
107 ctx.fireExceptionCaught(new Http3Exception(Http3ErrorCode.H3_STREAM_CREATION_ERROR,
108 "Unable to open control stream", f.cause()));
109 ctx.close();
110 } else {
111 Http3.setLocalControlStream(channel, (QuicStreamChannel) f.getNow());
112 }
113 });
114 }
115 }
116
117
118
119
120
121 public final boolean isGoAwayReceived() {
122 return localControlStreamHandler.isGoAwayReceived();
123 }
124
125
126
127
128
129
130 final ChannelHandler newCodec(Http3RequestStreamCodecState encodeState,
131 Http3RequestStreamCodecState decodeState) {
132 return codecFactory.newCodec(
133 Http3RequestStreamFrameTypeValidator.INSTANCE, encodeState, decodeState, nonStandardSettingsValidator);
134 }
135
136 final ChannelHandler newRequestStreamValidationHandler(
137 QuicStreamChannel forStream, Http3RequestStreamCodecState encodeState,
138 Http3RequestStreamCodecState decodeState) {
139 final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
140 assert qpackAttributes != null;
141 if (localControlStreamHandler.isServer()) {
142 return Http3RequestStreamValidationHandler.newServerValidator(qpackAttributes, qpackDecoder,
143 encodeState, decodeState);
144 }
145 return Http3RequestStreamValidationHandler.newClientValidator(localControlStreamHandler::isGoAwayReceived,
146 qpackAttributes, qpackDecoder, encodeState, decodeState);
147 }
148
149 final ChannelHandler newPushStreamValidationHandler(QuicStreamChannel forStream,
150 Http3RequestStreamCodecState decodeState) {
151 if (localControlStreamHandler.isServer()) {
152 return Http3PushStreamServerValidationHandler.INSTANCE;
153 }
154 final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
155 assert qpackAttributes != null;
156 return new Http3PushStreamClientValidationHandler(qpackAttributes, qpackDecoder, decodeState);
157 }
158
159 @Override
160 public void handlerAdded(ChannelHandlerContext ctx) {
161 QuicChannel channel = (QuicChannel) ctx.channel();
162 Http3.setQpackAttributes(channel, new QpackAttributes(channel, disableQpackDynamicTable));
163 if (ctx.channel().isActive()) {
164 createControlStreamIfNeeded(ctx);
165 }
166 }
167
168 @Override
169 public void channelActive(ChannelHandlerContext ctx) {
170 createControlStreamIfNeeded(ctx);
171
172 ctx.fireChannelActive();
173 }
174
175 @Override
176 public void channelRead(ChannelHandlerContext ctx, Object msg) {
177 if (msg instanceof QuicStreamChannel) {
178 QuicStreamChannel channel = (QuicStreamChannel) msg;
179 switch (channel.type()) {
180 case BIDIRECTIONAL:
181 initBidirectionalStream(ctx, channel);
182 break;
183 case UNIDIRECTIONAL:
184 initUnidirectionalStream(ctx, channel);
185 break;
186 default:
187 throw new Error("Unexpected channel type: " + channel.type());
188 }
189 }
190 ctx.fireChannelRead(msg);
191 }
192
193
194
195
196
197
198
199 abstract void initBidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel);
200
201
202
203
204
205
206
207 abstract void initUnidirectionalStream(ChannelHandlerContext ctx, QuicStreamChannel streamChannel);
208
209 long maxTableCapacity() {
210 return maxTableCapacity;
211 }
212
213
214
215
216 @Override
217 public boolean isSharable() {
218 return false;
219 }
220 }