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