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