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