b4e3c12b8e
Motivation: Http2ConnectionHandler#close(..) always runs the GOAWAY and graceful close logic. This coupling means that a user would have to override Http2ConnectionHandler#close(..) to modify the behavior, and the Http2FrameCodec and Http2MultiplexCodec are not extendable so you cannot override at this layer. Ideally we can totally decouple the close(..) of the transport and the GOAWAY graceful closure process completely, but to preserve backwards compatibility we can add an opt-out option to decouple where the application is responsible for sending a GOAWAY with error code equal to NO_ERROR as described in https://tools.ietf.org/html/rfc7540#section-6.8 in order to initiate graceful close. Modifications: - Http2ConnectionHandler supports an additional boolean constructor argument to opt out of close(..) going through the graceful close path. - Http2FrameCodecBuilder and Http2MultiplexCodec expose gracefulShutdownTimeoutMillis but do not hook them up properly. Since these are already exposed we should hook them up and make sure the timeout is applied properly. - Http2ConnectionHandler's goAway(..) method from Http2LifecycleManager should initiate the graceful closure process after writing a GOAWAY frame if the error code is NO_ERROR. This means that writing a Http2GoAwayFrame from Http2FrameCodec will initiate graceful close. Result: Http2ConnectionHandler#close(..) can now be decoupled from the graceful close process, and immediately close the underlying transport if desired.
188 lines
6.3 KiB
Java
188 lines
6.3 KiB
Java
/*
|
|
* Copyright 2017 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.util.internal.UnstableApi;
|
|
|
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
|
|
/**
|
|
* Builder for the {@link Http2FrameCodec}.
|
|
*/
|
|
@UnstableApi
|
|
public class Http2FrameCodecBuilder extends
|
|
AbstractHttp2ConnectionHandlerBuilder<Http2FrameCodec, Http2FrameCodecBuilder> {
|
|
|
|
private Http2FrameWriter frameWriter;
|
|
|
|
Http2FrameCodecBuilder(boolean server) {
|
|
server(server);
|
|
// For backwards compatibility we should disable to timeout by default at this layer.
|
|
gracefulShutdownTimeoutMillis(0);
|
|
}
|
|
|
|
/**
|
|
* Creates a builder for a HTTP/2 client.
|
|
*/
|
|
public static Http2FrameCodecBuilder forClient() {
|
|
return new Http2FrameCodecBuilder(false);
|
|
}
|
|
|
|
/**
|
|
* Creates a builder for a HTTP/2 server.
|
|
*/
|
|
public static Http2FrameCodecBuilder forServer() {
|
|
return new Http2FrameCodecBuilder(true);
|
|
}
|
|
|
|
// For testing only.
|
|
Http2FrameCodecBuilder frameWriter(Http2FrameWriter frameWriter) {
|
|
this.frameWriter = checkNotNull(frameWriter, "frameWriter");
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Http2Settings initialSettings() {
|
|
return super.initialSettings();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder initialSettings(Http2Settings settings) {
|
|
return super.initialSettings(settings);
|
|
}
|
|
|
|
@Override
|
|
public long gracefulShutdownTimeoutMillis() {
|
|
return super.gracefulShutdownTimeoutMillis();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
|
|
return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
|
|
}
|
|
|
|
@Override
|
|
public boolean isServer() {
|
|
return super.isServer();
|
|
}
|
|
|
|
@Override
|
|
public int maxReservedStreams() {
|
|
return super.maxReservedStreams();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder maxReservedStreams(int maxReservedStreams) {
|
|
return super.maxReservedStreams(maxReservedStreams);
|
|
}
|
|
|
|
@Override
|
|
public boolean isValidateHeaders() {
|
|
return super.isValidateHeaders();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder validateHeaders(boolean validateHeaders) {
|
|
return super.validateHeaders(validateHeaders);
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameLogger frameLogger() {
|
|
return super.frameLogger();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder frameLogger(Http2FrameLogger frameLogger) {
|
|
return super.frameLogger(frameLogger);
|
|
}
|
|
|
|
@Override
|
|
public boolean encoderEnforceMaxConcurrentStreams() {
|
|
return super.encoderEnforceMaxConcurrentStreams();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
|
|
return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams);
|
|
}
|
|
|
|
@Override
|
|
public Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector() {
|
|
return super.headerSensitivityDetector();
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder headerSensitivityDetector(
|
|
Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector) {
|
|
return super.headerSensitivityDetector(headerSensitivityDetector);
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
|
|
return super.encoderIgnoreMaxHeaderListSize(ignoreMaxHeaderListSize);
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
|
|
return super.initialHuffmanDecodeCapacity(initialHuffmanDecodeCapacity);
|
|
}
|
|
|
|
@Override
|
|
public Http2FrameCodecBuilder decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) {
|
|
return super.decoupleCloseAndGoAway(decoupleCloseAndGoAway);
|
|
}
|
|
|
|
/**
|
|
* Build a {@link Http2FrameCodec} object.
|
|
*/
|
|
@Override
|
|
public Http2FrameCodec build() {
|
|
Http2FrameWriter frameWriter = this.frameWriter;
|
|
if (frameWriter != null) {
|
|
// This is to support our tests and will never be executed by the user as frameWriter(...)
|
|
// is package-private.
|
|
DefaultHttp2Connection connection = new DefaultHttp2Connection(isServer(), maxReservedStreams());
|
|
Long maxHeaderListSize = initialSettings().maxHeaderListSize();
|
|
Http2FrameReader frameReader = new DefaultHttp2FrameReader(maxHeaderListSize == null ?
|
|
new DefaultHttp2HeadersDecoder(true) :
|
|
new DefaultHttp2HeadersDecoder(true, maxHeaderListSize));
|
|
|
|
if (frameLogger() != null) {
|
|
frameWriter = new Http2OutboundFrameLogger(frameWriter, frameLogger());
|
|
frameReader = new Http2InboundFrameLogger(frameReader, frameLogger());
|
|
}
|
|
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter);
|
|
if (encoderEnforceMaxConcurrentStreams()) {
|
|
encoder = new StreamBufferingEncoder(encoder);
|
|
}
|
|
Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, frameReader,
|
|
promisedRequestVerifier(), isAutoAckSettingsFrame());
|
|
|
|
return build(decoder, encoder, initialSettings());
|
|
}
|
|
return super.build();
|
|
}
|
|
|
|
@Override
|
|
protected Http2FrameCodec build(
|
|
Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) {
|
|
Http2FrameCodec codec = new Http2FrameCodec(encoder, decoder, initialSettings, decoupleCloseAndGoAway());
|
|
codec.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis());
|
|
return codec;
|
|
}
|
|
}
|