使用 JavaScript 实现一个基于 FSM 的 http 请求头 parser
JavaScript
内容过时
这篇文章上次修改于 2020 年 2 月 17 日 星期一,部分内容可能已经不适用。
目标
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 05 Jan 2020 17:39:08 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.10
Content-Length: 6
123Abc
--> parse -->
{
"reqline": "HTTP/1.1 200 OK",
"headers": {
"server": "nginx",
"date": "Sun, 05 Jan 2020 17:39:08 GMT",
"content-type": "text/html; charset=UTF-8",
"connection": "close",
"vary": "Accept-Encoding",
"x-powered-by": "PHP/5.6.10",
"content-length": "6",
}
"body": "123Abc"
}
状态机
Image not found
http parser fsm
代码实现
var httpParser = {};
(function () {
function Stream (str) {
this.pos = 0;
this.str = str;
}
Stream.prototype.hasNext = function () {
return this.pos < this.str.length;
}
Stream.prototype.next = function () {
return this.str[this.pos++];
}
const STATE = {
BEGIN : 0,
LINE_ALMOST_DONE : 1,
LINE_DONE : 2,
KEY_DONE : 3,
HEADER_ALMOST_DONE : 4,
HEADER_DONE : 5,
END : 6
};
httpParser.state = STATE.BEGIN;
function updateState(st){
httpParser.state = st;
}
function currentState(){
return httpParser.state;
}
httpParser.parse = function (str) {
if (str.length < 4) return -1;
updateState(STATE.BEGIN);
var result = {};
result["headers"] = {};
var lastword = "";
var word = "";
var stream = new Stream(str);
while (stream.hasNext()) {
var ch = stream.next();
switch (currentState()) {
case STATE.BEGIN:
// they might be just sending \n instead of \r\n so this would be
// the second \n to denote the end of headers
if (ch == '\r' || ch == '\n') {
updateState(STATE.LINE_ALMOST_DONE);
result["reqline"] = word;
word = "";
break;
}
word += ch;
break;
case STATE.LINE_ALMOST_DONE:
if (ch == '\n') {
updateState(STATE.LINE_DONE);
}else{
return -1;
}
break;
case STATE.LINE_DONE:
if (ch == ':') {
updateState(STATE.KEY_DONE);
lastword = word;
word = "";
break;
}
if (ch == '\r' || ch == '\n') {
updateState(STATE.HEADER_ALMOST_DONE);
lastword = "";
word = "";
break;
}
word += ch;
break;
case STATE.KEY_DONE:
if (ch == '\r' || ch == '\n') {
updateState(STATE.LINE_ALMOST_DONE);
result["headers"][lastword.toLowerCase()] = word;
lastword = "";
word = "";
break;
}
if (word == "" && ch == ' ') break;
word += ch;
break;
case STATE.HEADER_ALMOST_DONE:
if (ch == '\n') {
updateState(STATE.HEADER_DONE);
}else{
return -1;
}
break;
//Need to optimize
case STATE.HEADER_DONE:
word += ch;
while (stream.hasNext()){
word += stream.next();
}
result["body"] = word;
word = "";
break;
default:
return -2;
}
}
return result;
}
})();