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.channel.ChannelHandlerContext;
19 import io.netty.handler.codec.http.HttpHeaderNames;
20 import io.netty.handler.codec.http.HttpStatusClass;
21 import io.netty.handler.codec.http.HttpUtil;
22 import io.netty.handler.codec.http2.Http2Connection.Endpoint;
23 import io.netty.util.internal.logging.InternalLogger;
24 import io.netty.util.internal.logging.InternalLoggerFactory;
25
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map.Entry;
29
30 import static io.netty.handler.codec.http.HttpStatusClass.INFORMATIONAL;
31 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
32 import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
33 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
34 import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED;
35 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
36 import static io.netty.handler.codec.http2.Http2Exception.streamError;
37 import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
38 import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED;
39 import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
40 import static io.netty.util.internal.ObjectUtil.checkNotNull;
41 import static java.lang.Integer.MAX_VALUE;
42 import static java.lang.Math.min;
43
44
45
46
47
48
49
50
51
52
53 public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
54 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2ConnectionDecoder.class);
55 private Http2FrameListener internalFrameListener = new PrefaceFrameListener();
56 private final Http2Connection connection;
57 private Http2LifecycleManager lifecycleManager;
58 private final Http2ConnectionEncoder encoder;
59 private final Http2FrameReader frameReader;
60 private Http2FrameListener listener;
61 private final Http2PromisedRequestVerifier requestVerifier;
62 private final Http2SettingsReceivedConsumer settingsReceivedConsumer;
63 private final boolean autoAckPing;
64 private final Http2Connection.PropertyKey contentLengthKey;
65 private final boolean validateHeaders;
66
67 public DefaultHttp2ConnectionDecoder(Http2Connection connection,
68 Http2ConnectionEncoder encoder,
69 Http2FrameReader frameReader) {
70 this(connection, encoder, frameReader, ALWAYS_VERIFY);
71 }
72
73 public DefaultHttp2ConnectionDecoder(Http2Connection connection,
74 Http2ConnectionEncoder encoder,
75 Http2FrameReader frameReader,
76 Http2PromisedRequestVerifier requestVerifier) {
77 this(connection, encoder, frameReader, requestVerifier, true);
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public DefaultHttp2ConnectionDecoder(Http2Connection connection,
93 Http2ConnectionEncoder encoder,
94 Http2FrameReader frameReader,
95 Http2PromisedRequestVerifier requestVerifier,
96 boolean autoAckSettings) {
97 this(connection, encoder, frameReader, requestVerifier, autoAckSettings, true);
98 }
99
100 @Deprecated
101 public DefaultHttp2ConnectionDecoder(Http2Connection connection,
102 Http2ConnectionEncoder encoder,
103 Http2FrameReader frameReader,
104 Http2PromisedRequestVerifier requestVerifier,
105 boolean autoAckSettings,
106 boolean autoAckPing) {
107 this(connection, encoder, frameReader, requestVerifier, autoAckSettings, autoAckPing, true);
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 public DefaultHttp2ConnectionDecoder(Http2Connection connection,
126 Http2ConnectionEncoder encoder,
127 Http2FrameReader frameReader,
128 Http2PromisedRequestVerifier requestVerifier,
129 boolean autoAckSettings,
130 boolean autoAckPing,
131 boolean validateHeaders) {
132 this.validateHeaders = validateHeaders;
133 this.autoAckPing = autoAckPing;
134 if (autoAckSettings) {
135 settingsReceivedConsumer = null;
136 } else {
137 if (!(encoder instanceof Http2SettingsReceivedConsumer)) {
138 throw new IllegalArgumentException("disabling autoAckSettings requires the encoder to be a " +
139 Http2SettingsReceivedConsumer.class);
140 }
141 settingsReceivedConsumer = (Http2SettingsReceivedConsumer) encoder;
142 }
143 this.connection = checkNotNull(connection, "connection");
144 contentLengthKey = this.connection.newKey();
145 this.frameReader = checkNotNull(frameReader, "frameReader");
146 this.encoder = checkNotNull(encoder, "encoder");
147 this.requestVerifier = checkNotNull(requestVerifier, "requestVerifier");
148 if (connection.local().flowController() == null) {
149 connection.local().flowController(new DefaultHttp2LocalFlowController(connection));
150 }
151 connection.local().flowController().frameWriter(encoder.frameWriter());
152 }
153
154 @Override
155 public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
156 this.lifecycleManager = checkNotNull(lifecycleManager, "lifecycleManager");
157 }
158
159 @Override
160 public Http2Connection connection() {
161 return connection;
162 }
163
164 @Override
165 public final Http2LocalFlowController flowController() {
166 return connection.local().flowController();
167 }
168
169 @Override
170 public void frameListener(Http2FrameListener listener) {
171 this.listener = checkNotNull(listener, "listener");
172 }
173
174 @Override
175 public Http2FrameListener frameListener() {
176 return listener;
177 }
178
179 @Override
180 public boolean prefaceReceived() {
181 return FrameReadListener.class == internalFrameListener.getClass();
182 }
183
184 @Override
185 public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Http2Exception {
186 frameReader.readFrame(ctx, in, internalFrameListener);
187 }
188
189 @Override
190 public Http2Settings localSettings() {
191 Http2Settings settings = new Http2Settings();
192 Http2FrameReader.Configuration config = frameReader.configuration();
193 Http2HeadersDecoder.Configuration headersConfig = config.headersConfiguration();
194 Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
195 settings.initialWindowSize(flowController().initialWindowSize());
196 settings.maxConcurrentStreams(connection.remote().maxActiveStreams());
197 settings.headerTableSize(headersConfig.maxHeaderTableSize());
198 settings.maxFrameSize(frameSizePolicy.maxFrameSize());
199 settings.maxHeaderListSize(headersConfig.maxHeaderListSize());
200 if (!connection.isServer()) {
201
202 settings.pushEnabled(connection.local().allowPushTo());
203 }
204 return settings;
205 }
206
207 @Override
208 public void close() {
209 frameReader.close();
210 }
211
212
213
214
215
216
217
218
219 protected long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) {
220 return Http2CodecUtil.calculateMaxHeaderListSizeGoAway(maxHeaderListSize);
221 }
222
223 private int unconsumedBytes(Http2Stream stream) {
224 return flowController().unconsumedBytes(stream);
225 }
226
227 void onGoAwayRead0(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
228 throws Http2Exception {
229 listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData);
230 connection.goAwayReceived(lastStreamId, errorCode, debugData);
231 }
232
233 void onUnknownFrame0(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
234 ByteBuf payload) throws Http2Exception {
235 listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
236 }
237
238
239 private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception {
240 ContentLength contentLength = stream.getProperty(contentLengthKey);
241 if (contentLength != null) {
242 try {
243 contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd);
244 } finally {
245 if (isEnd) {
246 stream.removeProperty(contentLengthKey);
247 }
248 }
249 }
250 }
251
252
253
254
255 private final class FrameReadListener implements Http2FrameListener {
256 @Override
257 public int onDataRead(final ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
258 boolean endOfStream) throws Http2Exception {
259 Http2Stream stream = connection.stream(streamId);
260 Http2LocalFlowController flowController = flowController();
261 int readable = data.readableBytes();
262 int bytesToReturn = readable + padding;
263
264 final boolean shouldIgnore;
265 try {
266 shouldIgnore = shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "DATA");
267 } catch (Http2Exception e) {
268
269
270 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
271 flowController.consumeBytes(stream, bytesToReturn);
272 throw e;
273 } catch (Throwable t) {
274 throw connectionError(INTERNAL_ERROR, t, "Unhandled error on data stream id %d", streamId);
275 }
276
277 if (shouldIgnore) {
278
279
280 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
281 flowController.consumeBytes(stream, bytesToReturn);
282
283
284 verifyStreamMayHaveExisted(streamId, endOfStream, "DATA");
285
286
287 return bytesToReturn;
288 }
289 Http2Exception error = null;
290 switch (stream.state()) {
291 case OPEN:
292 case HALF_CLOSED_LOCAL:
293 break;
294 case HALF_CLOSED_REMOTE:
295 case CLOSED:
296 error = streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
297 stream.id(), stream.state());
298 break;
299 default:
300 error = streamError(stream.id(), PROTOCOL_ERROR,
301 "Stream %d in unexpected state: %s", stream.id(), stream.state());
302 break;
303 }
304
305 int unconsumedBytes = unconsumedBytes(stream);
306 try {
307 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
308
309 unconsumedBytes = unconsumedBytes(stream);
310
311
312 if (error != null) {
313 throw error;
314 }
315
316 verifyContentLength(stream, readable, endOfStream);
317
318
319
320 bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream);
321
322 if (endOfStream) {
323 lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
324 }
325
326 return bytesToReturn;
327 } catch (Http2Exception e) {
328
329
330
331 int delta = unconsumedBytes - unconsumedBytes(stream);
332 bytesToReturn -= delta;
333 throw e;
334 } catch (RuntimeException e) {
335
336
337
338 int delta = unconsumedBytes - unconsumedBytes(stream);
339 bytesToReturn -= delta;
340 throw e;
341 } finally {
342
343 flowController.consumeBytes(stream, bytesToReturn);
344 }
345 }
346
347 @Override
348 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
349 boolean endOfStream) throws Http2Exception {
350 onHeadersRead(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endOfStream);
351 }
352
353 @Override
354 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
355 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
356 Http2Stream stream = connection.stream(streamId);
357 boolean allowHalfClosedRemote = false;
358 boolean isTrailers = false;
359 if (stream == null && !connection.streamMayHaveExisted(streamId)) {
360 stream = connection.remote().createStream(streamId, endOfStream);
361
362 allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE;
363 } else if (stream != null) {
364 isTrailers = stream.isHeadersReceived();
365 }
366
367 if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "HEADERS")) {
368 return;
369 }
370
371 boolean isInformational = !connection.isServer() &&
372 HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL;
373 if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) {
374 throw streamError(streamId, PROTOCOL_ERROR,
375 "Stream %d received too many headers EOS: %s state: %s",
376 streamId, endOfStream, stream.state());
377 }
378
379 switch (stream.state()) {
380 case RESERVED_REMOTE:
381 stream.open(endOfStream);
382 break;
383 case OPEN:
384 case HALF_CLOSED_LOCAL:
385
386 break;
387 case HALF_CLOSED_REMOTE:
388 if (!allowHalfClosedRemote) {
389 throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
390 stream.id(), stream.state());
391 }
392 break;
393 case CLOSED:
394 throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
395 stream.id(), stream.state());
396 default:
397
398 throw connectionError(PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(),
399 stream.state());
400 }
401
402 if (!isTrailers) {
403
404 List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
405 if (contentLength != null && !contentLength.isEmpty()) {
406 try {
407 long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
408 if (cLength != -1) {
409 headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength);
410 stream.setProperty(contentLengthKey, new ContentLength(cLength));
411 }
412 } catch (IllegalArgumentException e) {
413 throw streamError(stream.id(), PROTOCOL_ERROR, e,
414 "Multiple content-length headers received");
415 }
416 }
417
418
419 } else if (validateHeaders && headers.size() > 0) {
420
421
422 for (Iterator<Entry<CharSequence, CharSequence>> iterator =
423 headers.iterator(); iterator.hasNext();) {
424 CharSequence name = iterator.next().getKey();
425 if (Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat(name)) {
426 throw streamError(stream.id(), PROTOCOL_ERROR,
427 "Found invalid Pseudo-Header in trailers: %s", name);
428 }
429 }
430 }
431
432 stream.headersReceived(isInformational);
433 verifyContentLength(stream, 0, endOfStream);
434 encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
435 listener.onHeadersRead(ctx, streamId, headers, streamDependency,
436 weight, exclusive, padding, endOfStream);
437
438 if (endOfStream) {
439 lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
440 }
441 }
442
443 @Override
444 public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
445 boolean exclusive) throws Http2Exception {
446 encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
447
448 listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
449 }
450
451 @Override
452 public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
453 Http2Stream stream = connection.stream(streamId);
454 if (stream == null) {
455 verifyStreamMayHaveExisted(streamId, false, "RST_STREAM");
456 return;
457 }
458
459 switch(stream.state()) {
460 case IDLE:
461 throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId);
462 case CLOSED:
463 return;
464 default:
465 break;
466 }
467
468 listener.onRstStreamRead(ctx, streamId, errorCode);
469
470 lifecycleManager.closeStream(stream, ctx.newSucceededFuture());
471 }
472
473 @Override
474 public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
475
476 Http2Settings settings = encoder.pollSentSettings();
477
478 if (settings != null) {
479 applyLocalSettings(settings);
480 }
481
482 listener.onSettingsAckRead(ctx);
483 }
484
485
486
487
488
489
490 private void applyLocalSettings(Http2Settings settings) throws Http2Exception {
491 Boolean pushEnabled = settings.pushEnabled();
492 final Http2FrameReader.Configuration config = frameReader.configuration();
493 final Http2HeadersDecoder.Configuration headerConfig = config.headersConfiguration();
494 final Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
495 if (pushEnabled != null) {
496 if (connection.isServer()) {
497 throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
498 }
499 connection.local().allowPushTo(pushEnabled);
500 }
501
502 Long maxConcurrentStreams = settings.maxConcurrentStreams();
503 if (maxConcurrentStreams != null) {
504 connection.remote().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE));
505 }
506
507 Long headerTableSize = settings.headerTableSize();
508 if (headerTableSize != null) {
509 headerConfig.maxHeaderTableSize(headerTableSize);
510 }
511
512 Long maxHeaderListSize = settings.maxHeaderListSize();
513 if (maxHeaderListSize != null) {
514 headerConfig.maxHeaderListSize(maxHeaderListSize, calculateMaxHeaderListSizeGoAway(maxHeaderListSize));
515 }
516
517 Integer maxFrameSize = settings.maxFrameSize();
518 if (maxFrameSize != null) {
519 frameSizePolicy.maxFrameSize(maxFrameSize);
520 }
521
522 Integer initialWindowSize = settings.initialWindowSize();
523 if (initialWindowSize != null) {
524 flowController().initialWindowSize(initialWindowSize);
525 }
526 }
527
528 @Override
529 public void onSettingsRead(final ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
530 if (settingsReceivedConsumer == null) {
531
532
533
534 encoder.writeSettingsAck(ctx, ctx.newPromise());
535
536 encoder.remoteSettings(settings);
537 } else {
538 settingsReceivedConsumer.consumeReceivedSettings(settings);
539 }
540
541 listener.onSettingsRead(ctx, settings);
542 }
543
544 @Override
545 public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
546 if (autoAckPing) {
547
548 encoder.writePing(ctx, true, data, ctx.newPromise());
549 }
550 listener.onPingRead(ctx, data);
551 }
552
553 @Override
554 public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
555 listener.onPingAckRead(ctx, data);
556 }
557
558 @Override
559 public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
560 Http2Headers headers, int padding) throws Http2Exception {
561
562 if (connection().isServer()) {
563 throw connectionError(PROTOCOL_ERROR, "A client cannot push.");
564 }
565
566 Http2Stream parentStream = connection.stream(streamId);
567
568 if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, parentStream, false, "PUSH_PROMISE")) {
569 return;
570 }
571
572 switch (parentStream.state()) {
573 case OPEN:
574 case HALF_CLOSED_LOCAL:
575
576 break;
577 default:
578
579 throw connectionError(PROTOCOL_ERROR,
580 "Stream %d in unexpected state for receiving push promise: %s",
581 parentStream.id(), parentStream.state());
582 }
583
584 if (!requestVerifier.isAuthoritative(ctx, headers)) {
585 throw streamError(promisedStreamId, PROTOCOL_ERROR,
586 "Promised request on stream %d for promised stream %d is not authoritative",
587 streamId, promisedStreamId);
588 }
589 if (!requestVerifier.isCacheable(headers)) {
590 throw streamError(promisedStreamId, PROTOCOL_ERROR,
591 "Promised request on stream %d for promised stream %d is not known to be cacheable",
592 streamId, promisedStreamId);
593 }
594 if (!requestVerifier.isSafe(headers)) {
595 throw streamError(promisedStreamId, PROTOCOL_ERROR,
596 "Promised request on stream %d for promised stream %d is not known to be safe",
597 streamId, promisedStreamId);
598 }
599
600
601 connection.remote().reservePushStream(promisedStreamId, parentStream);
602
603 listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
604 }
605
606 @Override
607 public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
608 throws Http2Exception {
609 onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
610 }
611
612 @Override
613 public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
614 throws Http2Exception {
615 Http2Stream stream = connection.stream(streamId);
616 if (stream == null || stream.state() == CLOSED || streamCreatedAfterGoAwaySent(streamId)) {
617
618 verifyStreamMayHaveExisted(streamId, false, "WINDOW_UPDATE");
619 return;
620 }
621
622
623 encoder.flowController().incrementWindowSize(stream, windowSizeIncrement);
624
625 listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
626 }
627
628 @Override
629 public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
630 ByteBuf payload) throws Http2Exception {
631 Http2Stream stream = connection.stream(streamId);
632 if (stream == null) {
633 return;
634 }
635
636 onUnknownFrame0(ctx, frameType, streamId, flags, payload);
637 }
638
639
640
641
642
643 private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int streamId, Http2Stream stream,
644 boolean endOfStream, String frameName) throws Http2Exception {
645 if (stream == null) {
646 if (streamCreatedAfterGoAwaySent(streamId)) {
647 logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent",
648 ctx.channel(), frameName, streamId);
649 return true;
650 }
651
652
653
654 verifyStreamMayHaveExisted(streamId, endOfStream, frameName);
655
656
657
658
659 throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d",
660 frameName, streamId);
661 }
662 if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) {
663
664
665
666
667
668 if (logger.isInfoEnabled()) {
669 logger.info("{} ignoring {} frame for stream {}", ctx.channel(), frameName,
670 stream.isResetSent() ? "RST_STREAM sent." :
671 "Stream created after GOAWAY sent. Last known stream by peer " +
672 connection.remote().lastStreamKnownByPeer());
673 }
674
675 return true;
676 }
677 return false;
678 }
679
680
681
682
683
684
685
686
687
688
689
690
691
692 private boolean streamCreatedAfterGoAwaySent(int streamId) {
693 Endpoint<?> remote = connection.remote();
694 return connection.goAwaySent() && remote.isValidStreamId(streamId) &&
695 streamId > remote.lastStreamKnownByPeer();
696 }
697
698 private void verifyStreamMayHaveExisted(int streamId, boolean endOfStream, String frameName)
699 throws Http2Exception {
700 if (!connection.streamMayHaveExisted(streamId)) {
701 throw connectionError(PROTOCOL_ERROR,
702 "Stream %d does not exist for inbound frame %s, endOfStream = %b",
703 streamId, frameName, endOfStream);
704 }
705 }
706 }
707
708 private final class PrefaceFrameListener implements Http2FrameListener {
709
710
711
712
713
714
715 private void verifyPrefaceReceived() throws Http2Exception {
716 if (!prefaceReceived()) {
717 throw connectionError(PROTOCOL_ERROR, "Received non-SETTINGS as first frame.");
718 }
719 }
720
721 @Override
722 public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
723 throws Http2Exception {
724 verifyPrefaceReceived();
725 return internalFrameListener.onDataRead(ctx, streamId, data, padding, endOfStream);
726 }
727
728 @Override
729 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
730 boolean endOfStream) throws Http2Exception {
731 verifyPrefaceReceived();
732 internalFrameListener.onHeadersRead(ctx, streamId, headers, padding, endOfStream);
733 }
734
735 @Override
736 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
737 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
738 verifyPrefaceReceived();
739 internalFrameListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
740 exclusive, padding, endOfStream);
741 }
742
743 @Override
744 public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
745 boolean exclusive) throws Http2Exception {
746 verifyPrefaceReceived();
747 internalFrameListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
748 }
749
750 @Override
751 public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
752 verifyPrefaceReceived();
753 internalFrameListener.onRstStreamRead(ctx, streamId, errorCode);
754 }
755
756 @Override
757 public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
758 verifyPrefaceReceived();
759 internalFrameListener.onSettingsAckRead(ctx);
760 }
761
762 @Override
763 public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
764
765
766 if (!prefaceReceived()) {
767 internalFrameListener = new FrameReadListener();
768 }
769 internalFrameListener.onSettingsRead(ctx, settings);
770 }
771
772 @Override
773 public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
774 verifyPrefaceReceived();
775 internalFrameListener.onPingRead(ctx, data);
776 }
777
778 @Override
779 public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
780 verifyPrefaceReceived();
781 internalFrameListener.onPingAckRead(ctx, data);
782 }
783
784 @Override
785 public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
786 Http2Headers headers, int padding) throws Http2Exception {
787 verifyPrefaceReceived();
788 internalFrameListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
789 }
790
791 @Override
792 public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
793 throws Http2Exception {
794 onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
795 }
796
797 @Override
798 public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
799 throws Http2Exception {
800 verifyPrefaceReceived();
801 internalFrameListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
802 }
803
804 @Override
805 public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
806 ByteBuf payload) throws Http2Exception {
807 onUnknownFrame0(ctx, frameType, streamId, flags, payload);
808 }
809 }
810
811 private static final class ContentLength {
812 private final long expected;
813 private long seen;
814
815 ContentLength(long expected) {
816 this.expected = expected;
817 }
818
819 void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception {
820 seen += bytes;
821
822 if (seen < 0) {
823 throw streamError(streamId, PROTOCOL_ERROR,
824 "Received amount of data did overflow and so not match content-length header %d", expected);
825 }
826
827 if (seen > expected) {
828 throw streamError(streamId, PROTOCOL_ERROR,
829 "Received amount of data %d does not match content-length header %d", seen, expected);
830 }
831
832 if (isEnd) {
833 if (seen == 0 && !server) {
834
835 return;
836 }
837
838
839 if (expected > seen) {
840 throw streamError(streamId, PROTOCOL_ERROR,
841 "Received amount of data %d does not match content-length header %d", seen, expected);
842 }
843 }
844 }
845 }
846 }