1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufUtil;
20 import io.netty.buffer.Unpooled;
21 import io.netty.channel.Channel;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelPromise;
24 import io.netty.channel.DefaultChannelPromise;
25 import io.netty.handler.ssl.ApplicationProtocolNames;
26 import io.netty.util.AsciiString;
27 import io.netty.util.LeakPresenceDetector;
28 import io.netty.util.concurrent.EventExecutor;
29
30 import static io.netty.buffer.Unpooled.directBuffer;
31 import static io.netty.buffer.Unpooled.unreleasableBuffer;
32 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
33 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
34 import static io.netty.handler.codec.http2.Http2Exception.headerListSizeError;
35 import static io.netty.util.CharsetUtil.UTF_8;
36 import static java.lang.Math.max;
37 import static java.lang.Math.min;
38 import static java.util.concurrent.TimeUnit.MILLISECONDS;
39 import static java.util.concurrent.TimeUnit.SECONDS;
40
41
42
43
44 public final class Http2CodecUtil {
45 public static final int CONNECTION_STREAM_ID = 0;
46 public static final int HTTP_UPGRADE_STREAM_ID = 1;
47 public static final CharSequence HTTP_UPGRADE_SETTINGS_HEADER = AsciiString.cached("HTTP2-Settings");
48 public static final CharSequence HTTP_UPGRADE_PROTOCOL_NAME = "h2c";
49 public static final CharSequence TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2;
50
51 public static final int PING_FRAME_PAYLOAD_LENGTH = 8;
52 public static final short MAX_UNSIGNED_BYTE = 0xff;
53
54
55
56
57 public static final int MAX_PADDING = 256;
58 public static final long MAX_UNSIGNED_INT = 0xffffffffL;
59 public static final int FRAME_HEADER_LENGTH = 9;
60 public static final int SETTING_ENTRY_LENGTH = 6;
61 public static final int PRIORITY_ENTRY_LENGTH = 5;
62 public static final int INT_FIELD_LENGTH = 4;
63 public static final short MAX_WEIGHT = 256;
64 public static final short MIN_WEIGHT = 1;
65
66 private static final ByteBuf CONNECTION_PREFACE = LeakPresenceDetector.staticInitializer(() ->
67 unreleasableBuffer(directBuffer(24).writeBytes("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(UTF_8)))
68 .asReadOnly());
69
70 private static final int MAX_PADDING_LENGTH_LENGTH = 1;
71 public static final int DATA_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH;
72 public static final int HEADERS_FRAME_HEADER_LENGTH =
73 FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH + INT_FIELD_LENGTH + 1;
74 public static final int PRIORITY_FRAME_LENGTH = FRAME_HEADER_LENGTH + PRIORITY_ENTRY_LENGTH;
75 public static final int RST_STREAM_FRAME_LENGTH = FRAME_HEADER_LENGTH + INT_FIELD_LENGTH;
76 public static final int PUSH_PROMISE_FRAME_HEADER_LENGTH =
77 FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH + INT_FIELD_LENGTH;
78 public static final int GO_AWAY_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + 2 * INT_FIELD_LENGTH;
79 public static final int WINDOW_UPDATE_FRAME_LENGTH = FRAME_HEADER_LENGTH + INT_FIELD_LENGTH;
80 public static final int CONTINUATION_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH;
81
82 public static final char SETTINGS_HEADER_TABLE_SIZE = 1;
83 public static final char SETTINGS_ENABLE_PUSH = 2;
84 public static final char SETTINGS_MAX_CONCURRENT_STREAMS = 3;
85 public static final char SETTINGS_INITIAL_WINDOW_SIZE = 4;
86 public static final char SETTINGS_MAX_FRAME_SIZE = 5;
87 public static final char SETTINGS_MAX_HEADER_LIST_SIZE = 6;
88 public static final char SETTINGS_ENABLE_CONNECT_PROTOCOL = 8;
89 public static final int NUM_STANDARD_SETTINGS = 7;
90
91 public static final long MAX_HEADER_TABLE_SIZE = MAX_UNSIGNED_INT;
92 public static final long MAX_CONCURRENT_STREAMS = MAX_UNSIGNED_INT;
93 public static final int MAX_INITIAL_WINDOW_SIZE = Integer.MAX_VALUE;
94 public static final int MAX_FRAME_SIZE_LOWER_BOUND = 0x4000;
95 public static final int MAX_FRAME_SIZE_UPPER_BOUND = 0xffffff;
96 public static final long MAX_HEADER_LIST_SIZE = MAX_UNSIGNED_INT;
97
98 public static final long MIN_HEADER_TABLE_SIZE = 0;
99 public static final long MIN_CONCURRENT_STREAMS = 0;
100 public static final int MIN_INITIAL_WINDOW_SIZE = 0;
101 public static final long MIN_HEADER_LIST_SIZE = 0;
102
103 public static final int DEFAULT_WINDOW_SIZE = 65535;
104 public static final short DEFAULT_PRIORITY_WEIGHT = 16;
105 public static final int DEFAULT_HEADER_TABLE_SIZE = 4096;
106
107
108
109
110
111 public static final long DEFAULT_HEADER_LIST_SIZE = 8192;
112 public static final int DEFAULT_MAX_FRAME_SIZE = MAX_FRAME_SIZE_LOWER_BOUND;
113
114
115
116
117 public static final int SMALLEST_MAX_CONCURRENT_STREAMS = 100;
118 static final int DEFAULT_MAX_RESERVED_STREAMS = SMALLEST_MAX_CONCURRENT_STREAMS;
119 static final int DEFAULT_MIN_ALLOCATION_CHUNK = 1024;
120 static final int DEFAULT_MAX_SMALL_CONTINUATION_FRAME = 16;
121
122
123
124
125
126
127 static final int DEFAULT_MAX_CONCURRENT_STREAMS = SMALLEST_MAX_CONCURRENT_STREAMS;
128
129
130
131
132
133
134
135
136 public static long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) {
137
138 return maxHeaderListSize + (maxHeaderListSize >>> 2);
139 }
140
141 public static final long DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS = MILLISECONDS.convert(30, SECONDS);
142
143 public static final int DEFAULT_MAX_QUEUED_CONTROL_FRAMES = 10000;
144
145
146
147
148
149
150
151 public static boolean isOutboundStream(boolean server, int streamId) {
152 boolean even = (streamId & 1) == 0;
153 return streamId > 0 && server == even;
154 }
155
156
157
158
159 public static boolean isStreamIdValid(int streamId) {
160 return streamId >= 0;
161 }
162
163 static boolean isStreamIdValid(int streamId, boolean server) {
164 return isStreamIdValid(streamId) && server == ((streamId & 1) == 0);
165 }
166
167
168
169
170 public static boolean isMaxFrameSizeValid(int maxFrameSize) {
171 return maxFrameSize >= MAX_FRAME_SIZE_LOWER_BOUND && maxFrameSize <= MAX_FRAME_SIZE_UPPER_BOUND;
172 }
173
174
175
176
177 public static ByteBuf connectionPrefaceBuf() {
178
179 return CONNECTION_PREFACE.retainedDuplicate();
180 }
181
182
183
184
185
186 public static Http2Exception getEmbeddedHttp2Exception(Throwable cause) {
187 while (cause != null) {
188 if (cause instanceof Http2Exception) {
189 return (Http2Exception) cause;
190 }
191 cause = cause.getCause();
192 }
193 return null;
194 }
195
196
197
198
199
200 public static ByteBuf toByteBuf(ChannelHandlerContext ctx, Throwable cause) {
201 if (cause == null || cause.getMessage() == null) {
202 return Unpooled.EMPTY_BUFFER;
203 }
204
205 return ByteBufUtil.writeUtf8(ctx.alloc(), cause.getMessage());
206 }
207
208
209
210
211 public static int readUnsignedInt(ByteBuf buf) {
212 return buf.readInt() & 0x7fffffff;
213 }
214
215
216
217
218 public static void writeFrameHeader(ByteBuf out, int payloadLength, byte type,
219 Http2Flags flags, int streamId) {
220 out.ensureWritable(FRAME_HEADER_LENGTH + payloadLength);
221 writeFrameHeaderInternal(out, payloadLength, type, flags, streamId);
222 }
223
224
225
226
227 public static int streamableBytes(StreamByteDistributor.StreamState state) {
228 return max(0, (int) min(state.pendingBytes(), state.windowSize()));
229 }
230
231
232
233
234
235
236
237
238
239 public static void headerListSizeExceeded(int streamId, long maxHeaderListSize,
240 boolean onDecode) throws Http2Exception {
241 throw headerListSizeError(streamId, PROTOCOL_ERROR, onDecode, "Header size exceeded max " +
242 "allowed size (%d)", maxHeaderListSize);
243 }
244
245
246
247
248
249
250
251
252 public static void headerListSizeExceeded(long maxHeaderListSize) throws Http2Exception {
253 throw connectionError(PROTOCOL_ERROR, "Header size exceeded max " +
254 "allowed size (%d)", maxHeaderListSize);
255 }
256
257 static void writeFrameHeaderInternal(ByteBuf out, int payloadLength, byte type,
258 Http2Flags flags, int streamId) {
259 out.writeMedium(payloadLength);
260 out.writeByte(type);
261 out.writeByte(flags.value());
262 out.writeInt(streamId);
263 }
264
265
266
267
268
269 static final class SimpleChannelPromiseAggregator extends DefaultChannelPromise {
270 private final ChannelPromise promise;
271 private int expectedCount;
272 private int doneCount;
273 private Throwable aggregateFailure;
274 private boolean doneAllocating;
275
276 SimpleChannelPromiseAggregator(ChannelPromise promise, Channel c, EventExecutor e) {
277 super(c, e);
278 assert promise != null && !promise.isDone();
279 this.promise = promise;
280 }
281
282
283
284
285
286
287 public ChannelPromise newPromise() {
288 assert !doneAllocating : "Done allocating. No more promises can be allocated.";
289 ++expectedCount;
290 return this;
291 }
292
293
294
295
296
297
298 public ChannelPromise doneAllocatingPromises() {
299 if (!doneAllocating) {
300 doneAllocating = true;
301 if (doneCount == expectedCount || expectedCount == 0) {
302 return setPromise();
303 }
304 }
305 return this;
306 }
307
308 @Override
309 public boolean tryFailure(Throwable cause) {
310 if (allowFailure()) {
311 ++doneCount;
312 setAggregateFailure(cause);
313 if (allPromisesDone()) {
314 return tryPromise();
315 }
316
317
318 return true;
319 }
320 return false;
321 }
322
323
324
325
326
327
328
329 @Override
330 public ChannelPromise setFailure(Throwable cause) {
331 if (allowFailure()) {
332 ++doneCount;
333 setAggregateFailure(cause);
334 if (allPromisesDone()) {
335 return setPromise();
336 }
337 }
338 return this;
339 }
340
341 @Override
342 public ChannelPromise setSuccess(Void result) {
343 if (awaitingPromises()) {
344 ++doneCount;
345 if (allPromisesDone()) {
346 setPromise();
347 }
348 }
349 return this;
350 }
351
352 @Override
353 public boolean trySuccess(Void result) {
354 if (awaitingPromises()) {
355 ++doneCount;
356 if (allPromisesDone()) {
357 return tryPromise();
358 }
359
360
361 return true;
362 }
363 return false;
364 }
365
366 private boolean allowFailure() {
367 return awaitingPromises() || expectedCount == 0;
368 }
369
370 private boolean awaitingPromises() {
371 return doneCount < expectedCount;
372 }
373
374 private boolean allPromisesDone() {
375 return doneCount == expectedCount && doneAllocating;
376 }
377
378 private ChannelPromise setPromise() {
379 if (aggregateFailure == null) {
380 promise.setSuccess();
381 return super.setSuccess(null);
382 } else {
383 promise.setFailure(aggregateFailure);
384 return super.setFailure(aggregateFailure);
385 }
386 }
387
388 private boolean tryPromise() {
389 if (aggregateFailure == null) {
390 promise.trySuccess();
391 return super.trySuccess(null);
392 } else {
393 promise.tryFailure(aggregateFailure);
394 return super.tryFailure(aggregateFailure);
395 }
396 }
397
398 private void setAggregateFailure(Throwable cause) {
399 if (aggregateFailure == null) {
400 aggregateFailure = cause;
401 }
402 }
403 }
404
405 public static void verifyPadding(int padding) {
406 if (padding < 0 || padding > MAX_PADDING) {
407 throw new IllegalArgumentException(String.format("Invalid padding '%d'. Padding must be between 0 and " +
408 "%d (inclusive).", padding, MAX_PADDING));
409 }
410 }
411 private Http2CodecUtil() { }
412 }