Accept two ways to start HTTP/2 over clear text
Motivation: HTTP/2 support two ways to start on a no-tls tcp connection, http/1.1 upgrade and prior knowlege methodology to start HTTP/2. Currently, the http2-server from example only support starting by upgrade. I think we can do a simple dispatch by peek first bytes from inbound that match to prior knowledge preface or not and determine which handlers to set into pipeline. Modifications: Add ClearTextHttp2ServerUpgradeHandler to support start HTTP/2 via clear text with two approach. And update example/http2-server to support this functionality. Result: netty HTTP/2 and the example http2-server accept for two ways to start HTTP/2 over clear text. Fixed memory leak problem Update fields to final Rename ClearText to cleartext Addressed comments for code improvement - Always prefer static, final, and private if possible - Add UnstableApi annotation - Used EmbeddedChannel.readInbound instead of unhandled inbound handler - More assertion Update javadoc for CleartextHttp2ServerUpgradeHandler Rename ClearTextHttp2ServerUpgradeHandler to CleartextHttp2ServerUpgradeHandler Removed redundant code about configure pipeline nit: PriorKnowledgeHandler Removed Mockito.spy, investigate conn state instead Add Http2UpgradeEvent Check null of the constructor arguments Rename Http2UpgradeEvent to PriorKnowledgeUpgradeEvent Update unit test
This commit is contained in:
parent
1419f5b601
commit
0ee36fef00
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* 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.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
||||||
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.buffer.Unpooled.unreleasableBuffer;
|
||||||
|
import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performing cleartext upgrade, by h2c HTTP upgrade or Prior Knowledge.
|
||||||
|
* This handler config pipeline for h2c upgrade when handler added.
|
||||||
|
* And will update pipeline once it detect the connection is starting HTTP/2 by
|
||||||
|
* prior knowledge or not.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
public final class CleartextHttp2ServerUpgradeHandler extends ChannelHandlerAdapter {
|
||||||
|
private static final ByteBuf CONNECTION_PREFACE = unreleasableBuffer(connectionPrefaceBuf());
|
||||||
|
|
||||||
|
private final HttpServerCodec httpServerCodec;
|
||||||
|
private final HttpServerUpgradeHandler httpServerUpgradeHandler;
|
||||||
|
private final ChannelHandler http2ServerHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the channel handler provide cleartext HTTP/2 upgrade from HTTP
|
||||||
|
* upgrade or prior knowledge
|
||||||
|
*
|
||||||
|
* @param httpServerCodec the http server codec
|
||||||
|
* @param httpServerUpgradeHandler the http server upgrade handler for HTTP/2
|
||||||
|
* @param http2ServerHandler the http2 server handler, will be added into pipeline
|
||||||
|
* when starting HTTP/2 by prior knowledge
|
||||||
|
*/
|
||||||
|
public CleartextHttp2ServerUpgradeHandler(HttpServerCodec httpServerCodec,
|
||||||
|
HttpServerUpgradeHandler httpServerUpgradeHandler,
|
||||||
|
ChannelHandler http2ServerHandler) {
|
||||||
|
this.httpServerCodec = checkNotNull(httpServerCodec, "httpServerCodec");
|
||||||
|
this.httpServerUpgradeHandler = checkNotNull(httpServerUpgradeHandler, "httpServerUpgradeHandler");
|
||||||
|
this.http2ServerHandler = checkNotNull(http2ServerHandler, "http2ServerHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
ctx.pipeline()
|
||||||
|
.addBefore(ctx.name(), null, new PriorKnowledgeHandler())
|
||||||
|
.addBefore(ctx.name(), null, httpServerCodec)
|
||||||
|
.replace(this, null, httpServerUpgradeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peek inbound message to determine current connection wants to start HTTP/2
|
||||||
|
* by HTTP upgrade or prior knowledge
|
||||||
|
*/
|
||||||
|
private final class PriorKnowledgeHandler extends ByteToMessageDecoder {
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||||
|
int prefaceLength = CONNECTION_PREFACE.readableBytes();
|
||||||
|
int bytesRead = Math.min(in.readableBytes(), prefaceLength);
|
||||||
|
|
||||||
|
if (!ByteBufUtil.equals(CONNECTION_PREFACE, CONNECTION_PREFACE.readerIndex(),
|
||||||
|
in, in.readerIndex(), bytesRead)) {
|
||||||
|
ctx.pipeline().remove(this);
|
||||||
|
} else if (bytesRead == prefaceLength) {
|
||||||
|
// Full h2 preface match, removed source codec, using http2 codec to handle
|
||||||
|
// following network traffic
|
||||||
|
ctx.pipeline()
|
||||||
|
.remove(httpServerCodec)
|
||||||
|
.remove(httpServerUpgradeHandler)
|
||||||
|
.replace(this, null, http2ServerHandler);
|
||||||
|
ctx.fireUserEventTriggered(PriorKnowledgeUpgradeEvent.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User event that is fired to notify about HTTP/2 protocol is started.
|
||||||
|
*/
|
||||||
|
public static final class PriorKnowledgeUpgradeEvent {
|
||||||
|
private static final PriorKnowledgeUpgradeEvent INSTANCE = new PriorKnowledgeUpgradeEvent();
|
||||||
|
|
||||||
|
private PriorKnowledgeUpgradeEvent() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* 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.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
||||||
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
|
||||||
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
|
||||||
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler.PriorKnowledgeUpgradeEvent;
|
||||||
|
import io.netty.handler.codec.http2.Http2Stream.State;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CleartextHttp2ServerUpgradeHandler}
|
||||||
|
*/
|
||||||
|
public class CleartextHttp2ServerUpgradeHandlerTest {
|
||||||
|
private EmbeddedChannel channel;
|
||||||
|
|
||||||
|
private Http2FrameListener frameListener;
|
||||||
|
|
||||||
|
private Http2ConnectionHandler http2ConnectionHandler;
|
||||||
|
|
||||||
|
private List<Object> userEvents;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
frameListener = mock(Http2FrameListener.class);
|
||||||
|
|
||||||
|
http2ConnectionHandler = new Http2ConnectionHandlerBuilder().frameListener(frameListener).build();
|
||||||
|
|
||||||
|
UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() {
|
||||||
|
@Override
|
||||||
|
public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
|
||||||
|
return new Http2ServerUpgradeCodec(http2ConnectionHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
userEvents = new ArrayList<Object>();
|
||||||
|
|
||||||
|
HttpServerCodec httpServerCodec = new HttpServerCodec();
|
||||||
|
HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, upgradeCodecFactory);
|
||||||
|
|
||||||
|
CleartextHttp2ServerUpgradeHandler handler = new CleartextHttp2ServerUpgradeHandler(
|
||||||
|
httpServerCodec, upgradeHandler, http2ConnectionHandler);
|
||||||
|
channel = new EmbeddedChannel(handler, new ChannelInboundHandlerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||||
|
userEvents.add(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
channel.finishAndReleaseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void priorKnowledge() throws Exception {
|
||||||
|
channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf());
|
||||||
|
|
||||||
|
ByteBuf settingsFrame = settingsFrameBuf();
|
||||||
|
|
||||||
|
assertFalse(channel.writeInbound(settingsFrame));
|
||||||
|
|
||||||
|
assertEquals(1, userEvents.size());
|
||||||
|
assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent);
|
||||||
|
|
||||||
|
assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams());
|
||||||
|
assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize());
|
||||||
|
|
||||||
|
verify(frameListener).onSettingsRead(
|
||||||
|
any(ChannelHandlerContext.class), eq(expectedSettings()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void upgrade() throws Exception {
|
||||||
|
String upgradeString = "GET / HTTP/1.1\r\n" +
|
||||||
|
"Host: example.com\r\n" +
|
||||||
|
"Connection: Upgrade, HTTP2-Settings\r\n" +
|
||||||
|
"Upgrade: h2c\r\n" +
|
||||||
|
"HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n";
|
||||||
|
ByteBuf upgrade = Unpooled.buffer().writeBytes(upgradeString.getBytes(CharsetUtil.US_ASCII));
|
||||||
|
|
||||||
|
assertFalse(channel.writeInbound(upgrade));
|
||||||
|
|
||||||
|
assertEquals(1, userEvents.size());
|
||||||
|
|
||||||
|
Object userEvent = userEvents.get(0);
|
||||||
|
assertTrue(userEvent instanceof UpgradeEvent);
|
||||||
|
assertEquals("h2c", ((UpgradeEvent) userEvent).protocol());
|
||||||
|
ReferenceCountUtil.release(userEvent);
|
||||||
|
|
||||||
|
assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams());
|
||||||
|
assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize());
|
||||||
|
|
||||||
|
assertEquals(1, http2ConnectionHandler.connection().numActiveStreams());
|
||||||
|
assertNotNull(http2ConnectionHandler.connection().stream(1));
|
||||||
|
|
||||||
|
Http2Stream stream = http2ConnectionHandler.connection().stream(1);
|
||||||
|
assertEquals(State.HALF_CLOSED_REMOTE, stream.state());
|
||||||
|
assertFalse(stream.isHeadersSent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void priorKnowledgeInFragments() throws Exception {
|
||||||
|
ByteBuf connectionPreface = Http2CodecUtil.connectionPrefaceBuf();
|
||||||
|
assertFalse(channel.writeInbound(connectionPreface.readBytes(5), connectionPreface));
|
||||||
|
|
||||||
|
ByteBuf settingsFrame = settingsFrameBuf();
|
||||||
|
assertFalse(channel.writeInbound(settingsFrame));
|
||||||
|
|
||||||
|
assertEquals(1, userEvents.size());
|
||||||
|
assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent);
|
||||||
|
|
||||||
|
assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams());
|
||||||
|
assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize());
|
||||||
|
|
||||||
|
verify(frameListener).onSettingsRead(
|
||||||
|
any(ChannelHandlerContext.class), eq(expectedSettings()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void downgrade() throws Exception {
|
||||||
|
String requestString = "GET / HTTP/1.1\r\n" +
|
||||||
|
"Host: example.com\r\n\r\n";
|
||||||
|
ByteBuf inbound = Unpooled.buffer().writeBytes(requestString.getBytes(CharsetUtil.US_ASCII));
|
||||||
|
|
||||||
|
assertTrue(channel.writeInbound(inbound));
|
||||||
|
|
||||||
|
Object firstInbound = channel.readInbound();
|
||||||
|
assertTrue(firstInbound instanceof HttpRequest);
|
||||||
|
HttpRequest request = (HttpRequest) firstInbound;
|
||||||
|
assertEquals(HttpMethod.GET, request.method());
|
||||||
|
assertEquals("/", request.uri());
|
||||||
|
assertEquals(HttpVersion.HTTP_1_1, request.protocolVersion());
|
||||||
|
assertEquals(new DefaultHttpHeaders().add("Host", "example.com"), request.headers());
|
||||||
|
|
||||||
|
((LastHttpContent) channel.readInbound()).release();
|
||||||
|
|
||||||
|
assertNull(channel.readInbound());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuf settingsFrameBuf() {
|
||||||
|
ByteBuf settingsFrame = Unpooled.buffer();
|
||||||
|
settingsFrame.writeMedium(12); // Payload length
|
||||||
|
settingsFrame.writeByte(0x4); // Frame type
|
||||||
|
settingsFrame.writeByte(0x0); // Flags
|
||||||
|
settingsFrame.writeInt(0x0); // StreamId
|
||||||
|
settingsFrame.writeShort(0x3);
|
||||||
|
settingsFrame.writeInt(100);
|
||||||
|
settingsFrame.writeShort(0x4);
|
||||||
|
settingsFrame.writeInt(65535);
|
||||||
|
|
||||||
|
return settingsFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Http2Settings expectedSettings() {
|
||||||
|
return new Http2Settings().maxConcurrentStreams(100).initialWindowSize(65535);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ package io.netty.example.http2.helloworld.server;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
||||||
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||||
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
||||||
@ -48,6 +49,14 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
|||||||
super(decoder, encoder, initialSettings);
|
super(decoder, encoder, initialSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Http2Headers http1HeadersToHttp2Headers(FullHttpRequest request) {
|
||||||
|
return new DefaultHttp2Headers()
|
||||||
|
.authority(request.headers().get("Host"))
|
||||||
|
.method("GET")
|
||||||
|
.path(request.uri())
|
||||||
|
.scheme("http");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via HTTP/2
|
* Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via HTTP/2
|
||||||
* on stream 1 (the stream specifically reserved for cleartext HTTP upgrade).
|
* on stream 1 (the stream specifically reserved for cleartext HTTP upgrade).
|
||||||
@ -55,11 +64,9 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
|||||||
@Override
|
@Override
|
||||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||||
if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {
|
if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {
|
||||||
// Write an HTTP/2 response to the upgrade request
|
HttpServerUpgradeHandler.UpgradeEvent upgradeEvent =
|
||||||
Http2Headers headers =
|
(HttpServerUpgradeHandler.UpgradeEvent) evt;
|
||||||
new DefaultHttp2Headers().status(OK.codeAsText())
|
onHeadersRead(ctx, 1, http1HeadersToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true);
|
||||||
.set(new AsciiString(UPGRADE_RESPONSE_HEADER), new AsciiString("true"));
|
|
||||||
encoder().writeHeaders(ctx, 1, headers, 0, true, ctx.newPromise());
|
|
||||||
}
|
}
|
||||||
super.userEventTriggered(ctx, evt);
|
super.userEventTriggered(ctx, evt);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import io.netty.handler.codec.http.HttpServerCodec;
|
|||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
|
||||||
|
import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler;
|
||||||
import io.netty.handler.codec.http2.Http2CodecUtil;
|
import io.netty.handler.codec.http2.Http2CodecUtil;
|
||||||
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
|
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
@ -88,9 +89,12 @@ public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
|
|||||||
private void configureClearText(SocketChannel ch) {
|
private void configureClearText(SocketChannel ch) {
|
||||||
final ChannelPipeline p = ch.pipeline();
|
final ChannelPipeline p = ch.pipeline();
|
||||||
final HttpServerCodec sourceCodec = new HttpServerCodec();
|
final HttpServerCodec sourceCodec = new HttpServerCodec();
|
||||||
|
final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory);
|
||||||
|
final CleartextHttp2ServerUpgradeHandler cleartextHttp2ServerUpgradeHandler =
|
||||||
|
new CleartextHttp2ServerUpgradeHandler(sourceCodec, upgradeHandler,
|
||||||
|
new HelloWorldHttp2HandlerBuilder().build());
|
||||||
|
|
||||||
p.addLast(sourceCodec);
|
p.addLast(cleartextHttp2ServerUpgradeHandler);
|
||||||
p.addLast(new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory));
|
|
||||||
p.addLast(new SimpleChannelInboundHandler<HttpMessage>() {
|
p.addLast(new SimpleChannelInboundHandler<HttpMessage>() {
|
||||||
@Override
|
@Override
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
protected void channelRead0(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
||||||
|
Loading…
Reference in New Issue
Block a user