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