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