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