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