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 java.io.Closeable;
19
20 import io.netty.buffer.ByteBuf;
21 import io.netty.channel.ChannelFuture;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelPromise;
24 import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
25 import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
26 import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
27 import io.netty.util.LeakPresenceDetector;
28 import io.netty.util.internal.PlatformDependent;
29 import io.netty.util.internal.logging.InternalLogger;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31
32 import static io.netty.buffer.Unpooled.directBuffer;
33 import static io.netty.buffer.Unpooled.unreleasableBuffer;
34 import static io.netty.handler.codec.http2.Http2CodecUtil.CONTINUATION_FRAME_HEADER_LENGTH;
35 import static io.netty.handler.codec.http2.Http2CodecUtil.DATA_FRAME_HEADER_LENGTH;
36 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
37 import static io.netty.handler.codec.http2.Http2CodecUtil.FRAME_HEADER_LENGTH;
38 import static io.netty.handler.codec.http2.Http2CodecUtil.GO_AWAY_FRAME_HEADER_LENGTH;
39 import static io.netty.handler.codec.http2.Http2CodecUtil.HEADERS_FRAME_HEADER_LENGTH;
40 import static io.netty.handler.codec.http2.Http2CodecUtil.INT_FIELD_LENGTH;
41 import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_BYTE;
42 import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_INT;
43 import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT;
44 import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT;
45 import static io.netty.handler.codec.http2.Http2CodecUtil.PING_FRAME_PAYLOAD_LENGTH;
46 import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_ENTRY_LENGTH;
47 import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_FRAME_LENGTH;
48 import static io.netty.handler.codec.http2.Http2CodecUtil.PUSH_PROMISE_FRAME_HEADER_LENGTH;
49 import static io.netty.handler.codec.http2.Http2CodecUtil.RST_STREAM_FRAME_LENGTH;
50 import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
51 import static io.netty.handler.codec.http2.Http2CodecUtil.WINDOW_UPDATE_FRAME_LENGTH;
52 import static io.netty.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid;
53 import static io.netty.handler.codec.http2.Http2CodecUtil.verifyPadding;
54 import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeaderInternal;
55 import static io.netty.handler.codec.http2.Http2Error.FRAME_SIZE_ERROR;
56 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
57 import static io.netty.handler.codec.http2.Http2FrameTypes.CONTINUATION;
58 import static io.netty.handler.codec.http2.Http2FrameTypes.DATA;
59 import static io.netty.handler.codec.http2.Http2FrameTypes.GO_AWAY;
60 import static io.netty.handler.codec.http2.Http2FrameTypes.HEADERS;
61 import static io.netty.handler.codec.http2.Http2FrameTypes.PING;
62 import static io.netty.handler.codec.http2.Http2FrameTypes.PRIORITY;
63 import static io.netty.handler.codec.http2.Http2FrameTypes.PUSH_PROMISE;
64 import static io.netty.handler.codec.http2.Http2FrameTypes.RST_STREAM;
65 import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS;
66 import static io.netty.handler.codec.http2.Http2FrameTypes.WINDOW_UPDATE;
67 import static io.netty.util.internal.ObjectUtil.checkNotNull;
68 import static io.netty.util.internal.ObjectUtil.checkPositive;
69 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
70 import static java.lang.Math.max;
71 import static java.lang.Math.min;
72
73
74
75
76 public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSizePolicy, Configuration {
77 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2FrameWriter.class);
78
79 private static final String STREAM_ID = "Stream ID";
80 private static final String STREAM_DEPENDENCY = "Stream Dependency";
81
82
83
84
85
86 private static final ByteBuf ZERO_BUFFER = LeakPresenceDetector.staticInitializer(() ->
87 unreleasableBuffer(directBuffer(MAX_UNSIGNED_BYTE).writeZero(MAX_UNSIGNED_BYTE)).asReadOnly());
88
89 private final Http2HeadersEncoder headersEncoder;
90 private int maxFrameSize;
91
92 public DefaultHttp2FrameWriter() {
93 this(new DefaultHttp2HeadersEncoder());
94 }
95
96 public DefaultHttp2FrameWriter(SensitivityDetector headersSensitivityDetector) {
97 this(new DefaultHttp2HeadersEncoder(headersSensitivityDetector));
98 }
99
100 public DefaultHttp2FrameWriter(SensitivityDetector headersSensitivityDetector, boolean ignoreMaxHeaderListSize) {
101 this(new DefaultHttp2HeadersEncoder(headersSensitivityDetector, ignoreMaxHeaderListSize));
102 }
103
104
105
106
107 public DefaultHttp2FrameWriter(Http2HeadersEncoder headersEncoder) {
108 this.headersEncoder = headersEncoder;
109 maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
110 }
111
112 @Override
113 public Configuration configuration() {
114 return this;
115 }
116
117 @Override
118 public Http2HeadersEncoder.Configuration headersConfiguration() {
119 return headersEncoder.configuration();
120 }
121
122 @Override
123 public Http2FrameSizePolicy frameSizePolicy() {
124 return this;
125 }
126
127 @Override
128 public void maxFrameSize(int max) throws Http2Exception {
129 if (!isMaxFrameSizeValid(max)) {
130 throw connectionError(FRAME_SIZE_ERROR, "Invalid MAX_FRAME_SIZE specified in sent settings: %d", max);
131 }
132 maxFrameSize = max;
133 }
134
135 @Override
136 public int maxFrameSize() {
137 return maxFrameSize;
138 }
139
140 @Override
141 public void close() {
142 if (headersEncoder instanceof Closeable) {
143 try {
144 ((Closeable) headersEncoder).close();
145 } catch (Exception e) {
146 throw new RuntimeException(e);
147 }
148 }
149 }
150
151 @Override
152 public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
153 int padding, boolean endStream, ChannelPromise promise) {
154 final SimpleChannelPromiseAggregator promiseAggregator =
155 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
156 ByteBuf frameHeader = null;
157 try {
158 verifyStreamId(streamId, STREAM_ID);
159 verifyPadding(padding);
160
161 int remainingData = data.readableBytes();
162 Http2Flags flags = new Http2Flags();
163 flags.endOfStream(false);
164 flags.paddingPresent(false);
165
166 if (remainingData > maxFrameSize) {
167 frameHeader = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
168 writeFrameHeaderInternal(frameHeader, maxFrameSize, DATA, flags, streamId);
169 do {
170
171 ctx.write(frameHeader.retainedSlice(), promiseAggregator.newPromise());
172
173
174 ctx.write(data.readRetainedSlice(maxFrameSize), promiseAggregator.newPromise());
175
176 remainingData -= maxFrameSize;
177
178 } while (remainingData > maxFrameSize);
179 }
180
181 if (padding == 0) {
182
183 if (frameHeader != null) {
184 frameHeader.release();
185 frameHeader = null;
186 }
187 ByteBuf frameHeader2 = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
188 flags.endOfStream(endStream);
189 writeFrameHeaderInternal(frameHeader2, remainingData, DATA, flags, streamId);
190 ctx.write(frameHeader2, promiseAggregator.newPromise());
191
192
193 ByteBuf lastFrame = data.readSlice(remainingData);
194 data = null;
195 ctx.write(lastFrame, promiseAggregator.newPromise());
196 } else {
197 if (remainingData != maxFrameSize) {
198 if (frameHeader != null) {
199 frameHeader.release();
200 frameHeader = null;
201 }
202 } else {
203 remainingData -= maxFrameSize;
204
205 ByteBuf lastFrame;
206 if (frameHeader == null) {
207 lastFrame = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
208 writeFrameHeaderInternal(lastFrame, maxFrameSize, DATA, flags, streamId);
209 } else {
210 lastFrame = frameHeader.slice();
211 frameHeader = null;
212 }
213 ctx.write(lastFrame, promiseAggregator.newPromise());
214
215
216 lastFrame = data.readableBytes() != maxFrameSize ? data.readSlice(maxFrameSize) : data;
217 data = null;
218 ctx.write(lastFrame, promiseAggregator.newPromise());
219 }
220
221 do {
222 int frameDataBytes = min(remainingData, maxFrameSize);
223 int framePaddingBytes = min(padding, max(0, maxFrameSize - 1 - frameDataBytes));
224
225
226 padding -= framePaddingBytes;
227 remainingData -= frameDataBytes;
228
229
230 ByteBuf frameHeader2 = ctx.alloc().buffer(DATA_FRAME_HEADER_LENGTH);
231 flags.endOfStream(endStream && remainingData == 0 && padding == 0);
232 flags.paddingPresent(framePaddingBytes > 0);
233 writeFrameHeaderInternal(frameHeader2, framePaddingBytes + frameDataBytes, DATA, flags, streamId);
234 writePaddingLength(frameHeader2, framePaddingBytes);
235 ctx.write(frameHeader2, promiseAggregator.newPromise());
236
237
238 if (data != null) {
239 if (remainingData == 0) {
240 ByteBuf lastFrame = data.readSlice(frameDataBytes);
241 data = null;
242 ctx.write(lastFrame, promiseAggregator.newPromise());
243 } else {
244 ctx.write(data.readRetainedSlice(frameDataBytes), promiseAggregator.newPromise());
245 }
246 }
247
248 if (paddingBytes(framePaddingBytes) > 0) {
249 ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes)),
250 promiseAggregator.newPromise());
251 }
252 } while (remainingData != 0 || padding != 0);
253 }
254 } catch (Throwable cause) {
255 if (frameHeader != null) {
256 frameHeader.release();
257 }
258
259
260 try {
261 if (data != null) {
262 data.release();
263 }
264 } finally {
265 promiseAggregator.setFailure(cause);
266 promiseAggregator.doneAllocatingPromises();
267 }
268 return promiseAggregator;
269 }
270 return promiseAggregator.doneAllocatingPromises();
271 }
272
273 @Override
274 public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
275 Http2Headers headers, int padding, boolean endStream, ChannelPromise promise) {
276 return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
277 false, 0, (short) 0, false, promise);
278 }
279
280 @Override
281 public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
282 Http2Headers headers, int streamDependency, short weight, boolean exclusive,
283 int padding, boolean endStream, ChannelPromise promise) {
284 return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
285 true, streamDependency, weight, exclusive, promise);
286 }
287
288 @Override
289 public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId,
290 int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
291 try {
292 verifyStreamId(streamId, STREAM_ID);
293 verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
294 verifyWeight(weight);
295
296 ByteBuf buf = ctx.alloc().buffer(PRIORITY_FRAME_LENGTH);
297 writeFrameHeaderInternal(buf, PRIORITY_ENTRY_LENGTH, PRIORITY, new Http2Flags(), streamId);
298 buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
299
300 buf.writeByte(weight - 1);
301 return ctx.write(buf, promise);
302 } catch (Throwable t) {
303 return promise.setFailure(t);
304 }
305 }
306
307 @Override
308 public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
309 ChannelPromise promise) {
310 try {
311 verifyStreamId(streamId, STREAM_ID);
312 verifyErrorCode(errorCode);
313
314 ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH);
315 writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId);
316 buf.writeInt((int) errorCode);
317 return ctx.write(buf, promise);
318 } catch (Throwable t) {
319 return promise.setFailure(t);
320 }
321 }
322
323 @Override
324 public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
325 ChannelPromise promise) {
326 try {
327 checkNotNull(settings, "settings");
328 int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
329 verifyPayloadLength(SETTINGS, payloadLength);
330 ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + payloadLength);
331 writeFrameHeaderInternal(buf, payloadLength, SETTINGS, new Http2Flags(), 0);
332 for (Http2Settings.PrimitiveEntry<Long> entry : settings.entries()) {
333 buf.writeChar(entry.key());
334 buf.writeInt(entry.value().intValue());
335 }
336 return ctx.write(buf, promise);
337 } catch (Throwable t) {
338 return promise.setFailure(t);
339 }
340 }
341
342 @Override
343 public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
344 try {
345 ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
346 writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
347 return ctx.write(buf, promise);
348 } catch (Throwable t) {
349 return promise.setFailure(t);
350 }
351 }
352
353 @Override
354 public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
355 Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
356 ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH);
357
358
359 writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0);
360 buf.writeLong(data);
361 return ctx.write(buf, promise);
362 }
363
364 @Override
365 public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId,
366 int promisedStreamId, Http2Headers headers, int padding, ChannelPromise promise) {
367 ByteBuf headerBlock = null;
368 ByteBuf fragment = null;
369 SimpleChannelPromiseAggregator promiseAggregator =
370 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
371 try {
372 verifyStreamId(streamId, STREAM_ID);
373 verifyStreamId(promisedStreamId, "Promised Stream ID");
374 verifyPadding(padding);
375
376
377 headerBlock = ctx.alloc().buffer();
378 headersEncoder.encodeHeaders(streamId, headers, headerBlock);
379
380
381 Http2Flags flags = new Http2Flags().paddingPresent(padding > 0);
382
383 int nonFragmentLength = INT_FIELD_LENGTH + padding;
384 int maxFragmentLength = maxFrameSize - nonFragmentLength;
385 fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength));
386
387 flags.endOfHeaders(!headerBlock.isReadable());
388
389 int payloadLength = fragment.readableBytes() + nonFragmentLength;
390 ByteBuf buf = ctx.alloc().buffer(PUSH_PROMISE_FRAME_HEADER_LENGTH);
391 writeFrameHeaderInternal(buf, payloadLength, PUSH_PROMISE, flags, streamId);
392 writePaddingLength(buf, padding);
393
394
395 buf.writeInt(promisedStreamId);
396 ctx.write(buf, promiseAggregator.newPromise());
397
398
399 ctx.write(fragment, promiseAggregator.newPromise());
400 fragment = null;
401
402
403 if (paddingBytes(padding) > 0) {
404 ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
405 }
406
407 if (!flags.endOfHeaders()) {
408 writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator);
409 }
410 } catch (Http2Exception e) {
411 promiseAggregator.setFailure(e);
412 } catch (Throwable t) {
413 promiseAggregator.setFailure(t);
414 promiseAggregator.doneAllocatingPromises();
415 PlatformDependent.throwException(t);
416 } finally {
417 if (fragment != null) {
418 fragment.release();
419 }
420 if (headerBlock != null) {
421 headerBlock.release();
422 }
423 }
424 return promiseAggregator.doneAllocatingPromises();
425 }
426
427 @Override
428 public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
429 ByteBuf debugData, ChannelPromise promise) {
430 SimpleChannelPromiseAggregator promiseAggregator =
431 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
432 try {
433 verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
434 verifyErrorCode(errorCode);
435
436
437
438 int maxDebugDataLength = maxFrameSize - 8;
439 if (debugData.readableBytes() > maxDebugDataLength) {
440 logger.warn("GOAWAY debug data of {} bytes exceeds the maximum of {} bytes allowed by " +
441 "SETTINGS_MAX_FRAME_SIZE, truncating it.", debugData.readableBytes(), maxDebugDataLength);
442 debugData = debugData.readSlice(maxDebugDataLength);
443 }
444
445 int payloadLength = 8 + debugData.readableBytes();
446 ByteBuf buf = ctx.alloc().buffer(GO_AWAY_FRAME_HEADER_LENGTH);
447
448
449 writeFrameHeaderInternal(buf, payloadLength, GO_AWAY, new Http2Flags(), 0);
450 buf.writeInt(lastStreamId);
451 buf.writeInt((int) errorCode);
452 ctx.write(buf, promiseAggregator.newPromise());
453 } catch (Throwable t) {
454 try {
455 debugData.release();
456 } finally {
457 promiseAggregator.setFailure(t);
458 promiseAggregator.doneAllocatingPromises();
459 }
460 return promiseAggregator;
461 }
462
463 try {
464 ctx.write(debugData, promiseAggregator.newPromise());
465 } catch (Throwable t) {
466 promiseAggregator.setFailure(t);
467 }
468 return promiseAggregator.doneAllocatingPromises();
469 }
470
471 @Override
472 public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
473 int windowSizeIncrement, ChannelPromise promise) {
474 try {
475 verifyStreamOrConnectionId(streamId, STREAM_ID);
476 verifyWindowSizeIncrement(windowSizeIncrement);
477
478 ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
479 writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
480 buf.writeInt(windowSizeIncrement);
481 return ctx.write(buf, promise);
482 } catch (Throwable t) {
483 return promise.setFailure(t);
484 }
485 }
486
487 @Override
488 public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
489 Http2Flags flags, ByteBuf payload, ChannelPromise promise) {
490 SimpleChannelPromiseAggregator promiseAggregator =
491 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
492 try {
493 verifyStreamOrConnectionId(streamId, STREAM_ID);
494
495
496 verifyPayloadLength(frameType, payload.readableBytes());
497 ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
498
499
500 writeFrameHeaderInternal(buf, payload.readableBytes(), frameType, flags, streamId);
501 ctx.write(buf, promiseAggregator.newPromise());
502 } catch (Throwable t) {
503 try {
504 payload.release();
505 } finally {
506 promiseAggregator.setFailure(t);
507 promiseAggregator.doneAllocatingPromises();
508 }
509 return promiseAggregator;
510 }
511 try {
512 ctx.write(payload, promiseAggregator.newPromise());
513 } catch (Throwable t) {
514 promiseAggregator.setFailure(t);
515 }
516 return promiseAggregator.doneAllocatingPromises();
517 }
518
519 private ChannelFuture writeHeadersInternal(ChannelHandlerContext ctx,
520 int streamId, Http2Headers headers, int padding, boolean endStream,
521 boolean hasPriority, int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
522 ByteBuf headerBlock = null;
523 ByteBuf fragment = null;
524 SimpleChannelPromiseAggregator promiseAggregator =
525 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
526 try {
527 verifyStreamId(streamId, STREAM_ID);
528 verifyPadding(padding);
529 if (hasPriority) {
530 verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
531 verifyWeight(weight);
532 }
533
534
535 headerBlock = ctx.alloc().buffer();
536 headersEncoder.encodeHeaders(streamId, headers, headerBlock);
537
538 Http2Flags flags =
539 new Http2Flags().endOfStream(endStream).priorityPresent(hasPriority).paddingPresent(padding > 0);
540
541
542 int nonFragmentBytes = padding + flags.getNumPriorityBytes();
543 int maxFragmentLength = maxFrameSize - nonFragmentBytes;
544 fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength));
545
546
547 flags.endOfHeaders(!headerBlock.isReadable());
548
549 int payloadLength = fragment.readableBytes() + nonFragmentBytes;
550 ByteBuf buf = ctx.alloc().buffer(HEADERS_FRAME_HEADER_LENGTH);
551 writeFrameHeaderInternal(buf, payloadLength, HEADERS, flags, streamId);
552 writePaddingLength(buf, padding);
553
554 if (hasPriority) {
555 buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
556
557
558 buf.writeByte(weight - 1);
559 }
560 ctx.write(buf, promiseAggregator.newPromise());
561
562
563 ctx.write(fragment, promiseAggregator.newPromise());
564 fragment = null;
565
566
567 if (paddingBytes(padding) > 0) {
568 ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
569 }
570
571 if (!flags.endOfHeaders()) {
572 writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator);
573 }
574 } catch (Http2Exception e) {
575 promiseAggregator.setFailure(e);
576 } catch (Throwable t) {
577 promiseAggregator.setFailure(t);
578 promiseAggregator.doneAllocatingPromises();
579 PlatformDependent.throwException(t);
580 } finally {
581 if (fragment != null) {
582 fragment.release();
583 }
584 if (headerBlock != null) {
585 headerBlock.release();
586 }
587 }
588 return promiseAggregator.doneAllocatingPromises();
589 }
590
591
592
593
594 private ChannelFuture writeContinuationFrames(ChannelHandlerContext ctx, int streamId,
595 ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) {
596 Http2Flags flags = new Http2Flags();
597
598 if (headerBlock.isReadable()) {
599 int fragmentReadableBytes;
600 ByteBuf buf = null;
601 try {
602 do {
603 fragmentReadableBytes = min(headerBlock.readableBytes(), maxFrameSize);
604 ByteBuf fragment = headerBlock.readRetainedSlice(fragmentReadableBytes);
605 boolean fragmentWritten = false;
606 try {
607 if (headerBlock.isReadable()) {
608 if (buf == null) {
609 buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH);
610 writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId);
611 }
612 ctx.write(buf.retainedSlice(), promiseAggregator.newPromise());
613 } else {
614
615
616 if (buf != null) {
617 buf.release();
618 buf = null;
619 }
620 flags = flags.endOfHeaders(true);
621 buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH);
622 writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId);
623 ctx.write(buf, promiseAggregator.newPromise());
624 buf = null;
625 }
626 ctx.write(fragment, promiseAggregator.newPromise());
627 fragmentWritten = true;
628 } finally {
629 if (!fragmentWritten) {
630 fragment.release();
631 }
632 }
633 } while (headerBlock.isReadable());
634 } finally {
635 if (buf != null) {
636 buf.release();
637 }
638 }
639 }
640 return promiseAggregator;
641 }
642
643
644
645
646 private static int paddingBytes(int padding) {
647
648
649 return padding - 1;
650 }
651
652 private static void writePaddingLength(ByteBuf buf, int padding) {
653 if (padding > 0) {
654
655
656 buf.writeByte(padding - 1);
657 }
658 }
659
660 private static void verifyStreamId(int streamId, String argumentName) {
661 checkPositive(streamId, argumentName);
662 }
663
664 private static void verifyStreamOrConnectionId(int streamId, String argumentName) {
665 checkPositiveOrZero(streamId, argumentName);
666 }
667
668 private static void verifyWeight(short weight) {
669 if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) {
670 throw new IllegalArgumentException("Invalid weight: " + weight);
671 }
672 }
673
674 private static void verifyErrorCode(long errorCode) {
675 if (errorCode < 0 || errorCode > MAX_UNSIGNED_INT) {
676 throw new IllegalArgumentException("Invalid errorCode: " + errorCode);
677 }
678 }
679
680 private void verifyPayloadLength(byte frameType, int payloadLength) {
681 if (payloadLength > maxFrameSize) {
682 throw new IllegalArgumentException("Payload length " + payloadLength + " of frame type 0x" +
683 Integer.toHexString(frameType & 0xFF) + " exceeds the maximum frame size " + maxFrameSize +
684 " advertised by the remote peer.");
685 }
686 }
687
688 private static void verifyWindowSizeIncrement(int windowSizeIncrement) {
689 checkPositiveOrZero(windowSizeIncrement, "windowSizeIncrement");
690 }
691 }