Revamp the Http2ConnectionHandler builder API
Related: #4572 Motivation: - A user might want to extend Http2ConnectionHandler and define his/her own static inner Builder class that extends Http2ConnectionHandler.BuilderBase. This introduces potential confusion because there's already Http2ConnectionHandler.Builder. Your IDE will warn about this name duplication as well. - BuilderBase exposes all setters with public modifier. A user's Builder might not want to expose them to enforce it to certain configuration. There's no way to hide them because it's public already and they are final. - BuilderBase.build(Http2ConnectionDecoder, Http2ConnectionEncoder) ignores most properties exposed by BuilderBase, such as validateHeaders, frameLogger and encoderEnforceMaxConcurrentStreams. If any build() method ignores the properties exposed by the builder, there's something wrong. - A user's Builder that extends BuilderBase might want to require more parameters in build(). There's no way to do that cleanly because build() is public and final already. Modifications: - Make BuilderBase and Builder top-level so that there's no duplicate name issue anymore. - Add AbstractHttp2ConnectionHandlerBuilder - Add Http2ConnectionHandlerBuilder - Add HttpToHttp2ConnectionHandlerBuilder - Make all builder methods in AbstractHttp2ConnectionHandlerBuilder protected so that a subclass can choose which methods to expose - Provide only a single build() method - Add connection() and codec() so that a user can still specify Http2Connection or Http2Connection(En|De)coder explicitly - Implement proper state validation mechanism so that it is prevented to invoke conflicting setters Result: Less confusing yet flexible builder API
This commit is contained in:
parent
253cd694ef
commit
2202e8f967
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* Abstract base class which defines commonly used features required to build {@link Http2ConnectionHandler} instances.
|
||||
*
|
||||
* <h3>Three ways to build a {@link Http2ConnectionHandler}</h3>
|
||||
* <h4>Let the builder create a {@link Http2ConnectionHandler}</h4>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>{@link #connection(Http2Connection)}</li>
|
||||
* <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <h4>Let the builder use the {@link Http2ConnectionHandler} you specified</h4>
|
||||
* 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.
|
||||
* <ul>
|
||||
* <li>{@link #server(boolean)}</li>
|
||||
* <li>{@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h4>Let the builder use the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified</h4>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>{@link #server(boolean)}</li>
|
||||
* <li>{@link #connection(Http2Connection)}</li>
|
||||
* <li>{@link #frameLogger(Http2FrameLogger)}</li>
|
||||
* <li>{@link #headerSensitivityDetector(SensitivityDetector)}</li>
|
||||
* <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Exposing necessary methods in a subclass</h3>
|
||||
* {@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 <T> The type of handler created by this builder.
|
||||
* @param <B> The concrete type of this builder.
|
||||
*/
|
||||
public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2ConnectionHandler,
|
||||
B extends AbstractHttp2ConnectionHandlerBuilder<T, B>> {
|
||||
|
||||
private static final long DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS = MILLISECONDS.convert(30, SECONDS);
|
||||
private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
|
||||
|
||||
// The properties that can always be set.
|
||||
private boolean validateHeaders = true;
|
||||
private Http2Settings initialSettings = new Http2Settings();
|
||||
private Http2FrameListener frameListener;
|
||||
private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;
|
||||
|
||||
// The property that will prohibit connection() and codec() if set by server(),
|
||||
// because this property is used only when this builder creates a Http2Connection.
|
||||
private Boolean isServer;
|
||||
|
||||
// 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 Http2FrameLogger frameLogger;
|
||||
private SensitivityDetector headerSensitivityDetector;
|
||||
private Boolean encoderEnforceMaxConcurrentStreams;
|
||||
|
||||
/**
|
||||
* Returns if HTTP headers should be validated according to
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
|
||||
*/
|
||||
protected boolean isValidateHeaders() {
|
||||
return validateHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if HTTP headers should be validated according to
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
|
||||
*/
|
||||
protected B validateHeaders(boolean validateHeaders) {
|
||||
this.validateHeaders = validateHeaders;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected long gracefulShutdownTimeoutMillis() {
|
||||
return gracefulShutdownTimeoutMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the graceful shutdown timeout of the {@link Http2Connection} in milliseconds.
|
||||
*/
|
||||
protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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", "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", "connection", connection);
|
||||
enforceConstraint("codec", "frameLogger", frameLogger);
|
||||
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 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 {@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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
return buildFromConnection(connection);
|
||||
}
|
||||
|
||||
private T buildFromConnection(Http2Connection connection) {
|
||||
Http2FrameReader reader = new DefaultHttp2FrameReader(validateHeaders);
|
||||
Http2FrameWriter writer = new DefaultHttp2FrameWriter(headerSensitivityDetector());
|
||||
|
||||
if (frameLogger != null) {
|
||||
reader = new Http2InboundFrameLogger(reader, frameLogger);
|
||||
writer = new Http2OutboundFrameLogger(writer, frameLogger);
|
||||
}
|
||||
|
||||
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
|
||||
boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams();
|
||||
|
||||
if (encoderEnforceMaxConcurrentStreams) {
|
||||
if (connection.isServer()) {
|
||||
encoder.close();
|
||||
reader.close();
|
||||
throw new IllegalArgumentException(
|
||||
"encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
|
||||
" not supported for server");
|
||||
}
|
||||
encoder = new StreamBufferingEncoder(encoder);
|
||||
}
|
||||
|
||||
Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader);
|
||||
return buildFromCodec(decoder, encoder);
|
||||
}
|
||||
|
||||
private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
|
||||
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 a 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.
|
||||
* <p>
|
||||
* The return of this method will be subject to the following:
|
||||
* <ul>
|
||||
* <li>{@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder</li>
|
||||
* <li>{@link #gracefulShutdownTimeoutMillis(long)} will always be set</li>
|
||||
* </ul>
|
||||
*/
|
||||
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 rejectee) {
|
||||
enforceConstraint(rejectee, "server/connection", decoder);
|
||||
enforceConstraint(rejectee, "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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -61,9 +61,9 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
* {@link Http2LocalFlowController}
|
||||
*/
|
||||
public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http2LifecycleManager,
|
||||
ChannelOutboundHandler {
|
||||
ChannelOutboundHandler {
|
||||
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ConnectionHandler.class);
|
||||
private static final long DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS = MILLISECONDS.convert(30, SECONDS);
|
||||
|
||||
private final Http2ConnectionDecoder decoder;
|
||||
private final Http2ConnectionEncoder encoder;
|
||||
@ -72,194 +72,6 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
private BaseDecoder byteDecoder;
|
||||
private long gracefulShutdownTimeoutMillis;
|
||||
|
||||
/**
|
||||
* Builder which builds {@link Http2ConnectionHandler} objects.
|
||||
*/
|
||||
public static final class Builder extends BuilderBase<Http2ConnectionHandler, Builder> {
|
||||
@Override
|
||||
public Http2ConnectionHandler build0(Http2ConnectionDecoder decoder,
|
||||
Http2ConnectionEncoder encoder) {
|
||||
return new Http2ConnectionHandler(decoder, encoder, initialSettings());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for a {@code builder} of any subclass of {@link Http2ConnectionHandler}.
|
||||
* @param <T> The type of handler created by this builder.
|
||||
* @param <B> The concrete type for this builder.
|
||||
*/
|
||||
public abstract static class BuilderBase<T extends Http2ConnectionHandler, B extends BuilderBase<T, B>> {
|
||||
private Http2Settings initialSettings = new Http2Settings();
|
||||
private Http2FrameListener frameListener;
|
||||
private Http2FrameLogger frameLogger;
|
||||
private Http2HeadersEncoder.SensitivityDetector headersSensativityDetector =
|
||||
Http2HeadersEncoder.NEVER_SENSITIVE;
|
||||
private boolean validateHeaders = true;
|
||||
private boolean server = true;
|
||||
private boolean encoderEnforceMaxConcurrentStreams;
|
||||
private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;
|
||||
|
||||
/**
|
||||
* Sets the listener for inbound frames.
|
||||
* This listener will only be set if the decoder's listener is {@code null}.
|
||||
*/
|
||||
public B frameListener(Http2FrameListener listener) {
|
||||
frameListener = listener;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if HTTP headers should be validated according to
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
|
||||
*/
|
||||
public B validateHeaders(boolean validate) {
|
||||
validateHeaders = validate;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if HTTP headers should be validated according to
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
|
||||
*/
|
||||
public final boolean isValidateHeaders() {
|
||||
return validateHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings to use for the initial connection settings exchange.
|
||||
*/
|
||||
public B initialSettings(Http2Settings settings) {
|
||||
initialSettings = settings;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings to use for the initial connection settings exchange.
|
||||
*/
|
||||
public final Http2Settings initialSettings() {
|
||||
return initialSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
|
||||
* or client mode ({@code false}).
|
||||
*/
|
||||
public B server(boolean isServer) {
|
||||
server = isServer;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logger that is used for the encoder and decoder.
|
||||
*/
|
||||
public B frameLogger(Http2FrameLogger logger) {
|
||||
frameLogger = logger;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
public B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
|
||||
this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the encoder should queue frames if the maximum number of concurrent streams
|
||||
* would otherwise be exceeded.
|
||||
*/
|
||||
public B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
|
||||
this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams;
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Http2HeadersEncoder.SensitivityDetector} that will be used.
|
||||
*/
|
||||
public B headersSensativityDetector(Http2HeadersEncoder.SensitivityDetector headersSensativityDetector) {
|
||||
this.headersSensativityDetector = checkNotNull(headersSensativityDetector, "headersSensativityDetector");
|
||||
return thisB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Http2Connection} and build a new instance.
|
||||
*/
|
||||
public final T build() {
|
||||
return build(new DefaultHttp2Connection(server));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new instance with an existing {@link Http2Connection}.
|
||||
* <p>
|
||||
* Methods that will be ignored due to objects already being created:
|
||||
* <ul><li>{@link #server(boolean)}</li></ul>
|
||||
*/
|
||||
public final T build(Http2Connection connection) {
|
||||
Http2FrameReader reader = new DefaultHttp2FrameReader(validateHeaders);
|
||||
Http2FrameWriter writer = new DefaultHttp2FrameWriter(headersSensativityDetector);
|
||||
if (frameLogger != null) {
|
||||
reader = new Http2InboundFrameLogger(reader, frameLogger);
|
||||
writer = new Http2OutboundFrameLogger(writer, frameLogger);
|
||||
}
|
||||
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
|
||||
if (encoderEnforceMaxConcurrentStreams) {
|
||||
if (connection.isServer()) {
|
||||
encoder.close();
|
||||
reader.close();
|
||||
throw new IllegalArgumentException(
|
||||
"encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
|
||||
" not supported for server");
|
||||
}
|
||||
encoder = new StreamBufferingEncoder(encoder);
|
||||
}
|
||||
Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader);
|
||||
return build(decoder, encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new instance with an existing {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder}.
|
||||
* <p>
|
||||
* Methods that will be ignored due to objects already being created:
|
||||
* <ul>
|
||||
* <li>{@link #server(boolean)}</li>
|
||||
* <li>{@link #frameLogger(Http2FrameLogger)}</li>
|
||||
* <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li>
|
||||
* <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)} (int)}</li>
|
||||
* <li>{@link #headersSensativityDetector(Http2HeadersEncoder.SensitivityDetector)}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
|
||||
final T handler;
|
||||
try {
|
||||
// Call the abstract build method
|
||||
handler = build0(decoder, encoder);
|
||||
} catch (RuntimeException e) {
|
||||
encoder.close();
|
||||
decoder.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Setup post build options
|
||||
handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
|
||||
if (handler.decoder().frameListener() == null) {
|
||||
handler.decoder().frameListener(frameListener);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub classes should override this to instantiate the concrete type.
|
||||
* <p>
|
||||
* The return of this method will be subject to the following:
|
||||
* <ul><li>{@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder</li><li>
|
||||
* {@link #gracefulShutdownTimeoutMillis(long)} will be set</li></ul>
|
||||
*/
|
||||
protected abstract T build0(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected B thisB() {
|
||||
return (B) this;
|
||||
}
|
||||
}
|
||||
|
||||
protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
this.initialSettings = checkNotNull(initialSettings, "initialSettings");
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
||||
|
||||
/**
|
||||
* Builder which builds {@link Http2ConnectionHandler} objects.
|
||||
*/
|
||||
public final class Http2ConnectionHandlerBuilder
|
||||
extends AbstractHttp2ConnectionHandlerBuilder<Http2ConnectionHandler, Http2ConnectionHandlerBuilder> {
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder validateHeaders(boolean validateHeaders) {
|
||||
return super.validateHeaders(validateHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder initialSettings(Http2Settings settings) {
|
||||
return super.initialSettings(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder frameListener(Http2FrameListener frameListener) {
|
||||
return super.frameListener(frameListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
|
||||
return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder server(boolean isServer) {
|
||||
return super.server(isServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder connection(Http2Connection connection) {
|
||||
return super.connection(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
|
||||
return super.codec(decoder, encoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder frameLogger(Http2FrameLogger frameLogger) {
|
||||
return super.frameLogger(frameLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder encoderEnforceMaxConcurrentStreams(
|
||||
boolean encoderEnforceMaxConcurrentStreams) {
|
||||
return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandlerBuilder headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) {
|
||||
return super.headerSensitivityDetector(headerSensitivityDetector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2ConnectionHandler build() {
|
||||
return super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Http2ConnectionHandler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
return new Http2ConnectionHandler(decoder, encoder, initialSettings);
|
||||
}
|
||||
}
|
@ -36,17 +36,6 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
||||
private final boolean validateHeaders;
|
||||
private int currentStreamId;
|
||||
|
||||
/**
|
||||
* Builder which builds {@link HttpToHttp2ConnectionHandler} objects.
|
||||
*/
|
||||
public static final class Builder extends BuilderBase<HttpToHttp2ConnectionHandler, Builder> {
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandler build0(Http2ConnectionDecoder decoder,
|
||||
Http2ConnectionEncoder encoder) {
|
||||
return new HttpToHttp2ConnectionHandler(decoder, encoder, initialSettings(), isValidateHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings, boolean validateHeaders) {
|
||||
super(decoder, encoder, initialSettings);
|
||||
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
||||
|
||||
/**
|
||||
* Builder which builds {@link HttpToHttp2ConnectionHandler} objects.
|
||||
*/
|
||||
public final class HttpToHttp2ConnectionHandlerBuilder extends
|
||||
AbstractHttp2ConnectionHandlerBuilder<HttpToHttp2ConnectionHandler, HttpToHttp2ConnectionHandlerBuilder> {
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder validateHeaders(boolean validateHeaders) {
|
||||
return super.validateHeaders(validateHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder initialSettings(Http2Settings settings) {
|
||||
return super.initialSettings(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder frameListener(Http2FrameListener frameListener) {
|
||||
return super.frameListener(frameListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
|
||||
return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder server(boolean isServer) {
|
||||
return super.server(isServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder connection(Http2Connection connection) {
|
||||
return super.connection(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder codec(Http2ConnectionDecoder decoder,
|
||||
Http2ConnectionEncoder encoder) {
|
||||
return super.codec(decoder, encoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder frameLogger(Http2FrameLogger frameLogger) {
|
||||
return super.frameLogger(frameLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder encoderEnforceMaxConcurrentStreams(
|
||||
boolean encoderEnforceMaxConcurrentStreams) {
|
||||
return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandlerBuilder headerSensitivityDetector(
|
||||
SensitivityDetector headerSensitivityDetector) {
|
||||
return super.headerSensitivityDetector(headerSensitivityDetector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpToHttp2ConnectionHandler build() {
|
||||
return super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpToHttp2ConnectionHandler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
return new HttpToHttp2ConnectionHandler(decoder, encoder, initialSettings, isValidateHeaders());
|
||||
}
|
||||
}
|
@ -305,9 +305,9 @@ public class DataCompressionHttp2Test {
|
||||
new DefaultHttp2ConnectionEncoder(serverConnection, frameWriter));
|
||||
Http2ConnectionDecoder decoder =
|
||||
new DefaultHttp2ConnectionDecoder(serverConnection, encoder, new DefaultHttp2FrameReader());
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder()
|
||||
.frameListener(new DelegatingDecompressorFrameListener(serverConnection, serverListener))
|
||||
.build(decoder, encoder);
|
||||
.codec(decoder, encoder).build();
|
||||
p.addLast(connectionHandler);
|
||||
serverChannelLatch.countDown();
|
||||
}
|
||||
@ -330,11 +330,11 @@ public class DataCompressionHttp2Test {
|
||||
Http2ConnectionDecoder decoder =
|
||||
new DefaultHttp2ConnectionDecoder(clientConnection, clientEncoder,
|
||||
new DefaultHttp2FrameReader());
|
||||
clientHandler = new Http2ConnectionHandler.Builder()
|
||||
clientHandler = new Http2ConnectionHandlerBuilder()
|
||||
.frameListener(new DelegatingDecompressorFrameListener(clientConnection, clientListener))
|
||||
// By default tests don't wait for server to gracefully shutdown streams
|
||||
.gracefulShutdownTimeoutMillis(0)
|
||||
.build(decoder, clientEncoder);
|
||||
.codec(decoder, clientEncoder).build();
|
||||
p.addLast(clientHandler);
|
||||
}
|
||||
});
|
||||
|
@ -24,7 +24,6 @@ import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.DefaultChannelPromise;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.junit.After;
|
||||
@ -180,7 +179,7 @@ public class Http2ConnectionHandlerTest {
|
||||
}
|
||||
|
||||
private Http2ConnectionHandler newHandler() throws Exception {
|
||||
Http2ConnectionHandler handler = new Http2ConnectionHandler.Builder().build(decoder, encoder);
|
||||
Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder().codec(decoder, encoder).build();
|
||||
handler.handlerAdded(ctx);
|
||||
return handler;
|
||||
}
|
||||
@ -426,11 +425,11 @@ public class Http2ConnectionHandlerTest {
|
||||
verify(ctx, times(1)).flush();
|
||||
}
|
||||
|
||||
private ByteBuf dummyData() {
|
||||
return Unpooled.buffer().writeBytes("abcdefgh".getBytes(CharsetUtil.UTF_8));
|
||||
private static ByteBuf dummyData() {
|
||||
return Unpooled.buffer().writeBytes("abcdefgh".getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private ByteBuf addSettingsHeader(ByteBuf buf) {
|
||||
private static ByteBuf addSettingsHeader(ByteBuf buf) {
|
||||
buf.writeMedium(Http2CodecUtil.SETTING_ENTRY_LENGTH);
|
||||
buf.writeByte(Http2FrameTypes.SETTINGS);
|
||||
buf.writeByte(0);
|
||||
|
@ -482,7 +482,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
serverFrameCountDown =
|
||||
new FrameCountDown(serverListener, serverSettingsAckLatch,
|
||||
requestLatch, dataLatch, trailersLatch, goAwayLatch);
|
||||
p.addLast(new Http2ConnectionHandler.Builder()
|
||||
p.addLast(new Http2ConnectionHandlerBuilder()
|
||||
.server(true)
|
||||
.frameListener(serverFrameCountDown)
|
||||
.validateHeaders(false)
|
||||
@ -496,7 +496,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(new Http2ConnectionHandler.Builder()
|
||||
p.addLast(new Http2ConnectionHandlerBuilder()
|
||||
.server(false)
|
||||
.frameListener(clientListener)
|
||||
.validateHeaders(false)
|
||||
|
@ -508,7 +508,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
serverFrameCountDown =
|
||||
new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch);
|
||||
p.addLast(new HttpToHttp2ConnectionHandler.Builder()
|
||||
p.addLast(new HttpToHttp2ConnectionHandlerBuilder()
|
||||
.server(true)
|
||||
.frameListener(serverFrameCountDown)
|
||||
.build());
|
||||
@ -521,7 +521,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandler.Builder()
|
||||
HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandlerBuilder()
|
||||
.server(false)
|
||||
.frameListener(clientListener)
|
||||
.gracefulShutdownTimeoutMillis(0)
|
||||
|
@ -115,8 +115,9 @@ public class StreamBufferingEncoderTest {
|
||||
encoder = new StreamBufferingEncoder(defaultEncoder);
|
||||
DefaultHttp2ConnectionDecoder decoder =
|
||||
new DefaultHttp2ConnectionDecoder(connection, encoder, mock(Http2FrameReader.class));
|
||||
Http2ConnectionHandler handler = new Http2ConnectionHandler.Builder()
|
||||
.frameListener(mock(Http2FrameListener.class)).build(decoder, encoder);
|
||||
Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder()
|
||||
.frameListener(mock(Http2FrameListener.class))
|
||||
.codec(decoder, encoder).build();
|
||||
|
||||
// Set LifeCycleManager on encoder and decoder
|
||||
when(ctx.channel()).thenReturn(channel);
|
||||
|
@ -30,6 +30,7 @@ import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
|
||||
import io.netty.handler.codec.http2.Http2Connection;
|
||||
import io.netty.handler.codec.http2.Http2FrameLogger;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
||||
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNames;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
|
||||
@ -57,14 +58,15 @@ public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
final Http2Connection connection = new DefaultHttp2Connection(false);
|
||||
connectionHandler = new HttpToHttp2ConnectionHandler.Builder()
|
||||
connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
|
||||
.frameListener(new DelegatingDecompressorFrameListener(connection,
|
||||
new InboundHttp2ToHttpAdapter.Builder(connection)
|
||||
.maxContentLength(maxContentLength)
|
||||
.propagateSettings(true)
|
||||
.build()))
|
||||
.frameLogger(logger)
|
||||
.build(connection);
|
||||
.connection(connection)
|
||||
.build();
|
||||
responseHandler = new HttpResponseHandler();
|
||||
settingsHandler = new Http2SettingsHandler(ch.newPromise());
|
||||
if (sslCtx != null) {
|
||||
|
@ -23,10 +23,8 @@ import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.Http2Exception;
|
||||
import io.netty.handler.codec.http2.Http2Flags;
|
||||
import io.netty.handler.codec.http2.Http2FrameListener;
|
||||
import io.netty.handler.codec.http2.Http2FrameLogger;
|
||||
import io.netty.handler.codec.http2.Http2Headers;
|
||||
import io.netty.handler.codec.http2.Http2Settings;
|
||||
import io.netty.util.AsciiString;
|
||||
@ -36,31 +34,16 @@ import static io.netty.buffer.Unpooled.copiedBuffer;
|
||||
import static io.netty.buffer.Unpooled.unreleasableBuffer;
|
||||
import static io.netty.example.http2.Http2ExampleUtil.UPGRADE_RESPONSE_HEADER;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static io.netty.handler.logging.LogLevel.INFO;
|
||||
|
||||
/**
|
||||
* A simple handler that responds with the message "Hello World!".
|
||||
*/
|
||||
public final class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener {
|
||||
|
||||
private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class);
|
||||
static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8));
|
||||
|
||||
public static final class Builder extends BuilderBase<HelloWorldHttp2Handler, Builder> {
|
||||
public Builder() {
|
||||
frameLogger(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelloWorldHttp2Handler build0(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
|
||||
HelloWorldHttp2Handler handler = new HelloWorldHttp2Handler(decoder, encoder, initialSettings());
|
||||
frameListener(handler);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
private HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
super(decoder, encoder, initialSettings);
|
||||
}
|
||||
|
||||
@ -99,8 +82,7 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
|
||||
throws Http2Exception {
|
||||
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) {
|
||||
int processed = data.readableBytes() + padding;
|
||||
if (endOfStream) {
|
||||
sendResponse(ctx, streamId, data.retain());
|
||||
@ -109,11 +91,11 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||
boolean endOfStream) throws Http2Exception {
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId,
|
||||
Http2Headers headers, int padding, boolean endOfStream) {
|
||||
if (endOfStream) {
|
||||
ByteBuf content = ctx.alloc().buffer();
|
||||
content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate());
|
||||
content.writeBytes(RESPONSE_BYTES.duplicate());
|
||||
ByteBufUtil.writeAscii(content, " - via HTTP/2");
|
||||
sendResponse(ctx, streamId, content);
|
||||
}
|
||||
@ -121,52 +103,50 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
|
||||
short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
|
||||
short weight, boolean exclusive, int padding, boolean endOfStream) {
|
||||
onHeadersRead(ctx, streamId, headers, padding, endOfStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||
boolean exclusive) throws Http2Exception {
|
||||
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
||||
short weight, boolean exclusive) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
|
||||
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
|
||||
public void onSettingsAckRead(ChannelHandlerContext ctx) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
|
||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, Http2Headers headers,
|
||||
int padding) throws Http2Exception {
|
||||
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||
Http2Headers headers, int padding) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
|
||||
throws Http2Exception {
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
|
||||
throws Http2Exception {
|
||||
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||
ByteBuf payload) throws Http2Exception {
|
||||
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||
Http2Flags flags, ByteBuf payload) {
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.example.http2.helloworld.server;
|
||||
|
||||
import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
||||
import io.netty.handler.codec.http2.Http2FrameLogger;
|
||||
import io.netty.handler.codec.http2.Http2Settings;
|
||||
|
||||
import static io.netty.handler.logging.LogLevel.INFO;
|
||||
|
||||
public final class HelloWorldHttp2HandlerBuilder
|
||||
extends AbstractHttp2ConnectionHandlerBuilder<HelloWorldHttp2Handler, HelloWorldHttp2HandlerBuilder> {
|
||||
|
||||
private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class);
|
||||
|
||||
public HelloWorldHttp2HandlerBuilder() {
|
||||
frameLogger(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelloWorldHttp2Handler build() {
|
||||
return super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HelloWorldHttp2Handler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
|
||||
Http2Settings initialSettings) {
|
||||
|
||||
HelloWorldHttp2Handler handler = new HelloWorldHttp2Handler(decoder, encoder, initialSettings);
|
||||
frameListener(handler);
|
||||
return handler;
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
|
||||
@Override
|
||||
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
|
||||
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
|
||||
ctx.pipeline().addLast(new HelloWorldHttp2Handler.Builder().build());
|
||||
ctx.pipeline().addLast(new HelloWorldHttp2HandlerBuilder().build());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
@Override
|
||||
public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
|
||||
if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
|
||||
return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler.Builder().build());
|
||||
return new Http2ServerUpgradeCodec(new HelloWorldHttp2HandlerBuilder().build());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -20,9 +20,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2Connection;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2FrameReader;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2FrameWriter;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
||||
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNames;
|
||||
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
|
||||
@ -59,10 +57,12 @@ public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
|
||||
InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapter.Builder(connection)
|
||||
.propagateSettings(true).validateHttpHeaders(false).maxContentLength(MAX_CONTENT_LENGTH).build();
|
||||
|
||||
ctx.pipeline().addLast(new HttpToHttp2ConnectionHandler.Builder()
|
||||
.frameListener(listener)
|
||||
// .frameLogger(TilesHttp2ToHttpHandler.logger)
|
||||
.build(connection));
|
||||
ctx.pipeline().addLast(new HttpToHttp2ConnectionHandlerBuilder()
|
||||
.frameListener(listener)
|
||||
// .frameLogger(TilesHttp2ToHttpHandler.logger)
|
||||
.connection(connection)
|
||||
.build());
|
||||
|
||||
ctx.pipeline().addLast(new Http2RequestHandler());
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,6 @@
|
||||
*/
|
||||
package io.netty.microbench.http2;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_FRAME_SIZE_UPPER_BOUND;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@ -55,6 +49,7 @@ import io.netty.handler.codec.http2.Http2Connection;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionHandlerBuilder;
|
||||
import io.netty.handler.codec.http2.Http2FrameAdapter;
|
||||
import io.netty.handler.codec.http2.Http2FrameWriter;
|
||||
import io.netty.handler.codec.http2.Http2Headers;
|
||||
@ -64,12 +59,6 @@ import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext;
|
||||
import io.netty.microbench.util.AbstractSharedExecutorMicrobenchmark;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
@ -78,6 +67,17 @@ import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_FRAME_SIZE_UPPER_BOUND;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public class Http2FrameWriterBenchmark extends AbstractSharedExecutorMicrobenchmark {
|
||||
private static final EnvironmentParameters NIO_UNPOOLED_PARAMS =
|
||||
@ -263,9 +263,10 @@ public class Http2FrameWriterBenchmark extends AbstractSharedExecutorMicrobenchm
|
||||
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, environment.writer());
|
||||
Http2ConnectionDecoder decoder =
|
||||
new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader());
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
|
||||
.encoderEnforceMaxConcurrentStreams(false)
|
||||
.frameListener(new Http2FrameAdapter()).build(decoder, encoder);
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder()
|
||||
.encoderEnforceMaxConcurrentStreams(false)
|
||||
.frameListener(new Http2FrameAdapter())
|
||||
.codec(decoder, encoder).build();
|
||||
p.addLast(connectionHandler);
|
||||
environment.context(p.lastContext());
|
||||
// Must wait for context to be set.
|
||||
@ -293,9 +294,10 @@ public class Http2FrameWriterBenchmark extends AbstractSharedExecutorMicrobenchm
|
||||
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, env.writer());
|
||||
Http2ConnectionDecoder decoder =
|
||||
new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader());
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
|
||||
.encoderEnforceMaxConcurrentStreams(false)
|
||||
.frameListener(new Http2FrameAdapter()).build(decoder, encoder);
|
||||
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder()
|
||||
.encoderEnforceMaxConcurrentStreams(false)
|
||||
.frameListener(new Http2FrameAdapter())
|
||||
.codec(decoder, encoder).build();
|
||||
env.context(new EmbeddedChannelWriteReleaseHandlerContext(alloc, connectionHandler) {
|
||||
@Override
|
||||
protected void handleException(Throwable t) {
|
||||
|
Loading…
Reference in New Issue
Block a user