View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
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.HttpMethod;
21  import io.netty.handler.codec.http.HttpStatusClass;
22  import io.netty.handler.codec.http.HttpUtil;
23  import io.netty.handler.codec.http2.Http2Connection.Endpoint;
24  import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
25  import io.netty.util.internal.logging.InternalLogger;
26  import io.netty.util.internal.logging.InternalLoggerFactory;
27  
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map.Entry;
31  
32  import static io.netty.handler.codec.http.HttpStatusClass.INFORMATIONAL;
33  import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
34  import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
35  import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
36  import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED;
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.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
40  import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED;
41  import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
42  import static io.netty.util.internal.ObjectUtil.checkNotNull;
43  import static java.lang.Integer.MAX_VALUE;
44  import static java.lang.Math.min;
45  
46  /**
47   * Provides the default implementation for processing inbound frame events and delegates to a
48   * {@link Http2FrameListener}
49   * <p>
50   * This class will read HTTP/2 frames and delegate the events to a {@link Http2FrameListener}
51   * <p>
52   * This interface enforces inbound flow control functionality through
53   * {@link Http2LocalFlowController}
54   */
55  public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
56      private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2ConnectionDecoder.class);
57      private Http2FrameListener internalFrameListener = new PrefaceFrameListener();
58      private final Http2Connection connection;
59      private Http2LifecycleManager lifecycleManager;
60      private final Http2ConnectionEncoder encoder;
61      private final Http2FrameReader frameReader;
62      private Http2FrameListener listener;
63      private final Http2PromisedRequestVerifier requestVerifier;
64      private final Http2SettingsReceivedConsumer settingsReceivedConsumer;
65      private final boolean autoAckPing;
66      private final Http2Connection.PropertyKey contentLengthKey;
67      private final boolean validateHeaders;
68      private final boolean validateRequiredPseudoHeaders;
69  
70      public DefaultHttp2ConnectionDecoder(Http2Connection connection,
71                                           Http2ConnectionEncoder encoder,
72                                           Http2FrameReader frameReader) {
73          this(connection, encoder, frameReader, ALWAYS_VERIFY);
74      }
75  
76      public DefaultHttp2ConnectionDecoder(Http2Connection connection,
77                                           Http2ConnectionEncoder encoder,
78                                           Http2FrameReader frameReader,
79                                           Http2PromisedRequestVerifier requestVerifier) {
80          this(connection, encoder, frameReader, requestVerifier, true);
81      }
82  
83      /**
84       * Create a new instance.
85       * @param connection The {@link Http2Connection} associated with this decoder.
86       * @param encoder The {@link Http2ConnectionEncoder} associated with this decoder.
87       * @param frameReader Responsible for reading/parsing the raw frames. As opposed to this object which applies
88       *                    h2 semantics on top of the frames.
89       * @param requestVerifier Determines if push promised streams are valid.
90       * @param autoAckSettings {@code false} to disable automatically applying and sending settings acknowledge frame.
91       *  The {@code Http2ConnectionEncoder} is expected to be an instance of {@link Http2SettingsReceivedConsumer} and
92       *  will apply the earliest received but not yet ACKed SETTINGS when writing the SETTINGS ACKs.
93       * {@code true} to enable automatically applying and sending settings acknowledge frame.
94       */
95      public DefaultHttp2ConnectionDecoder(Http2Connection connection,
96                                           Http2ConnectionEncoder encoder,
97                                           Http2FrameReader frameReader,
98                                           Http2PromisedRequestVerifier requestVerifier,
99                                           boolean autoAckSettings) {
100         this(connection, encoder, frameReader, requestVerifier, autoAckSettings, true);
101     }
102 
103     @Deprecated
104     public DefaultHttp2ConnectionDecoder(Http2Connection connection,
105                                          Http2ConnectionEncoder encoder,
106                                          Http2FrameReader frameReader,
107                                          Http2PromisedRequestVerifier requestVerifier,
108                                          boolean autoAckSettings,
109                                          boolean autoAckPing) {
110         this(connection, encoder, frameReader, requestVerifier, autoAckSettings, autoAckPing, true);
111     }
112 
113     /**
114      * Create a new instance.
115      * @param connection The {@link Http2Connection} associated with this decoder.
116      * @param encoder The {@link Http2ConnectionEncoder} associated with this decoder.
117      * @param frameReader Responsible for reading/parsing the raw frames. As opposed to this object which applies
118      *                    h2 semantics on top of the frames.
119      * @param requestVerifier Determines if push promised streams are valid.
120      * @param autoAckSettings {@code false} to disable automatically applying and sending settings acknowledge frame.
121      *                        The {@code Http2ConnectionEncoder} is expected to be an instance of
122      *                        {@link Http2SettingsReceivedConsumer} and will apply the earliest received but not yet
123      *                        ACKed SETTINGS when writing the SETTINGS ACKs. {@code true} to enable automatically
124      *                        applying and sending settings acknowledge frame.
125      * @param autoAckPing {@code false} to disable automatically sending ping acknowledge frame. {@code true} to enable
126      *                    automatically sending ping ack frame.
127      */
128     public DefaultHttp2ConnectionDecoder(Http2Connection connection,
129                                          Http2ConnectionEncoder encoder,
130                                          Http2FrameReader frameReader,
131                                          Http2PromisedRequestVerifier requestVerifier,
132                                          boolean autoAckSettings,
133                                          boolean autoAckPing,
134                                          boolean validateHeaders) {
135         this(connection, encoder, frameReader, requestVerifier, autoAckSettings, autoAckPing, validateHeaders, false);
136     }
137 
138     /**
139      * Create a new instance.
140      * @param connection The {@link Http2Connection} associated with this decoder.
141      * @param encoder The {@link Http2ConnectionEncoder} associated with this decoder.
142      * @param frameReader Responsible for reading/parsing the raw frames. As opposed to this object which applies
143      *                    h2 semantics on top of the frames.
144      * @param requestVerifier Determines if push promised streams are valid.
145      * @param autoAckSettings {@code false} to disable automatically applying and sending settings acknowledge frame.
146      *                        The {@code Http2ConnectionEncoder} is expected to be an instance of
147      *                        {@link Http2SettingsReceivedConsumer} and will apply the earliest received but not yet
148      *                        ACKed SETTINGS when writing the SETTINGS ACKs. {@code true} to enable automatically
149      *                        applying and sending settings acknowledge frame.
150      * @param autoAckPing {@code false} to disable automatically sending ping acknowledge frame. {@code true} to enable
151      *                    automatically sending ping ack frame.
152      * @param validateHeaders {@code true} to validate headers according to
153      *                        <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
154      * @param validateRequiredPseudoHeaders {@code true} to reject request/response HEADERS that omit a mandatory
155      *        pseudo-header field, according to
156      *        <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3">RFC 9113, 8.3</a>.
157      */
158     public DefaultHttp2ConnectionDecoder(Http2Connection connection,
159                                          Http2ConnectionEncoder encoder,
160                                          Http2FrameReader frameReader,
161                                          Http2PromisedRequestVerifier requestVerifier,
162                                          boolean autoAckSettings,
163                                          boolean autoAckPing,
164                                          boolean validateHeaders,
165                                          boolean validateRequiredPseudoHeaders) {
166         this.validateHeaders = validateHeaders;
167         this.validateRequiredPseudoHeaders = validateRequiredPseudoHeaders;
168         this.autoAckPing = autoAckPing;
169         if (autoAckSettings) {
170             settingsReceivedConsumer = null;
171         } else {
172             if (!(encoder instanceof Http2SettingsReceivedConsumer)) {
173                 throw new IllegalArgumentException("disabling autoAckSettings requires the encoder to be a " +
174                         Http2SettingsReceivedConsumer.class);
175             }
176             settingsReceivedConsumer = (Http2SettingsReceivedConsumer) encoder;
177         }
178         this.connection = checkNotNull(connection, "connection");
179         contentLengthKey = this.connection.newKey();
180         this.frameReader = checkNotNull(frameReader, "frameReader");
181         this.encoder = checkNotNull(encoder, "encoder");
182         this.requestVerifier = checkNotNull(requestVerifier, "requestVerifier");
183         if (connection.local().flowController() == null) {
184             connection.local().flowController(new DefaultHttp2LocalFlowController(connection));
185         }
186         connection.local().flowController().frameWriter(encoder.frameWriter());
187     }
188 
189     @Override
190     public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
191         this.lifecycleManager = checkNotNull(lifecycleManager, "lifecycleManager");
192     }
193 
194     @Override
195     public Http2Connection connection() {
196         return connection;
197     }
198 
199     @Override
200     public final Http2LocalFlowController flowController() {
201         return connection.local().flowController();
202     }
203 
204     @Override
205     public void frameListener(Http2FrameListener listener) {
206         this.listener = checkNotNull(listener, "listener");
207     }
208 
209     @Override
210     public Http2FrameListener frameListener() {
211         return listener;
212     }
213 
214     @Override
215     public boolean prefaceReceived() {
216         return FrameReadListener.class == internalFrameListener.getClass();
217     }
218 
219     @Override
220     public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Http2Exception {
221         frameReader.readFrame(ctx, in, internalFrameListener);
222     }
223 
224     @Override
225     public Http2Settings localSettings() {
226         Http2Settings settings = new Http2Settings();
227         Http2FrameReader.Configuration config = frameReader.configuration();
228         Http2HeadersDecoder.Configuration headersConfig = config.headersConfiguration();
229         Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
230         settings.initialWindowSize(flowController().initialWindowSize());
231         settings.maxConcurrentStreams(connection.remote().maxActiveStreams());
232         settings.headerTableSize(headersConfig.maxHeaderTableSize());
233         settings.maxFrameSize(frameSizePolicy.maxFrameSize());
234         settings.maxHeaderListSize(headersConfig.maxHeaderListSize());
235         if (!connection.isServer()) {
236             // Only set the pushEnabled flag if this is a client endpoint.
237             settings.pushEnabled(connection.local().allowPushTo());
238         }
239         return settings;
240     }
241 
242     @Override
243     public void close() {
244         frameReader.close();
245     }
246 
247     /**
248      * Calculate the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount.
249      * @param maxHeaderListSize
250      *      <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a> for the local
251      *      endpoint.
252      * @return the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount.
253      */
254     protected long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) {
255         return Http2CodecUtil.calculateMaxHeaderListSizeGoAway(maxHeaderListSize);
256     }
257 
258     private int unconsumedBytes(Http2Stream stream) {
259         return flowController().unconsumedBytes(stream);
260     }
261 
262     void onGoAwayRead0(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
263             throws Http2Exception {
264         listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData);
265         connection.goAwayReceived(lastStreamId, errorCode, debugData);
266     }
267 
268     // See https://tools.ietf.org/html/rfc7540#section-8.1.2.6
269     private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception {
270         ContentLength contentLength = stream.getProperty(contentLengthKey);
271         if (contentLength != null) {
272             try {
273                 contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd);
274             } finally {
275                 if (isEnd) {
276                     stream.removeProperty(contentLengthKey);
277                 }
278             }
279         }
280     }
281 
282     /**
283      * Validates that an initial request or response HEADERS frame carries the mandatory pseudo-header fields,
284      * as required by <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3">RFC 9113, 8.3</a>.
285      * Trailers and informational (1xx) responses are handled by the caller and do not reach this method.
286      */
287     private static void validateRequiredPseudoHeaders(boolean server, int streamId, Http2Headers headers)
288             throws Http2Exception {
289         if (server) {
290             // Request pseudo-header fields (RFC 9113, 8.3.1).
291             CharSequence method = headers.method();
292             if (method == null) {
293                 throw streamError(streamId, PROTOCOL_ERROR,
294                         "Request is missing mandatory :method pseudo-header field.");
295             }
296             // CONNECT (RFC 9113, 8.5) omits :scheme/:path and carries :authority; extended CONNECT (RFC 8441),
297             // identified by :protocol, follows the regular :scheme/:path rules.
298             if (HttpMethod.CONNECT.asciiName().contentEquals(method) &&
299                     !headers.contains(PseudoHeaderName.PROTOCOL.value())) {
300                 if (headers.authority() == null) {
301                     throw streamError(streamId, PROTOCOL_ERROR,
302                             "CONNECT request is missing mandatory :authority pseudo-header field.");
303                 }
304             } else {
305                 if (headers.scheme() == null) {
306                     throw streamError(streamId, PROTOCOL_ERROR,
307                             "Request is missing mandatory :scheme pseudo-header field.");
308                 }
309                 CharSequence path = headers.path();
310                 if (path == null || path.length() == 0) {
311                     throw streamError(streamId, PROTOCOL_ERROR,
312                             "Request is missing mandatory :path pseudo-header field.");
313                 }
314             }
315         } else {
316             // Response pseudo-header fields (RFC 9113, 8.3.2).
317             if (headers.status() == null) {
318                 throw streamError(streamId, PROTOCOL_ERROR,
319                         "Response is missing mandatory :status pseudo-header field.");
320             }
321         }
322     }
323 
324     /**
325      * Handles all inbound frames from the network.
326      */
327     private final class FrameReadListener implements Http2FrameListener {
328         @Override
329         public int onDataRead(final ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
330                               boolean endOfStream) throws Http2Exception {
331             Http2Stream stream = connection.stream(streamId);
332             Http2LocalFlowController flowController = flowController();
333             int readable = data.readableBytes();
334             int bytesToReturn = readable + padding;
335 
336             final boolean shouldIgnore;
337             try {
338                 shouldIgnore = shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "DATA");
339             } catch (Http2Exception e) {
340                 // Ignoring this frame. We still need to count the frame towards the connection flow control
341                 // window, but we immediately mark all bytes as consumed.
342                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
343                 flowController.consumeBytes(stream, bytesToReturn);
344                 throw e;
345             } catch (Throwable t) {
346                 throw connectionError(INTERNAL_ERROR, t, "Unhandled error on data stream id %d", streamId);
347             }
348 
349             if (shouldIgnore) {
350                 // Ignoring this frame. We still need to count the frame towards the connection flow control
351                 // window, but we immediately mark all bytes as consumed.
352                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
353                 flowController.consumeBytes(stream, bytesToReturn);
354 
355                 // Verify that the stream may have existed after we apply flow control.
356                 verifyStreamMayHaveExisted(streamId, endOfStream, "DATA");
357 
358                 // All bytes have been consumed.
359                 return bytesToReturn;
360             }
361             Http2Exception error = null;
362             switch (stream.state()) {
363                 case OPEN:
364                 case HALF_CLOSED_LOCAL:
365                     break;
366                 case HALF_CLOSED_REMOTE:
367                 case CLOSED:
368                     error = streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
369                         stream.id(), stream.state());
370                     break;
371                 default:
372                     error = streamError(stream.id(), PROTOCOL_ERROR,
373                         "Stream %d in unexpected state: %s", stream.id(), stream.state());
374                     break;
375             }
376 
377             int unconsumedBytes = unconsumedBytes(stream);
378             try {
379                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
380                 // Update the unconsumed bytes after flow control is applied.
381                 unconsumedBytes = unconsumedBytes(stream);
382 
383                 // If the stream is in an invalid state to receive the frame, throw the error.
384                 if (error != null) {
385                     throw error;
386                 }
387 
388                 verifyContentLength(stream, readable, endOfStream);
389 
390                 // Call back the application and retrieve the number of bytes that have been
391                 // immediately processed.
392                 bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream);
393 
394                 if (endOfStream) {
395                     lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
396                 }
397 
398                 return bytesToReturn;
399             } catch (Http2Exception | RuntimeException e) {
400                 // If an exception happened during delivery, the listener may have returned part
401                 // of the bytes before the error occurred. If that's the case, subtract that from
402                 // the total processed bytes so that we don't return too many bytes.
403                 int delta = unconsumedBytes - unconsumedBytes(stream);
404                 bytesToReturn -= delta;
405                 throw e;
406             } finally {
407                 // If appropriate, return the processed bytes to the flow controller.
408                 flowController.consumeBytes(stream, bytesToReturn);
409             }
410         }
411 
412         @Override
413         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
414                 boolean endOfStream) throws Http2Exception {
415             onHeadersRead(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endOfStream);
416         }
417 
418         @Override
419         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
420                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
421             Http2Stream stream = connection.stream(streamId);
422             boolean allowHalfClosedRemote = false;
423             boolean isTrailers = false;
424             if (stream == null && !connection.streamMayHaveExisted(streamId)) {
425                 stream = connection.remote().createStream(streamId, endOfStream);
426                 // Allow the state to be HALF_CLOSE_REMOTE if we're creating it in that state.
427                 allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE;
428             } else if (stream != null) {
429                 isTrailers = stream.isHeadersReceived();
430             }
431 
432             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "HEADERS")) {
433                 return;
434             }
435 
436             boolean isInformational = !connection.isServer() &&
437                     HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL;
438             if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) {
439                 throw streamError(streamId, PROTOCOL_ERROR,
440                                   "Stream %d received too many headers EOS: %s state: %s",
441                                   streamId, endOfStream, stream.state());
442             }
443 
444             switch (stream.state()) {
445                 case RESERVED_REMOTE:
446                     stream.open(endOfStream);
447                     break;
448                 case OPEN:
449                 case HALF_CLOSED_LOCAL:
450                     // Allowed to receive headers in these states.
451                     break;
452                 case HALF_CLOSED_REMOTE:
453                     if (!allowHalfClosedRemote) {
454                         throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
455                                 stream.id(), stream.state());
456                     }
457                     break;
458                 case CLOSED:
459                     throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
460                             stream.id(), stream.state());
461                 default:
462                     // Connection error.
463                     throw connectionError(PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(),
464                             stream.state());
465             }
466 
467             if (!isTrailers) {
468                 if (validateRequiredPseudoHeaders && !isInformational) {
469                     // Reject initial request/response HEADERS that omit a mandatory pseudo-header (RFC 9113, 8.3).
470                     validateRequiredPseudoHeaders(connection.isServer(), stream.id(), headers);
471                 }
472                 // extract the content-length header
473                 List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
474                 if (contentLength != null && !contentLength.isEmpty()) {
475                     try {
476                         long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
477                         if (cLength != -1) {
478                             headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength);
479                             stream.setProperty(contentLengthKey, new ContentLength(cLength));
480                         }
481                     } catch (IllegalArgumentException e) {
482                         throw streamError(stream.id(), PROTOCOL_ERROR, e,
483                                 "Multiple content-length headers received");
484                     }
485                 }
486                 // Use size() instead of isEmpty() for backward compatibility with grpc-java prior to 1.59.1,
487                 // see https://github.com/grpc/grpc-java/issues/10665
488             } else if (validateHeaders && headers.size() > 0) {
489                 // Need to check trailers don't contain pseudo headers. According to RFC 9113
490                 // Trailers MUST NOT include pseudo-header fields (Section 8.3).
491                 for (Iterator<Entry<CharSequence, CharSequence>> iterator =
492                     headers.iterator(); iterator.hasNext();) {
493                     CharSequence name = iterator.next().getKey();
494                     if (Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat(name)) {
495                         throw streamError(stream.id(), PROTOCOL_ERROR,
496                                 "Found invalid Pseudo-Header in trailers: %s", name);
497                     }
498                 }
499             }
500 
501             stream.headersReceived(isInformational);
502             verifyContentLength(stream, 0, endOfStream);
503             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
504             listener.onHeadersRead(ctx, streamId, headers, streamDependency,
505                     weight, exclusive, padding, endOfStream);
506             // If the headers completes this stream, close it.
507             if (endOfStream) {
508                 lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
509             }
510         }
511 
512         @Override
513         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
514                 boolean exclusive) throws Http2Exception {
515             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
516 
517             listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
518         }
519 
520         @Override
521         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
522             Http2Stream stream = connection.stream(streamId);
523             if (stream == null) {
524                 verifyStreamMayHaveExisted(streamId, false, "RST_STREAM");
525                 return;
526             }
527 
528             switch(stream.state()) {
529             case IDLE:
530                 throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId);
531             case CLOSED:
532                 return; // RST_STREAM frames must be ignored for closed streams.
533             default:
534                 break;
535             }
536 
537             listener.onRstStreamRead(ctx, streamId, errorCode);
538 
539             lifecycleManager.closeStream(stream, ctx.newSucceededFuture());
540         }
541 
542         @Override
543         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
544             // Apply oldest outstanding local settings here. This is a synchronization point between endpoints.
545             Http2Settings settings = encoder.pollSentSettings();
546 
547             if (settings != null) {
548                 applyLocalSettings(settings);
549             }
550 
551             listener.onSettingsAckRead(ctx);
552         }
553 
554         /**
555          * Applies settings sent from the local endpoint.
556          * <p>
557          * This method is only called after the local settings have been acknowledged from the remote endpoint.
558          */
559         private void applyLocalSettings(Http2Settings settings) throws Http2Exception {
560             Boolean pushEnabled = settings.pushEnabled();
561             final Http2FrameReader.Configuration config = frameReader.configuration();
562             final Http2HeadersDecoder.Configuration headerConfig = config.headersConfiguration();
563             final Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
564             if (pushEnabled != null) {
565                 if (connection.isServer()) {
566                     throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
567                 }
568                 connection.local().allowPushTo(pushEnabled);
569             }
570 
571             Long maxConcurrentStreams = settings.maxConcurrentStreams();
572             if (maxConcurrentStreams != null) {
573                 connection.remote().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE));
574             }
575 
576             Long headerTableSize = settings.headerTableSize();
577             if (headerTableSize != null) {
578                 headerConfig.maxHeaderTableSize(headerTableSize);
579             }
580 
581             Long maxHeaderListSize = settings.maxHeaderListSize();
582             if (maxHeaderListSize != null) {
583                 headerConfig.maxHeaderListSize(maxHeaderListSize, calculateMaxHeaderListSizeGoAway(maxHeaderListSize));
584             }
585 
586             Integer maxFrameSize = settings.maxFrameSize();
587             if (maxFrameSize != null) {
588                 frameSizePolicy.maxFrameSize(maxFrameSize);
589             }
590 
591             Integer initialWindowSize = settings.initialWindowSize();
592             if (initialWindowSize != null) {
593                 flowController().initialWindowSize(initialWindowSize);
594             }
595         }
596 
597         @Override
598         public void onSettingsRead(final ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
599             if (settingsReceivedConsumer == null) {
600                 // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
601                 // remote peer applies these settings before any subsequent frames that we may send which depend upon
602                 // these new settings. See https://github.com/netty/netty/issues/6520.
603                 encoder.writeSettingsAck(ctx, ctx.newPromise());
604 
605                 encoder.remoteSettings(settings);
606             } else {
607                 settingsReceivedConsumer.consumeReceivedSettings(settings);
608             }
609 
610             listener.onSettingsRead(ctx, settings);
611         }
612 
613         @Override
614         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
615             if (autoAckPing) {
616                 // Send an ack back to the remote client.
617                 encoder.writePing(ctx, true, data, ctx.newPromise());
618             }
619             listener.onPingRead(ctx, data);
620         }
621 
622         @Override
623         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
624             listener.onPingAckRead(ctx, data);
625         }
626 
627         @Override
628         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
629                 Http2Headers headers, int padding) throws Http2Exception {
630             // A client cannot push.
631             if (connection().isServer()) {
632                 throw connectionError(PROTOCOL_ERROR, "A client cannot push.");
633             }
634 
635             Http2Stream parentStream = connection.stream(streamId);
636 
637             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, parentStream, false, "PUSH_PROMISE")) {
638                 return;
639             }
640 
641             switch (parentStream.state()) {
642               case OPEN:
643               case HALF_CLOSED_LOCAL:
644                   // Allowed to receive push promise in these states.
645                   break;
646               default:
647                   // Connection error.
648                   throw connectionError(PROTOCOL_ERROR,
649                       "Stream %d in unexpected state for receiving push promise: %s",
650                       parentStream.id(), parentStream.state());
651             }
652 
653             if (!requestVerifier.isAuthoritative(ctx, headers)) {
654                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
655                         "Promised request on stream %d for promised stream %d is not authoritative",
656                         streamId, promisedStreamId);
657             }
658             if (!requestVerifier.isCacheable(headers)) {
659                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
660                         "Promised request on stream %d for promised stream %d is not known to be cacheable",
661                         streamId, promisedStreamId);
662             }
663             if (!requestVerifier.isSafe(headers)) {
664                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
665                         "Promised request on stream %d for promised stream %d is not known to be safe",
666                         streamId, promisedStreamId);
667             }
668 
669             // Reserve the push stream based with a priority based on the current stream's priority.
670             connection.remote().reservePushStream(promisedStreamId, parentStream);
671 
672             listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
673         }
674 
675         @Override
676         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
677                 throws Http2Exception {
678             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
679         }
680 
681         @Override
682         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
683                 throws Http2Exception {
684             Http2Stream stream = connection.stream(streamId);
685             if (stream == null || stream.state() == CLOSED || streamCreatedAfterGoAwaySent(streamId)) {
686                 // Ignore this frame.
687                 verifyStreamMayHaveExisted(streamId, false, "WINDOW_UPDATE");
688                 return;
689             }
690 
691             // Update the outbound flow control window.
692             encoder.flowController().incrementWindowSize(stream, windowSizeIncrement);
693 
694             listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
695         }
696 
697         @Override
698         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
699                 ByteBuf payload) throws Http2Exception {
700             Http2Stream stream = connection.stream(streamId);
701             if (stream == null) {
702                 return;
703             }
704 
705             listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
706         }
707 
708         /**
709          * Helper method to determine if a frame that has the semantics of headers or data should be ignored for the
710          * {@code stream} (which may be {@code null}) associated with {@code streamId}.
711          */
712         private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int streamId, Http2Stream stream,
713                 boolean endOfStream, String frameName) throws Http2Exception {
714             if (stream == null) {
715                 if (streamCreatedAfterGoAwaySent(streamId)) {
716                     logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent",
717                             ctx.channel(), frameName, streamId);
718                     return true;
719                 }
720 
721                 // Make sure it's not an out-of-order frame, like a rogue DATA frame, for a stream that could
722                 // never have existed.
723                 verifyStreamMayHaveExisted(streamId, endOfStream, frameName);
724 
725                 // Its possible that this frame would result in stream ID out of order creation (PROTOCOL ERROR) and its
726                 // also possible that this frame is received on a CLOSED stream (STREAM_CLOSED after a RST_STREAM is
727                 // sent). We don't have enough information to know for sure, so we choose the lesser of the two errors.
728                 throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d",
729                                   frameName, streamId);
730             }
731             if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) {
732                 // If we have sent a reset stream it is assumed the stream will be closed after the write completes.
733                 // If we have not sent a reset, but the stream was created after a GoAway this is not supported by
734                 // DefaultHttp2Connection and if a custom Http2Connection is used it is assumed the lifetime is managed
735                 // elsewhere so we don't close the stream or otherwise modify the stream's state.
736 
737                 if (logger.isInfoEnabled()) {
738                     logger.info("{} ignoring {} frame for stream {}", ctx.channel(), frameName,
739                             stream.isResetSent() ? "RST_STREAM sent." :
740                                     "Stream created after GOAWAY sent. Last known stream by peer " +
741                                      connection.remote().lastStreamKnownByPeer());
742                 }
743 
744                 return true;
745             }
746             return false;
747         }
748 
749         /**
750          * Helper method for determining whether or not to ignore inbound frames. A stream is considered to be created
751          * after a {@code GOAWAY} is sent if the following conditions hold:
752          * <p/>
753          * <ul>
754          *     <li>A {@code GOAWAY} must have been sent by the local endpoint</li>
755          *     <li>The {@code streamId} must identify a legitimate stream id for the remote endpoint to be creating</li>
756          *     <li>{@code streamId} is greater than the Last Known Stream ID which was sent by the local endpoint
757          *     in the last {@code GOAWAY} frame</li>
758          * </ul>
759          * <p/>
760          */
761         private boolean streamCreatedAfterGoAwaySent(int streamId) {
762             Endpoint<?> remote = connection.remote();
763             return connection.goAwaySent() && remote.isValidStreamId(streamId) &&
764                     streamId > remote.lastStreamKnownByPeer();
765         }
766 
767         private void verifyStreamMayHaveExisted(int streamId, boolean endOfStream, String frameName)
768                 throws Http2Exception {
769             if (!connection.streamMayHaveExisted(streamId)) {
770                 throw connectionError(PROTOCOL_ERROR,
771                         "Stream %d does not exist for inbound frame %s, endOfStream = %b",
772                         streamId, frameName, endOfStream);
773             }
774         }
775     }
776 
777     private final class PrefaceFrameListener implements Http2FrameListener {
778         /**
779          * Verifies that the HTTP/2 connection preface has been received from the remote endpoint.
780          * It is possible that the current call to
781          * {@link Http2FrameReader#readFrame(ChannelHandlerContext, ByteBuf, Http2FrameListener)} will have multiple
782          * frames to dispatch. So it may be OK for this class to get legitimate frames for the first readFrame.
783          */
784         private void verifyPrefaceReceived() throws Http2Exception {
785             if (!prefaceReceived()) {
786                 throw connectionError(PROTOCOL_ERROR, "Received non-SETTINGS as first frame.");
787             }
788         }
789 
790         @Override
791         public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
792                 throws Http2Exception {
793             verifyPrefaceReceived();
794             return internalFrameListener.onDataRead(ctx, streamId, data, padding, endOfStream);
795         }
796 
797         @Override
798         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
799                 boolean endOfStream) throws Http2Exception {
800             verifyPrefaceReceived();
801             internalFrameListener.onHeadersRead(ctx, streamId, headers, padding, endOfStream);
802         }
803 
804         @Override
805         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
806                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
807             verifyPrefaceReceived();
808             internalFrameListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
809                     exclusive, padding, endOfStream);
810         }
811 
812         @Override
813         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
814                 boolean exclusive) throws Http2Exception {
815             verifyPrefaceReceived();
816             internalFrameListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
817         }
818 
819         @Override
820         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
821             verifyPrefaceReceived();
822             internalFrameListener.onRstStreamRead(ctx, streamId, errorCode);
823         }
824 
825         @Override
826         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
827             verifyPrefaceReceived();
828             internalFrameListener.onSettingsAckRead(ctx);
829         }
830 
831         @Override
832         public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
833             // The first settings should change the internalFrameListener to the "real" listener
834             // that expects the preface to be verified.
835             if (!prefaceReceived()) {
836                 internalFrameListener = new FrameReadListener();
837             }
838             internalFrameListener.onSettingsRead(ctx, settings);
839         }
840 
841         @Override
842         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
843             verifyPrefaceReceived();
844             internalFrameListener.onPingRead(ctx, data);
845         }
846 
847         @Override
848         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
849             verifyPrefaceReceived();
850             internalFrameListener.onPingAckRead(ctx, data);
851         }
852 
853         @Override
854         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
855                 Http2Headers headers, int padding) throws Http2Exception {
856             verifyPrefaceReceived();
857             internalFrameListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
858         }
859 
860         @Override
861         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
862                 throws Http2Exception {
863             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
864         }
865 
866         @Override
867         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
868                 throws Http2Exception {
869             verifyPrefaceReceived();
870             internalFrameListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
871         }
872 
873         @Override
874         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
875                 ByteBuf payload) throws Http2Exception {
876             verifyPrefaceReceived();
877             internalFrameListener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
878         }
879     }
880 
881     private static final class ContentLength {
882         private final long expected;
883         private long seen;
884 
885         ContentLength(long expected) {
886             this.expected = expected;
887         }
888 
889         void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception {
890             seen += bytes;
891             // Check for overflow
892             if (seen < 0) {
893                 throw streamError(streamId, PROTOCOL_ERROR,
894                         "Received amount of data did overflow and so not match content-length header %d", expected);
895             }
896             // Check if we received more data then what was advertised via the content-length header.
897             if (seen > expected) {
898                 throw streamError(streamId, PROTOCOL_ERROR,
899                         "Received amount of data %d does not match content-length header %d", seen, expected);
900             }
901 
902             if (isEnd) {
903                 if (seen == 0 && !server) {
904                     // This may be a response to a HEAD request, let's just allow it.
905                     return;
906                 }
907 
908                 // Check that we really saw what was told via the content-length header.
909                 if (expected > seen) {
910                     throw streamError(streamId, PROTOCOL_ERROR,
911                             "Received amount of data %d does not match content-length header %d", seen, expected);
912                 }
913             }
914         }
915     }
916 }