View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a 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
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.http2;
17  
18  import io.netty.channel.Channel;
19  import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
20  
21  import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
22  import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
23  import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
24  import static io.netty.util.internal.ObjectUtil.checkNotNull;
25  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
26  
27  /**
28   * Abstract base class which defines commonly used features required to build {@link Http2ConnectionHandler} instances.
29   *
30   * <h3>Three ways to build a {@link Http2ConnectionHandler}</h3>
31   * <h4>Let the builder create a {@link Http2ConnectionHandler}</h4>
32   * Simply call all the necessary setter methods, and then use {@link #build()} to build a new
33   * {@link Http2ConnectionHandler}. Setting the following properties are prohibited because they are used for
34   * other ways of building a {@link Http2ConnectionHandler}.
35   * conflicts with this option:
36   * <ul>
37   *   <li>{@link #connection(Http2Connection)}</li>
38   *   <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
39   * </ul>
40   *
41   *
42   * <h4>Let the builder use the {@link Http2ConnectionHandler} you specified</h4>
43   * Call {@link #connection(Http2Connection)} to tell the builder that you want to build the handler from the
44   * {@link Http2Connection} you specified. Setting the following properties are prohibited and thus will trigger
45   * an {@link IllegalStateException} because they conflict with this option.
46   * <ul>
47   *   <li>{@link #server(boolean)}</li>
48   *   <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
49   * </ul>
50   *
51   * <h4>Let the builder use the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified</h4>
52   * Call {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)} to tell the builder that you want to built the
53   * handler from the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified. Setting the
54   * following properties are prohibited and thus will trigger an {@link IllegalStateException} because they conflict
55   * with this option:
56   * <ul>
57   *   <li>{@link #server(boolean)}</li>
58   *   <li>{@link #connection(Http2Connection)}</li>
59   *   <li>{@link #frameLogger(Http2FrameLogger)}</li>
60   *   <li>{@link #headerSensitivityDetector(SensitivityDetector)}</li>
61   *   <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li>
62   *   <li>{@link #encoderIgnoreMaxHeaderListSize(boolean)}</li>
63   * </ul>
64   *
65   * <h3>Exposing necessary methods in a subclass</h3>
66   * {@link #build()} method and all property access methods are {@code protected}. Choose the methods to expose to the
67   * users of your builder implementation and make them {@code public}.
68   *
69   * @param <T> The type of handler created by this builder.
70   * @param <B> The concrete type of this builder.
71   */
72  public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2ConnectionHandler,
73                                                              B extends AbstractHttp2ConnectionHandlerBuilder<T, B>> {
74  
75      private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
76  
77      private static final int DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER = 200;
78  
79      // The properties that can always be set.
80      private Http2Settings initialSettings = Http2Settings.defaultSettings();
81      private Http2FrameListener frameListener;
82      private long gracefulShutdownTimeoutMillis = Http2CodecUtil.DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;
83      private boolean decoupleCloseAndGoAway;
84      private boolean flushPreface = true;
85  
86      // The property that will prohibit connection() and codec() if set by server(),
87      // because this property is used only when this builder creates an Http2Connection.
88      private Boolean isServer;
89      private Integer maxReservedStreams;
90  
91      // The property that will prohibit server() and codec() if set by connection().
92      private Http2Connection connection;
93  
94      // The properties that will prohibit server() and connection() if set by codec().
95      private Http2ConnectionDecoder decoder;
96      private Http2ConnectionEncoder encoder;
97  
98      // The properties that are:
99      // * mutually exclusive against codec() and
100     // * OK to use with server() and connection()
101     private Boolean validateHeaders;
102     private Boolean validateRequiredPseudoHeaders;
103     private Http2FrameLogger frameLogger;
104     private SensitivityDetector headerSensitivityDetector;
105     private Boolean encoderEnforceMaxConcurrentStreams;
106     private Boolean encoderIgnoreMaxHeaderListSize;
107     private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY;
108     private boolean autoAckSettingsFrame = true;
109     private boolean autoAckPingFrame = true;
110     private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES;
111     private int maxConsecutiveEmptyFrames = 2;
112     private Integer maxDecodedRstFramesPerWindow;
113     private int maxDecodedRstFramesSecondsPerWindow = 30;
114     private Integer maxEncodedRstFramesPerWindow;
115     private int maxEncodedRstFramesSecondsPerWindow = 30;
116     private int maxSmallContinuationFrames = Http2CodecUtil.DEFAULT_MAX_SMALL_CONTINUATION_FRAME;
117 
118     /**
119      * Sets the {@link Http2Settings} to use for the initial connection settings exchange.
120      */
121     protected Http2Settings initialSettings() {
122         return initialSettings;
123     }
124 
125     /**
126      * Sets the {@link Http2Settings} to use for the initial connection settings exchange.
127      */
128     protected B initialSettings(Http2Settings settings) {
129         initialSettings = checkNotNull(settings, "settings");
130         return self();
131     }
132 
133     /**
134      * Returns the listener of inbound frames.
135      *
136      * @return {@link Http2FrameListener} if set, or {@code null} if not set.
137      */
138     protected Http2FrameListener frameListener() {
139         return frameListener;
140     }
141 
142     /**
143      * Sets the listener of inbound frames.
144      * This listener will only be set if the decoder's listener is {@code null}.
145      */
146     protected B frameListener(Http2FrameListener frameListener) {
147         this.frameListener = checkNotNull(frameListener, "frameListener");
148         return self();
149     }
150 
151     /**
152      * Returns the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. Returns -1 if the
153      * timeout is indefinite.
154      */
155     protected long gracefulShutdownTimeoutMillis() {
156         return gracefulShutdownTimeoutMillis;
157     }
158 
159     /**
160      * Sets the graceful shutdown timeout of the {@link Http2Connection} in milliseconds.
161      */
162     protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
163         if (gracefulShutdownTimeoutMillis < -1) {
164             throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis +
165                                                " (expected: -1 for indefinite or >= 0)");
166         }
167         this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
168         return self();
169     }
170 
171     /**
172      * Returns if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
173      * or client mode ({@code false}).
174      */
175     protected boolean isServer() {
176         return isServer != null ? isServer : true;
177     }
178 
179     /**
180      * Sets if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
181      * or client mode ({@code false}).
182      */
183     protected B server(boolean isServer) {
184         enforceConstraint("server", "connection", connection);
185         enforceConstraint("server", "codec", decoder);
186         enforceConstraint("server", "codec", encoder);
187 
188         this.isServer = isServer;
189         return self();
190     }
191 
192     /**
193      * Get the maximum number of streams which can be in the reserved state at any given time.
194      * <p>
195      * By default this value will be ignored on the server for local endpoint. This is because the RFC provides
196      * no way to explicitly communicate a limit to how many states can be in the reserved state, and instead relies
197      * on the peer to send RST_STREAM frames when they will be rejected.
198      */
199     protected int maxReservedStreams() {
200         return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS;
201     }
202 
203     /**
204      * Set the maximum number of streams which can be in the reserved state at any given time.
205      */
206     protected B maxReservedStreams(int maxReservedStreams) {
207         enforceConstraint("server", "connection", connection);
208         enforceConstraint("server", "codec", decoder);
209         enforceConstraint("server", "codec", encoder);
210 
211         this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams");
212         return self();
213     }
214 
215     /**
216      * Returns the {@link Http2Connection} to use.
217      *
218      * @return {@link Http2Connection} if set, or {@code null} if not set.
219      */
220     protected Http2Connection connection() {
221         return connection;
222     }
223 
224     /**
225      * Sets the {@link Http2Connection} to use.
226      */
227     protected B connection(Http2Connection connection) {
228         enforceConstraint("connection", "maxReservedStreams", maxReservedStreams);
229         enforceConstraint("connection", "server", isServer);
230         enforceConstraint("connection", "codec", decoder);
231         enforceConstraint("connection", "codec", encoder);
232 
233         this.connection = checkNotNull(connection, "connection");
234 
235         return self();
236     }
237 
238     /**
239      * Returns the {@link Http2ConnectionDecoder} to use.
240      *
241      * @return {@link Http2ConnectionDecoder} if set, or {@code null} if not set.
242      */
243     protected Http2ConnectionDecoder decoder() {
244         return decoder;
245     }
246 
247     /**
248      * Returns the {@link Http2ConnectionEncoder} to use.
249      *
250      * @return {@link Http2ConnectionEncoder} if set, or {@code null} if not set.
251      */
252     protected Http2ConnectionEncoder encoder() {
253         return encoder;
254     }
255 
256     /**
257      * Sets the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} to use.
258      */
259     protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
260         enforceConstraint("codec", "server", isServer);
261         enforceConstraint("codec", "maxReservedStreams", maxReservedStreams);
262         enforceConstraint("codec", "connection", connection);
263         enforceConstraint("codec", "frameLogger", frameLogger);
264         enforceConstraint("codec", "validateHeaders", validateHeaders);
265         enforceConstraint("codec", "validateRequiredPseudoHeaders", validateRequiredPseudoHeaders);
266         enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector);
267         enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams);
268 
269         checkNotNull(decoder, "decoder");
270         checkNotNull(encoder, "encoder");
271 
272         if (decoder.connection() != encoder.connection()) {
273             throw new IllegalArgumentException("The specified encoder and decoder have different connections.");
274         }
275 
276         this.decoder = decoder;
277         this.encoder = encoder;
278 
279         return self();
280     }
281 
282     /**
283      * Returns if HTTP headers should be validated according to
284      * <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
285      */
286     protected boolean isValidateHeaders() {
287         return validateHeaders != null ? validateHeaders : true;
288     }
289 
290     /**
291      * Sets if HTTP headers should be validated according to
292      * <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
293      */
294     protected B validateHeaders(boolean validateHeaders) {
295         enforceNonCodecConstraints("validateHeaders");
296         this.validateHeaders = validateHeaders;
297         return self();
298     }
299 
300     /**
301      * Returns if mandatory pseudo-header fields are validated according to
302      * <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3">RFC 9113, 8.3</a>. Disabled by default.
303      */
304     protected boolean isValidateRequiredPseudoHeaders() {
305         return validateRequiredPseudoHeaders != null ? validateRequiredPseudoHeaders : false;
306     }
307 
308     /**
309      * Sets if request and response {@code HEADERS} that omit a mandatory pseudo-header field are rejected,
310      * according to <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3">RFC 9113, 8.3</a>.
311      * Disabled by default.
312      */
313     protected B validateRequiredPseudoHeaders(boolean validateRequiredPseudoHeaders) {
314         enforceNonCodecConstraints("validateRequiredPseudoHeaders");
315         this.validateRequiredPseudoHeaders = validateRequiredPseudoHeaders;
316         return self();
317     }
318 
319     /**
320      * Returns the logger that is used for the encoder and decoder.
321      *
322      * @return {@link Http2FrameLogger} if set, or {@code null} if not set.
323      */
324     protected Http2FrameLogger frameLogger() {
325         return frameLogger;
326     }
327 
328     /**
329      * Sets the logger that is used for the encoder and decoder.
330      */
331     protected B frameLogger(Http2FrameLogger frameLogger) {
332         enforceNonCodecConstraints("frameLogger");
333         this.frameLogger = checkNotNull(frameLogger, "frameLogger");
334         return self();
335     }
336 
337     /**
338      * Returns if the encoder should queue frames if the maximum number of concurrent streams
339      * would otherwise be exceeded.
340      */
341     protected boolean encoderEnforceMaxConcurrentStreams() {
342         return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false;
343     }
344 
345     /**
346      * Sets if the encoder should queue frames if the maximum number of concurrent streams
347      * would otherwise be exceeded.
348      */
349     protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
350         enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams");
351         this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams;
352         return self();
353     }
354 
355     /**
356      * Returns the maximum number of queued control frames that are allowed before the connection is closed.
357      * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer
358      * floods us with frames that would have us produce control frames, but stops to read from the underlying socket.
359      *
360      * {@code 0} means no protection is in place.
361      */
362     protected int encoderEnforceMaxQueuedControlFrames() {
363         return maxQueuedControlFrames;
364     }
365 
366     /**
367      * Sets the maximum number of queued control frames that are allowed before the connection is closed.
368      * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer
369      * floods us with frames that would have us produce control frames, but stops to read from the underlying socket.
370      *
371      * {@code 0} means no protection should be applied.
372      */
373     protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) {
374         enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames");
375         this.maxQueuedControlFrames = checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames");
376         return self();
377     }
378 
379     /**
380      * Returns the {@link SensitivityDetector} to use.
381      */
382     protected SensitivityDetector headerSensitivityDetector() {
383         return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR;
384     }
385 
386     /**
387      * Sets the {@link SensitivityDetector} to use.
388      */
389     protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) {
390         enforceNonCodecConstraints("headerSensitivityDetector");
391         this.headerSensitivityDetector = checkNotNull(headerSensitivityDetector, "headerSensitivityDetector");
392         return self();
393     }
394 
395     /**
396      * Sets if the <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a>
397      * should be ignored when encoding headers.
398      * @param ignoreMaxHeaderListSize {@code true} to ignore
399      * <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a>.
400      * @return this.
401      */
402     protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
403         enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize");
404         encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize;
405         return self();
406     }
407 
408     /**
409      * Does nothing, do not call.
410      *
411      * @deprecated Huffman decoding no longer depends on having a decode capacity.
412      */
413     @Deprecated
414     protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
415         return self();
416     }
417 
418     /**
419      * Set the {@link Http2PromisedRequestVerifier} to use.
420      * @return this.
421      */
422     protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) {
423         enforceNonCodecConstraints("promisedRequestVerifier");
424         this.promisedRequestVerifier = checkNotNull(promisedRequestVerifier, "promisedRequestVerifier");
425         return self();
426     }
427 
428     /**
429      * Get the {@link Http2PromisedRequestVerifier} to use.
430      * @return the {@link Http2PromisedRequestVerifier} to use.
431      */
432     protected Http2PromisedRequestVerifier promisedRequestVerifier() {
433         return promisedRequestVerifier;
434     }
435 
436     /**
437      * Returns the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
438      * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
439      * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
440      *
441      * {@code 0} means no protection is in place.
442      */
443     protected int decoderEnforceMaxConsecutiveEmptyDataFrames() {
444         return maxConsecutiveEmptyFrames;
445     }
446 
447     /**
448      * Sets the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
449      * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
450      * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
451      *
452      * {@code 0} means no protection should be applied.
453      */
454     protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) {
455         enforceNonCodecConstraints("maxConsecutiveEmptyFrames");
456         this.maxConsecutiveEmptyFrames = checkPositiveOrZero(
457                 maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames");
458         return self();
459     }
460 
461     /**
462      * Sets the maximum number RST frames that are allowed per window before
463      * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
464      * so use up a lot of CPU.
465      *
466      * {@code 0} for any of the parameters means no protection should be applied.
467      */
468     protected B decoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
469         enforceNonCodecConstraints("decoderEnforceMaxRstFramesPerWindow");
470         this.maxDecodedRstFramesPerWindow = checkPositiveOrZero(
471                 maxRstFramesPerWindow, "maxRstFramesPerWindow");
472         this.maxDecodedRstFramesSecondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
473         return self();
474     }
475 
476     /**
477      * Sets the maximum number RST frames that are allowed per window before
478      * the connection is closed. This allows to protect against the remote peer that will trigger us to generate a flood
479      * of RST frames and so use up a lot of CPU.
480      *
481      * {@code 0} for any of the parameters means no protection should be applied.
482      */
483     protected B encoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
484         enforceNonCodecConstraints("encoderEnforceMaxRstFramesPerWindow");
485         this.maxEncodedRstFramesPerWindow = checkPositiveOrZero(
486                 maxRstFramesPerWindow, "maxRstFramesPerWindow");
487         this.maxEncodedRstFramesSecondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
488         return self();
489     }
490 
491     /**
492      * Returns the maximum number of small CONTINUATION frames per HEADERS block that are allowed
493      * before the connection is closed. Small is defined as 8 KiB, half the minimum allowed HTTP2 frame size.
494      * This setting is to protect against the remote peer flooding us with such frames.
495      *
496      * {@code 0} means no protection is in place.
497      */
498     protected int decoderEnforceMaxSmallContinuationFrames() {
499         return maxSmallContinuationFrames;
500     }
501 
502     /**
503      * Returns the maximum number of small CONTINUATION frames per HEADERS block that are allowed
504      * before the connection is closed. Small is defined as 8 KiB, half the minimum allowed HTTP2 frame size.
505      * This setting is to protect against the remote peer flooding us with such frames.
506      * {@code 0} means no protection should be applied.
507      */
508     protected B decoderEnforceMaxSmallContinuationFrames(int maxSmallContinuationFrames) {
509         enforceNonCodecConstraints("maxSmallContinuationFrames");
510         this.maxSmallContinuationFrames = checkPositiveOrZero(
511                 maxSmallContinuationFrames, "maxSmallContinuationFrames");
512         return self();
513     }
514 
515     /**
516      * Determine if settings frame should automatically be acknowledged and applied.
517      * @return this.
518      */
519     protected B autoAckSettingsFrame(boolean autoAckSettings) {
520         enforceNonCodecConstraints("autoAckSettingsFrame");
521         autoAckSettingsFrame = autoAckSettings;
522         return self();
523     }
524 
525     /**
526      * Determine if the SETTINGS frames should be automatically acknowledged and applied.
527      * @return {@code true} if the SETTINGS frames should be automatically acknowledged and applied.
528      */
529     protected boolean isAutoAckSettingsFrame() {
530         return autoAckSettingsFrame;
531     }
532 
533     /**
534      * Determine if PING frame should automatically be acknowledged or not.
535      * @return this.
536      */
537     protected B autoAckPingFrame(boolean autoAckPingFrame) {
538         enforceNonCodecConstraints("autoAckPingFrame");
539         this.autoAckPingFrame = autoAckPingFrame;
540         return self();
541     }
542 
543     /**
544      * Determine if the PING frames should be automatically acknowledged or not.
545      * @return {@code true} if the PING frames should be automatically acknowledged.
546      */
547     protected boolean isAutoAckPingFrame() {
548         return autoAckPingFrame;
549     }
550 
551     /**
552      * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close.
553      * @param decoupleCloseAndGoAway {@code true} to make {@link Channel#close()} directly close the underlying
554      *   transport, and not attempt graceful closure via GOAWAY.
555      * @return {@code this}.
556      */
557     protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) {
558         this.decoupleCloseAndGoAway = decoupleCloseAndGoAway;
559         return self();
560     }
561 
562     /**
563      * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close.
564      */
565     protected boolean decoupleCloseAndGoAway() {
566         return decoupleCloseAndGoAway;
567     }
568 
569     /**
570      * Determine if the <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">Preface</a>
571      * should be automatically flushed when the {@link Channel} becomes active or not.
572      * <p>
573      * Client may choose to opt-out from this automatic behavior and manage flush manually if it's ready to send
574      * request frames immediately after the preface. It may help to avoid unnecessary latency.
575      *
576      * @param flushPreface {@code true} to automatically flush, {@code false otherwise}.
577      * @return {@code this}.
578      * @see <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">HTTP/2 Connection Preface</a>
579      */
580     protected B flushPreface(boolean flushPreface) {
581         this.flushPreface = flushPreface;
582         return self();
583     }
584 
585     /**
586      * Determine if the <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">Preface</a>
587      * should be automatically flushed when the {@link Channel} becomes active or not.
588      * <p>
589      * Client may choose to opt-out from this automatic behavior and manage flush manually if it's ready to send
590      * request frames immediately after the preface. It may help to avoid unnecessary latency.
591      *
592      * @return {@code true} if automatically flushed.
593      * @see <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">HTTP/2 Connection Preface</a>
594      */
595     protected boolean flushPreface() {
596         return flushPreface;
597     }
598 
599     /**
600      * Create a new {@link Http2ConnectionHandler}.
601      */
602     protected T build() {
603         if (encoder != null) {
604             assert decoder != null;
605             return buildFromCodec(decoder, encoder);
606         }
607 
608         Http2Connection connection = this.connection;
609         if (connection == null) {
610             connection = new DefaultHttp2Connection(isServer(), maxReservedStreams());
611         }
612 
613         return buildFromConnection(connection);
614     }
615 
616     private T buildFromConnection(Http2Connection connection) {
617         // Enforce the advertised maxConcurrentStreams limit on the remote endpoint immediately,
618         // without waiting for the SETTINGS_ACK round-trip.
619         enforceMaxActiveStreams(connection, initialSettings);
620 
621         Long maxHeaderListSize = initialSettings.maxHeaderListSize();
622         Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(),
623                 maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize,
624                 /* initialHuffmanDecodeCapacity= */ -1), maxSmallContinuationFrames);
625         Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ?
626                 new DefaultHttp2FrameWriter(headerSensitivityDetector()) :
627                 new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize);
628 
629         if (frameLogger != null) {
630             reader = new Http2InboundFrameLogger(reader, frameLogger);
631             writer = new Http2OutboundFrameLogger(writer, frameLogger);
632         }
633 
634         Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
635         boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams();
636 
637         if (maxQueuedControlFrames != 0) {
638             encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames);
639         }
640         final int maxEncodedRstFrames;
641         if (maxEncodedRstFramesPerWindow == null) {
642             // Only enable by default on the server.
643             if (isServer()) {
644                 maxEncodedRstFrames = DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER;
645             } else {
646                 maxEncodedRstFrames = 0;
647             }
648         } else {
649             maxEncodedRstFrames = maxEncodedRstFramesPerWindow;
650         }
651         if (maxEncodedRstFrames > 0 && maxEncodedRstFramesSecondsPerWindow > 0) {
652             encoder = new Http2MaxRstFrameLimitEncoder(
653                     encoder, maxEncodedRstFrames, maxEncodedRstFramesSecondsPerWindow);
654         }
655         if (encoderEnforceMaxConcurrentStreams) {
656             if (connection.isServer()) {
657                 encoder.close();
658                 reader.close();
659                 throw new IllegalArgumentException(
660                         "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
661                         " not supported for server");
662             }
663             encoder = new StreamBufferingEncoder(encoder);
664         }
665 
666         DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader,
667             promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame(), isValidateHeaders(),
668             isValidateRequiredPseudoHeaders());
669         return buildFromCodec(decoder, encoder);
670     }
671 
672     private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
673         // Enforce the advertised maxConcurrentStreams limit on the remote endpoint immediately,
674         // without waiting for the SETTINGS_ACK round-trip.
675         enforceMaxActiveStreams(encoder.connection(), initialSettings);
676 
677         int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames();
678         if (maxConsecutiveEmptyDataFrames > 0) {
679             decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
680         }
681         final int maxDecodedRstFrames;
682         if (maxDecodedRstFramesPerWindow == null) {
683             // Only enable by default on the server.
684             if (isServer()) {
685                 maxDecodedRstFrames = DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER;
686             } else {
687                 maxDecodedRstFrames = 0;
688             }
689         } else {
690             maxDecodedRstFrames = maxDecodedRstFramesPerWindow;
691         }
692         if (maxDecodedRstFrames > 0 && maxDecodedRstFramesSecondsPerWindow > 0) {
693             decoder = new Http2MaxRstFrameDecoder(decoder, maxDecodedRstFrames, maxDecodedRstFramesSecondsPerWindow);
694         }
695         final T handler;
696         try {
697             // Call the abstract build method
698             handler = build(decoder, encoder, initialSettings);
699         } catch (Throwable t) {
700             encoder.close();
701             decoder.close();
702             throw new IllegalStateException("failed to build an Http2ConnectionHandler", t);
703         }
704 
705         // Setup post build options
706         handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
707         if (handler.decoder().frameListener() == null) {
708             handler.decoder().frameListener(frameListener);
709         }
710         return handler;
711     }
712 
713     private static void enforceMaxActiveStreams(Http2Connection connection, Http2Settings initialSettings) {
714         Long maxConcurrentStreams = initialSettings.maxConcurrentStreams();
715         if (maxConcurrentStreams != null) {
716             connection.remote().maxActiveStreams((int) Math.min(maxConcurrentStreams, Integer.MAX_VALUE));
717         }
718     }
719 
720     /**
721      * Implement this method to create a new {@link Http2ConnectionHandler} or its subtype instance.
722      * <p>
723      * The return of this method will be subject to the following:
724      * <ul>
725      *   <li>{@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder</li>
726      *   <li>{@link #gracefulShutdownTimeoutMillis(long)} will always be set</li>
727      * </ul>
728      */
729     protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
730                                Http2Settings initialSettings) throws Exception;
731 
732     /**
733      * Returns {@code this}.
734      */
735     @SuppressWarnings("unchecked")
736     protected final B self() {
737         return (B) this;
738     }
739 
740     private void enforceNonCodecConstraints(String rejected) {
741         enforceConstraint(rejected, "server/connection", decoder);
742         enforceConstraint(rejected, "server/connection", encoder);
743     }
744 
745     private static void enforceConstraint(String methodName, String rejectorName, Object value) {
746         if (value != null) {
747             throw new IllegalStateException(
748                     methodName + "() cannot be called because " + rejectorName + "() has been called already.");
749         }
750     }
751 }