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 833b92a5aa
commit 37e6fd0599
4 changed files with 26 additions and 3 deletions

View File

@ -15,7 +15,9 @@
*/
package io.netty.example.http2.server;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@ -24,7 +26,6 @@ import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderUtil;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
@ -33,6 +34,11 @@ import static io.netty.handler.codec.http.HttpVersion.*;
* HTTP handler that responds with a "Hello World"
*/
public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler<HttpRequest> {
private final String establishApproach;
public HelloWorldHttp1Handler(String establishApproach) {
this.establishApproach = checkNotNull(establishApproach, "establishApproach");
}
@Override
public void messageReceived(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
@ -43,6 +49,7 @@ public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler<HttpRequ
ByteBuf content = ctx.alloc().buffer();
content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate());
ByteBufUtil.writeAscii(content, " - via " + req.protocolVersion() + " (" + establishApproach + ")");
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
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.logging.LogLevel.INFO;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
@ -112,7 +113,10 @@ public class HelloWorldHttp2Handler extends Http2ConnectionHandler {
Http2Headers headers, int streamDependency, short weight,
boolean exclusive, int padding, boolean endStream) throws Http2Exception {
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
protected ChannelHandler createHttp1RequestHandler() {
return new HelloWorldHttp1Handler();
return new HelloWorldHttp1Handler("ALPN Negotiation");
}
@Override

View File

@ -19,7 +19,9 @@ package io.netty.example.http2.server;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
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.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
@ -66,6 +68,16 @@ public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
ch.pipeline().addLast(sourceCodec);
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());
}