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