InternalInputBuffer处理HTTP请求行-Tomcat源码-我们到底能走多远系列(11)

我们到底能走多远系列(11)

扯淡:

  最近行情不好吗?跳槽的比较少嘛,哈哈。有些人一直抗拒跳槽,觉得弊端很多:什么业务积累,职务,别人觉得你不可靠啊等等。我就想:这一辈子时间有限,何必为了一颗可以乘凉的树,放弃穿过森林的机会呢?祝在跳槽路上的朋友 顺利!(ps:个人喜欢面试 那种刺激感)

  最爽不顾躺着,最美不过夕阳。秋天的夕阳是一年中最华丽的,各位不要错过哦。

主题:

  在tomcat中,一个http请求,会被送到Http11Processor类,执行这个类的process(Socket theSocket) 处理的传入的Socket,Socket里面装的就是http消息。

  tomcat是如何调用到Http11Processor的process方法的,可以参照:http://blog.csdn.net/woorh/article/details/8017323

  Http11Processor在org.apache.coyote.http11包下。

  Http11Processor的rocess方法中,用inputBuffer.parseRequestLine();调用了解析http消息的请求行。这里的inputBuffer是tomcat自定义的InternalInputBuffer。

  需要了解的是:

  1,org.apache.coyote.Request 是tomcat内部使用用于存放关于request消息的数据结构

  2,org.apache.tomcat.util.buf.MessageBytes 用于存放消息,在org.apache.coyote.Request中大量用于存放解析后的byte字符

  3,org.apache.tomcat.util.buf.ByteChunk 真正用于存放数据的数据结构,存放的是byte[],org.apache.tomcat.util.buf.MessageBytes使用它。

  大流程:

  http消息通过inputBuffer解析后放到Request中,Request把它放到相应的MessageBytes,最后MessageBytes把它存到ByteChunk里。

  以上都可以通过方法调用来完成流程。

  主要关注的是解析的源代码,在查看源代码前需要了解http请求行的结构:可以参照:http://www.cnblogs.com/killbug/archive/2012/10/10/2719142.html

  阅读前准备

  1,方法中全部的异常时,会调用getString方法,其实就是StringManager的写日志方法,这是tomcat中统一的管理日志的方法。详细的解释在前一篇中已经几时过了:Tomcat StringManager阅读学习

  2,

转义字符 意义 ASCII码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\\ 代表一个反斜线字符''\' 092

源码:

  

public void parseRequestLine()
        throws IOException {

        int start = 0;

        //
        // Skipping blank lines
        // 忽略空行
        //

        byte chr = 0;
        do {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos++];

        } while ((chr == Constants.CR) || (chr == Constants.LF));

        pos--;

        // Mark the current buffer position
        start = pos;

        //
        // Reading the method name
        // Method name is always US-ASCII
        //
        // space类似于开关一样,当为false时,查内容,为true时,去除空行时间
        boolean space = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says no CR or LF in method name
            if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
                throw new IllegalArgumentException(
                        sm.getString("iib.invalidmethod"));
            }
            // Spec says single SP but it also says be tolerant of HT
            // 查出第一个空格,tab居然也是允许的
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;//跳出循环
                // 把下标记录下来,这里的method()得到一个Requast的MessageBytes:methodMB
                request.method().setBytes(buf, start, pos - start);
            }

            pos++;

        }
        
        // Spec says single SP but also says be tolerant of multiple and/or HT
        // 忽略空格后面的空格或者tab,因为是忽略的内容所以不需要什么start
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;// 忽略的方式就是继续移动下标
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos; // 出现start了,后面肯定是需要记录下标
        int end = 0;
        int questionPos = -1;

        //
        // Reading the URI
        // 上面是源码的注释,URI是什么?你懂的
        //

        boolean eol = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says single SP but it also says be tolerant of HT
            // 寻找第二个空格,第一个空格和第二个空格之间就是传说中的URI
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.CR) 
                       || (buf[pos] == Constants.LF)) {
                // HTTP/0.9 style request
                // 为了兼容HTTP/0.9格式
                eol = true;
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.QUESTION) // 遇到‘?’了
                       && (questionPos == -1)) {
                // 把问号的位置先记录下来
                questionPos = pos;
            }

            pos++;

        }
        // 把可能包含问号的URI的起始位和结束位记录下来
        request.unparsedURI().setBytes(buf, start, end - start);
        if (questionPos >= 0) {// 有问号的情况
            // 问号位置记录
            request.queryString().setBytes(buf, questionPos + 1, 
                                           end - questionPos - 1);
            // 把URI记录下来
            request.requestURI().setBytes(buf, start, questionPos - start);
        } else {
            request.requestURI().setBytes(buf, start, end - start);
        }

        // Spec says single SP but also says be tolerant of multiple and/or HT
        // 这段算是重复代码吧,就是忽略空格用和tab用的
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos;
        end = 0;

        //
        // Reading the protocol
        // Protocol is always US-ASCII
        //
        // eol标志位是为了标记是否是HTTP/0.9 style request 前面代码已经提到
        // 最后一样:protocol(HTTP/ 1.1或1.0)
        while (!eol) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            // 查出  /r/n(CRLF)
            if (buf[pos] == Constants.CR) {
                end = pos;
            } else if (buf[pos] == Constants.LF) {
                if (end == 0)
                    end = pos;
                eol = true;
            }

            pos++;

        }
        // 至此把head分成三部分,放到Request定义好的MessageBytes中去了
        if ((end - start) > 0) {
            request.protocol().setBytes(buf, start, end - start);
        } else {
            request.protocol().setString("");
        }

    }

总结习点:

  1,利用表示位来控制解析式每个while的功能,上面代码用的是space

  2,不用截断的方式存储需要的内容,而是记录开始和结束的下标。

 

 

让我们继续前行

 

----------------------------------------------------------------------

 

努力不一定成功,但不努力肯定不会成功。
共勉

 

posted on 2012-10-21 22:12  每当变幻时  阅读(4057)  评论(2编辑  收藏  举报

导航