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.netty5.handler.codec.http2;
17  
18  import io.netty5.channel.Channel;
19  import io.netty5.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
20  import io.netty5.util.internal.UnstableApi;
21  
22  import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
23  import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
24  import static io.netty5.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
25  import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
26  import static java.util.Objects.requireNonNull;
27  
28  /**
29   * Abstract base class which defines commonly used features required to build {@link Http2ConnectionHandler} instances.
30   *
31   * <h3>Three ways to build a {@link Http2ConnectionHandler}</h3>
32   * <h4>Let the builder create a {@link Http2ConnectionHandler}</h4>
33   * Simply call all the necessary setter methods, and then use {@link #build()} to build a new
34   * {@link Http2ConnectionHandler}. Setting the following properties are prohibited because they are used for
35   * other ways of building a {@link Http2ConnectionHandler}.
36   * conflicts with this option:
37   * <ul>
38   *   <li>{@link #connection(Http2Connection)}</li>
39   *   <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
40   * </ul>
41   *
42   *
43   * <h4>Let the builder use the {@link Http2ConnectionHandler} you specified</h4>
44   * Call {@link #connection(Http2Connection)} to tell the builder that you want to build the handler from the
45   * {@link Http2Connection} you specified. Setting the following properties are prohibited and thus will trigger
46   * an {@link IllegalStateException} because they conflict with this option.
47   * <ul>
48   *   <li>{@link #server(boolean)}</li>
49   *   <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
50   * </ul>
51   *
52   * <h4>Let the builder use the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified</h4>
53   * Call {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)} to tell the builder that you want to built the
54   * handler from the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified. Setting the
55   * following properties are prohibited and thus will trigger an {@link IllegalStateException} because they conflict
56   * with this option:
57   * <ul>
58   *   <li>{@link #server(boolean)}</li>
59   *   <li>{@link #connection(Http2Connection)}</li>
60   *   <li>{@link #frameLogger(Http2FrameLogger)}</li>
61   *   <li>{@link #headerSensitivityDetector(SensitivityDetector)}</li>
62   *   <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li>
63   *   <li>{@link #encoderIgnoreMaxHeaderListSize(boolean)}</li>
64   * </ul>
65   *
66   * <h3>Exposing necessary methods in a subclass</h3>
67   * {@link #build()} method and all property access methods are {@code protected}. Choose the methods to expose to the
68   * users of your builder implementation and make them {@code public}.
69   *
70   * @param <T> The type of handler created by this builder.
71   * @param <B> The concrete type of this builder.
72   */
73  @UnstableApi
74  public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2ConnectionHandler,
75                                                              B extends AbstractHttp2ConnectionHandlerBuilder<T, B>> {
76  
77      private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
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 Http2FrameLogger frameLogger;
103     private SensitivityDetector headerSensitivityDetector;
104     private Boolean encoderEnforceMaxConcurrentStreams;
105     private Boolean encoderIgnoreMaxHeaderListSize;
106     private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY;
107     private boolean autoAckSettingsFrame = true;
108     private boolean autoAckPingFrame = true;
109     private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES;
110     private int maxConsecutiveEmptyFrames = 2;
111 
112     /**
113      * Sets the {@link Http2Settings} to use for the initial connection settings exchange.
114      */
115     protected Http2Settings initialSettings() {
116         return initialSettings;
117     }
118 
119     /**
120      * Sets the {@link Http2Settings} to use for the initial connection settings exchange.
121      */
122     protected B initialSettings(Http2Settings settings) {
123         initialSettings = requireNonNull(settings, "settings");
124         return self();
125     }
126 
127     /**
128      * Returns the listener of inbound frames.
129      *
130      * @return {@link Http2FrameListener} if set, or {@code null} if not set.
131      */
132     protected Http2FrameListener frameListener() {
133         return frameListener;
134     }
135 
136     /**
137      * Sets the listener of inbound frames.
138      * This listener will only be set if the decoder's listener is {@code null}.
139      */
140     protected B frameListener(Http2FrameListener frameListener) {
141         this.frameListener = requireNonNull(frameListener, "frameListener");
142         return self();
143     }
144 
145     /**
146      * Returns the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. Returns -1 if the
147      * timeout is indefinite.
148      */
149     protected long gracefulShutdownTimeoutMillis() {
150         return gracefulShutdownTimeoutMillis;
151     }
152 
153     /**
154      * Sets the graceful shutdown timeout of the {@link Http2Connection} in milliseconds.
155      */
156     protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
157         if (gracefulShutdownTimeoutMillis < -1) {
158             throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis +
159                                                " (expected: -1 for indefinite or >= 0)");
160         }
161         this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
162         return self();
163     }
164 
165     /**
166      * Returns if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
167      * or client mode ({@code false}).
168      */
169     protected boolean isServer() {
170         return isServer != null ? isServer : true;
171     }
172 
173     /**
174      * Sets if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
175      * or client mode ({@code false}).
176      */
177     protected B server(boolean isServer) {
178         enforceConstraint("server", "connection", connection);
179         enforceConstraint("server", "codec", decoder);
180         enforceConstraint("server", "codec", encoder);
181 
182         this.isServer = isServer;
183         return self();
184     }
185 
186     /**
187      * Get the maximum number of streams which can be in the reserved state at any given time.
188      * <p>
189      * By default this value will be ignored on the server for local endpoint. This is because the RFC provides
190      * no way to explicitly communicate a limit to how many states can be in the reserved state, and instead relies
191      * on the peer to send RST_STREAM frames when they will be rejected.
192      */
193     protected int maxReservedStreams() {
194         return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS;
195     }
196 
197     /**
198      * Set the maximum number of streams which can be in the reserved state at any given time.
199      */
200     protected B maxReservedStreams(int maxReservedStreams) {
201         enforceConstraint("server", "connection", connection);
202         enforceConstraint("server", "codec", decoder);
203         enforceConstraint("server", "codec", encoder);
204 
205         this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams");
206         return self();
207     }
208 
209     /**
210      * Returns the {@link Http2Connection} to use.
211      *
212      * @return {@link Http2Connection} if set, or {@code null} if not set.
213      */
214     protected Http2Connection connection() {
215         return connection;
216     }
217 
218     /**
219      * Sets the {@link Http2Connection} to use.
220      */
221     protected B connection(Http2Connection connection) {
222         enforceConstraint("connection", "maxReservedStreams", maxReservedStreams);
223         enforceConstraint("connection", "server", isServer);
224         enforceConstraint("connection", "codec", decoder);
225         enforceConstraint("connection", "codec", encoder);
226 
227         this.connection = requireNonNull(connection, "connection");
228 
229         return self();
230     }
231 
232     /**
233      * Returns the {@link Http2ConnectionDecoder} to use.
234      *
235      * @return {@link Http2ConnectionDecoder} if set, or {@code null} if not set.
236      */
237     protected Http2ConnectionDecoder decoder() {
238         return decoder;
239     }
240 
241     /**
242      * Returns the {@link Http2ConnectionEncoder} to use.
243      *
244      * @return {@link Http2ConnectionEncoder} if set, or {@code null} if not set.
245      */
246     protected Http2ConnectionEncoder encoder() {
247         return encoder;
248     }
249 
250     /**
251      * Sets the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} to use.
252      */
253     protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
254         enforceConstraint("codec", "server", isServer);
255         enforceConstraint("codec", "maxReservedStreams", maxReservedStreams);
256         enforceConstraint("codec", "connection", connection);
257         enforceConstraint("codec", "frameLogger", frameLogger);
258         enforceConstraint("codec", "validateHeaders", validateHeaders);
259         enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector);
260         enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams);
261 
262         requireNonNull(decoder, "decoder");
263         requireNonNull(encoder, "encoder");
264 
265         if (decoder.connection() != encoder.connection()) {
266             throw new IllegalArgumentException("The specified encoder and decoder have different connections.");
267         }
268 
269         this.decoder = decoder;
270         this.encoder = encoder;
271 
272         return self();
273     }
274 
275     /**
276      * Returns if HTTP headers should be validated according to
277      * <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
278      */
279     protected boolean isValidateHeaders() {
280         return validateHeaders != null ? validateHeaders : true;
281     }
282 
283     /**
284      * Sets if HTTP headers should be validated according to
285      * <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
286      */
287     protected B validateHeaders(boolean validateHeaders) {
288         enforceNonCodecConstraints("validateHeaders");
289         this.validateHeaders = validateHeaders;
290         return self();
291     }
292 
293     /**
294      * Returns the logger that is used for the encoder and decoder.
295      *
296      * @return {@link Http2FrameLogger} if set, or {@code null} if not set.
297      */
298     protected Http2FrameLogger frameLogger() {
299         return frameLogger;
300     }
301 
302     /**
303      * Sets the logger that is used for the encoder and decoder.
304      */
305     protected B frameLogger(Http2FrameLogger frameLogger) {
306         enforceNonCodecConstraints("frameLogger");
307         this.frameLogger = requireNonNull(frameLogger, "frameLogger");
308         return self();
309     }
310 
311     /**
312      * Returns if the encoder should queue frames if the maximum number of concurrent streams
313      * would otherwise be exceeded.
314      */
315     protected boolean encoderEnforceMaxConcurrentStreams() {
316         return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false;
317     }
318 
319     /**
320      * Sets if the encoder should queue frames if the maximum number of concurrent streams
321      * would otherwise be exceeded.
322      */
323     protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
324         enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams");
325         this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams;
326         return self();
327     }
328 
329     /**
330      * Returns the maximum number of queued control frames that are allowed before the connection is closed.
331      * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer
332      * floods us with frames that would have us produce control frames, but stops to read from the underlying socket.
333      *
334      * {@code 0} means no protection is in place.
335      */
336     protected int encoderEnforceMaxQueuedControlFrames() {
337         return maxQueuedControlFrames;
338     }
339 
340     /**
341      * Sets the maximum number of queued control frames that are allowed before the connection is closed.
342      * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer
343      * floods us with frames that would have us produce control frames, but stops to read from the underlying socket.
344      *
345      * {@code 0} means no protection should be applied.
346      */
347     protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) {
348         enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames");
349         this.maxQueuedControlFrames = checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames");
350         return self();
351     }
352 
353     /**
354      * Returns the {@link SensitivityDetector} to use.
355      */
356     protected SensitivityDetector headerSensitivityDetector() {
357         return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR;
358     }
359 
360     /**
361      * Sets the {@link SensitivityDetector} to use.
362      */
363     protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) {
364         enforceNonCodecConstraints("headerSensitivityDetector");
365         this.headerSensitivityDetector = requireNonNull(headerSensitivityDetector, "headerSensitivityDetector");
366         return self();
367     }
368 
369     /**
370      * Sets if the <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a>
371      * should be ignored when encoding headers.
372      * @param ignoreMaxHeaderListSize {@code true} to ignore
373      * <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_HEADER_LIST_SIZE</a>.
374      * @return this.
375      */
376     protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
377         enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize");
378         encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize;
379         return self();
380     }
381 
382     /**
383      * Does nothing, do not call.
384      *
385      * @deprecated Huffman decoding no longer depends on having a decode capacity.
386      */
387     @Deprecated
388     protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
389         return self();
390     }
391 
392     /**
393      * Set the {@link Http2PromisedRequestVerifier} to use.
394      * @return this.
395      */
396     protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) {
397         enforceNonCodecConstraints("promisedRequestVerifier");
398         this.promisedRequestVerifier = requireNonNull(promisedRequestVerifier, "promisedRequestVerifier");
399         return self();
400     }
401 
402     /**
403      * Get the {@link Http2PromisedRequestVerifier} to use.
404      * @return the {@link Http2PromisedRequestVerifier} to use.
405      */
406     protected Http2PromisedRequestVerifier promisedRequestVerifier() {
407         return promisedRequestVerifier;
408     }
409 
410     /**
411      * Returns the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
412      * the connection is closed. This allows to protected against the remote peer flooding us with such frames and
413      * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
414      *
415      * {@code 0} means no protection is in place.
416      */
417     protected int decoderEnforceMaxConsecutiveEmptyDataFrames() {
418         return maxConsecutiveEmptyFrames;
419     }
420 
421     /**
422      * Sets the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
423      * the connection is closed. This allows to protected against the remote peer flooding us with such frames and
424      * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
425      *
426      * {@code 0} means no protection should be applied.
427      */
428     protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) {
429         enforceNonCodecConstraints("maxConsecutiveEmptyFrames");
430         this.maxConsecutiveEmptyFrames = checkPositiveOrZero(
431                 maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames");
432         return self();
433     }
434 
435     /**
436      * Determine if settings frame should automatically be acknowledged and applied.
437      * @return this.
438      */
439     protected B autoAckSettingsFrame(boolean autoAckSettings) {
440         enforceNonCodecConstraints("autoAckSettingsFrame");
441         autoAckSettingsFrame = autoAckSettings;
442         return self();
443     }
444 
445     /**
446      * Determine if the SETTINGS frames should be automatically acknowledged and applied.
447      * @return {@code true} if the SETTINGS frames should be automatically acknowledged and applied.
448      */
449     protected boolean isAutoAckSettingsFrame() {
450         return autoAckSettingsFrame;
451     }
452 
453     /**
454      * Determine if PING frame should automatically be acknowledged or not.
455      * @return this.
456      */
457     protected B autoAckPingFrame(boolean autoAckPingFrame) {
458         enforceNonCodecConstraints("autoAckPingFrame");
459         this.autoAckPingFrame = autoAckPingFrame;
460         return self();
461     }
462 
463     /**
464      * Determine if the PING frames should be automatically acknowledged or not.
465      * @return {@code true} if the PING frames should be automatically acknowledged.
466      */
467     protected boolean isAutoAckPingFrame() {
468         return autoAckPingFrame;
469     }
470 
471     /**
472      * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close.
473      * @param decoupleCloseAndGoAway {@code true} to make {@link Channel#close()} directly close the underlying
474      *   transport, and not attempt graceful closure via GOAWAY.
475      * @return {@code this}.
476      */
477     protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) {
478         this.decoupleCloseAndGoAway = decoupleCloseAndGoAway;
479         return self();
480     }
481 
482     /**
483      * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close.
484      */
485     protected boolean decoupleCloseAndGoAway() {
486         return decoupleCloseAndGoAway;
487     }
488 
489     /**
490      * Determine if the <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">Preface</a>
491      * should be automatically flushed when the {@link Channel} becomes active or not.
492      * <p>
493      * Client may choose to opt-out from this automatic behavior and manage flush manually if it's ready to send
494      * request frames immediately after the preface. It may help to avoid unnecessary latency.
495      *
496      * @param flushPreface {@code true} to automatically flush, {@code false otherwise}.
497      * @return {@code this}.
498      * @see <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">HTTP/2 Connection Preface</a>
499      */
500     protected B flushPreface(boolean flushPreface) {
501         this.flushPreface = flushPreface;
502         return self();
503     }
504 
505     /**
506      * Determine if the <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">Preface</a>
507      * should be automatically flushed when the {@link Channel} becomes active or not.
508      * <p>
509      * Client may choose to opt-out from this automatic behavior and manage flush manually if it's ready to send
510      * request frames immediately after the preface. It may help to avoid unnecessary latency.
511      *
512      * @return {@code true} if automatically flushed.
513      * @see <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.5">HTTP/2 Connection Preface</a>
514      */
515     protected boolean flushPreface() {
516         return flushPreface;
517     }
518 
519     /**
520      * Create a new {@link Http2ConnectionHandler}.
521      */
522     protected T build() {
523         if (encoder != null) {
524             assert decoder != null;
525             return buildFromCodec(decoder, encoder);
526         }
527 
528         Http2Connection connection = this.connection;
529         if (connection == null) {
530             connection = new DefaultHttp2Connection(isServer(), maxReservedStreams());
531         }
532 
533         return buildFromConnection(connection);
534     }
535 
536     private T buildFromConnection(Http2Connection connection) {
537         Long maxHeaderListSize = initialSettings.maxHeaderListSize();
538         Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(),
539                 maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize));
540         Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ?
541                 new DefaultHttp2FrameWriter(headerSensitivityDetector()) :
542                 new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize);
543 
544         if (frameLogger != null) {
545             reader = new Http2InboundFrameLogger(reader, frameLogger);
546             writer = new Http2OutboundFrameLogger(writer, frameLogger);
547         }
548 
549         Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
550         boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams();
551 
552         if (maxQueuedControlFrames != 0) {
553             encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames);
554         }
555         if (encoderEnforceMaxConcurrentStreams) {
556             if (connection.isServer()) {
557                 encoder.close();
558                 reader.close();
559                 throw new IllegalArgumentException(
560                         "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
561                         " not supported for server");
562             }
563             encoder = new StreamBufferingEncoder(encoder);
564         }
565 
566         DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader,
567                 promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame());
568         return buildFromCodec(decoder, encoder);
569     }
570 
571     private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
572         int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames();
573         if (maxConsecutiveEmptyDataFrames > 0) {
574             decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
575         }
576         final T handler;
577         try {
578             // Call the abstract build method
579             handler = build(decoder, encoder, initialSettings);
580         } catch (Throwable t) {
581             encoder.close();
582             decoder.close();
583             throw new IllegalStateException("failed to build an Http2ConnectionHandler", t);
584         }
585 
586         // Setup post build options
587         handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
588         if (handler.decoder().frameListener() == null) {
589             handler.decoder().frameListener(frameListener);
590         }
591         return handler;
592     }
593 
594     /**
595      * Implement this method to create a new {@link Http2ConnectionHandler} or its subtype instance.
596      * <p>
597      * The return of this method will be subject to the following:
598      * <ul>
599      *   <li>{@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder</li>
600      *   <li>{@link #gracefulShutdownTimeoutMillis(long)} will always be set</li>
601      * </ul>
602      */
603     protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
604                                Http2Settings initialSettings) throws Exception;
605 
606     /**
607      * Returns {@code this}.
608      */
609     @SuppressWarnings("unchecked")
610     protected final B self() {
611         return (B) this;
612     }
613 
614     private void enforceNonCodecConstraints(String rejected) {
615         enforceConstraint(rejected, "server/connection", decoder);
616         enforceConstraint(rejected, "server/connection", encoder);
617     }
618 
619     private static void enforceConstraint(String methodName, String rejectorName, Object value) {
620         if (value != null) {
621             throw new IllegalStateException(
622                     methodName + "() cannot be called because " + rejectorName + "() has been called already.");
623         }
624     }
625 }