All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 35.0.0.Beta1
Show newest version
/*
 * Copyright 2015 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package io.netty.handler.codec.http2;

import io.netty.channel.Channel;
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.UnstableApi;

import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;

/**
 * Abstract base class which defines commonly used features required to build {@link Http2ConnectionHandler} instances.
 *
 * 

Three ways to build a {@link Http2ConnectionHandler}

*

Let the builder create a {@link Http2ConnectionHandler}

* Simply call all the necessary setter methods, and then use {@link #build()} to build a new * {@link Http2ConnectionHandler}. Setting the following properties are prohibited because they are used for * other ways of building a {@link Http2ConnectionHandler}. * conflicts with this option: *
    *
  • {@link #connection(Http2Connection)}
  • *
  • {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}
  • *
* * *

Let the builder use the {@link Http2ConnectionHandler} you specified

* Call {@link #connection(Http2Connection)} to tell the builder that you want to build the handler from the * {@link Http2Connection} you specified. Setting the following properties are prohibited and thus will trigger * an {@link IllegalStateException} because they conflict with this option. *
    *
  • {@link #server(boolean)}
  • *
  • {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}
  • *
* *

Let the builder use the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified

* Call {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)} to tell the builder that you want to built the * handler from the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified. Setting the * following properties are prohibited and thus will trigger an {@link IllegalStateException} because they conflict * with this option: *
    *
  • {@link #server(boolean)}
  • *
  • {@link #connection(Http2Connection)}
  • *
  • {@link #frameLogger(Http2FrameLogger)}
  • *
  • {@link #headerSensitivityDetector(SensitivityDetector)}
  • *
  • {@link #encoderEnforceMaxConcurrentStreams(boolean)}
  • *
  • {@link #encoderIgnoreMaxHeaderListSize(boolean)}
  • *
* *

Exposing necessary methods in a subclass

* {@link #build()} method and all property access methods are {@code protected}. Choose the methods to expose to the * users of your builder implementation and make them {@code public}. * * @param The type of handler created by this builder. * @param The concrete type of this builder. */ @UnstableApi public abstract class AbstractHttp2ConnectionHandlerBuilder> { private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE; // The properties that can always be set. private Http2Settings initialSettings = Http2Settings.defaultSettings(); private Http2FrameListener frameListener; private long gracefulShutdownTimeoutMillis = Http2CodecUtil.DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS; private boolean decoupleCloseAndGoAway; // The property that will prohibit connection() and codec() if set by server(), // because this property is used only when this builder creates an Http2Connection. private Boolean isServer; private Integer maxReservedStreams; // The property that will prohibit server() and codec() if set by connection(). private Http2Connection connection; // The properties that will prohibit server() and connection() if set by codec(). private Http2ConnectionDecoder decoder; private Http2ConnectionEncoder encoder; // The properties that are: // * mutually exclusive against codec() and // * OK to use with server() and connection() private Boolean validateHeaders; private Http2FrameLogger frameLogger; private SensitivityDetector headerSensitivityDetector; private Boolean encoderEnforceMaxConcurrentStreams; private Boolean encoderIgnoreMaxHeaderListSize; private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY; private boolean autoAckSettingsFrame = true; private boolean autoAckPingFrame = true; private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES; private int maxConsecutiveEmptyFrames = 2; /** * Sets the {@link Http2Settings} to use for the initial connection settings exchange. */ protected Http2Settings initialSettings() { return initialSettings; } /** * Sets the {@link Http2Settings} to use for the initial connection settings exchange. */ protected B initialSettings(Http2Settings settings) { initialSettings = checkNotNull(settings, "settings"); return self(); } /** * Returns the listener of inbound frames. * * @return {@link Http2FrameListener} if set, or {@code null} if not set. */ protected Http2FrameListener frameListener() { return frameListener; } /** * Sets the listener of inbound frames. * This listener will only be set if the decoder's listener is {@code null}. */ protected B frameListener(Http2FrameListener frameListener) { this.frameListener = checkNotNull(frameListener, "frameListener"); return self(); } /** * Returns the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. Returns -1 if the * timeout is indefinite. */ protected long gracefulShutdownTimeoutMillis() { return gracefulShutdownTimeoutMillis; } /** * Sets the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. */ protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { if (gracefulShutdownTimeoutMillis < -1) { throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis + " (expected: -1 for indefinite or >= 0)"); } this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis; return self(); } /** * Returns if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true}) * or client mode ({@code false}). */ protected boolean isServer() { return isServer != null ? isServer : true; } /** * Sets if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true}) * or client mode ({@code false}). */ protected B server(boolean isServer) { enforceConstraint("server", "connection", connection); enforceConstraint("server", "codec", decoder); enforceConstraint("server", "codec", encoder); this.isServer = isServer; return self(); } /** * Get the maximum number of streams which can be in the reserved state at any given time. *

* By default this value will be ignored on the server for local endpoint. This is because the RFC provides * no way to explicitly communicate a limit to how many states can be in the reserved state, and instead relies * on the peer to send RST_STREAM frames when they will be rejected. */ protected int maxReservedStreams() { return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS; } /** * Set the maximum number of streams which can be in the reserved state at any given time. */ protected B maxReservedStreams(int maxReservedStreams) { enforceConstraint("server", "connection", connection); enforceConstraint("server", "codec", decoder); enforceConstraint("server", "codec", encoder); this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams"); return self(); } /** * Returns the {@link Http2Connection} to use. * * @return {@link Http2Connection} if set, or {@code null} if not set. */ protected Http2Connection connection() { return connection; } /** * Sets the {@link Http2Connection} to use. */ protected B connection(Http2Connection connection) { enforceConstraint("connection", "maxReservedStreams", maxReservedStreams); enforceConstraint("connection", "server", isServer); enforceConstraint("connection", "codec", decoder); enforceConstraint("connection", "codec", encoder); this.connection = checkNotNull(connection, "connection"); return self(); } /** * Returns the {@link Http2ConnectionDecoder} to use. * * @return {@link Http2ConnectionDecoder} if set, or {@code null} if not set. */ protected Http2ConnectionDecoder decoder() { return decoder; } /** * Returns the {@link Http2ConnectionEncoder} to use. * * @return {@link Http2ConnectionEncoder} if set, or {@code null} if not set. */ protected Http2ConnectionEncoder encoder() { return encoder; } /** * Sets the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} to use. */ protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { enforceConstraint("codec", "server", isServer); enforceConstraint("codec", "maxReservedStreams", maxReservedStreams); enforceConstraint("codec", "connection", connection); enforceConstraint("codec", "frameLogger", frameLogger); enforceConstraint("codec", "validateHeaders", validateHeaders); enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector); enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams); checkNotNull(decoder, "decoder"); checkNotNull(encoder, "encoder"); if (decoder.connection() != encoder.connection()) { throw new IllegalArgumentException("The specified encoder and decoder have different connections."); } this.decoder = decoder; this.encoder = encoder; return self(); } /** * Returns if HTTP headers should be validated according to * RFC 7540, 8.1.2.6. */ protected boolean isValidateHeaders() { return validateHeaders != null ? validateHeaders : true; } /** * Sets if HTTP headers should be validated according to * RFC 7540, 8.1.2.6. */ protected B validateHeaders(boolean validateHeaders) { enforceNonCodecConstraints("validateHeaders"); this.validateHeaders = validateHeaders; return self(); } /** * Returns the logger that is used for the encoder and decoder. * * @return {@link Http2FrameLogger} if set, or {@code null} if not set. */ protected Http2FrameLogger frameLogger() { return frameLogger; } /** * Sets the logger that is used for the encoder and decoder. */ protected B frameLogger(Http2FrameLogger frameLogger) { enforceNonCodecConstraints("frameLogger"); this.frameLogger = checkNotNull(frameLogger, "frameLogger"); return self(); } /** * Returns if the encoder should queue frames if the maximum number of concurrent streams * would otherwise be exceeded. */ protected boolean encoderEnforceMaxConcurrentStreams() { return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false; } /** * Sets if the encoder should queue frames if the maximum number of concurrent streams * would otherwise be exceeded. */ protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) { enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams"); this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams; return self(); } /** * Returns the maximum number of queued control frames that are allowed before the connection is closed. * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer * floods us with frames that would have us produce control frames, but stops to read from the underlying socket. * * {@code 0} means no protection is in place. */ protected int encoderEnforceMaxQueuedControlFrames() { return maxQueuedControlFrames; } /** * Sets the maximum number of queued control frames that are allowed before the connection is closed. * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer * floods us with frames that would have us produce control frames, but stops to read from the underlying socket. * * {@code 0} means no protection should be applied. */ protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) { enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames"); this.maxQueuedControlFrames = ObjectUtil.checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames"); return self(); } /** * Returns the {@link SensitivityDetector} to use. */ protected SensitivityDetector headerSensitivityDetector() { return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR; } /** * Sets the {@link SensitivityDetector} to use. */ protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) { enforceNonCodecConstraints("headerSensitivityDetector"); this.headerSensitivityDetector = checkNotNull(headerSensitivityDetector, "headerSensitivityDetector"); return self(); } /** * Sets if the SETTINGS_MAX_HEADER_LIST_SIZE * should be ignored when encoding headers. * @param ignoreMaxHeaderListSize {@code true} to ignore * SETTINGS_MAX_HEADER_LIST_SIZE. * @return this. */ protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) { enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize"); this.encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize; return self(); } /** * Does nothing, do not call. * * @deprecated Huffman decoding no longer depends on having a decode capacity. */ @Deprecated protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { return self(); } /** * Set the {@link Http2PromisedRequestVerifier} to use. * @return this. */ protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) { enforceNonCodecConstraints("promisedRequestVerifier"); this.promisedRequestVerifier = checkNotNull(promisedRequestVerifier, "promisedRequestVerifier"); return self(); } /** * Get the {@link Http2PromisedRequestVerifier} to use. * @return the {@link Http2PromisedRequestVerifier} to use. */ protected Http2PromisedRequestVerifier promisedRequestVerifier() { return promisedRequestVerifier; } /** * Returns the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before * the connection is closed. This allows to protected against the remote peer flooding us with such frames and * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag. * * {@code 0} means no protection is in place. */ protected int decoderEnforceMaxConsecutiveEmptyDataFrames() { return maxConsecutiveEmptyFrames; } /** * Sets the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before * the connection is closed. This allows to protected against the remote peer flooding us with such frames and * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag. * * {@code 0} means no protection should be applied. */ protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) { enforceNonCodecConstraints("maxConsecutiveEmptyFrames"); this.maxConsecutiveEmptyFrames = ObjectUtil.checkPositiveOrZero( maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames"); return self(); } /** * Determine if settings frame should automatically be acknowledged and applied. * @return this. */ protected B autoAckSettingsFrame(boolean autoAckSettings) { enforceNonCodecConstraints("autoAckSettingsFrame"); this.autoAckSettingsFrame = autoAckSettings; return self(); } /** * Determine if the SETTINGS frames should be automatically acknowledged and applied. * @return {@code true} if the SETTINGS frames should be automatically acknowledged and applied. */ protected boolean isAutoAckSettingsFrame() { return autoAckSettingsFrame; } /** * Determine if PING frame should automatically be acknowledged or not. * @return this. */ protected B autoAckPingFrame(boolean autoAckPingFrame) { enforceNonCodecConstraints("autoAckPingFrame"); this.autoAckPingFrame = autoAckPingFrame; return self(); } /** * Determine if the PING frames should be automatically acknowledged or not. * @return {@code true} if the PING frames should be automatically acknowledged. */ protected boolean isAutoAckPingFrame() { return autoAckPingFrame; } /** * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close. * @param decoupleCloseAndGoAway {@code true} to make {@link Channel#close()} directly close the underlying * transport, and not attempt graceful closure via GOAWAY. * @return {@code this}. */ protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { this.decoupleCloseAndGoAway = decoupleCloseAndGoAway; return self(); } /** * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close. */ protected boolean decoupleCloseAndGoAway() { return decoupleCloseAndGoAway; } /** * Create a new {@link Http2ConnectionHandler}. */ protected T build() { if (encoder != null) { assert decoder != null; return buildFromCodec(decoder, encoder); } Http2Connection connection = this.connection; if (connection == null) { connection = new DefaultHttp2Connection(isServer(), maxReservedStreams()); } return buildFromConnection(connection); } private T buildFromConnection(Http2Connection connection) { Long maxHeaderListSize = initialSettings.maxHeaderListSize(); Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(), maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize, /* initialHuffmanDecodeCapacity= */ -1)); Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ? new DefaultHttp2FrameWriter(headerSensitivityDetector()) : new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize); if (frameLogger != null) { reader = new Http2InboundFrameLogger(reader, frameLogger); writer = new Http2OutboundFrameLogger(writer, frameLogger); } Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer); boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams(); if (maxQueuedControlFrames != 0) { encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames); } if (encoderEnforceMaxConcurrentStreams) { if (connection.isServer()) { encoder.close(); reader.close(); throw new IllegalArgumentException( "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams + " not supported for server"); } encoder = new StreamBufferingEncoder(encoder); } DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader, promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame()); return buildFromCodec(decoder, encoder); } private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames(); if (maxConsecutiveEmptyDataFrames > 0) { decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames); } final T handler; try { // Call the abstract build method handler = build(decoder, encoder, initialSettings); } catch (Throwable t) { encoder.close(); decoder.close(); throw new IllegalStateException("failed to build an Http2ConnectionHandler", t); } // Setup post build options handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); if (handler.decoder().frameListener() == null) { handler.decoder().frameListener(frameListener); } return handler; } /** * Implement this method to create a new {@link Http2ConnectionHandler} or its subtype instance. *

* The return of this method will be subject to the following: *

    *
  • {@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder
  • *
  • {@link #gracefulShutdownTimeoutMillis(long)} will always be set
  • *
*/ protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) throws Exception; /** * Returns {@code this}. */ @SuppressWarnings("unchecked") protected final B self() { return (B) this; } private void enforceNonCodecConstraints(String rejected) { enforceConstraint(rejected, "server/connection", decoder); enforceConstraint(rejected, "server/connection", encoder); } private static void enforceConstraint(String methodName, String rejectorName, Object value) { if (value != null) { throw new IllegalStateException( methodName + "() cannot be called because " + rejectorName + "() has been called already."); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy