使用有限状态自动机构造HTTP报文解析器

首先看一下HTTP请求报文的格式
这里写图片描述
不同于TCP或者IP报文的首部,由于HTTP的报文首部是文本形式的,我们就无法按字节来提取HTTP首部中的字段。所以要对HTTP请求报文做解析,目的是从文本中提取各字段的值。
从上图可以看出,HTTP请求报文由首部和正文两部分组成,他们由一个空行隔开,在这里我们仅对报文首部做解析。首部又有请求行和请求头两部分。
从图中可以看出报文的各字段都由一些特殊的字符分割,很容易可以联想到在编译原理中构造词法分析器时使用的有限状态自动机,将对于不同字段的识别过程设置为不同的状态,每当识别到分割字符时,就改变当前状态。这样将整个报文首部逐字符扫描一遍后,就可以解析出每个字段了。
状态间的转换过程很容易通过代码理解,这里就不给出相应的状态转换图了。下面给出程序代码,在这里仅对GET、POST和HEAD方法做了识别,其他方法则识别为UNKNOWN。

参考资料:
nginx-0.1.0源码
开源项目zaver https://github.com/zyearn/zaver/blob/master/src/http_parse.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define BUFFER_SIZE 4096

#define HTTP_INVALID_METHOD  1
#define HTTP_INVALID_REQUEST 2
#define HTTP_INVALID_HEAD    3

#define HTTP_UNKNOWN 1
#define HTTP_GET     2
#define HTTP_HEAD    3
#define HTTP_POST    4

#define CR '\r'
#define LF '\n'

//请求头结构
typedef struct http_request_header_s{
    void *key;
    void *key_end;
    void *value;
    void *value_end;
    struct http_request_header_s *next;
} http_request_header_t;

//请求结构
typedef struct {
    int fd;
    char buffer[BUFFER_SIZE];
    size_t check_index;
    size_t read_index;
    int state;

    //请求行
    void *request_start;
    void *request_end;
    int method;
    void *method_start;
    void *method_end;
    void *uri;
    void *uri_end;
    int major_digit;
    int minor_digit;

    //请求头部
    http_request_header_t *header_list; //头部字段链表
    void *cur_key;
    void *cur_key_end;
    void *cur_value;
    void *cur_value_end;
} http_request_t;

//向请求头链表添加新结点
void http_header_list_add(http_request_header_t **head, http_request_header_t *p)
{
    if(*head == NULL){
        *head = p;
        p->next = NULL;
        return;
    }
    http_request_header_t *t = (*head)->next;
    (*head)->next = p;
    p->next = t;
}

//解析请求行
int http_parse_request_line(http_request_t *request)
{
    enum {
        sw_start = 0,
        sw_method,
        sw_spaces_before_uri,
        sw_after_slash_in_uri,
        sw_http,
        sw_http_H,
        sw_http_HT,
        sw_http_HTT,
        sw_http_HTTP,
        sw_first_major_digit,
        sw_major_digit,
        sw_first_minor_digit,
        sw_minor_digit,
        sw_spaces_after_digit,
        sw_almost_done
    } state;

    state = request->state;

    char ch, *p;
    size_t pi;
    for(pi = request->check_index; pi < request->read_index; pi++){
        p = &request->buffer[pi];
        ch = *p;

        switch(state){
            case sw_start :{
                request->request_start = p;
                if(ch == CR || ch == LF)
                    break;
                if(ch == ' ')
                    break;
                if(ch < 'A' || ch > 'Z')
                    return HTTP_INVALID_REQUEST;

                request->method_start = p;
                state = sw_method;
                break;
            }

            case sw_method :{
                
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值