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