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