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 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             } catch (RuntimeException e) {
407                 // If an exception happened during delivery, the listener may have returned part
408                 // of the bytes before the error occurred. If that's the case, subtract that from
409                 // the total processed bytes so that we don't return too many bytes.
410                 int delta = unconsumedBytes - unconsumedBytes(stream);
411                 bytesToReturn -= delta;
412                 throw e;
413             } finally {
414                 // If appropriate, return the processed bytes to the flow controller.
415                 flowController.consumeBytes(stream, bytesToReturn);
416             }
417         }
418 
419         @Override
420         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
421                 boolean endOfStream) throws Http2Exception {
422             onHeadersRead(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endOfStream);
423         }
424 
425         @Override
426         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
427                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
428             Http2Stream stream = connection.stream(streamId);
429             boolean allowHalfClosedRemote = false;
430             boolean isTrailers = false;
431             if (stream == null && !connection.streamMayHaveExisted(streamId)) {
432                 stream = connection.remote().createStream(streamId, endOfStream);
433                 // Allow the state to be HALF_CLOSE_REMOTE if we're creating it in that state.
434                 allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE;
435             } else if (stream != null) {
436                 isTrailers = stream.isHeadersReceived();
437             }
438 
439             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "HEADERS")) {
440                 return;
441             }
442 
443             boolean isInformational = !connection.isServer() &&
444                     HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL;
445             if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) {
446                 throw streamError(streamId, PROTOCOL_ERROR,
447                                   "Stream %d received too many headers EOS: %s state: %s",
448                                   streamId, endOfStream, stream.state());
449             }
450 
451             switch (stream.state()) {
452                 case RESERVED_REMOTE:
453                     stream.open(endOfStream);
454                     break;
455                 case OPEN:
456                 case HALF_CLOSED_LOCAL:
457                     // Allowed to receive headers in these states.
458                     break;
459                 case HALF_CLOSED_REMOTE:
460                     if (!allowHalfClosedRemote) {
461                         throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
462                                 stream.id(), stream.state());
463                     }
464                     break;
465                 case CLOSED:
466                     throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
467                             stream.id(), stream.state());
468                 default:
469                     // Connection error.
470                     throw connectionError(PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(),
471                             stream.state());
472             }
473 
474             if (!isTrailers) {
475                 if (validateRequiredPseudoHeaders && !isInformational) {
476                     // Reject initial request/response HEADERS that omit a mandatory pseudo-header (RFC 9113, 8.3).
477                     validateRequiredPseudoHeaders(connection.isServer(), stream.id(), headers);
478                 }
479                 // extract the content-length header
480                 List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
481                 if (contentLength != null && !contentLength.isEmpty()) {
482                     try {
483                         long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
484                         if (cLength != -1) {
485                             headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength);
486                             stream.setProperty(contentLengthKey, new ContentLength(cLength));
487                         }
488                     } catch (IllegalArgumentException e) {
489                         throw streamError(stream.id(), PROTOCOL_ERROR, e,
490                                 "Multiple content-length headers received");
491                     }
492                 }
493                 // Use size() instead of isEmpty() for backward compatibility with grpc-java prior to 1.59.1,
494                 // see https://github.com/grpc/grpc-java/issues/10665
495             } else if (validateHeaders && headers.size() > 0) {
496                 // Need to check trailers don't contain pseudo headers. According to RFC 9113
497                 // Trailers MUST NOT include pseudo-header fields (Section 8.3).
498                 for (Iterator<Entry<CharSequence, CharSequence>> iterator =
499                     headers.iterator(); iterator.hasNext();) {
500                     CharSequence name = iterator.next().getKey();
501                     if (Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat(name)) {
502                         throw streamError(stream.id(), PROTOCOL_ERROR,
503                                 "Found invalid Pseudo-Header in trailers: %s", name);
504                     }
505                 }
506             }
507 
508             stream.headersReceived(isInformational);
509             verifyContentLength(stream, 0, endOfStream);
510             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
511             listener.onHeadersRead(ctx, streamId, headers, streamDependency,
512                     weight, exclusive, padding, endOfStream);
513             // If the headers completes this stream, close it.
514             if (endOfStream) {
515                 lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
516             }
517         }
518 
519         @Override
520         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
521                 boolean exclusive) throws Http2Exception {
522             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
523 
524             listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
525         }
526 
527         @Override
528         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
529             Http2Stream stream = connection.stream(streamId);
530             if (stream == null) {
531                 verifyStreamMayHaveExisted(streamId, false, "RST_STREAM");
532                 return;
533             }
534 
535             switch(stream.state()) {
536             case IDLE:
537                 throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId);
538             case CLOSED:
539                 return; // RST_STREAM frames must be ignored for closed streams.
540             default:
541                 break;
542             }
543 
544             listener.onRstStreamRead(ctx, streamId, errorCode);
545 
546             lifecycleManager.closeStream(stream, ctx.newSucceededFuture());
547         }
548 
549         @Override
550         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
551             // Apply oldest outstanding local settings here. This is a synchronization point between endpoints.
552             Http2Settings settings = encoder.pollSentSettings();
553 
554             if (settings != null) {
555                 applyLocalSettings(settings);
556             }
557 
558             listener.onSettingsAckRead(ctx);
559         }
560 
561         /**
562          * Applies settings sent from the local endpoint.
563          * <p>
564          * This method is only called after the local settings have been acknowledged from the remote endpoint.
565          */
566         private void applyLocalSettings(Http2Settings settings) throws Http2Exception {
567             Boolean pushEnabled = settings.pushEnabled();
568             final Http2FrameReader.Configuration config = frameReader.configuration();
569             final Http2HeadersDecoder.Configuration headerConfig = config.headersConfiguration();
570             final Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
571             if (pushEnabled != null) {
572                 if (connection.isServer()) {
573                     throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
574                 }
575                 connection.local().allowPushTo(pushEnabled);
576             }
577 
578             Long maxConcurrentStreams = settings.maxConcurrentStreams();
579             if (maxConcurrentStreams != null) {
580                 connection.remote().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE));
581             }
582 
583             Long headerTableSize = settings.headerTableSize();
584             if (headerTableSize != null) {
585                 headerConfig.maxHeaderTableSize(headerTableSize);
586             }
587 
588             Long maxHeaderListSize = settings.maxHeaderListSize();
589             if (maxHeaderListSize != null) {
590                 headerConfig.maxHeaderListSize(maxHeaderListSize, calculateMaxHeaderListSizeGoAway(maxHeaderListSize));
591             }
592 
593             Integer maxFrameSize = settings.maxFrameSize();
594             if (maxFrameSize != null) {
595                 frameSizePolicy.maxFrameSize(maxFrameSize);
596             }
597 
598             Integer initialWindowSize = settings.initialWindowSize();
599             if (initialWindowSize != null) {
600                 flowController().initialWindowSize(initialWindowSize);
601             }
602         }
603 
604         @Override
605         public void onSettingsRead(final ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
606             if (settingsReceivedConsumer == null) {
607                 // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
608                 // remote peer applies these settings before any subsequent frames that we may send which depend upon
609                 // these new settings. See https://github.com/netty/netty/issues/6520.
610                 encoder.writeSettingsAck(ctx, ctx.newPromise());
611 
612                 encoder.remoteSettings(settings);
613             } else {
614                 settingsReceivedConsumer.consumeReceivedSettings(settings);
615             }
616 
617             listener.onSettingsRead(ctx, settings);
618         }
619 
620         @Override
621         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
622             if (autoAckPing) {
623                 // Send an ack back to the remote client.
624                 encoder.writePing(ctx, true, data, ctx.newPromise());
625             }
626             listener.onPingRead(ctx, data);
627         }
628 
629         @Override
630         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
631             listener.onPingAckRead(ctx, data);
632         }
633 
634         @Override
635         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
636                 Http2Headers headers, int padding) throws Http2Exception {
637             // A client cannot push.
638             if (connection().isServer()) {
639                 throw connectionError(PROTOCOL_ERROR, "A client cannot push.");
640             }
641 
642             Http2Stream parentStream = connection.stream(streamId);
643 
644             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, parentStream, false, "PUSH_PROMISE")) {
645                 return;
646             }
647 
648             switch (parentStream.state()) {
649               case OPEN:
650               case HALF_CLOSED_LOCAL:
651                   // Allowed to receive push promise in these states.
652                   break;
653               default:
654                   // Connection error.
655                   throw connectionError(PROTOCOL_ERROR,
656                       "Stream %d in unexpected state for receiving push promise: %s",
657                       parentStream.id(), parentStream.state());
658             }
659 
660             if (!requestVerifier.isAuthoritative(ctx, headers)) {
661                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
662                         "Promised request on stream %d for promised stream %d is not authoritative",
663                         streamId, promisedStreamId);
664             }
665             if (!requestVerifier.isCacheable(headers)) {
666                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
667                         "Promised request on stream %d for promised stream %d is not known to be cacheable",
668                         streamId, promisedStreamId);
669             }
670             if (!requestVerifier.isSafe(headers)) {
671                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
672                         "Promised request on stream %d for promised stream %d is not known to be safe",
673                         streamId, promisedStreamId);
674             }
675 
676             // Reserve the push stream based with a priority based on the current stream's priority.
677             connection.remote().reservePushStream(promisedStreamId, parentStream);
678 
679             listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
680         }
681 
682         @Override
683         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
684                 throws Http2Exception {
685             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
686         }
687 
688         @Override
689         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
690                 throws Http2Exception {
691             Http2Stream stream = connection.stream(streamId);
692             if (stream == null || stream.state() == CLOSED || streamCreatedAfterGoAwaySent(streamId)) {
693                 // Ignore this frame.
694                 verifyStreamMayHaveExisted(streamId, false, "WINDOW_UPDATE");
695                 return;
696             }
697 
698             // Update the outbound flow control window.
699             encoder.flowController().incrementWindowSize(stream, windowSizeIncrement);
700 
701             listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
702         }
703 
704         @Override
705         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
706                 ByteBuf payload) throws Http2Exception {
707             Http2Stream stream = connection.stream(streamId);
708             if (stream == null) {
709                 return;
710             }
711 
712             listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
713         }
714 
715         /**
716          * Helper method to determine if a frame that has the semantics of headers or data should be ignored for the
717          * {@code stream} (which may be {@code null}) associated with {@code streamId}.
718          */
719         private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int streamId, Http2Stream stream,
720                 boolean endOfStream, String frameName) throws Http2Exception {
721             if (stream == null) {
722                 if (streamCreatedAfterGoAwaySent(streamId)) {
723                     logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent",
724                             ctx.channel(), frameName, streamId);
725                     return true;
726                 }
727 
728                 // Make sure it's not an out-of-order frame, like a rogue DATA frame, for a stream that could
729                 // never have existed.
730                 verifyStreamMayHaveExisted(streamId, endOfStream, frameName);
731 
732                 // Its possible that this frame would result in stream ID out of order creation (PROTOCOL ERROR) and its
733                 // also possible that this frame is received on a CLOSED stream (STREAM_CLOSED after a RST_STREAM is
734                 // sent). We don't have enough information to know for sure, so we choose the lesser of the two errors.
735                 throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d",
736                                   frameName, streamId);
737             }
738             if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) {
739                 // If we have sent a reset stream it is assumed the stream will be closed after the write completes.
740                 // If we have not sent a reset, but the stream was created after a GoAway this is not supported by
741                 // DefaultHttp2Connection and if a custom Http2Connection is used it is assumed the lifetime is managed
742                 // elsewhere so we don't close the stream or otherwise modify the stream's state.
743 
744                 if (logger.isInfoEnabled()) {
745                     logger.info("{} ignoring {} frame for stream {}", ctx.channel(), frameName,
746                             stream.isResetSent() ? "RST_STREAM sent." :
747                                     "Stream created after GOAWAY sent. Last known stream by peer " +
748                                      connection.remote().lastStreamKnownByPeer());
749                 }
750 
751                 return true;
752             }
753             return false;
754         }
755 
756         /**
757          * Helper method for determining whether or not to ignore inbound frames. A stream is considered to be created
758          * after a {@code GOAWAY} is sent if the following conditions hold:
759          * <p/>
760          * <ul>
761          *     <li>A {@code GOAWAY} must have been sent by the local endpoint</li>
762          *     <li>The {@code streamId} must identify a legitimate stream id for the remote endpoint to be creating</li>
763          *     <li>{@code streamId} is greater than the Last Known Stream ID which was sent by the local endpoint
764          *     in the last {@code GOAWAY} frame</li>
765          * </ul>
766          * <p/>
767          */
768         private boolean streamCreatedAfterGoAwaySent(int streamId) {
769             Endpoint<?> remote = connection.remote();
770             return connection.goAwaySent() && remote.isValidStreamId(streamId) &&
771                     streamId > remote.lastStreamKnownByPeer();
772         }
773 
774         private void verifyStreamMayHaveExisted(int streamId, boolean endOfStream, String frameName)
775                 throws Http2Exception {
776             if (!connection.streamMayHaveExisted(streamId)) {
777                 throw connectionError(PROTOCOL_ERROR,
778                         "Stream %d does not exist for inbound frame %s, endOfStream = %b",
779                         streamId, frameName, endOfStream);
780             }
781         }
782     }
783 
784     private final class PrefaceFrameListener implements Http2FrameListener {
785         /**
786          * Verifies that the HTTP/2 connection preface has been received from the remote endpoint.
787          * It is possible that the current call to
788          * {@link Http2FrameReader#readFrame(ChannelHandlerContext, ByteBuf, Http2FrameListener)} will have multiple
789          * frames to dispatch. So it may be OK for this class to get legitimate frames for the first readFrame.
790          */
791         private void verifyPrefaceReceived() throws Http2Exception {
792             if (!prefaceReceived()) {
793                 throw connectionError(PROTOCOL_ERROR, "Received non-SETTINGS as first frame.");
794             }
795         }
796 
797         @Override
798         public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
799                 throws Http2Exception {
800             verifyPrefaceReceived();
801             return internalFrameListener.onDataRead(ctx, streamId, data, padding, endOfStream);
802         }
803 
804         @Override
805         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
806                 boolean endOfStream) throws Http2Exception {
807             verifyPrefaceReceived();
808             internalFrameListener.onHeadersRead(ctx, streamId, headers, padding, endOfStream);
809         }
810 
811         @Override
812         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
813                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
814             verifyPrefaceReceived();
815             internalFrameListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
816                     exclusive, padding, endOfStream);
817         }
818 
819         @Override
820         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
821                 boolean exclusive) throws Http2Exception {
822             verifyPrefaceReceived();
823             internalFrameListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
824         }
825 
826         @Override
827         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
828             verifyPrefaceReceived();
829             internalFrameListener.onRstStreamRead(ctx, streamId, errorCode);
830         }
831 
832         @Override
833         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
834             verifyPrefaceReceived();
835             internalFrameListener.onSettingsAckRead(ctx);
836         }
837 
838         @Override
839         public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
840             // The first settings should change the internalFrameListener to the "real" listener
841             // that expects the preface to be verified.
842             if (!prefaceReceived()) {
843                 internalFrameListener = new FrameReadListener();
844             }
845             internalFrameListener.onSettingsRead(ctx, settings);
846         }
847 
848         @Override
849         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
850             verifyPrefaceReceived();
851             internalFrameListener.onPingRead(ctx, data);
852         }
853 
854         @Override
855         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
856             verifyPrefaceReceived();
857             internalFrameListener.onPingAckRead(ctx, data);
858         }
859 
860         @Override
861         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
862                 Http2Headers headers, int padding) throws Http2Exception {
863             verifyPrefaceReceived();
864             internalFrameListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
865         }
866 
867         @Override
868         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
869                 throws Http2Exception {
870             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
871         }
872 
873         @Override
874         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
875                 throws Http2Exception {
876             verifyPrefaceReceived();
877             internalFrameListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
878         }
879 
880         @Override
881         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
882                 ByteBuf payload) throws Http2Exception {
883             verifyPrefaceReceived();
884             internalFrameListener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
885         }
886     }
887 
888     private static final class ContentLength {
889         private final long expected;
890         private long seen;
891 
892         ContentLength(long expected) {
893             this.expected = expected;
894         }
895 
896         void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception {
897             seen += bytes;
898             // Check for overflow
899             if (seen < 0) {
900                 throw streamError(streamId, PROTOCOL_ERROR,
901                         "Received amount of data did overflow and so not match content-length header %d", expected);
902             }
903             // Check if we received more data then what was advertised via the content-length header.
904             if (seen > expected) {
905                 throw streamError(streamId, PROTOCOL_ERROR,
906                         "Received amount of data %d does not match content-length header %d", seen, expected);
907             }
908 
909             if (isEnd) {
910                 if (seen == 0 && !server) {
911                     // This may be a response to a HEAD request, let's just allow it.
912                     return;
913                 }
914 
915                 // Check that we really saw what was told via the content-length header.
916                 if (expected > seen) {
917                     throw streamError(streamId, PROTOCOL_ERROR,
918                             "Received amount of data %d does not match content-length header %d", seen, expected);
919                 }
920             }
921         }
922     }
923 }