Netty 源码中如何解析Http协议 - Go语言中文社区

Netty 源码中如何解析Http协议


翻阅Netty的源码会发现Netty4.1(Netty5已废弃)中加入了对Http报头的预处理Handler,可见Netty团队对使用Netty作为web服务器的通信框架持鼓励态度。

的确,主流的Tomcat由于其诞生远早于Netty(那时连Java.nio都没有推出),已经不太可能将底层的架构改为Netty。而Netty作为Java.nio最受瞩目的开源框架,早晚会有基于其的web服务器发行。

那么Netty官方提供的HttpHandler是如何工作的呢?在官方的Example中可以看到这样一段代码:


    package io.netty.example.http.file;
    public class HttpStaticFileServerInitializer extends ChannelInitializer<SocketChannel> {
    ......
    public void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc()));
        }
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(65536));
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpStaticFileServerHandler());
    }
}

这段代码负责初始化Tcp链接,对每个Tcp链接都会按以上的顺序往ChannelPipeline中添加Handler,并将这一Pipeline与Channel相绑定。
看一下除去SSLHandler外,其他公共的Handler是如何实现的。
首先加入ChannelPipeline的是HttpServerCodec(),从类的声明可见其继承了
CombinedChannelDuplexHandler<HttpRequestDecoder,HttpResponseEncode>
这个双向的Handler可以在msg的Inbound和Outbound时都起到处理数据的作用。数据Inbound时的业务逻辑由HttpRequestDecoder处理,
HttpRequestDecoder类图

HttpObjectDecoder中负责header的具体解析,从UML图上可以看出,HttpObjectDecoder继承了ByteToMessageDecoder,在官网上ByteToessageDecoder有这样一段解释:If a custom frame decoder is required , then one needs to be careful when implementing one with ByteToMessageDecoder.

LinearParser是HttpObjectDecoder的内部类负责字符串的解析,看到其继承了HeaderParser,循着HeaderParser看到其实现了ByteProcessor接口,这里的实现其实还是比较复杂,贴上HeaderParser的代码

    private static class HeaderParser implements ByteProcessor {
   .....
           public AppendableCharSequence parse(ByteBuf buffer) {
            final int oldSize = size;
            seq.reset();
            int i = buffer.forEachByte(this);
            if (i == -1) {
                size = oldSize;
                return null;
            }
            buffer.readerIndex(i + 1);
            return seq;
        }
        public void reset() {
            size = 0;
        }
        @Override
        public boolean process(byte value) throws Exception {
            char nextByte = (char) (value & 0xFF);
            if (nextByte == HttpConstants.CR) {
                return true;
            }
            if (nextByte == HttpConstants.LF) {
                return false;
            }
            if (++ size > maxLength) {
                throw newException(maxLength);
            }
            seq.append(nextByte);
            return true;
        }
   }

这里buffer.forEachBytes(this)是核心的代码,forEachBytes(this)可以接受实现了ByteProcessor的类。每次用bytebuf中取出一个byte传入process方法,这里就是通过this传入的HeaderParser类中的process方法。反复迭代直到process方法返回false为止,这里可以看到,遇到CR时虽然返回true但是不将其加入最后传出的字符串(seq),直到遇到LF表示迭代结束,将seq返回。

至此可以得到结论,每次headerparser以CR LF为关键字从header中提取出一行。

接下来是解析Http请求报文的具体流程,来到HttpObjectDecoder的decode部分,

protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {

        if (resetRequested) {

            resetNow();
        }
    switch (currentState) {
       case SKIP_CONTROL_CHARS: {}          
       case READ_INITIAL: {}
       case READ_HEADER: {}
       case READ_VARIABLE_LENGTH_CONTENT: {}
       case READ_FIXED_LENGTH_CONTENT: {}
       case READ_CHUNK_SIZE: {}
       case READ_CHUNKED_CONTENT:{}
       case READ_CHUNK_DELIMITER: {}
       case READ_CHUNK_FOOTER: {}
       case BAD_MESSAGE: {}
       case UPGRADED: {}
    }
}

由于篇幅的限制每个case中的具体逻辑都省略了,但是从case的名称还是可以看出每一部分具体负责什么。在这个类被初始化时,currentState会被初始化为SKIP_CONTROL_CHARS。每次decode函数都会接受由Lineparse传来的一行字符串,看到在解析头部时会有readinitial和readHeader为
部分这是因为Http的请求第一行通常为GET /sample.jsp HTTP/1.1之后的header格式为Accept-Language:zh-cn。可以看到两者格式不同,因此需要优不同的解析逻辑。之后对header中的contentlength与chunk部分进行解析。需要注意的是每一句case的最后都会有out.add(message),这句命令将解析完的message加入传到下个ChannelPipeline的数据结构中。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_21519863/article/details/68062844
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-04-18 14:30:43
  • 阅读 ( 1839 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢