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 verifyAssociatedWithAStream();
347 verifyNotProcessingHeaders();
348
349
350
351 int minLength = flags.getPaddingPresenceFieldLength() + INT_FIELD_LENGTH;
352 if (payloadLength < minLength) {
353
354
355
356 throw connectionError(FRAME_SIZE_ERROR,
357 "Frame length %d too small for PUSH_PROMISE frame with stream id %d.", payloadLength, streamId);
358 }
359 }
360
361 private void verifyPingFrame() throws Http2Exception {
362 verifyNotProcessingHeaders();
363 if (streamId != 0) {
364 throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero.");
365 }
366 if (payloadLength != PING_FRAME_PAYLOAD_LENGTH) {
367 throw connectionError(FRAME_SIZE_ERROR,
368 "Frame length %d incorrect size for ping.", payloadLength);
369 }
370 }
371
372 private void verifyGoAwayFrame() throws Http2Exception {
373 verifyNotProcessingHeaders();
374
375 if (streamId != 0) {
376 throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero.");
377 }
378 if (payloadLength < 8) {
379 throw connectionError(FRAME_SIZE_ERROR, "Frame length %d too small.", payloadLength);
380 }
381 }
382
383 private void verifyWindowUpdateFrame() throws Http2Exception {
384 verifyNotProcessingHeaders();
385 verifyStreamOrConnectionId(streamId, "Stream ID");
386
387 if (payloadLength != INT_FIELD_LENGTH) {
388 throw connectionError(FRAME_SIZE_ERROR, "Invalid frame length %d.", payloadLength);
389 }
390 }
391
392 private void verifyContinuationFrame() throws Http2Exception {
393 verifyAssociatedWithAStream();
394
395 if (headersContinuation == null) {
396 throw connectionError(PROTOCOL_ERROR, "Received %s frame but not currently processing headers.",
397 frameType);
398 }
399
400 if (streamId != headersContinuation.getStreamId()) {
401 throw connectionError(PROTOCOL_ERROR, "Continuation stream ID does not match pending headers. "
402 + "Expected %d, but received %d.", headersContinuation.getStreamId(), streamId);
403 }
404
405 if (headersContinuation.numSmallFragments() >= maxSmallContinuationFrames) {
406 throw connectionError(ENHANCE_YOUR_CALM,
407 "Number of small consecutive continuations frames %d exceeds maximum: %d",
408 headersContinuation.numSmallFragments(), maxSmallContinuationFrames);
409 }
410 }
411
412 private void verifyUnknownFrame() throws Http2Exception {
413 verifyNotProcessingHeaders();
414 }
415
416 private void readDataFrame(ChannelHandlerContext ctx, ByteBuf payload,
417 Http2FrameListener listener) throws Http2Exception {
418 int padding = readPadding(payload);
419
420
421
422 int dataLength = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
423
424 payload.writerIndex(payload.readerIndex() + dataLength);
425 listener.onDataRead(ctx, streamId, payload, padding, flags.endOfStream());
426 }
427
428 private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload,
429 Http2FrameListener listener) throws Http2Exception {
430 final int headersStreamId = streamId;
431 final Http2Flags headersFlags = flags;
432 final int padding = readPadding(payload);
433
434
435
436 if (flags.priorityPresent()) {
437 long word1 = payload.readUnsignedInt();
438 final boolean exclusive = (word1 & 0x80000000L) != 0;
439 final int streamDependency = (int) (word1 & 0x7FFFFFFFL);
440 if (streamDependency == streamId) {
441
442
443
444
445 throw connectionError(
446 PROTOCOL_ERROR, "HEADERS frame for stream %d cannot depend on itself.", streamId);
447 }
448 final short weight = (short) (payload.readUnsignedByte() + 1);
449 final int lenToRead = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
450
451
452 headersContinuation = new HeadersContinuation() {
453 @Override
454 public int getStreamId() {
455 return headersStreamId;
456 }
457
458 @Override
459 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
460 Http2FrameListener listener) throws Http2Exception {
461 final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder();
462 hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders);
463 if (endOfHeaders) {
464 listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), streamDependency,
465 weight, exclusive, padding, headersFlags.endOfStream());
466 }
467 }
468 };
469
470
471 headersContinuation.processFragment(flags.endOfHeaders(), payload, lenToRead, listener);
472 resetHeadersContinuationIfEnd(flags.endOfHeaders());
473 return;
474 }
475
476
477
478 headersContinuation = new HeadersContinuation() {
479 @Override
480 public int getStreamId() {
481 return headersStreamId;
482 }
483
484 @Override
485 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
486 Http2FrameListener listener) throws Http2Exception {
487 final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder();
488 hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders);
489 if (endOfHeaders) {
490 listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), padding,
491 headersFlags.endOfStream());
492 }
493 }
494 };
495
496
497 int len = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
498 headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener);
499 resetHeadersContinuationIfEnd(flags.endOfHeaders());
500 }
501
502 private void resetHeadersContinuationIfEnd(boolean endOfHeaders) {
503 if (endOfHeaders) {
504 closeHeadersContinuation();
505 }
506 }
507
508 private void readPriorityFrame(ChannelHandlerContext ctx, ByteBuf payload,
509 Http2FrameListener listener) throws Http2Exception {
510 long word1 = payload.readUnsignedInt();
511 boolean exclusive = (word1 & 0x80000000L) != 0;
512 int streamDependency = (int) (word1 & 0x7FFFFFFFL);
513 if (streamDependency == streamId) {
514 throw streamError(streamId, PROTOCOL_ERROR, "A stream cannot depend on itself.");
515 }
516 short weight = (short) (payload.readUnsignedByte() + 1);
517 listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
518 }
519
520 private void readRstStreamFrame(ChannelHandlerContext ctx, ByteBuf payload,
521 Http2FrameListener listener) throws Http2Exception {
522 long errorCode = payload.readUnsignedInt();
523 listener.onRstStreamRead(ctx, streamId, errorCode);
524 }
525
526 private void readSettingsFrame(ChannelHandlerContext ctx, ByteBuf payload,
527 Http2FrameListener listener) throws Http2Exception {
528 if (flags.ack()) {
529 listener.onSettingsAckRead(ctx);
530 } else {
531 int numSettings = payloadLength / SETTING_ENTRY_LENGTH;
532 Http2Settings settings = new Http2Settings();
533 for (int index = 0; index < numSettings; ++index) {
534 char id = (char) payload.readUnsignedShort();
535 long value = payload.readUnsignedInt();
536 try {
537 settings.put(id, Long.valueOf(value));
538 } catch (IllegalArgumentException e) {
539 if (id == SETTINGS_INITIAL_WINDOW_SIZE) {
540 throw connectionError(FLOW_CONTROL_ERROR, e,
541 "Failed setting initial window size: %s", e.getMessage());
542 }
543 throw connectionError(PROTOCOL_ERROR, e, "Protocol error: %s", e.getMessage());
544 }
545 }
546 listener.onSettingsRead(ctx, settings);
547 }
548 }
549
550 private void readPushPromiseFrame(final ChannelHandlerContext ctx, ByteBuf payload,
551 Http2FrameListener listener) throws Http2Exception {
552 final int pushPromiseStreamId = streamId;
553 final int padding = readPadding(payload);
554 final int promisedStreamId = readUnsignedInt(payload);
555
556
557 headersContinuation = new HeadersContinuation() {
558 @Override
559 public int getStreamId() {
560 return pushPromiseStreamId;
561 }
562
563 @Override
564 public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
565 Http2FrameListener listener) throws Http2Exception {
566 headersBlockBuilder().addFragment(fragment, len, ctx.alloc(), endOfHeaders);
567 if (endOfHeaders) {
568 listener.onPushPromiseRead(ctx, pushPromiseStreamId, promisedStreamId,
569 headersBlockBuilder().headers(), padding);
570 }
571 }
572 };
573
574
575 int len = lengthWithoutTrailingPadding(payload.readableBytes(), padding);
576 headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener);
577 resetHeadersContinuationIfEnd(flags.endOfHeaders());
578 }
579
580 private void readPingFrame(ChannelHandlerContext ctx, long data,
581 Http2FrameListener listener) throws Http2Exception {
582 if (flags.ack()) {
583 listener.onPingAckRead(ctx, data);
584 } else {
585 listener.onPingRead(ctx, data);
586 }
587 }
588
589 private void readGoAwayFrame(ChannelHandlerContext ctx, ByteBuf payload,
590 Http2FrameListener listener) throws Http2Exception {
591 int lastStreamId = readUnsignedInt(payload);
592 long errorCode = payload.readUnsignedInt();
593 listener.onGoAwayRead(ctx, lastStreamId, errorCode, payload);
594 }
595
596 private void readWindowUpdateFrame(ChannelHandlerContext ctx, ByteBuf payload,
597 Http2FrameListener listener) throws Http2Exception {
598 int windowSizeIncrement = readUnsignedInt(payload);
599 if (windowSizeIncrement == 0) {
600
601
602 if (streamId == CONNECTION_STREAM_ID) {
603 throw connectionError(PROTOCOL_ERROR,
604 "Received WINDOW_UPDATE with delta 0 for connection stream");
605 } else {
606 throw streamError(streamId, PROTOCOL_ERROR,
607 "Received WINDOW_UPDATE with delta 0 for stream: %d", streamId);
608 }
609 }
610 listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
611 }
612
613 private void readContinuationFrame(ByteBuf payload, Http2FrameListener listener)
614 throws Http2Exception {
615
616 headersContinuation.processFragment(flags.endOfHeaders(), payload,
617 payloadLength, listener);
618 resetHeadersContinuationIfEnd(flags.endOfHeaders());
619 }
620
621 private void readUnknownFrame(ChannelHandlerContext ctx, ByteBuf payload,
622 Http2FrameListener listener) throws Http2Exception {
623 listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
624 }
625
626
627
628
629
630 private int readPadding(ByteBuf payload) {
631 if (!flags.paddingPresent()) {
632 return 0;
633 }
634 return payload.readUnsignedByte() + 1;
635 }
636
637
638
639
640
641 private static int lengthWithoutTrailingPadding(int readableBytes, int padding) throws Http2Exception {
642 if (padding == 0) {
643 return readableBytes;
644 }
645 int n = readableBytes - (padding - 1);
646 if (n < 0) {
647 throw connectionError(PROTOCOL_ERROR, "Frame payload too small for padding.");
648 }
649 return n;
650 }
651
652
653
654
655
656
657 private abstract class HeadersContinuation {
658 private final HeadersBlockBuilder builder = new HeadersBlockBuilder();
659
660
661
662
663 abstract int getStreamId();
664
665
666
667
668
669
670 final int numSmallFragments() {
671 return builder.numSmallFragments();
672 }
673
674
675
676
677
678
679
680
681 abstract void processFragment(boolean endOfHeaders, ByteBuf fragment, int len,
682 Http2FrameListener listener) throws Http2Exception;
683
684 final HeadersBlockBuilder headersBlockBuilder() {
685 return builder;
686 }
687
688
689
690
691 final void close() {
692 builder.close();
693 }
694 }
695
696
697
698
699
700 protected class HeadersBlockBuilder {
701 private ByteBuf headerBlock;
702 private int numSmallFragments;
703
704
705
706
707
708 private void headerSizeExceeded() throws Http2Exception {
709 close();
710 headerListSizeExceeded(headersDecoder.configuration().maxHeaderListSizeGoAway());
711 }
712
713
714
715
716
717
718 int numSmallFragments() {
719 return numSmallFragments;
720 }
721
722
723
724
725
726
727
728
729
730
731 final void addFragment(ByteBuf fragment, int len, ByteBufAllocator alloc,
732 boolean endOfHeaders) throws Http2Exception {
733 if (maxSmallContinuationFrames > 0 && !endOfHeaders && len < FRAGMENT_THRESHOLD) {
734
735 numSmallFragments++;
736 }
737
738 if (headerBlock == null) {
739 if (len > headersDecoder.configuration().maxHeaderListSizeGoAway()) {
740 headerSizeExceeded();
741 }
742 if (endOfHeaders) {
743
744
745 headerBlock = fragment.readRetainedSlice(len);
746 } else {
747 headerBlock = alloc.buffer(len).writeBytes(fragment, len);
748 }
749 return;
750 }
751 if (headersDecoder.configuration().maxHeaderListSizeGoAway() - len <
752 headerBlock.readableBytes()) {
753 headerSizeExceeded();
754 }
755 if (headerBlock.isWritable(len)) {
756
757 headerBlock.writeBytes(fragment, len);
758 } else {
759
760 ByteBuf buf = alloc.buffer(headerBlock.readableBytes() + len);
761 buf.writeBytes(headerBlock).writeBytes(fragment, len);
762 headerBlock.release();
763 headerBlock = buf;
764 }
765 }
766
767
768
769
770
771 Http2Headers headers() throws Http2Exception {
772 try {
773 return headersDecoder.decodeHeaders(streamId, headerBlock);
774 } finally {
775 close();
776 }
777 }
778
779
780
781
782 void close() {
783 if (headerBlock != null) {
784 headerBlock.release();
785 headerBlock = null;
786 }
787
788
789 headersContinuation = null;
790 }
791 }
792
793
794
795
796
797 private void verifyNotProcessingHeaders() throws Http2Exception {
798 if (headersContinuation != null) {
799 throw connectionError(PROTOCOL_ERROR, "Received frame of type %s while processing headers on stream %d.",
800 frameType, headersContinuation.getStreamId());
801 }
802 }
803
804 private void verifyAssociatedWithAStream() throws Http2Exception {
805 if (streamId == 0) {
806 throw connectionError(PROTOCOL_ERROR, "Frame of type %s must be associated with a stream.", frameType);
807 }
808 }
809
810 private static void verifyStreamOrConnectionId(int streamId, String argumentName)
811 throws Http2Exception {
812 if (streamId < 0) {
813 throw connectionError(PROTOCOL_ERROR, "%s must be >= 0", argumentName);
814 }
815 }
816 }