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