1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.handler.codec.http2;
16
17 import io.netty.buffer.ByteBuf;
18 import io.netty.buffer.ByteBufAllocator;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.http2.Http2FrameReader.Configuration;
21 import io.netty.util.internal.ObjectUtil;
22 import io.netty.util.internal.PlatformDependent;
23
24 import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
25 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
26 import static io.netty.handler.codec.http2.Http2CodecUtil.FRAME_HEADER_LENGTH;
27 import static io.netty.handler.codec.http2.Http2CodecUtil.INT_FIELD_LENGTH;
28 import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND;
29 import static io.netty.handler.codec.http2.Http2CodecUtil.PING_FRAME_PAYLOAD_LENGTH;
30 import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_ENTRY_LENGTH;
31 import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_INITIAL_WINDOW_SIZE;
32 import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
33 import static io.netty.handler.codec.http2.Http2CodecUtil.headerListSizeExceeded;
34 import static io.netty.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid;
35 import static io.netty.handler.codec.http2.Http2CodecUtil.readUnsignedInt;
36 import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
37 import static io.netty.handler.codec.http2.Http2Error.FLOW_CONTROL_ERROR;
38 import static io.netty.handler.codec.http2.Http2Error.FRAME_SIZE_ERROR;
39 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
40 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
41 import static io.netty.handler.codec.http2.Http2Exception.streamError;
42 import static io.netty.handler.codec.http2.Http2FrameTypes.CONTINUATION;
43 import static io.netty.handler.codec.http2.Http2FrameTypes.DATA;
44 import static io.netty.handler.codec.http2.Http2FrameTypes.GO_AWAY;
45 import static io.netty.handler.codec.http2.Http2FrameTypes.HEADERS;
46 import static io.netty.handler.codec.http2.Http2FrameTypes.PING;
47 import static io.netty.handler.codec.http2.Http2FrameTypes.PRIORITY;
48 import static io.netty.handler.codec.http2.Http2FrameTypes.PUSH_PROMISE;
49 import static io.netty.handler.codec.http2.Http2FrameTypes.RST_STREAM;
50 import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS;
51 import static io.netty.handler.codec.http2.Http2FrameTypes.WINDOW_UPDATE;
52
53
54
55
56 public class DefaultHttp2FrameReader implements Http2FrameReader, Http2FrameSizePolicy, Configuration {
57 private static final int FRAGMENT_THRESHOLD = MAX_FRAME_SIZE_LOWER_BOUND / 2;
58 private final Http2HeadersDecoder headersDecoder;
59
60
61
62
63 private boolean readingHeaders = true;
64
65
66
67
68 private boolean readError;
69 private byte frameType;
70 private int streamId;
71 private Http2Flags flags;
72 private int payloadLength;
73 private HeadersContinuation headersContinuation;
74 private int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
75 private final int maxSmallContinuationFrames;
76
77
78
79
80
81
82 public DefaultHttp2FrameReader() {
83 this(true);
84 }
85
86
87
88
89
90
91 public DefaultHttp2FrameReader(boolean validateHeaders) {
92 this(new DefaultHttp2HeadersDecoder(validateHeaders));
93 }
94
95 public DefaultHttp2FrameReader(Http2HeadersDecoder headersDecoder) {
96 this(headersDecoder, Http2CodecUtil.DEFAULT_MAX_SMALL_CONTINUATION_FRAME);
97 }
98
99 public DefaultHttp2FrameReader(Http2HeadersDecoder headersDecoder, int maxSmallContinuationFrames) {
100 this.headersDecoder = ObjectUtil.checkNotNull(headersDecoder, "headersDecoder");
101 this.maxSmallContinuationFrames = ObjectUtil.checkPositiveOrZero(
102 maxSmallContinuationFrames, "maxSmallContinuationFrames");
103 }
104
105 @Override
106 public Http2HeadersDecoder.Configuration headersConfiguration() {
107 return headersDecoder.configuration();
108 }
109
110 @Override
111 public Configuration configuration() {
112 return this;
113 }
114
115 @Override
116 public Http2FrameSizePolicy frameSizePolicy() {
117 return this;
118 }
119
120 @Override
121 public void maxFrameSize(int max) throws Http2Exception {
122 if (!isMaxFrameSizeValid(max)) {
123
124
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 closeHeadersContinuation();
138 }
139
140 private void closeHeadersContinuation() {
141 if (headersContinuation != null) {
142 headersContinuation.close();
143 headersContinuation = null;
144 }
145 }
146
147 @Override
148 public void readFrame(ChannelHandlerContext ctx, ByteBuf input, Http2FrameListener listener)
149 throws Http2Exception {
150 if (readError) {
151 input.skipBytes(input.readableBytes());
152 return;
153 }
154 try {
155 do {
156 if (readingHeaders && !preProcessFrame(input)) {
157 return;
158 }
159
160
161
162
163
164
165 if (input.readableBytes() < payloadLength) {
166 return;
167 }
168
169 ByteBuf framePayload = input.readSlice(payloadLength);
170
171
172 readingHeaders = true;
173 verifyFrameState();
174 processPayloadState(ctx, framePayload, listener);
175 } while (input.isReadable());
176 } catch (Http2Exception e) {
177 readError = !Http2Exception.isStreamError(e);
178 throw e;
179 } catch (RuntimeException e) {
180 readError = true;
181 throw e;
182 } catch (Throwable cause) {
183 readError = true;
184 PlatformDependent.throwException(cause);
185 }
186 }
187
188 private boolean preProcessFrame(ByteBuf in) throws Http2Exception {
189
190
191 if (in.readableBytes() < FRAME_HEADER_LENGTH) {
192
193 return false;
194 }
195 payloadLength = in.readUnsignedMedium();
196 if (payloadLength > maxFrameSize) {
197 throw connectionError(FRAME_SIZE_ERROR, "Frame length: %d exceeds maximum: %d", payloadLength,
198 maxFrameSize);
199 }
200 frameType = in.readByte();
201 flags = new Http2Flags(in.readUnsignedByte());
202 streamId = readUnsignedInt(in);
203 readingHeaders = false;
204 return true;
205 }
206
207 private void verifyFrameState() throws Http2Exception {
208 switch (frameType) {
209 case DATA:
210 verifyDataFrame();
211 break;
212 case HEADERS:
213 verifyHeadersFrame();
214 break;
215 case PRIORITY:
216 verifyPriorityFrame();
217 break;
218 case RST_STREAM:
219 verifyRstStreamFrame();
220 break;
221 case SETTINGS:
222 verifySettingsFrame();
223 break;
224 case PUSH_PROMISE:
225 verifyPushPromiseFrame();
226 break;
227 case PING:
228 verifyPingFrame();
229 break;
230 case GO_AWAY:
231 verifyGoAwayFrame();
232 break;
233 case WINDOW_UPDATE:
234 verifyWindowUpdateFrame();
235 break;
236 case CONTINUATION:
237 verifyContinuationFrame();
238 break;
239 default:
240
241 verifyUnknownFrame();
242 break;
243 }
244 }
245
246 private void processPayloadState(ChannelHandlerContext ctx, ByteBuf in, Http2FrameListener listener)
247 throws Http2Exception {
248
249
250 assert in.readableBytes() == payloadLength;
251
252 switch (frameType) {
253 case DATA:
254 readDataFrame(ctx, in, listener);
255 break;
256 case HEADERS:
257 readHeadersFrame(ctx, in, listener);
258 break;
259 case PRIORITY:
260 readPriorityFrame(ctx, in, listener);
261 break;
262 case RST_STREAM:
263 readRstStreamFrame(ctx, in, listener);
264 break;
265 case SETTINGS:
266 readSettingsFrame(ctx, in, listener);
267 break;
268 case PUSH_PROMISE:
269 readPushPromiseFrame(ctx, in, listener);
270 break;
271 case PING:
272 readPingFrame(ctx, in.readLong(), listener);
273 break;
274 case GO_AWAY:
275 readGoAwayFrame(ctx, in, listener);
276 break;
277 case WINDOW_UPDATE:
278 readWindowUpdateFrame(ctx, in, listener);
279 break;
280 case CONTINUATION:
281 readContinuationFrame(in, listener);
282 break;
283 default:
284 readUnknownFrame(ctx, in, listener);
285 break;
286 }
287 }
288
289 private void verifyDataFrame() throws Http2Exception {
290 verifyAssociatedWithAStream();
291 verifyNotProcessingHeaders();
292
293 if (payloadLength < flags.getPaddingPresenceFieldLength()) {
294 throw streamError(streamId, FRAME_SIZE_ERROR,
295 "Frame length %d too small.", payloadLength);
296 }
297 }
298
299 private void verifyHeadersFrame() throws Http2Exception {
300 verifyAssociatedWithAStream();
301 verifyNotProcessingHeaders();
302
303 int requiredLength = flags.getPaddingPresenceFieldLength() + flags.getNumPriorityBytes();
304 if (payloadLength < requiredLength) {
305
306
307
308 throw connectionError(FRAME_SIZE_ERROR,
309 "Frame length %d too small for HEADERS frame with stream %d.", payloadLength, streamId);
310 }
311 }
312
313 private void verifyPriorityFrame() throws Http2Exception {
314 verifyAssociatedWithAStream();
315 verifyNotProcessingHeaders();
316
317 if (payloadLength != PRIORITY_ENTRY_LENGTH) {
318 throw streamError(streamId, FRAME_SIZE_ERROR,
319 "Invalid frame length %d.", payloadLength);
320 }
321 }
322
323 private void verifyRstStreamFrame() throws Http2Exception {
324 verifyAssociatedWithAStream();
325 verifyNotProcessingHeaders();
326
327 if (payloadLength != INT_FIELD_LENGTH) {
328 throw connectionError(FRAME_SIZE_ERROR, "Invalid frame length %d.", payloadLength);
329 }
330 }
331
332 private void verifySettingsFrame() throws Http2Exception {
333 verifyNotProcessingHeaders();
334 if (streamId != 0) {
335 throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero.");
336 }
337 if (flags.ack() && payloadLength > 0) {
338 throw connectionError(FRAME_SIZE_ERROR, "Ack settings frame must have an empty payload.");
339 }
340 if (payloadLength % SETTING_ENTRY_LENGTH > 0) {
341 throw connectionError(FRAME_SIZE_ERROR, "Frame length %d invalid.", payloadLength);
342 }
343 }
344
345 private void verifyPushPromiseFrame() throws Http2Exception {
346 verifyNotProcessingHeaders();
347
348
349
350 int minLength = flags.getPaddingPresenceFieldLength() + INT_FIELD_LENGTH;
351 if (payloadLength < minLength) {
352
353
354
355 throw connectionError(FRAME_SIZE_ERROR,
356 "Frame length %d too small for PUSH_PROMISE frame with stream id %d.", payloadLength, streamId);
357 }
358 }
359
360 private void verifyPingFrame() throws Http2Exception {
361 verifyNotProcessingHeaders();
362 if (streamId != 0) {
363 throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero.");
364 }
365 if (payloadLength != PING_FRAME_PAYLOAD_LENGTH) {
366 throw connectionError(FRAME_SIZE_ERROR,
367 "Frame length %d incorrect size for ping.", payloadLength);
368 }
369 }
370
371 private void verifyGoAwayFrame() throws Http2Exception {
372 verifyNotProcessingHeaders();
373
374 if (streamId != 0) {
375 throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero.");
376 }
377 if (payloadLength < 8) {
378 throw connectionError(FRAME_SIZE_ERROR, "Frame length %d too small.", payloadLength);
379 }
380 }
381
382 private void verifyWindowUpdateFrame() throws Http2Exception {
383 verifyNotProcessingHeaders();
384 verifyStreamOrConnectionId(streamId, "Stream ID");
385
386 if (payloadLength != INT_FIELD_LENGTH) {
387 throw connectionError(FRAME_SIZE_ERROR, "Invalid frame length %d.", payloadLength);
388 }
389 }
390
391 private void verifyContinuationFrame() throws Http2Exception {
392 verifyAssociatedWithAStream();
393
394 if (headersContinuation == null) {
395 throw connectionError(PROTOCOL_ERROR, "Received %s frame but not currently processing headers.",
396 frameType);
397 }
398
399 if (streamId != headersContinuation.getStreamId()) {
400 throw connectionError(PROTOCOL_ERROR, "Continuation stream ID does not match pending headers. "
401 + "Expected %d, but received %d.", headersContinuation.getStreamId(), streamId);
402 }
403
404 if (headersContinuation.numSmallFragments() >= maxSmallContinuationFrames) {
405 throw connectionError(ENHANCE_YOUR_CALM,
406 "Number of small consecutive continuations frames %d exceeds maximum: %d",
407 headersContinuation.numSmallFragments(), maxSmallContinuationFrames);
408 }
409 }
410
411 private void verifyUnknownFrame() throws Http2Exception {
412 verifyNotProcessingHeaders();
413 }
414
415 private void readDataFrame(ChannelHandlerContext ctx, ByteBuf payload,
416 Http2FrameListener listener) throws Http2Exception {
417 int padding = readPadding(payload);
418
419
420
421 int dataLength = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
422
423 payload.writerIndex(payload.readerIndex() + dataLength);
424 listener.onDataRead(ctx, streamId, payload, padding, flags.endOfStream());
425 }
426
427 private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload,
428 Http2FrameListener listener) throws Http2Exception {
429 final int headersStreamId = streamId;
430 final Http2Flags headersFlags = flags;
431 final int padding = readPadding(payload);
432
433
434
435 if (flags.priorityPresent()) {
436 long word1 = payload.readUnsignedInt();
437 final boolean exclusive = (word1 & 0x80000000L) != 0;
438 final int streamDependency = (int) (word1 & 0x7FFFFFFFL);
439 if (streamDependency == streamId) {
440
441
442
443
444 throw connectionError(
445 PROTOCOL_ERROR, "HEADERS frame for stream %d cannot depend on itself.", streamId);
446 }
447 final short weight = (short) (payload.readUnsignedByte() + 1);
448 final int lenToRead = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
449
450
451 headersContinuation = new HeadersContinuation() {
452 @Override
453 public int getStreamId() {
454 return headersStreamId;
455 }
456
457 @Override
458 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
459 Http2FrameListener listener) throws Http2Exception {
460 final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder();
461 hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders);
462 if (endOfHeaders) {
463 listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), streamDependency,
464 weight, exclusive, padding, headersFlags.endOfStream());
465 }
466 }
467 };
468
469
470 headersContinuation.processFragment(flags.endOfHeaders(), payload, lenToRead, listener);
471 resetHeadersContinuationIfEnd(flags.endOfHeaders());
472 return;
473 }
474
475
476
477 headersContinuation = new HeadersContinuation() {
478 @Override
479 public int getStreamId() {
480 return headersStreamId;
481 }
482
483 @Override
484 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
485 Http2FrameListener listener) throws Http2Exception {
486 final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder();
487 hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders);
488 if (endOfHeaders) {
489 listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), padding,
490 headersFlags.endOfStream());
491 }
492 }
493 };
494
495
496 int len = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
497 headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener);
498 resetHeadersContinuationIfEnd(flags.endOfHeaders());
499 }
500
501 private void resetHeadersContinuationIfEnd(boolean endOfHeaders) {
502 if (endOfHeaders) {
503 closeHeadersContinuation();
504 }
505 }
506
507 private void readPriorityFrame(ChannelHandlerContext ctx, ByteBuf payload,
508 Http2FrameListener listener) throws Http2Exception {
509 long word1 = payload.readUnsignedInt();
510 boolean exclusive = (word1 & 0x80000000L) != 0;
511 int streamDependency = (int) (word1 & 0x7FFFFFFFL);
512 if (streamDependency == streamId) {
513 throw streamError(streamId, PROTOCOL_ERROR, "A stream cannot depend on itself.");
514 }
515 short weight = (short) (payload.readUnsignedByte() + 1);
516 listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
517 }
518
519 private void readRstStreamFrame(ChannelHandlerContext ctx, ByteBuf payload,
520 Http2FrameListener listener) throws Http2Exception {
521 long errorCode = payload.readUnsignedInt();
522 listener.onRstStreamRead(ctx, streamId, errorCode);
523 }
524
525 private void readSettingsFrame(ChannelHandlerContext ctx, ByteBuf payload,
526 Http2FrameListener listener) throws Http2Exception {
527 if (flags.ack()) {
528 listener.onSettingsAckRead(ctx);
529 } else {
530 int numSettings = payloadLength / SETTING_ENTRY_LENGTH;
531 Http2Settings settings = new Http2Settings();
532 for (int index = 0; index < numSettings; ++index) {
533 char id = (char) payload.readUnsignedShort();
534 long value = payload.readUnsignedInt();
535 try {
536 settings.put(id, Long.valueOf(value));
537 } catch (IllegalArgumentException e) {
538 if (id == SETTINGS_INITIAL_WINDOW_SIZE) {
539 throw connectionError(FLOW_CONTROL_ERROR, e,
540 "Failed setting initial window size: %s", e.getMessage());
541 }
542 throw connectionError(PROTOCOL_ERROR, e, "Protocol error: %s", e.getMessage());
543 }
544 }
545 listener.onSettingsRead(ctx, settings);
546 }
547 }
548
549 private void readPushPromiseFrame(final ChannelHandlerContext ctx, ByteBuf payload,
550 Http2FrameListener listener) throws Http2Exception {
551 final int pushPromiseStreamId = streamId;
552 final int padding = readPadding(payload);
553 final int promisedStreamId = readUnsignedInt(payload);
554
555
556 headersContinuation = new HeadersContinuation() {
557 @Override
558 public int getStreamId() {
559 return pushPromiseStreamId;
560 }
561
562 @Override
563 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
564 Http2FrameListener listener) throws Http2Exception {
565 headersBlockBuilder().addFragment(fragment, len, ctx.alloc(), endOfHeaders);
566 if (endOfHeaders) {
567 listener.onPushPromiseRead(ctx, pushPromiseStreamId, promisedStreamId,
568 headersBlockBuilder().headers(), padding);
569 }
570 }
571 };
572
573
574 int len = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
575 headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener);
576 resetHeadersContinuationIfEnd(flags.endOfHeaders());
577 }
578
579 private void readPingFrame(ChannelHandlerContext ctx, long data,
580 Http2FrameListener listener) throws Http2Exception {
581 if (flags.ack()) {
582 listener.onPingAckRead(ctx, data);
583 } else {
584 listener.onPingRead(ctx, data);
585 }
586 }
587
588 private void readGoAwayFrame(ChannelHandlerContext ctx, ByteBuf payload,
589 Http2FrameListener listener) throws Http2Exception {
590 int lastStreamId = readUnsignedInt(payload);
591 long errorCode = payload.readUnsignedInt();
592 listener.onGoAwayRead(ctx, lastStreamId, errorCode, payload);
593 }
594
595 private void readWindowUpdateFrame(ChannelHandlerContext ctx, ByteBuf payload,
596 Http2FrameListener listener) throws Http2Exception {
597 int windowSizeIncrement = readUnsignedInt(payload);
598 if (windowSizeIncrement == 0) {
599
600
601 if (streamId == CONNECTION_STREAM_ID) {
602 throw connectionError(PROTOCOL_ERROR,
603 "Received WINDOW_UPDATE with delta 0 for connection stream");
604 } else {
605 throw streamError(streamId, PROTOCOL_ERROR,
606 "Received WINDOW_UPDATE with delta 0 for stream: %d", streamId);
607 }
608 }
609 listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
610 }
611
612 private void readContinuationFrame(ByteBuf payload, Http2FrameListener listener)
613 throws Http2Exception {
614
615 headersContinuation.processFragment(flags.endOfHeaders(), payload,
616 payloadLength, listener);
617 resetHeadersContinuationIfEnd(flags.endOfHeaders());
618 }
619
620 private void readUnknownFrame(ChannelHandlerContext ctx, ByteBuf payload,
621 Http2FrameListener listener) throws Http2Exception {
622 listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
623 }
624
625
626
627
628
629 private int readPadding(ByteBuf payload) {
630 if (!flags.paddingPresent()) {
631 return 0;
632 }
633 return payload.readUnsignedByte() + 1;
634 }
635
636
637
638
639
640 private static int lengthWithoutTrailingPadding(int readableBytes, int padding) throws Http2Exception {
641 if (padding == 0) {
642 return readableBytes;
643 }
644 int n = readableBytes - (padding - 1);
645 if (n < 0) {
646 throw connectionError(PROTOCOL_ERROR, "Frame payload too small for padding.");
647 }
648 return n;
649 }
650
651
652
653
654
655
656 private abstract class HeadersContinuation {
657 private final HeadersBlockBuilder builder = new HeadersBlockBuilder();
658
659
660
661
662 abstract int getStreamId();
663
664
665
666
667
668
669 final int numSmallFragments() {
670 return builder.numSmallFragments();
671 }
672
673
674
675
676
677
678
679
680 abstract void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
681 Http2FrameListener listener) throws Http2Exception;
682
683 final HeadersBlockBuilder headersBlockBuilder() {
684 return builder;
685 }
686
687
688
689
690 final void close() {
691 builder.close();
692 }
693 }
694
695
696
697
698
699 protected class HeadersBlockBuilder {
700 private ByteBuf headerBlock;
701 private int numSmallFragments;
702
703
704
705
706
707 private void headerSizeExceeded() throws Http2Exception {
708 close();
709 headerListSizeExceeded(headersDecoder.configuration().maxHeaderListSizeGoAway());
710 }
711
712
713
714
715
716
717 int numSmallFragments() {
718 return numSmallFragments;
719 }
720
721
722
723
724
725
726
727
728
729
730 final void addFragment(ByteBuf fragment, int len, ByteBufAllocator alloc,
731 boolean endOfHeaders) throws Http2Exception {
732 if (maxSmallContinuationFrames > 0 && !endOfHeaders && len < FRAGMENT_THRESHOLD) {
733
734 numSmallFragments++;
735 }
736
737 if (headerBlock == null) {
738 if (len > headersDecoder.configuration().maxHeaderListSizeGoAway()) {
739 headerSizeExceeded();
740 }
741 if (endOfHeaders) {
742
743
744 headerBlock = fragment.readRetainedSlice(len);
745 } else {
746 headerBlock = alloc.buffer(len).writeBytes(fragment, len);
747 }
748 return;
749 }
750 if (headersDecoder.configuration().maxHeaderListSizeGoAway() - len <
751 headerBlock.readableBytes()) {
752 headerSizeExceeded();
753 }
754 if (headerBlock.isWritable(len)) {
755
756 headerBlock.writeBytes(fragment, len);
757 } else {
758
759 ByteBuf buf = alloc.buffer(headerBlock.readableBytes() + len);
760 buf.writeBytes(headerBlock).writeBytes(fragment, len);
761 headerBlock.release();
762 headerBlock = buf;
763 }
764 }
765
766
767
768
769
770 Http2Headers headers() throws Http2Exception {
771 try {
772 return headersDecoder.decodeHeaders(streamId, headerBlock);
773 } finally {
774 close();
775 }
776 }
777
778
779
780
781 void close() {
782 if (headerBlock != null) {
783 headerBlock.release();
784 headerBlock = null;
785 }
786
787
788 headersContinuation = null;
789 }
790 }
791
792
793
794
795
796 private void verifyNotProcessingHeaders() throws Http2Exception {
797 if (headersContinuation != null) {
798 throw connectionError(PROTOCOL_ERROR, "Received frame of type %s while processing headers on stream %d.",
799 frameType, headersContinuation.getStreamId());
800 }
801 }
802
803 private void verifyAssociatedWithAStream() throws Http2Exception {
804 if (streamId == 0) {
805 throw connectionError(PROTOCOL_ERROR, "Frame of type %s must be associated with a stream.", frameType);
806 }
807 }
808
809 private static void verifyStreamOrConnectionId(int streamId, String argumentName)
810 throws Http2Exception {
811 if (streamId < 0) {
812 throw connectionError(PROTOCOL_ERROR, "%s must be >= 0", argumentName);
813 }
814 }
815 }