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