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