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