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