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