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