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 }