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 }