Skip to content

Commit dbd82e0

Browse files
committed
Let Http2ServerUpgradeCodec support Http2FrameCodec
Motivation: Http2ServerUpgradeCodec should support Http2FrameCodec. Modifications: - Add support for Http2FrameCodec - Add example that uses Http2FrameCodec Result: More flexible use of Http2ServerUpgradeCodec
1 parent 0afe4e0 commit dbd82e0

File tree

6 files changed

+396
-1
lines changed

6 files changed

+396
-1
lines changed

codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int
363363
dataFrame.streamId(streamId);
364364
ctx.fireChannelRead(dataFrame);
365365

366-
// We return the bytes in bytesConsumed() once the stream channel consumed the bytes.
366+
// We return the bytes in consumeBytes() once the stream channel consumed the bytes.
367367
return 0;
368368
}
369369
}

codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.netty.buffer.ByteBuf;
1818
import io.netty.buffer.ByteBufUtil;
1919
import io.netty.channel.ChannelHandler;
20+
import io.netty.channel.ChannelHandlerAdapter;
2021
import io.netty.channel.ChannelHandlerContext;
2122
import io.netty.handler.codec.base64.Base64;
2223
import io.netty.handler.codec.http.FullHttpRequest;
@@ -95,6 +96,25 @@ public Http2ServerUpgradeCodec(String handlerName, Http2Codec http2Codec) {
9596
this(handlerName, http2Codec.frameCodec().connectionHandler(), http2Codec);
9697
}
9798

99+
/**
100+
* Creates the codec using a default name for the connection handler when adding to the
101+
* pipeline.
102+
*
103+
* @param http2Codec the HTTP/2 frame handler.
104+
* @param handlers the handlers that will handle the {@link Http2Frame}s.
105+
*/
106+
public Http2ServerUpgradeCodec(final Http2FrameCodec http2Codec, final ChannelHandler... handlers) {
107+
this(null, http2Codec.connectionHandler(), new ChannelHandlerAdapter() {
108+
109+
@Override
110+
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
111+
ctx.pipeline().addLast(http2Codec);
112+
ctx.pipeline().addLast(handlers);
113+
ctx.pipeline().remove(this);
114+
}
115+
});
116+
}
117+
98118
Http2ServerUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler,
99119
ChannelHandler upgradeToHandler) {
100120
this.handlerName = handlerName;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2016 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5+
* "License"); you may not use this file except in compliance with the License. You may obtain a
6+
* copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under the License
11+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12+
* or implied. See the License for the specific language governing permissions and limitations under
13+
* the License.
14+
*/
15+
16+
package io.netty.example.http2.helloworld.frame.server;
17+
18+
import io.netty.buffer.ByteBuf;
19+
import io.netty.buffer.ByteBufUtil;
20+
import io.netty.channel.ChannelDuplexHandler;
21+
import io.netty.channel.ChannelHandler.Sharable;
22+
import io.netty.channel.ChannelHandlerContext;
23+
import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
24+
import io.netty.handler.codec.http2.DefaultHttp2Headers;
25+
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
26+
import io.netty.handler.codec.http2.DefaultHttp2WindowUpdateFrame;
27+
import io.netty.handler.codec.http2.Http2DataFrame;
28+
import io.netty.handler.codec.http2.Http2Headers;
29+
import io.netty.handler.codec.http2.Http2HeadersFrame;
30+
import io.netty.util.CharsetUtil;
31+
32+
import static io.netty.buffer.Unpooled.copiedBuffer;
33+
import static io.netty.buffer.Unpooled.unreleasableBuffer;
34+
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
35+
36+
/**
37+
* A simple handler that responds with the message "Hello World!".
38+
*
39+
* <p>This example is making use of the "frame codec" http2 API. This API is very experimental and incomplete.
40+
*/
41+
@Sharable
42+
public class HelloWorldHttp2Handler extends ChannelDuplexHandler {
43+
44+
static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8));
45+
46+
@Override
47+
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
48+
super.exceptionCaught(ctx, cause);
49+
cause.printStackTrace();
50+
ctx.close();
51+
}
52+
53+
@Override
54+
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
55+
if (msg instanceof Http2HeadersFrame) {
56+
onHeadersRead(ctx, (Http2HeadersFrame) msg);
57+
} else if (msg instanceof Http2DataFrame) {
58+
onDataRead(ctx, (Http2DataFrame) msg);
59+
} else {
60+
super.channelRead(ctx, msg);
61+
}
62+
}
63+
64+
/**
65+
* If receive a frame with end-of-stream set, send a pre-canned response.
66+
*/
67+
public void onDataRead(ChannelHandlerContext ctx, Http2DataFrame data) throws Exception {
68+
int consumed = data.padding() + data.content().readableBytes();
69+
int streamId = data.streamId();
70+
71+
if (data.isEndStream()) {
72+
sendResponse(ctx, streamId, data.content());
73+
} else {
74+
// We do not send back the response to the remote-peer, so we need to release it.
75+
data.release();
76+
}
77+
78+
// Update the flowcontroller
79+
ctx.write(new DefaultHttp2WindowUpdateFrame(consumed).streamId(streamId));
80+
}
81+
82+
/**
83+
* If receive a frame with end-of-stream set, send a pre-canned response.
84+
*/
85+
public void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame headers)
86+
throws Exception {
87+
if (headers.isEndStream()) {
88+
ByteBuf content = ctx.alloc().buffer();
89+
content.writeBytes(RESPONSE_BYTES.duplicate());
90+
ByteBufUtil.writeAscii(content, " - via HTTP/2");
91+
sendResponse(ctx, headers.streamId(), content);
92+
}
93+
}
94+
95+
/**
96+
* Sends a "Hello World" DATA frame to the client.
97+
*/
98+
private static void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
99+
// Send a frame for the response status
100+
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
101+
ctx.write(new DefaultHttp2HeadersFrame(headers).streamId(streamId));
102+
ctx.writeAndFlush(new DefaultHttp2DataFrame(payload, true).streamId(streamId));
103+
}
104+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2016 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5+
* "License"); you may not use this file except in compliance with the License. You may obtain a
6+
* copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under the License
11+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12+
* or implied. See the License for the specific language governing permissions and limitations under
13+
* the License.
14+
*/
15+
package io.netty.example.http2.helloworld.frame.server;
16+
17+
import io.netty.channel.ChannelHandlerContext;
18+
import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler;
19+
import io.netty.handler.codec.http.HttpObjectAggregator;
20+
import io.netty.handler.codec.http.HttpServerCodec;
21+
import io.netty.handler.codec.http2.Http2FrameCodec;
22+
import io.netty.handler.ssl.ApplicationProtocolNames;
23+
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
24+
25+
/**
26+
* Negotiates with the browser if HTTP2 or HTTP is going to be used. Once decided, the Netty
27+
* pipeline is setup with the correct handlers for the selected protocol.
28+
*/
29+
public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
30+
31+
private static final int MAX_CONTENT_LENGTH = 1024 * 100;
32+
33+
protected Http2OrHttpHandler() {
34+
super(ApplicationProtocolNames.HTTP_1_1);
35+
}
36+
37+
@Override
38+
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
39+
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
40+
ctx.pipeline().addLast(new Http2FrameCodec(true), new HelloWorldHttp2Handler());
41+
return;
42+
}
43+
44+
if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
45+
ctx.pipeline().addLast(new HttpServerCodec(),
46+
new HttpObjectAggregator(MAX_CONTENT_LENGTH),
47+
new HelloWorldHttp1Handler("ALPN Negotiation"));
48+
return;
49+
}
50+
51+
throw new IllegalStateException("unknown protocol: " + protocol);
52+
}
53+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2016 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.netty.example.http2.helloworld.frame.server;
18+
19+
import io.netty.bootstrap.ServerBootstrap;
20+
import io.netty.channel.Channel;
21+
import io.netty.channel.ChannelOption;
22+
import io.netty.channel.EventLoopGroup;
23+
import io.netty.channel.nio.NioEventLoopGroup;
24+
import io.netty.channel.socket.nio.NioServerSocketChannel;
25+
import io.netty.handler.codec.http2.Http2SecurityUtil;
26+
import io.netty.handler.logging.LogLevel;
27+
import io.netty.handler.logging.LoggingHandler;
28+
import io.netty.handler.ssl.ApplicationProtocolConfig;
29+
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
30+
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
31+
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
32+
import io.netty.handler.ssl.ApplicationProtocolNames;
33+
import io.netty.handler.ssl.OpenSsl;
34+
import io.netty.handler.ssl.SslContext;
35+
import io.netty.handler.ssl.SslContextBuilder;
36+
import io.netty.handler.ssl.SslProvider;
37+
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
38+
import io.netty.handler.ssl.util.SelfSignedCertificate;
39+
40+
/**
41+
* A HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the
42+
* server with the example client.
43+
*
44+
* <p>This example is making use of the "multiplexing" http2 API, where streams are mapped to child
45+
* Channels. This API is very experimental and incomplete.
46+
*/
47+
public final class Http2Server {
48+
49+
static final boolean SSL = System.getProperty("ssl") != null;
50+
51+
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));
52+
53+
public static void main(String[] args) throws Exception {
54+
// Configure SSL.
55+
final SslContext sslCtx;
56+
if (SSL) {
57+
SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
58+
SelfSignedCertificate ssc = new SelfSignedCertificate();
59+
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
60+
.sslProvider(provider)
61+
/* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification.
62+
* Please refer to the HTTP/2 specification for cipher requirements. */
63+
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
64+
.applicationProtocolConfig(new ApplicationProtocolConfig(
65+
Protocol.ALPN,
66+
// NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
67+
SelectorFailureBehavior.NO_ADVERTISE,
68+
// ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
69+
SelectedListenerFailureBehavior.ACCEPT,
70+
ApplicationProtocolNames.HTTP_2,
71+
ApplicationProtocolNames.HTTP_1_1))
72+
.build();
73+
} else {
74+
sslCtx = null;
75+
}
76+
// Configure the server.
77+
EventLoopGroup group = new NioEventLoopGroup();
78+
try {
79+
ServerBootstrap b = new ServerBootstrap();
80+
b.option(ChannelOption.SO_BACKLOG, 1024);
81+
b.group(group)
82+
.channel(NioServerSocketChannel.class)
83+
.handler(new LoggingHandler(LogLevel.INFO))
84+
.childHandler(new Http2ServerInitializer(sslCtx));
85+
86+
Channel ch = b.bind(PORT).sync().channel();
87+
88+
System.err.println("Open your HTTP/2-enabled web browser and navigate to " +
89+
(SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
90+
91+
ch.closeFuture().sync();
92+
} finally {
93+
group.shutdownGracefully();
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)