Tomcat源码分析使用NIO接收HTTP请求(四)----解析请求头
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; } }
第七步: 解决第六步的问题。在第四步中我们定义了一个parseHeader方法,在这个方法中有一个prevChr局部变量,现在我们将它提升为全局变量。在定义一个全局变量chr。删除掉parseRequestLine方法中的chr局部变量,在修改其中的一些代码(下面图片中的代码位置)。删除parseHeader类中chr局部变量。运行结果如下图。
![]()
结束 !!!