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