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>, and to reject
157      *        PUSH_PROMISE field blocks that do not carry a complete set of request pseudo-header fields, according
158      *        to <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.4.1">RFC 9113, 8.4.1</a>.
159      */
160     public DefaultHttp2ConnectionDecoder(Http2Connection connection,
161                                          Http2ConnectionEncoder encoder,
162                                          Http2FrameReader frameReader,
163                                          Http2PromisedRequestVerifier requestVerifier,
164                                          boolean autoAckSettings,
165                                          boolean autoAckPing,
166                                          boolean validateHeaders,
167                                          boolean validateRequiredPseudoHeaders) {
168         this.validateHeaders = validateHeaders;
169         this.validateRequiredPseudoHeaders = validateRequiredPseudoHeaders;
170         this.autoAckPing = autoAckPing;
171         if (autoAckSettings) {
172             settingsReceivedConsumer = null;
173         } else {
174             if (!(encoder instanceof Http2SettingsReceivedConsumer)) {
175                 throw new IllegalArgumentException("disabling autoAckSettings requires the encoder to be a " +
176                         Http2SettingsReceivedConsumer.class);
177             }
178             settingsReceivedConsumer = (Http2SettingsReceivedConsumer) encoder;
179         }
180         this.connection = checkNotNull(connection, "connection");
181         contentLengthKey = this.connection.newKey();
182         this.frameReader = checkNotNull(frameReader, "frameReader");
183         this.encoder = checkNotNull(encoder, "encoder");
184         this.requestVerifier = checkNotNull(requestVerifier, "requestVerifier");
185         if (connection.local().flowController() == null) {
186             connection.local().flowController(new DefaultHttp2LocalFlowController(connection));
187         }
188         connection.local().flowController().frameWriter(encoder.frameWriter());
189     }
190 
191     @Override
192     public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
193         this.lifecycleManager = checkNotNull(lifecycleManager, "lifecycleManager");
194     }
195 
196     @Override
197     public Http2Connection connection() {
198         return connection;
199     }
200 
201     @Override
202     public final Http2LocalFlowController flowController() {
203         return connection.local().flowController();
204     }
205 
206     @Override
207     public void frameListener(Http2FrameListener listener) {
208         this.listener = checkNotNull(listener, "listener");
209     }
210 
211     @Override
212     public Http2FrameListener frameListener() {
213         return listener;
214     }
215 
216     @Override
217     public boolean prefaceReceived() {
218         return FrameReadListener.class == internalFrameListener.getClass();
219     }
220 
221     @Override
222     public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Http2Exception {
223         frameReader.readFrame(ctx, in, internalFrameListener);
224     }
225 
226     @Override
227     public Http2Settings localSettings() {
228         Http2Settings settings = new Http2Settings();
229         Http2FrameReader.Configuration config = frameReader.configuration();
230         Http2HeadersDecoder.Configuration headersConfig = config.headersConfiguration();
231         Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
232         settings.initialWindowSize(flowController().initialWindowSize());
233         settings.maxConcurrentStreams(connection.remote().maxActiveStreams());
234         settings.headerTableSize(headersConfig.maxHeaderTableSize());
235         settings.maxFrameSize(frameSizePolicy.maxFrameSize());
236         settings.maxHeaderListSize(headersConfig.maxHeaderListSize());
237         if (!connection.isServer()) {
238             // Only set the pushEnabled flag if this is a client endpoint.
239             settings.pushEnabled(connection.local().allowPushTo());
240         }
241         return settings;
242     }
243 
244     @Override
245     public void close() {
246         frameReader.close();
247     }
248 
249     /**
250      * Calculate the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount.
251      * @param maxHeaderListSize
252      *      <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a> for the local
253      *      endpoint.
254      * @return the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount.
255      */
256     protected long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) {
257         return Http2CodecUtil.calculateMaxHeaderListSizeGoAway(maxHeaderListSize);
258     }
259 
260     private int unconsumedBytes(Http2Stream stream) {
261         return flowController().unconsumedBytes(stream);
262     }
263 
264     void onGoAwayRead0(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
265             throws Http2Exception {
266         listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData);
267         connection.goAwayReceived(lastStreamId, errorCode, debugData);
268     }
269 
270     // See https://tools.ietf.org/html/rfc7540#section-8.1.2.6
271     private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception {
272         ContentLength contentLength = stream.getProperty(contentLengthKey);
273         if (contentLength != null) {
274             try {
275                 contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd);
276             } finally {
277                 if (isEnd) {
278                     stream.removeProperty(contentLengthKey);
279                 }
280             }
281         }
282     }
283 
284     /**
285      * Validates that an initial request or response HEADERS frame, or a PUSH_PROMISE field block (which always
286      * carries a request header set, per <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.4.1">
287      * RFC 9113, 8.4.1</a>), carries the mandatory pseudo-header fields, as required by
288      * <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3">RFC 9113, 8.3</a>.
289      * Trailers and informational (1xx) responses are handled by the caller and do not reach this method.
290      */
291     private static void validateRequiredPseudoHeaders(boolean server, int streamId, Http2Headers headers)
292             throws Http2Exception {
293         if (server) {
294             // Request pseudo-header fields (RFC 9113, 8.3.1).
295             CharSequence method = headers.method();
296             if (method == null) {
297                 throw streamError(streamId, PROTOCOL_ERROR,
298                         "Request is missing mandatory :method pseudo-header field.");
299             }
300             // CONNECT (RFC 9113, 8.5) omits :scheme/:path and carries :authority; extended CONNECT (RFC 8441),
301             // identified by :protocol, follows the regular :scheme/:path rules.
302             if (HttpMethod.CONNECT.asciiName().contentEquals(method) &&
303                     !headers.contains(PseudoHeaderName.PROTOCOL.value())) {
304                 if (headers.authority() == null) {
305                     throw streamError(streamId, PROTOCOL_ERROR,
306                             "CONNECT request is missing mandatory :authority pseudo-header field.");
307                 }
308             } else {
309                 if (headers.scheme() == null) {
310                     throw streamError(streamId, PROTOCOL_ERROR,
311                             "Request is missing mandatory :scheme pseudo-header field.");
312                 }
313                 CharSequence path = headers.path();
314                 if (path == null || path.length() == 0) {
315                     throw streamError(streamId, PROTOCOL_ERROR,
316                             "Request is missing mandatory :path pseudo-header field.");
317                 }
318             }
319         } else {
320             // Response pseudo-header fields (RFC 9113, 8.3.2).
321             if (headers.status() == null) {
322                 throw streamError(streamId, PROTOCOL_ERROR,
323                         "Response is missing mandatory :status pseudo-header field.");
324             }
325         }
326     }
327 
328     /**
329      * Validates that pseudo-headers are defined and are used in the correct
330      * endpoint direction (RFC 9113, Sections 8.1.1 and 8.3).
331      *
332      * @param isServer true if processing an inbound request on the server
333      * @param streamId the target HTTP/2 stream ID
334      * @param headers  the HTTP/2 headers container
335      * @throws Http2Exception if a pseudo-header is undefined or used in the
336      *                        wrong endpoint direction
337      */
338     private static void validatePseudoHeadersDirection(boolean isServer, int streamId, Http2Headers headers)
339             throws Http2Exception {
340         for (Entry<CharSequence, CharSequence> entry : headers) {
341             CharSequence name = entry.getKey();
342             if (PseudoHeaderName.hasPseudoHeaderFormat(name)) {
343                 PseudoHeaderName pseudo = PseudoHeaderName.getPseudoHeader(name);
344                 if (pseudo == null) {
345                     // Undefined pseudo header
346                     throw streamError(streamId, PROTOCOL_ERROR,
347                                       "Undefined pseudo-header '%s' received.", name);
348                 }
349                 if (isServer && !pseudo.isRequestOnly()) {
350                     throw streamError(streamId, PROTOCOL_ERROR,
351                                       "Response-only pseudo-header '%s' received in request.", name);
352                 }
353                 if (!isServer && pseudo.isRequestOnly()) {
354                     throw streamError(streamId, PROTOCOL_ERROR,
355                                       "Request-only pseudo-header '%s' received in response.", name);
356                 }
357             } else {
358                 //Pseudo-headers MUST precede regular headers, so we can stop checking
359                 break;
360             }
361         }
362     }
363 
364     /**
365      * Handles all inbound frames from the network.
366      */
367     private final class FrameReadListener implements Http2FrameListener {
368         @Override
369         public int onDataRead(final ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
370                               boolean endOfStream) throws Http2Exception {
371             Http2Stream stream = connection.stream(streamId);
372             Http2LocalFlowController flowController = flowController();
373             int readable = data.readableBytes();
374             int bytesToReturn = readable + padding;
375 
376             final boolean shouldIgnore;
377             try {
378                 shouldIgnore = shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "DATA");
379             } catch (Http2Exception e) {
380                 // Ignoring this frame. We still need to count the frame towards the connection flow control
381                 // window, but we immediately mark all bytes as consumed.
382                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
383                 flowController.consumeBytes(stream, bytesToReturn);
384                 throw e;
385             } catch (Throwable t) {
386                 throw connectionError(INTERNAL_ERROR, t, "Unhandled error on data stream id %d", streamId);
387             }
388 
389             if (shouldIgnore) {
390                 // Ignoring this frame. We still need to count the frame towards the connection flow control
391                 // window, but we immediately mark all bytes as consumed.
392                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
393                 flowController.consumeBytes(stream, bytesToReturn);
394 
395                 // Verify that the stream may have existed after we apply flow control.
396                 verifyStreamMayHaveExisted(streamId, endOfStream, "DATA");
397 
398                 // All bytes have been consumed.
399                 return bytesToReturn;
400             }
401             Http2Exception error = null;
402             switch (stream.state()) {
403                 case OPEN:
404                 case HALF_CLOSED_LOCAL:
405                     break;
406                 case HALF_CLOSED_REMOTE:
407                 case CLOSED:
408                     error = streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
409                         stream.id(), stream.state());
410                     break;
411                 default:
412                     // IDLE, RESERVED_LOCAL or RESERVED_REMOTE: this must be treated as a connection error
413                     // (RFC 9113, section 5.1).
414                     error = connectionError(PROTOCOL_ERROR,
415                         "Stream %d in unexpected state: %s", stream.id(), stream.state());
416                     break;
417             }
418 
419             int unconsumedBytes = unconsumedBytes(stream);
420             try {
421                 flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream);
422                 // Update the unconsumed bytes after flow control is applied.
423                 unconsumedBytes = unconsumedBytes(stream);
424 
425                 // If the stream is in an invalid state to receive the frame, throw the error.
426                 if (error != null) {
427                     throw error;
428                 }
429 
430                 verifyContentLength(stream, readable, endOfStream);
431 
432                 // Call back the application and retrieve the number of bytes that have been
433                 // immediately processed.
434                 bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream);
435 
436                 if (endOfStream) {
437                     lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
438                 }
439 
440                 return bytesToReturn;
441             } catch (Http2Exception | RuntimeException e) {
442                 // If an exception happened during delivery, the listener may have returned part
443                 // of the bytes before the error occurred. If that's the case, subtract that from
444                 // the total processed bytes so that we don't return too many bytes.
445                 int delta = unconsumedBytes - unconsumedBytes(stream);
446                 bytesToReturn -= delta;
447                 throw e;
448             } finally {
449                 // If appropriate, return the processed bytes to the flow controller.
450                 flowController.consumeBytes(stream, bytesToReturn);
451             }
452         }
453 
454         @Override
455         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
456                 boolean endOfStream) throws Http2Exception {
457             onHeadersRead(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endOfStream);
458         }
459 
460         @Override
461         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
462                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
463             Http2Stream stream = connection.stream(streamId);
464             boolean allowHalfClosedRemote = false;
465             boolean isTrailers = false;
466             if (stream == null && !connection.streamMayHaveExisted(streamId)) {
467                 stream = connection.remote().createStream(streamId, endOfStream);
468                 // Allow the state to be HALF_CLOSE_REMOTE if we're creating it in that state.
469                 allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE;
470             } else if (stream != null) {
471                 isTrailers = stream.isHeadersReceived();
472             }
473 
474             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, endOfStream, "HEADERS")) {
475                 return;
476             }
477 
478             boolean isInformational = !connection.isServer() &&
479                     HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL;
480             if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) {
481                 throw streamError(streamId, PROTOCOL_ERROR,
482                                   "Stream %d received too many headers EOS: %s state: %s",
483                                   streamId, endOfStream, stream.state());
484             }
485 
486             switch (stream.state()) {
487                 case RESERVED_REMOTE:
488                     stream.open(endOfStream);
489                     break;
490                 case OPEN:
491                 case HALF_CLOSED_LOCAL:
492                     // Allowed to receive headers in these states.
493                     break;
494                 case HALF_CLOSED_REMOTE:
495                     if (!allowHalfClosedRemote) {
496                         throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
497                                 stream.id(), stream.state());
498                     }
499                     break;
500                 case CLOSED:
501                     throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s",
502                             stream.id(), stream.state());
503                 default:
504                     // Connection error.
505                     throw connectionError(PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(),
506                             stream.state());
507             }
508 
509             if (!isTrailers) {
510                 if (validateRequiredPseudoHeaders && !isInformational) {
511                     // Reject initial request/response HEADERS that omit a mandatory pseudo-header (RFC 9113, 8.3).
512                     validateRequiredPseudoHeaders(connection.isServer(), stream.id(), headers);
513                 }
514                 if (validateHeaders) {
515                     //Enforce RFC 9113 section 8.3: Reject wrong direction pseudo-headers
516                     //https://github.com/netty/netty/issues/17449
517                     validatePseudoHeadersDirection(connection.isServer(), streamId, headers);
518                 }
519                 // extract the content-length header
520                 List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
521                 if (contentLength != null && !contentLength.isEmpty()) {
522                     try {
523                         long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
524                         if (cLength != -1) {
525                             headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength);
526                             stream.setProperty(contentLengthKey, new ContentLength(cLength));
527                         }
528                     } catch (IllegalArgumentException e) {
529                         throw streamError(stream.id(), PROTOCOL_ERROR, e,
530                                 "Multiple content-length headers received");
531                     }
532                 }
533                 // Use size() instead of isEmpty() for backward compatibility with grpc-java prior to 1.59.1,
534                 // see https://github.com/grpc/grpc-java/issues/10665
535             } else if (validateHeaders && headers.size() > 0) {
536                 // Need to check trailers don't contain pseudo headers. According to RFC 9113
537                 // Trailers MUST NOT include pseudo-header fields (Section 8.3).
538                 for (Iterator<Entry<CharSequence, CharSequence>> iterator =
539                     headers.iterator(); iterator.hasNext();) {
540                     CharSequence name = iterator.next().getKey();
541                     if (Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat(name)) {
542                         throw streamError(stream.id(), PROTOCOL_ERROR,
543                                 "Found invalid Pseudo-Header in trailers: %s", name);
544                     }
545                 }
546             }
547 
548             stream.headersReceived(isInformational);
549             verifyContentLength(stream, 0, endOfStream);
550             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
551             listener.onHeadersRead(ctx, streamId, headers, streamDependency,
552                     weight, exclusive, padding, endOfStream);
553             // If the headers completes this stream, close it.
554             if (endOfStream) {
555                 lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
556             }
557         }
558 
559         @Override
560         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
561                 boolean exclusive) throws Http2Exception {
562             encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
563 
564             listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
565         }
566 
567         @Override
568         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
569             Http2Stream stream = connection.stream(streamId);
570             if (stream == null) {
571                 verifyStreamMayHaveExisted(streamId, false, "RST_STREAM");
572                 return;
573             }
574 
575             switch(stream.state()) {
576             case IDLE:
577                 throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId);
578             case CLOSED:
579                 return; // RST_STREAM frames must be ignored for closed streams.
580             default:
581                 break;
582             }
583 
584             listener.onRstStreamRead(ctx, streamId, errorCode);
585 
586             lifecycleManager.closeStream(stream, ctx.newSucceededFuture());
587         }
588 
589         @Override
590         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
591             // Apply oldest outstanding local settings here. This is a synchronization point between endpoints.
592             Http2Settings settings = encoder.pollSentSettings();
593 
594             if (settings != null) {
595                 applyLocalSettings(settings);
596             }
597 
598             listener.onSettingsAckRead(ctx);
599         }
600 
601         /**
602          * Applies settings sent from the local endpoint.
603          * <p>
604          * This method is only called after the local settings have been acknowledged from the remote endpoint.
605          */
606         private void applyLocalSettings(Http2Settings settings) throws Http2Exception {
607             Boolean pushEnabled = settings.pushEnabled();
608             final Http2FrameReader.Configuration config = frameReader.configuration();
609             final Http2HeadersDecoder.Configuration headerConfig = config.headersConfiguration();
610             final Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy();
611             if (pushEnabled != null) {
612                 if (connection.isServer()) {
613                     throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
614                 }
615                 connection.local().allowPushTo(pushEnabled);
616             }
617 
618             Long maxConcurrentStreams = settings.maxConcurrentStreams();
619             if (maxConcurrentStreams != null) {
620                 connection.remote().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE));
621             }
622 
623             Long headerTableSize = settings.headerTableSize();
624             if (headerTableSize != null) {
625                 headerConfig.maxHeaderTableSize(headerTableSize);
626             }
627 
628             Long maxHeaderListSize = settings.maxHeaderListSize();
629             if (maxHeaderListSize != null) {
630                 headerConfig.maxHeaderListSize(maxHeaderListSize, calculateMaxHeaderListSizeGoAway(maxHeaderListSize));
631             }
632 
633             Integer maxFrameSize = settings.maxFrameSize();
634             if (maxFrameSize != null) {
635                 frameSizePolicy.maxFrameSize(maxFrameSize);
636             }
637 
638             Integer initialWindowSize = settings.initialWindowSize();
639             if (initialWindowSize != null) {
640                 flowController().initialWindowSize(initialWindowSize);
641             }
642         }
643 
644         @Override
645         public void onSettingsRead(final ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
646             if (settingsReceivedConsumer == null) {
647                 // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
648                 // remote peer applies these settings before any subsequent frames that we may send which depend upon
649                 // these new settings. See https://github.com/netty/netty/issues/6520.
650                 encoder.writeSettingsAck(ctx, ctx.newPromise());
651 
652                 encoder.remoteSettings(settings);
653             } else {
654                 settingsReceivedConsumer.consumeReceivedSettings(settings);
655             }
656 
657             listener.onSettingsRead(ctx, settings);
658         }
659 
660         @Override
661         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
662             if (autoAckPing) {
663                 // Send an ack back to the remote client.
664                 encoder.writePing(ctx, true, data, ctx.newPromise());
665             }
666             listener.onPingRead(ctx, data);
667         }
668 
669         @Override
670         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
671             listener.onPingAckRead(ctx, data);
672         }
673 
674         @Override
675         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
676                 Http2Headers headers, int padding) throws Http2Exception {
677             // A client cannot push.
678             if (connection().isServer()) {
679                 throw connectionError(PROTOCOL_ERROR, "A client cannot push.");
680             }
681 
682             Http2Stream parentStream = connection.stream(streamId);
683 
684             if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, parentStream, false, "PUSH_PROMISE")) {
685                 return;
686             }
687 
688             switch (parentStream.state()) {
689               case OPEN:
690               case HALF_CLOSED_LOCAL:
691                   // Allowed to receive push promise in these states.
692                   break;
693               default:
694                   // Connection error.
695                   throw connectionError(PROTOCOL_ERROR,
696                       "Stream %d in unexpected state for receiving push promise: %s",
697                       parentStream.id(), parentStream.state());
698             }
699 
700             // Reserve the push stream, with a priority based on the current stream's priority, before running
701             // the request verifier below. This ensures the promised stream's existence is tracked even if the
702             // request is subsequently rejected, so the stream ID cannot be silently reused or forgotten.
703             connection.remote().reservePushStream(promisedStreamId, parentStream);
704 
705             if (validateRequiredPseudoHeaders) {
706                 // A PUSH_PROMISE field block carries a complete set of request header fields, regardless of the
707                 // role of the local endpoint. Reject an incomplete promised request on the promised stream
708                 // (RFC 9113, 8.4.1).
709                 validateRequiredPseudoHeaders(true, promisedStreamId, headers);
710             }
711 
712             // extract the content-length header
713             List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
714             if (contentLength != null && !contentLength.isEmpty()) {
715                 long cLength;
716                 try {
717                     cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
718                 } catch (IllegalArgumentException e) {
719                     throw streamError(promisedStreamId, PROTOCOL_ERROR, e,
720                         "Promised request on stream %d for promised stream %d contains invalid content-length header",
721                         streamId, promisedStreamId);
722                 }
723                 if (cLength != -1 && cLength != 0) {
724                     throw streamError(promisedStreamId, PROTOCOL_ERROR,
725                         "Promised request on stream %d for promised stream %d contains invalid content-length header",
726                         streamId, promisedStreamId);
727                 }
728             }
729 
730             if (!requestVerifier.isAuthoritative(ctx, headers)) {
731                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
732                         "Promised request on stream %d for promised stream %d is not authoritative",
733                         streamId, promisedStreamId);
734             }
735             if (!requestVerifier.isCacheable(headers)) {
736                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
737                         "Promised request on stream %d for promised stream %d is not known to be cacheable",
738                         streamId, promisedStreamId);
739             }
740             if (!requestVerifier.isSafe(headers)) {
741                 throw streamError(promisedStreamId, PROTOCOL_ERROR,
742                         "Promised request on stream %d for promised stream %d is not known to be safe",
743                         streamId, promisedStreamId);
744             }
745 
746             listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
747         }
748 
749         @Override
750         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
751                 throws Http2Exception {
752             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
753         }
754 
755         @Override
756         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
757                 throws Http2Exception {
758             Http2Stream stream = connection.stream(streamId);
759             if (stream == null || stream.state() == CLOSED || streamCreatedAfterGoAwaySent(streamId)) {
760                 // Ignore this frame.
761                 verifyStreamMayHaveExisted(streamId, false, "WINDOW_UPDATE");
762                 return;
763             }
764 
765             // Update the outbound flow control window.
766             encoder.flowController().incrementWindowSize(stream, windowSizeIncrement);
767 
768             listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
769         }
770 
771         @Override
772         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
773                 ByteBuf payload) throws Http2Exception {
774             Http2Stream stream = connection.stream(streamId);
775             if (stream == null) {
776                 return;
777             }
778 
779             listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
780         }
781 
782         /**
783          * Helper method to determine if a frame that has the semantics of headers or data should be ignored for the
784          * {@code stream} (which may be {@code null}) associated with {@code streamId}.
785          */
786         private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int streamId, Http2Stream stream,
787                 boolean endOfStream, String frameName) throws Http2Exception {
788             if (stream == null) {
789                 if (streamCreatedAfterGoAwaySent(streamId)) {
790                     logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent",
791                             ctx.channel(), frameName, streamId);
792                     return true;
793                 }
794 
795                 // Make sure it's not an out-of-order frame, like a rogue DATA frame, for a stream that could
796                 // never have existed.
797                 verifyStreamMayHaveExisted(streamId, endOfStream, frameName);
798 
799                 // Its possible that this frame would result in stream ID out of order creation (PROTOCOL ERROR) and its
800                 // also possible that this frame is received on a CLOSED stream (STREAM_CLOSED after a RST_STREAM is
801                 // sent). We don't have enough information to know for sure, so we choose the lesser of the two errors.
802                 throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d",
803                                   frameName, streamId);
804             }
805             if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) {
806                 // If we have sent a reset stream it is assumed the stream will be closed after the write completes.
807                 // If we have not sent a reset, but the stream was created after a GoAway this is not supported by
808                 // DefaultHttp2Connection and if a custom Http2Connection is used it is assumed the lifetime is managed
809                 // elsewhere so we don't close the stream or otherwise modify the stream's state.
810 
811                 if (logger.isInfoEnabled()) {
812                     logger.info("{} ignoring {} frame for stream {}", ctx.channel(), frameName,
813                             stream.isResetSent() ? "RST_STREAM sent." :
814                                     "Stream created after GOAWAY sent. Last known stream by peer " +
815                                      connection.remote().lastStreamKnownByPeer());
816                 }
817 
818                 return true;
819             }
820             return false;
821         }
822 
823         /**
824          * Helper method for determining whether or not to ignore inbound frames. A stream is considered to be created
825          * after a {@code GOAWAY} is sent if the following conditions hold:
826          * <p/>
827          * <ul>
828          *     <li>A {@code GOAWAY} must have been sent by the local endpoint</li>
829          *     <li>The {@code streamId} must identify a legitimate stream id for the remote endpoint to be creating</li>
830          *     <li>{@code streamId} is greater than the Last Known Stream ID which was sent by the local endpoint
831          *     in the last {@code GOAWAY} frame</li>
832          * </ul>
833          * <p/>
834          */
835         private boolean streamCreatedAfterGoAwaySent(int streamId) {
836             Endpoint<?> remote = connection.remote();
837             return connection.goAwaySent() && remote.isValidStreamId(streamId) &&
838                     streamId > remote.lastStreamKnownByPeer();
839         }
840 
841         private void verifyStreamMayHaveExisted(int streamId, boolean endOfStream, String frameName)
842                 throws Http2Exception {
843             if (!connection.streamMayHaveExisted(streamId)) {
844                 throw connectionError(PROTOCOL_ERROR,
845                         "Stream %d does not exist for inbound frame %s, endOfStream = %b",
846                         streamId, frameName, endOfStream);
847             }
848         }
849     }
850 
851     private final class PrefaceFrameListener implements Http2FrameListener {
852         /**
853          * Verifies that the HTTP/2 connection preface has been received from the remote endpoint.
854          * It is possible that the current call to
855          * {@link Http2FrameReader#readFrame(ChannelHandlerContext, ByteBuf, Http2FrameListener)} will have multiple
856          * frames to dispatch. So it may be OK for this class to get legitimate frames for the first readFrame.
857          */
858         private void verifyPrefaceReceived() throws Http2Exception {
859             if (!prefaceReceived()) {
860                 throw connectionError(PROTOCOL_ERROR, "Received non-SETTINGS as first frame.");
861             }
862         }
863 
864         @Override
865         public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
866                 throws Http2Exception {
867             verifyPrefaceReceived();
868             return internalFrameListener.onDataRead(ctx, streamId, data, padding, endOfStream);
869         }
870 
871         @Override
872         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
873                 boolean endOfStream) throws Http2Exception {
874             verifyPrefaceReceived();
875             internalFrameListener.onHeadersRead(ctx, streamId, headers, padding, endOfStream);
876         }
877 
878         @Override
879         public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
880                 short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
881             verifyPrefaceReceived();
882             internalFrameListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
883                     exclusive, padding, endOfStream);
884         }
885 
886         @Override
887         public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
888                 boolean exclusive) throws Http2Exception {
889             verifyPrefaceReceived();
890             internalFrameListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
891         }
892 
893         @Override
894         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
895             verifyPrefaceReceived();
896             internalFrameListener.onRstStreamRead(ctx, streamId, errorCode);
897         }
898 
899         @Override
900         public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
901             verifyPrefaceReceived();
902             internalFrameListener.onSettingsAckRead(ctx);
903         }
904 
905         @Override
906         public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
907             // The first settings should change the internalFrameListener to the "real" listener
908             // that expects the preface to be verified.
909             if (!prefaceReceived()) {
910                 internalFrameListener = new FrameReadListener();
911             }
912             internalFrameListener.onSettingsRead(ctx, settings);
913         }
914 
915         @Override
916         public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
917             verifyPrefaceReceived();
918             internalFrameListener.onPingRead(ctx, data);
919         }
920 
921         @Override
922         public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
923             verifyPrefaceReceived();
924             internalFrameListener.onPingAckRead(ctx, data);
925         }
926 
927         @Override
928         public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
929                 Http2Headers headers, int padding) throws Http2Exception {
930             verifyPrefaceReceived();
931             internalFrameListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
932         }
933 
934         @Override
935         public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
936                 throws Http2Exception {
937             onGoAwayRead0(ctx, lastStreamId, errorCode, debugData);
938         }
939 
940         @Override
941         public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
942                 throws Http2Exception {
943             verifyPrefaceReceived();
944             internalFrameListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
945         }
946 
947         @Override
948         public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
949                 ByteBuf payload) throws Http2Exception {
950             verifyPrefaceReceived();
951             internalFrameListener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
952         }
953     }
954 
955     private static final class ContentLength {
956         private final long expected;
957         private long seen;
958 
959         ContentLength(long expected) {
960             this.expected = expected;
961         }
962 
963         void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception {
964             seen += bytes;
965             // Check for overflow
966             if (seen < 0) {
967                 throw streamError(streamId, PROTOCOL_ERROR,
968                         "Received amount of data did overflow and so not match content-length header %d", expected);
969             }
970             // Check if we received more data then what was advertised via the content-length header.
971             if (seen > expected) {
972                 throw streamError(streamId, PROTOCOL_ERROR,
973                         "Received amount of data %d does not match content-length header %d", seen, expected);
974             }
975 
976             if (isEnd) {
977                 if (seen == 0 && !server) {
978                     // This may be a response to a HEAD request, let's just allow it.
979                     return;
980                 }
981 
982                 // Check that we really saw what was told via the content-length header.
983                 if (expected > seen) {
984                     throw streamError(streamId, PROTOCOL_ERROR,
985                             "Received amount of data %d does not match content-length header %d", seen, expected);
986                 }
987             }
988         }
989     }
990 }