Featured image of post 使用 JavaScript 实现一个基于 FSM 的 http 请求头 parser

使用 JavaScript 实现一个基于 FSM 的 http 请求头 parser

为啥会有人搞这么奇怪的东西...

目标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 –>

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
    "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"
}

状态机

http parser fsm

代码实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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;
    }

})();
FrostMiKu
Built with Hugo
主题 StackJimmy 设计