首页 > 编程语言 >Tomcat源码分析使用NIO接收HTTP请求(四)----解析请求头

Tomcat源码分析使用NIO接收HTTP请求(四)----解析请求头

时间:2022-11-28 14:55:21浏览次数:42  
标签:HTTP 请求 HeaderParsePosition HEADER chr 源码 byteBuffer byte headerData

User-Agent: PostmanRuntime/7.28.4
Accept: text/html
Postman-Token: c125824d-ae13-4082-9ae0-87c1750476b8
Host: localhost:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

我们本章的任务是解析请求头。上面的请求头协议是本节使用的样例协议。这个样例协议是使用Postman生成的。我们整体的思路是一行一行循环的对协议进行解析。我们先来解析User-Agent: PostmanRuntime/7.28.4这一行协议,剩余行与其原理一样。关于这一行的解析思路与解析请求行一样。依然是使用MessageBytes类保存一个bytebuffer和起止位。与请求行所不同的是,在请求头中使用MimeHeaders对请求头进行了封装。在MimeHeaders类中包含一个MimeHeaderField内部类,该内部类包含两个字段,类型均为MessageBytes,这两个字段存储的内容是请求行中的name和value,以User-Agent: PostmanRuntime/7.28.4这一行为例,如果解析这一行那么name值为                 User-Agent,value值为PostmanRuntime/7.28.4,又因为请求行中包含多行协议所以一定会存在多个MimeHeaderField来保存请求行,所以在MimeHeaders类可以定义一个MimeHeaderField类型的数组用来存储MimeHeaderField。

第一步: 新建MimeHeaders类(虽然是两个类,但是是写在一个文件中的)

ublic class MimeHeaders {
    /**请求头默认大小*/
    public static final int DEFAULT_HEADER_SIZE=8;
    private MimeHeaderField[] headers = new MimeHeaderField[DEFAULT_HEADER_SIZE];

    /**当前字段*/
    private int count;

    /**字段限制*/
    private int limit = -1;

    public MessageBytes addValue(byte b[], int startN, int len) {
        MimeHeaderField mhf=createHeader();
        mhf.getName().setBytes(b, startN, len);
        return mhf.getValue();
    }

    private MimeHeaderField createHeader() {
        if (limit > -1 && count >= limit) {
            throw new RuntimeException();
        }
        MimeHeaderField mh;
        int len = headers.length;
        if (count >= len) {
            // 动态扩容
            int newLength = count * 2;
            if (limit > 0 && newLength > limit) {
                newLength = limit;
            }
            MimeHeaderField tmp[] = new MimeHeaderField[newLength];
            System.arraycopy(headers, 0, tmp, 0, len);
            headers = tmp;
        }
        if ((mh = headers[count]) == null) {
            headers[count] = mh = new MimeHeaderField();
        }
        count++;
        return mh;
    }
}
class MimeHeaderField {
    private final MessageBytes nameB = MessageBytes.newInstance();
    private final MessageBytes valueB = MessageBytes.newInstance();
    public MessageBytes getName() {return nameB;}
    public MessageBytes getValue() {return valueB;}
}

 第二步: 解析请求头。首先在Http11InputBuffer新键一个静态内部类和二个枚举类,用他们保存解析请求 头中所处于的临时状态。

private static class HeaderParseData {
    int lineStart = 0;
    int start = 0;
    int realPos = 0;
    int lastSignificantChar = 0;
    MessageBytes headerValue = null;
    public void recycle() {
        lineStart = 0;
        start = 0;
        realPos = 0;
        lastSignificantChar = 0;
        headerValue = null;
    }
}
private enum HeaderParsePosition {
    HEADER_START,
    HEADER_NAME,
    HEADER_VALUE_START,
    HEADER_VALUE,
    HEADER_MULTI_LINE,
    HEADER_SKIPLINE
}

private enum HeaderParseStatus {
    DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
}

第三步: 在Request类中添加MimeHeaders属性。在Http11InputBuffer类中添加HeaderParsePosition、MimeHeaders、HeaderParseData、parsingHeader属性。

 

第四步: 在Http11InputBuffer类中新建一个parseHeader方法

public HeaderParseStatus parseHeader() throws IOException {
    int chr = byteBuffer.position();
    int prevChr = chr;
    while (headerParsePos == HeaderParsePosition.HEADER_START) {
        if (byteBuffer.position() >= byteBuffer.limit()) {
            if (!fill()) {
                headerParsePos = HeaderParsePosition.HEADER_START;
                return HeaderParseStatus.NEED_MORE_DATA;
            }
        }
        prevChr = chr;
        chr = byteBuffer.get();

        if (chr == (byte) '\r' && prevChr != (byte) '\r') {
        } else if (prevChr == (byte) '\r' && chr == '\n') {
            return HeaderParseStatus.DONE;
        } else {
            if (prevChr == (byte) '\r') {
                byteBuffer.position(byteBuffer.position() - 2);
            } else {
                byteBuffer.position(byteBuffer.position() - 1);
            }
            break;
        }
    }
    if (headerParsePos == HeaderParsePosition.HEADER_START) {
        headerData.start = byteBuffer.position();
        headerData.lineStart = headerData.start;
        headerParsePos = HeaderParsePosition.HEADER_NAME;
    }
    while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
        if (byteBuffer.position() >= byteBuffer.limit()) {
            if (!fill()) {
                headerParsePos = HeaderParsePosition.HEADER_START;
                return HeaderParseStatus.NEED_MORE_DATA;
            }
        }
        int pos = byteBuffer.position();
        chr = byteBuffer.get();
        if (chr == (byte) ':') {
            headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
            headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start,
                    pos - headerData.start);
            pos = byteBuffer.position();
            headerData.start = pos;
            headerData.realPos = pos;
            headerData.lastSignificantChar = pos;
            break;
        }
        // 将大写字母转化为小写字母
        if ((chr >= (byte) 'A') && (chr <= (byte) 'Z')) {
            byteBuffer.put(pos, (byte) (chr - ((byte)'A' - (byte)'a')));
        }
    }
    while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START) {
        if (headerParsePos == HeaderParsePosition.HEADER_VALUE_START) {
            while (true) {
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (!fill()) {
                        return HeaderParseStatus.NEED_MORE_DATA;
                    }
                }

                chr = byteBuffer.get();
                if (!(chr == (byte) ' ' || chr == (byte) '\t')) {
                    headerParsePos = HeaderParsePosition.HEADER_VALUE;
                    byteBuffer.position(byteBuffer.position() - 1);
                    break;
                }
            }
        }

        if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {
            boolean eol = false;
            while (!eol) {
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (!fill()) {
                        return HeaderParseStatus.NEED_MORE_DATA;
                    }
                }
                prevChr = chr;
                chr = byteBuffer.get();
                if (prevChr == '\r' && chr == '\n') {
                    eol = true;
                } else if(chr == (byte) ' ' || chr == (byte) '\t') { // 清除空格
                    byteBuffer.put(headerData.realPos, (byte)chr);
                    headerData.realPos++;
                }else {
                    byteBuffer.put(headerData.realPos, (byte)chr);
                    headerData.realPos++;
                    headerData.lastSignificantChar = headerData.realPos;
                }
            }
            headerData.realPos = headerData.lastSignificantChar;
            headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
        }
        byte peek = byteBuffer.get(byteBuffer.position());
        if (headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
            if ((peek != (byte) ' ') && (peek != '\t')) {
                headerParsePos = HeaderParsePosition.HEADER_START;
                break;
            } else {
                byteBuffer.put(headerData.realPos, peek);
                headerData.realPos++;
                headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
            }
        }
    }
    headerData.headerValue.setBytes(byteBuffer.array(), headerData.start,
            headerData.lastSignificantChar - headerData.start);
    headerData.recycle();
    return HeaderParseStatus.HAVE_MORE_HEADERS;
}

第五步: 在新键一个parseHeaders方法

public boolean parseHeaders() throws IOException {
    HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;

    do {
        status = parseHeader();
    } while (status == HeaderParseStatus.HAVE_MORE_HEADERS);
    if (status == HeaderParseStatus.DONE) {
        parsingHeader = false;
        return true;
    } else {
        return false;
    }
}
第六步: 在Poller类中调用parseHeaders()方法.运行程序查看输出会发现一个小问题,User-Agent这个字段换行了。这个问题的原因是因为我们在解析请求行时并没有读取末尾的换符号。

 

 第七步: 解决第六步的问题。在第四步中我们定义了一个parseHeader方法,在这个方法中有一个prevChr局部变量,现在我们将它提升为全局变量。在定义一个全局变量chr。删除掉parseRequestLine方法中的chr局部变量,在修改其中的一些代码(下面图片中的代码位置)。删除parseHeader类中chr局部变量。运行结果如下图。


 

 

 

结束  !!!

标签:HTTP,请求,HeaderParsePosition,HEADER,chr,源码,byteBuffer,byte,headerData
From: https://www.cnblogs.com/yishi-san/p/16932180.html

相关文章

  • https://www.cnblogs.com/liyue3/p/16924616.html
    Android12源码网盘下载路径:“iTOP-3588开发板\01_【1TOP-RK3588开发板】基础资料\03_iTOP-RK3588开发板Android12源码”源码是分卷压缩包,需要全部下载下来放在同......
  • unftp源码分析
    unftp源码分析https://github.com/bolcom/unFTP //startstheFTPserverasaTokiotask.fnstart_ftp(log:&Logger,root_log:&Logger,m:&cl......
  • 直播app源码,HTML + jQuery 实现轮播图
    直播app源码,HTML+jQuery实现轮播图一:HTML页面部分1、首先创建可视窗口添加6张图片,添加CSS样式 2、添加左右切换按钮,设置样式 3、添加图片导航器,设置样式,添加悬......
  • 20221128 源码理解 spring-boot-starter-web【归档】
    版本信息<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>版本2.6.7目录Ser......
  • 20220628 HttpMessageConverter
    概述用于在HTTP请求和响应之间进行转换的策略接口。接口定义publicinterfaceHttpMessageConverter<T>{booleancanRead(Class<?>clazz,@NullableMediaTyp......
  • 20220620 DispatcherServlet 处理请求
    概述DispatcherServlet的继承关系DispatcherServlet初始化触发初始化时机处理第一个请求时需要初始化这里执行的是servlet.init方法,也就是Servlet初始化生命周......
  • springCloud将http请求网关更改为https请求
    如果需要将http请求换成https请求,大致需要三步。第一步:生成证书。网上有很多通过阿里云生成证书的教程。也有通过JDK生成证书。在jdk目录打开命令窗口,执行一下命令:keytoo......
  • easylogging++的那些事(四)源码分析(二)日志记录宏(二)条件日志宏
    目录CLOG_IF宏宏展开Info日志宏CLOG_IF(xxx,INFO,xxx)Trace日志宏CLOG_IF(xxx,TRACE,xxx)Debug日志宏CLOG_IF(xxx,DEBUG,xxx)Fatal日志宏CLOG_IF(xxx,FATA......
  • 让uniGUI支持https
    今天在专家的帮助下,成功的让uniGUI支持https了。首先,去申请个免费的证书。我同事去阿里申请的,申请回是一个zip文件,里面有两个文件,一个扩展是per,一个key然后,把这两个证书......
  • Http协议
    一http协议简介HTTP协议是HyperTextTransferProtocol(超文本传输协议)的缩写,是用于万维网(WWW:WorldWideWeb)服务器与本地浏览器之间传输超文本的传送协议。HTTP是......