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.buffer.ByteBuf;
19 import io.netty.buffer.Unpooled;
20 import io.netty.channel.Channel;
21 import io.netty.channel.ChannelFuture;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.handler.codec.quic.QuicChannel;
24 import io.netty.handler.codec.quic.QuicStreamChannel;
25 import io.netty.handler.codec.quic.QuicStreamType;
26 import io.netty.util.CharsetUtil;
27 import io.netty.util.internal.ObjectUtil;
28 import io.netty.util.internal.StringUtil;
29 import org.jetbrains.annotations.Nullable;
30
31 import static io.netty.channel.ChannelFutureListener.CLOSE_ON_FAILURE;
32 import static io.netty.handler.codec.http3.Http3ErrorCode.H3_INTERNAL_ERROR;
33 import static io.netty.handler.codec.quic.QuicStreamType.UNIDIRECTIONAL;
34
35 final class Http3CodecUtils {
36
37
38 static final long MIN_RESERVED_FRAME_TYPE = 0x1f * 1 + 0x21;
39 static final long MAX_RESERVED_FRAME_TYPE = 0x1f * (long) Integer.MAX_VALUE + 0x21;
40
41
42 static final int HTTP3_DATA_FRAME_TYPE = 0x0;
43 static final int HTTP3_HEADERS_FRAME_TYPE = 0x1;
44 static final int HTTP3_CANCEL_PUSH_FRAME_TYPE = 0x3;
45 static final int HTTP3_SETTINGS_FRAME_TYPE = 0x4;
46 static final int HTTP3_PUSH_PROMISE_FRAME_TYPE = 0x5;
47 static final int HTTP3_GO_AWAY_FRAME_TYPE = 0x7;
48 static final int HTTP3_MAX_PUSH_ID_FRAME_TYPE = 0xd;
49
50 static final int HTTP3_CANCEL_PUSH_FRAME_MAX_LEN = 8;
51 static final int HTTP3_SETTINGS_FRAME_MAX_LEN = 256;
52 static final int HTTP3_GO_AWAY_FRAME_MAX_LEN = 8;
53 static final int HTTP3_MAX_PUSH_ID_FRAME_MAX_LEN = 8;
54
55 static final int HTTP3_CONTROL_STREAM_TYPE = 0x00;
56 static final int HTTP3_PUSH_STREAM_TYPE = 0x01;
57 static final int HTTP3_QPACK_ENCODER_STREAM_TYPE = 0x02;
58 static final int HTTP3_QPACK_DECODER_STREAM_TYPE = 0x03;
59
60
61
62
63
64 static final long DEFAULT_MAX_FIELD_SECTION_SIZE = 8192;
65
66
67 static final int DEFAULT_MAX_UNKNOWN_FRAME_PAYLOAD_LENGTH = 32 * 1024;
68
69 private Http3CodecUtils() { }
70
71 static long checkIsReservedFrameType(long type) {
72 return ObjectUtil.checkInRange(type, MIN_RESERVED_FRAME_TYPE, MAX_RESERVED_FRAME_TYPE, "type");
73 }
74
75 static boolean isReservedFrameType(long type) {
76 return type >= MIN_RESERVED_FRAME_TYPE && type <= MAX_RESERVED_FRAME_TYPE;
77 }
78
79
80
81
82
83
84
85 static boolean isServerInitiatedQuicStream(QuicStreamChannel channel) {
86
87
88 return channel.streamId() % 2 != 0;
89 }
90
91 static boolean isReservedHttp2FrameType(long type) {
92 switch ((int) type) {
93
94
95 case 0x2:
96 case 0x6:
97 case 0x8:
98 case 0x9:
99 return true;
100 default:
101 return false;
102 }
103 }
104
105 static boolean isReservedHttp2Setting(long key) {
106
107
108 return 0x2L <= key && key <= 0x5L;
109 }
110
111
112
113
114
115
116
117 static int numBytesForVariableLengthInteger(long value) {
118 if (value <= 63) {
119 return 1;
120 }
121 if (value <= 16383) {
122 return 2;
123 }
124 if (value <= 1073741823) {
125 return 4;
126 }
127 if (value <= 4611686018427387903L) {
128 return 8;
129 }
130 throw new IllegalArgumentException();
131 }
132
133
134
135
136
137
138
139 static void writeVariableLengthInteger(ByteBuf out, long value) {
140 int numBytes = numBytesForVariableLengthInteger(value);
141 writeVariableLengthInteger(out, value, numBytes);
142 }
143
144
145
146
147
148
149
150 static void writeVariableLengthInteger(ByteBuf out, long value, int numBytes) {
151 int writerIndex = out.writerIndex();
152 switch (numBytes) {
153 case 1:
154 out.writeByte((byte) value);
155 break;
156 case 2:
157 out.writeShort((short) value);
158 encodeLengthIntoBuffer(out, writerIndex, (byte) 0x40);
159 break;
160 case 4:
161 out.writeInt((int) value);
162 encodeLengthIntoBuffer(out, writerIndex, (byte) 0x80);
163 break;
164 case 8:
165 out.writeLong(value);
166 encodeLengthIntoBuffer(out, writerIndex, (byte) 0xc0);
167 break;
168 default:
169 throw new IllegalArgumentException();
170 }
171 }
172
173 private static void encodeLengthIntoBuffer(ByteBuf out, int index, byte b) {
174 out.setByte(index, out.getByte(index) | b);
175 }
176
177
178
179
180
181
182
183 static long readVariableLengthInteger(ByteBuf in, int len) {
184 switch (len) {
185 case 1:
186 return in.readUnsignedByte();
187 case 2:
188 return in.readUnsignedShort() & 0x3fff;
189 case 4:
190 return in.readUnsignedInt() & 0x3fffffff;
191 case 8:
192 return in.readLong() & 0x3fffffffffffffffL;
193 default:
194 throw new IllegalArgumentException();
195 }
196 }
197
198
199
200
201
202
203
204 static int numBytesForVariableLengthInteger(byte b) {
205 byte val = (byte) (b >> 6);
206 if ((val & 1) != 0) {
207 if ((val & 2) != 0) {
208 return 8;
209 }
210 return 2;
211 }
212 if ((val & 2) != 0) {
213 return 4;
214 }
215 return 1;
216 }
217
218 static void criticalStreamClosed(ChannelHandlerContext ctx) {
219 if (ctx.channel().parent().isActive()) {
220
221 Http3CodecUtils.connectionError(
222 ctx, Http3ErrorCode.H3_CLOSED_CRITICAL_STREAM, "Critical stream closed.", false);
223 }
224 }
225
226
227
228
229
230
231
232 static void connectionError(ChannelHandlerContext ctx, Http3Exception exception, boolean fireException) {
233 if (fireException) {
234 ctx.fireExceptionCaught(exception);
235 }
236 connectionError(ctx.channel(), exception.errorCode(), exception.getMessage());
237 }
238
239
240
241
242
243
244
245
246
247 static void connectionError(ChannelHandlerContext ctx, Http3ErrorCode errorCode,
248 @Nullable String msg, boolean fireException) {
249 if (fireException) {
250 ctx.fireExceptionCaught(new Http3Exception(errorCode, msg));
251 }
252 connectionError(ctx.channel(), errorCode, msg);
253 }
254
255
256
257
258
259
260 static void closeOnFailure(ChannelFuture future) {
261 if (future.isDone() && !future.isSuccess()) {
262 future.channel().close();
263 return;
264 }
265 future.addListener(CLOSE_ON_FAILURE);
266 }
267
268
269
270
271
272
273
274
275 static void connectionError(Channel channel, Http3ErrorCode errorCode, @Nullable String msg) {
276 final QuicChannel quicChannel;
277
278 if (channel instanceof QuicChannel) {
279 quicChannel = (QuicChannel) channel;
280 } else {
281 quicChannel = (QuicChannel) channel.parent();
282 }
283 final ByteBuf buffer;
284 if (msg != null) {
285
286 buffer = quicChannel.alloc().buffer();
287 buffer.writeCharSequence(msg, CharsetUtil.US_ASCII);
288 } else {
289 buffer = Unpooled.EMPTY_BUFFER;
290 }
291 quicChannel.close(true, errorCode.code, buffer);
292 }
293
294 static void streamError(ChannelHandlerContext ctx, Http3ErrorCode errorCode) {
295 ((QuicStreamChannel) ctx.channel()).shutdownOutput(errorCode.code);
296 }
297
298 static void readIfNoAutoRead(ChannelHandlerContext ctx) {
299 if (!ctx.channel().config().isAutoRead()) {
300 ctx.read();
301 }
302 }
303
304
305
306
307
308
309
310
311 @Nullable
312 static Http3ConnectionHandler getConnectionHandlerOrClose(QuicChannel ch) {
313 Http3ConnectionHandler connectionHandler = ch.pipeline().get(Http3ConnectionHandler.class);
314 if (connectionHandler == null) {
315 connectionError(ch, H3_INTERNAL_ERROR, "Couldn't obtain the " +
316 StringUtil.simpleClassName(Http3ConnectionHandler.class) + " of the parent Channel");
317 return null;
318 }
319 return connectionHandler;
320 }
321
322
323
324
325
326
327
328
329 static void verifyIsUnidirectional(QuicStreamChannel ch) {
330 if (ch.type() != UNIDIRECTIONAL) {
331 throw new IllegalArgumentException("Invalid stream type: " + ch.type() + " for stream: " + ch.streamId());
332 }
333 }
334 }