netty5/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java

138 lines
5.4 KiB
Java
Raw Normal View History

/*
* Copyright 2014 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.buffer.ByteBuf;
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientUpgradeHandler;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.collection.CharObjectMap;
import io.netty.util.internal.UnstableApi;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static io.netty.handler.codec.base64.Base64Dialect.URL_SAFE;
import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME;
import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER;
import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
import static io.netty.util.CharsetUtil.UTF_8;
import static io.netty.util.ReferenceCountUtil.release;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Client-side cleartext upgrade codec from HTTP to HTTP/2.
*/
@UnstableApi
public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec {
private static final List<CharSequence> UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER);
private final String handlerName;
private final Http2ConnectionHandler connectionHandler;
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
private final ChannelHandler upgradeToHandler;
public Http2ClientUpgradeCodec(Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
this(null, frameCodec, upgradeToHandler);
}
public Http2ClientUpgradeCodec(String handlerName, Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
this(handlerName, frameCodec.connectionHandler(), upgradeToHandler);
}
/**
* Creates the codec using a default name for the connection handler when adding to the
* pipeline.
*
* @param connectionHandler the HTTP/2 connection handler
*/
public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler) {
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
this((String) null, connectionHandler);
}
/**
* Creates the codec providing an upgrade to the given handler for HTTP/2.
*
* @param handlerName the name of the HTTP/2 connection handler to be used in the pipeline,
* or {@code null} to auto-generate the name
* @param connectionHandler the HTTP/2 connection handler
*/
public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler) {
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
this(handlerName, connectionHandler, connectionHandler);
}
private Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, ChannelHandler
upgradeToHandler) {
this.handlerName = handlerName;
this.connectionHandler = checkNotNull(connectionHandler, "connectionHandler");
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
this.upgradeToHandler = checkNotNull(upgradeToHandler, "upgradeToHandler");
}
@Override
public CharSequence protocol() {
return HTTP_UPGRADE_PROTOCOL_NAME;
}
@Override
public Collection<CharSequence> setUpgradeHeaders(ChannelHandlerContext ctx,
HttpRequest upgradeRequest) {
CharSequence settingsValue = getSettingsHeaderValue(ctx);
upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue);
return UPGRADE_HEADERS;
}
@Override
public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse)
throws Exception {
// Reserve local stream 1 for the response.
connectionHandler.onHttpClientUpgrade();
// Add the handler to the pipeline.
HTTP/2 Child Channel and FrameCodec Feature Parity. Motivation: This PR (unfortunately) does 4 things: 1) Add outbound flow control to the Http2MultiplexCodec: The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is, if a H2 stream used up all its flow control window, the corresponding child channel should be marked unwritable and a writability-changed event should be fired. Similarly, a unwritable child channel should be marked writable and a writability-event should be fired, once a WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer, AbstractHttp2StreamChannel and Http2MultiplexCodec. 2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A Http2Stream2 object allows an application to attach state to it, and so a application handler no longer needs to maintain stream state (i.e. in a map(id -> state)) himself. 3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2. Also those stream state events have been found hard and complex to work with, when porting gRPC to the Http2FrameCodec. 4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc. Modifications: 1) A child channel's writability and a H2 stream's outbound flow control window interact, as described in the motivation. A channel handler is free to ignore the channel's writability, in which case the parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received. The connection-level flow control window is ignored for now. That is, a child channel's writability is only affected by the stream-level flow control window. So a child channel could be marked writable, even though the connection-level flow control window is zero. 2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream() methods. It's recommended for a user to extend from this handler, to use those advanced features. 3) As explained in the documentation, a new inbound stream active can be detected by checking if the Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream closed event can be listened to by adding a listener to the Http2Stream2.closeFuture(). 4) Add a simple Http2FrameCodecBuilder and implement the missing frame types. Result: 1) The Http2MultiplexCodec supports outbound flow control. 2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create new outbound streams. 3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame are no longer necessary. 4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
ctx.pipeline().addAfter(ctx.name(), handlerName, upgradeToHandler);
}
/**
* Converts the current settings for the handler to the Base64-encoded representation used in
* the HTTP2-Settings upgrade header.
*/
private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) {
ByteBuf buf = null;
ByteBuf encodedBuf = null;
try {
// Get the local settings for the handler.
Http2Settings settings = connectionHandler.decoder().localSettings();
// Serialize the payload of the SETTINGS frame.
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
buf = ctx.alloc().buffer(payloadLength);
for (CharObjectMap.PrimitiveEntry<Long> entry : settings.entries()) {
buf.writeChar(entry.key());
buf.writeInt(entry.value().intValue());
}
// Base64 encode the payload and then convert to a string for the header.
encodedBuf = Base64.encode(buf, URL_SAFE);
return encodedBuf.toString(UTF_8);
} finally {
release(buf);
release(encodedBuf);
}
}
}