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