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 SimpleChannelPromiseAggregator promiseAggregator =
363 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
364 try {
365 verifyStreamId(streamId, STREAM_ID);
366 verifyStreamId(promisedStreamId, "Promised Stream ID");
367 verifyPadding(padding);
368
369
370 headerBlock = ctx.alloc().buffer();
371 headersEncoder.encodeHeaders(streamId, headers, headerBlock);
372
373
374 Http2Flags flags = new Http2Flags().paddingPresent(padding > 0);
375
376 int nonFragmentLength = INT_FIELD_LENGTH + padding;
377 int maxFragmentLength = maxFrameSize - nonFragmentLength;
378 ByteBuf fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength));
379
380 flags.endOfHeaders(!headerBlock.isReadable());
381
382 int payloadLength = fragment.readableBytes() + nonFragmentLength;
383 ByteBuf buf = ctx.alloc().buffer(PUSH_PROMISE_FRAME_HEADER_LENGTH);
384 writeFrameHeaderInternal(buf, payloadLength, PUSH_PROMISE, flags, streamId);
385 writePaddingLength(buf, padding);
386
387
388 buf.writeInt(promisedStreamId);
389 ctx.write(buf, promiseAggregator.newPromise());
390
391
392 ctx.write(fragment, promiseAggregator.newPromise());
393
394
395 if (paddingBytes(padding) > 0) {
396 ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
397 }
398
399 if (!flags.endOfHeaders()) {
400 writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator);
401 }
402 } catch (Http2Exception e) {
403 promiseAggregator.setFailure(e);
404 } catch (Throwable t) {
405 promiseAggregator.setFailure(t);
406 promiseAggregator.doneAllocatingPromises();
407 PlatformDependent.throwException(t);
408 } finally {
409 if (headerBlock != null) {
410 headerBlock.release();
411 }
412 }
413 return promiseAggregator.doneAllocatingPromises();
414 }
415
416 @Override
417 public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
418 ByteBuf debugData, ChannelPromise promise) {
419 SimpleChannelPromiseAggregator promiseAggregator =
420 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
421 try {
422 verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
423 verifyErrorCode(errorCode);
424
425 int payloadLength = 8 + debugData.readableBytes();
426 ByteBuf buf = ctx.alloc().buffer(GO_AWAY_FRAME_HEADER_LENGTH);
427
428
429 writeFrameHeaderInternal(buf, payloadLength, GO_AWAY, new Http2Flags(), 0);
430 buf.writeInt(lastStreamId);
431 buf.writeInt((int) errorCode);
432 ctx.write(buf, promiseAggregator.newPromise());
433 } catch (Throwable t) {
434 try {
435 debugData.release();
436 } finally {
437 promiseAggregator.setFailure(t);
438 promiseAggregator.doneAllocatingPromises();
439 }
440 return promiseAggregator;
441 }
442
443 try {
444 ctx.write(debugData, promiseAggregator.newPromise());
445 } catch (Throwable t) {
446 promiseAggregator.setFailure(t);
447 }
448 return promiseAggregator.doneAllocatingPromises();
449 }
450
451 @Override
452 public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
453 int windowSizeIncrement, ChannelPromise promise) {
454 try {
455 verifyStreamOrConnectionId(streamId, STREAM_ID);
456 verifyWindowSizeIncrement(windowSizeIncrement);
457
458 ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
459 writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
460 buf.writeInt(windowSizeIncrement);
461 return ctx.write(buf, promise);
462 } catch (Throwable t) {
463 return promise.setFailure(t);
464 }
465 }
466
467 @Override
468 public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
469 Http2Flags flags, ByteBuf payload, ChannelPromise promise) {
470 SimpleChannelPromiseAggregator promiseAggregator =
471 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
472 try {
473 verifyStreamOrConnectionId(streamId, STREAM_ID);
474 ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
475
476
477 writeFrameHeaderInternal(buf, payload.readableBytes(), frameType, flags, streamId);
478 ctx.write(buf, promiseAggregator.newPromise());
479 } catch (Throwable t) {
480 try {
481 payload.release();
482 } finally {
483 promiseAggregator.setFailure(t);
484 promiseAggregator.doneAllocatingPromises();
485 }
486 return promiseAggregator;
487 }
488 try {
489 ctx.write(payload, promiseAggregator.newPromise());
490 } catch (Throwable t) {
491 promiseAggregator.setFailure(t);
492 }
493 return promiseAggregator.doneAllocatingPromises();
494 }
495
496 private ChannelFuture writeHeadersInternal(ChannelHandlerContext ctx,
497 int streamId, Http2Headers headers, int padding, boolean endStream,
498 boolean hasPriority, int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
499 ByteBuf headerBlock = null;
500 SimpleChannelPromiseAggregator promiseAggregator =
501 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
502 try {
503 verifyStreamId(streamId, STREAM_ID);
504 if (hasPriority) {
505 verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
506 verifyPadding(padding);
507 verifyWeight(weight);
508 }
509
510
511 headerBlock = ctx.alloc().buffer();
512 headersEncoder.encodeHeaders(streamId, headers, headerBlock);
513
514 Http2Flags flags =
515 new Http2Flags().endOfStream(endStream).priorityPresent(hasPriority).paddingPresent(padding > 0);
516
517
518 int nonFragmentBytes = padding + flags.getNumPriorityBytes();
519 int maxFragmentLength = maxFrameSize - nonFragmentBytes;
520 ByteBuf fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength));
521
522
523 flags.endOfHeaders(!headerBlock.isReadable());
524
525 int payloadLength = fragment.readableBytes() + nonFragmentBytes;
526 ByteBuf buf = ctx.alloc().buffer(HEADERS_FRAME_HEADER_LENGTH);
527 writeFrameHeaderInternal(buf, payloadLength, HEADERS, flags, streamId);
528 writePaddingLength(buf, padding);
529
530 if (hasPriority) {
531 buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
532
533
534 buf.writeByte(weight - 1);
535 }
536 ctx.write(buf, promiseAggregator.newPromise());
537
538
539 ctx.write(fragment, promiseAggregator.newPromise());
540
541
542 if (paddingBytes(padding) > 0) {
543 ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
544 }
545
546 if (!flags.endOfHeaders()) {
547 writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator);
548 }
549 } catch (Http2Exception e) {
550 promiseAggregator.setFailure(e);
551 } catch (Throwable t) {
552 promiseAggregator.setFailure(t);
553 promiseAggregator.doneAllocatingPromises();
554 PlatformDependent.throwException(t);
555 } finally {
556 if (headerBlock != null) {
557 headerBlock.release();
558 }
559 }
560 return promiseAggregator.doneAllocatingPromises();
561 }
562
563
564
565
566 private ChannelFuture writeContinuationFrames(ChannelHandlerContext ctx, int streamId,
567 ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) {
568 Http2Flags flags = new Http2Flags();
569
570 if (headerBlock.isReadable()) {
571
572 int fragmentReadableBytes;
573 ByteBuf buf = null;
574
575 do {
576 fragmentReadableBytes = min(headerBlock.readableBytes(), maxFrameSize);
577 ByteBuf fragment = headerBlock.readRetainedSlice(fragmentReadableBytes);
578
579 if (headerBlock.isReadable()) {
580 if (buf == null) {
581 buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH);
582 writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId);
583 }
584 ctx.write(buf.retainedSlice(), promiseAggregator.newPromise());
585 } else {
586
587 if (buf != null) {
588 buf.release();
589 }
590 flags = flags.endOfHeaders(true);
591 buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH);
592 writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId);
593 ctx.write(buf, promiseAggregator.newPromise());
594 }
595 ctx.write(fragment, promiseAggregator.newPromise());
596
597 } while (headerBlock.isReadable());
598 }
599 return promiseAggregator;
600 }
601
602
603
604
605 private static int paddingBytes(int padding) {
606
607
608 return padding - 1;
609 }
610
611 private static void writePaddingLength(ByteBuf buf, int padding) {
612 if (padding > 0) {
613
614
615 buf.writeByte(padding - 1);
616 }
617 }
618
619 private static void verifyStreamId(int streamId, String argumentName) {
620 checkPositive(streamId, argumentName);
621 }
622
623 private static void verifyStreamOrConnectionId(int streamId, String argumentName) {
624 checkPositiveOrZero(streamId, argumentName);
625 }
626
627 private static void verifyWeight(short weight) {
628 if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) {
629 throw new IllegalArgumentException("Invalid weight: " + weight);
630 }
631 }
632
633 private static void verifyErrorCode(long errorCode) {
634 if (errorCode < 0 || errorCode > MAX_UNSIGNED_INT) {
635 throw new IllegalArgumentException("Invalid errorCode: " + errorCode);
636 }
637 }
638
639 private static void verifyWindowSizeIncrement(int windowSizeIncrement) {
640 checkPositiveOrZero(windowSizeIncrement, "windowSizeIncrement");
641 }
642 }