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