概述
- HttpRequestDecoder 即把 ByteBuf 解码到 HttpRequest 和 HttpContent。
- HttpResponseEncoder 即把 HttpResponse 或 HttpContent 编码到 ByteBuf。
- HttpServerCodec 即 HttpRequestDecoder 和 HttpResponseEncoder 的结合。
本文想要回答的问题是:
在netty搭建http server时
- HttpRequest和HttpContent是如何透传给用户自定义的ChannelInBoundHandler
- ByteBuf是怎么解析成HttpRequest和多个HttpContent(最后一个为LastHttpContent)
例子
public class HttpServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
//pipeline.addLast(new HttpServerCodec());相当于
//pipeline.addLast(new HttpReuqestDecoder());
//pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpServerHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
HttpServerHandler类
class HttpServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
if (msg instanceof HttpRequest) {
// 请求,解码器将请求转换成HttpRequest对象
HttpRequest request = (HttpRequest) msg;
// 获取请求参数
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
String name = "netty";
if(queryStringDecoder.parameters().get("name") != null) {
name = queryStringDecoder.parameters().get("name").get(0);
}
// 响应HTML
String responseHtml = "<html><body>Hello, " + name + "</body></html>";
byte[] responseBytes = responseHtml.getBytes("UTF-8");
int contentLength = responseBytes.length;
// 构造FullHttpResponse对象,FullHttpResponse包含message body
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseBytes));
response.headers().set("Content-Type", "text/html; charset=utf-8");
response.headers().set("Content-Length", Integer.toString(contentLength));
ctx.writeAndFlush(response);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
原始的ByteBuf解析
io.netty.handler.codec.ByteToMessageDecoder#channelRead
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
CodecOutputList out = CodecOutputList.newInstance();
try {
ByteBuf data = (ByteBuf) msg;
first = cumulation == null;
if (first) {
cumulation = data;
} else {
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
}
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
throw e;
} catch (Exception e) {
throw new DecoderException(e);
} finally {
if (cumulation != null && !cumulation.isReadable()) {
numReads = 0;
cumulation.release();
cumulation = null;
} else if (++ numReads >= discardAfterReads) {
// We did enough reads already try to discard some bytes so we not risk to see a OOME.
// See https://github.com/netty/netty/issues/4275
numReads = 0;
discardSomeReadBytes();
}
int size = out.size();
decodeWasNull = !out.insertSinceRecycled();
fireChannelRead(ctx, out, size);
out.recycle();
}
} else {
ctx.fireChannelRead(msg);
}
}
解析
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try {
//此处通过while循环将ByteBuf分成多次解析
//才有了后面的解析成一个HttpRequest和多个HttpContent
while (in.isReadable()) {
int outSize = out.size();
if (outSize > 0) {
fireChannelRead(ctx, out, outSize);
out.clear();
// Check if this handler was removed before continuing with decoding.
// If it was removed, it is not safe to continue to operate on the buffer.
//
// See:
// - https://github.com/netty/netty/issues/4635
if (ctx.isRemoved()) {
break;
}
outSize = 0;
}
int oldInputLength = in.readableBytes();
decodeRemovalReentryProtection(ctx, in, out);
// Check if this handler was removed before continuing the loop.
// If it was removed, it is not safe to continue to operate on the buffer.
//
// See https://github.com/netty/netty/issues/1664
if (ctx.isRemoved()) {
break;
}
if (outSize == out.size()) {
if (oldInputLength == in.readableBytes()) {
break;
} else {
continue;
}
}
if (oldInputLength == in.readableBytes()) {
throw new DecoderException(
StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
}
if (isSingleDecode()) {
break;
}
}
} catch (DecoderException e) {
throw e;
} catch (Exception cause) {
throw new DecoderException(cause);
}
}
解析
final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
throws Exception {
decodeState = STATE_CALLING_CHILD_DECODE;
try {
decode(ctx, in, out);
} finally {
boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
decodeState = STATE_INIT;
if (removePending) {
handlerRemoved(ctx);
}
}
}
io.netty.handler.codec.http.HttpObjectDecoder#decode
将ByteBuf解析成HttpRequest和HttpContent
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
if (this.resetRequested) {
this.resetNow();
}
int toRead;
int toRead;
AppendableCharSequence line;
switch(this.currentState) {
case SKIP_CONTROL_CHARS:
if (!skipControlCharacters(buffer)) {
return;
}
this.currentState = HttpObjectDecoder.State.READ_INITIAL;
case READ_INITIAL:
try {
//解析http的initialLine
//initialLine example:GET /favicon.ico HTTP/1.1\r\n
line = this.lineParser.parse(buffer);
if (line == null) {
return;
}
//example: ["GET", "/favicon.ico", "/favicon.ico"]
String[] initialLine = splitInitialLine(line);
if (initialLine.length < 3) {
this.currentState = HttpObjectDecoder.State.SKIP_CONTROL_CHARS;
return;
}
//createMessage是抽象方法,此处用了模板方法模式,整体流程由父类实现,细节由子类实现
//如果由子类HttpRequestDecoder实现,则创建的是DefaultHttpRequest
//后面这个HttpRequest才可以透传给用户自定义的ChannelInboundHandler
this.message = this.createMessage(initialLine);
this.currentState = HttpObjectDecoder.State.READ_HEADER;
} catch (Exception var9) {
out.add(this.invalidMessage(buffer, var9));
return;
}
case READ_HEADER:
try {
//此处解析http的头部参数
HttpObjectDecoder.State nextState = this.readHeaders(buffer);
if (nextState == null) {
return;
}
this.currentState = nextState;
switch(nextState) {
case SKIP_CONTROL_CHARS:
out.add(this.message);
out.add(LastHttpContent.EMPTY_LAST_CONTENT);
this.resetNow();
return;
case READ_CHUNK_SIZE:
if (!this.chunkedSupported) {
throw new IllegalArgumentException("Chunked messages not supported");
}
out.add(this.message);
return;
default:
long contentLength = this.contentLength();
if (contentLength != 0L && (contentLength != -1L || !this.isDecodingRequest())) {
assert nextState == HttpObjectDecoder.State.READ_FIXED_LENGTH_CONTENT || nextState == HttpObjectDecoder.State.READ_VARIABLE_LENGTH_CONTENT;
out.add(this.message);
if (nextState == HttpObjectDecoder.State.READ_FIXED_LENGTH_CONTENT) {
this.chunkSize = contentLength;
}
return;
}
out.add(this.message);
out.add(LastHttpContent.EMPTY_LAST_CONTENT);
this.resetNow();
return;
}
} catch (Exception var10) {
out.add(this.invalidMessage(buffer, var10));
return;
}
case READ_CHUNK_SIZE:
try {
line = this.lineParser.parse(buffer);
if (line == null) {
return;
}
toRead = getChunkSize(line.toString());
this.chunkSize = (long)toRead;
if (toRead == 0) {
this.currentState = HttpObjectDecoder.State.READ_CHUNK_FOOTER;
return;
}
this.currentState = HttpObjectDecoder.State.READ_CHUNKED_CONTENT;
} catch (Exception var8) {
out.add(this.invalidChunk(buffer, var8));
return;
}
case READ_CHUNKED_CONTENT:
assert this.chunkSize <= 2147483647L;
toRead = Math.min((int)this.chunkSize, this.maxChunkSize);
toRead = Math.min(toRead, buffer.readableBytes());
if (toRead == 0) {
return;
}
HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead));
this.chunkSize -= (long)toRead;
out.add(chunk);
if (this.chunkSize != 0L) {
return;
}
this.currentState = HttpObjectDecoder.State.READ_CHUNK_DELIMITER;
case READ_CHUNK_DELIMITER:
toRead = buffer.writerIndex();
toRead = buffer.readerIndex();
while(toRead > toRead) {
byte next = buffer.getByte(toRead++);
if (next == 10) {
this.currentState = HttpObjectDecoder.State.READ_CHUNK_SIZE;
break;
}
}
buffer.readerIndex(toRead);
return;
case READ_VARIABLE_LENGTH_CONTENT:
toRead = Math.min(buffer.readableBytes(), this.maxChunkSize);
if (toRead > 0) {
ByteBuf content = buffer.readRetainedSlice(toRead);
out.add(new DefaultHttpContent(content));
}
return;
case READ_FIXED_LENGTH_CONTENT:
//此处解析http的body
toRead = buffer.readableBytes();
if (toRead == 0) {
return;
}
toRead = Math.min(toRead, this.maxChunkSize);
if ((long)toRead > this.chunkSize) {
toRead = (int)this.chunkSize;
}
ByteBuf content = buffer.readRetainedSlice(toRead);
this.chunkSize -= (long)toRead;
if (this.chunkSize == 0L) {
//创建HttpContent
out.add(new DefaultLastHttpContent(content, this.validateHeaders));
this.resetNow();
} else {
out.add(new DefaultHttpContent(content));
}
return;
case READ_CHUNK_FOOTER:
try {
LastHttpContent trailer = this.readTrailingHeaders(buffer);
if (trailer == null) {
return;
}
out.add(trailer);
this.resetNow();
return;
} catch (Exception var7) {
out.add(this.invalidChunk(buffer, var7));
return;
}
case BAD_MESSAGE:
buffer.skipBytes(buffer.readableBytes());
break;
case UPGRADED:
toRead = buffer.readableBytes();
if (toRead > 0) {
out.add(buffer.readBytes(toRead));
}
}
}