HTTP/2 Server Example No Response for HTTP/1.x Only Clients

Motiviation:
The HTTP/2 server example just hangs when a client is using only HTTP with no ALPN or upgrade attempts. We should still send some kind of response.

Modifications:
The HTTP/2 server example has a special handler to detect no upgrade HTTP clients and generate a response.

Result:
Clients that just use HTTP with no upgrade will no appear hung when interacting with the HTTP/2 server example.
This commit is contained in:
Scott Mitchell 2015-05-08 12:38:58 -07:00
parent 9bf636076a
commit 04c0d77287
4 changed files with 26 additions and 3 deletions

View File

@ -15,7 +15,9 @@
*/ */
package io.netty.example.http2.server; package io.netty.example.http2.server;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@ -31,11 +33,15 @@ import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/** /**
* HTTP handler that responds with a "Hello World" * HTTP handler that responds with a "Hello World"
*/ */
public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler<HttpRequest> { public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler<HttpRequest> {
private final String establishApproach;
public HelloWorldHttp1Handler(String establishApproach) {
this.establishApproach = checkNotNull(establishApproach, "establishApproach");
}
@Override @Override
public void channelRead0(ChannelHandlerContext ctx, HttpRequest req) throws Exception { public void channelRead0(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
@ -46,6 +52,7 @@ public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler<HttpRequ
ByteBuf content = ctx.alloc().buffer(); ByteBuf content = ctx.alloc().buffer();
content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate()); content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate());
ByteBufUtil.writeAscii(content, " - via " + req.protocolVersion() + " (" + establishApproach + ")");
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");

View File

@ -21,6 +21,7 @@ import static io.netty.example.http2.Http2ExampleUtil.UPGRADE_RESPONSE_HEADER;
import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.logging.LogLevel.INFO; import static io.netty.handler.logging.LogLevel.INFO;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.DefaultHttp2Connection;
@ -112,7 +113,10 @@ public class HelloWorldHttp2Handler extends Http2ConnectionHandler {
Http2Headers headers, int streamDependency, short weight, Http2Headers headers, int streamDependency, short weight,
boolean exclusive, int padding, boolean endStream) throws Http2Exception { boolean exclusive, int padding, boolean endStream) throws Http2Exception {
if (endStream) { if (endStream) {
sendResponse(ctx, streamId, RESPONSE_BYTES.duplicate()); ByteBuf content = ctx.alloc().buffer();
content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate());
ByteBufUtil.writeAscii(content, " - via HTTP/2");
sendResponse(ctx, streamId, content);
} }
} }

View File

@ -48,7 +48,7 @@ public class Http2OrHttpHandler extends Http2OrHttpChooser {
@Override @Override
protected ChannelHandler createHttp1RequestHandler() { protected ChannelHandler createHttp1RequestHandler() {
return new HelloWorldHttp1Handler(); return new HelloWorldHttp1Handler("ALPN Negotiation");
} }
@Override @Override

View File

@ -19,7 +19,9 @@ package io.netty.example.http2.server;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpServerCodec; 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.http2.Http2ServerUpgradeCodec; import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
@ -66,6 +68,16 @@ public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
ch.pipeline().addLast(sourceCodec); ch.pipeline().addLast(sourceCodec);
ch.pipeline().addLast(upgradeHandler); ch.pipeline().addLast(upgradeHandler);
ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpMessage>() {
@Override
protected void messageReceived(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
// If this handler is hit then no upgrade has been attempted and the client is just talking HTTP.
System.err.println("Directly talking: " + msg.protocolVersion() + " (no upgrade was attempted)");
ctx.pipeline().replace(this, "http-hello-world",
new HelloWorldHttp1Handler("Direct. No Upgrade Attempted."));
ctx.fireChannelRead(msg);
}
});
ch.pipeline().addLast(new UserEventLogger()); ch.pipeline().addLast(new UserEventLogger());
} }