Netty搭建http server

概述

  • HttpRequestDecoder 即把 ByteBuf 解码到 HttpRequest 和 HttpContent。
  • HttpResponseEncoder 即把 HttpResponse 或 HttpContent 编码到 ByteBuf。
  • HttpServerCodec 即 HttpRequestDecoder 和 HttpResponseEncoder 的结合。

本文想要回答的问题是:

在netty搭建http server时

  1. HttpRequest和HttpContent是如何透传给用户自定义的ChannelInBoundHandler
  2. 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));
            }
        }

    }

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值