ragel 解析http 响应

/*************************************************************************
    > File Name: http_response_parser.h
    > Author: eular
    > Brief:
    > Created Time: Sun 06 Feb 2022 11:08:08 PM CST
 ************************************************************************/

#ifndef __HTTPD_HTTP_RESPONSE_H__
#define __HTTPD_HTTP_RESPONSE_H__

#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <error.h>

typedef void (*element_cb)(void *user_data, const char *at, size_t length);
typedef void (*field_cb)(void *user_data, const char *field, size_t flen, const char *value, size_t vlen);

typedef struct http_response_parser {
    const char *begin;      // 内容开始
    size_t      len;        // 内容长度
    int32_t     cs;         // 错误标志

    const char *field_begin;// 键开始
    size_t      field_len;  // 键长度
    const char *value_begin;// 值开始
    size_t      value_len;  // 值长度

    void *user_data;        // 用户数据

    element_cb http_version;    // http版本回调
    element_cb http_status;     // http状态码回调
    element_cb http_reason;     // http原因回调
    element_cb http_data;       // http响应数据回调
    field_cb   http_field;      // http响应的键值对回调

    const char *response_data;  // http响应数据起始位置
    size_t      response_len;   // http响应数据的长度
} http_response_parser;

void    http_response_parser_init(http_response_parser *parser, void *user_data);
void    http_response_parser_execute(http_response_parser *parser, const char *buffer, size_t len, size_t off);
bool    http_response_parser_finished(http_response_parser *parser);
bool    http_response_parser_has_error(http_response_parser *parser);

#endif // __HTTPD_HTTP_RESPONSE_H__

#include "http_response_parser.h"

#include <assert.h>

%%{
    machine http_response_parser;

    action mark {
        if (parser) {
            parser->begin = fpc;
        }
    }

    action done {
        printf("parser done\n");
        // fbreak会使变量p++;
    }

    action http_version {
        if (parser && parser->http_version) {
            parser->len = fpc - parser->begin;
            parser->http_version(parser->user_data, parser->begin, parser->len);
        }
        // temp.clear();
        // len = fpc - begin;
        // temp.append(begin, len);
        // cout << "http_version() [" << temp << "]" << endl;
    }

    action http_status {
        if (parser && parser->http_status) {
            parser->len = fpc - parser->begin;
            parser->http_status(parser->user_data, parser->begin, parser->len);
        }
    }

    action http_status_reason {
        if (parser && parser->http_reason) {
            parser->len = fpc - parser->begin;
            parser->http_reason(parser->user_data, parser->begin, parser->len);
        }
    }

    action write_field {
        if (parser) {
            parser->field_begin = parser->begin;
            parser->field_len = fpc - parser->begin;
        }
    }

    action write_value {
        if (parser) {
            parser->value_begin = parser->begin;
            parser->value_len = fpc - parser->begin;
        }
    }

    action write_field_value {
        if (parser && parser->http_field) {
            parser->http_field(parser->user_data, parser->field_begin,
                parser->field_len, parser->value_begin, parser->value_len);
        }
    }

    action http_set_data {
        if (parser) {
            parser->response_data = parser->begin;
            parser->response_len  = fpc - parser->begin;
            if (parser->http_data) {
                parser->http_data(parser->user_data, parser->response_data, parser->response_len);
            }
        }
    }

    CRLF = ("\r\n" | "\n");
    HTTP_CTL = (0 - 31) | 127 ;
    HTTP_separator = ( "(" | ")" | "<" | ">" | "@"
                     | "," | ";" | ":" | "\\" | "\""
                     | "/" | "[" | "]" | "?" | "="
                     | "{" | "}" | " " | "\t" ) ;
    lws = CRLF? (" " | "\t")+ ;

    # for Response_Line
    http_number = ( "1." ("0" | "1") ) ;
    HTTP_Version = ("HTTP/" http_number) >mark %http_version;
    http_status = (digit{3}) >mark %http_status;
    reason = (alpha | " " | "-");
    http_status_desc = reason* >mark %http_status_reason;

    Response_Line = (HTTP_Version " " http_status " " http_status_desc CRLF);

    # for message_header
    content = ((any -- HTTP_CTL) | lws);
    token = ascii -- ( HTTP_CTL | HTTP_separator ) ;
    field_name = ( token )+ >mark %write_field;
    field_value = content* >mark %write_value;

    message_header = field_name ":" lws* field_value :> CRLF %write_field_value;

    data_content = ascii -- (HTTP_CTL);
    http_data = data_content* >mark %http_set_data;

    main := Response_Line ( message_header )* ( CRLF ) (http_data) %done;
    write data;
}%%

void http_response_parser_init(http_response_parser *parser, void *user_data)
{
    int cs = 0;
    %% write init;
    if (parser) {
        parser->begin       = nullptr;
        parser->len         = 0;
        parser->user_data   = user_data;
        parser->cs          = cs;

        parser->http_version = nullptr;
        parser->http_status  = nullptr;
        parser->http_reason  = nullptr;
        parser->http_field   = nullptr;

        parser->response_data = nullptr;
        parser->response_len  = 0;
    }
}

void http_response_parser_execute(http_response_parser *parser, const char *buffer, size_t len, size_t off)
{
    if (parser == nullptr) {
        return;
    }
    
    if (buffer == nullptr) {
        parser->cs = -EINVAL;
    }

    const char *p, *pe, *eof;
    int cs = parser->cs;

    assert(off <= len && "offset past end of buffer");

    p   = buffer + off;
    pe  = buffer + len;
    eof = pe;

    assert(pe - p == (int)(len - off) && "pointers aren't same distance");
    %% write exec;
    assert(p <= pe && "Buffer overflow after parsing.");

    if (!http_response_parser_has_error(parser)) {
        parser->cs = cs;
    }
}

bool http_response_parser_finished(http_response_parser *parser)
{
    return parser->cs >= http_response_parser_first_final;
}

bool http_response_parser_has_error(http_response_parser *parser)
{
    return parser->cs == http_response_parser_error;
}
#include "http_response_parser.h"
#include <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

void on_http_version(void *data, const char *begin, size_t len)
{
    string temp;
    temp.append(begin, len);
    cout << __func__ << "() [" << temp << "]" << endl;
}

void on_http_status(void *data, const char *begin, size_t len)
{
    string temp;
    temp.append(begin, len);
    cout << __func__ << "() [" << temp << "]" << endl;
}

void on_http_reason(void *data, const char *begin, size_t len)
{
    string temp;
    temp.append(begin, len);
    cout << __func__ << "() [" << temp << "]" << endl;
}

void on_http_data(void *data, const char *begin, size_t len)
{
    string temp;
    temp.append(begin, len);
    cout << __func__ << "() [" << temp << "]" << endl;
}

void on_http_field(void *data, const char *filed, size_t flen, const char *value, size_t vlen)
{
    string temp;
    temp.append(filed, flen);
    temp.append(": ");
    temp.append(value, vlen);
    cout << __func__ << "() [" << temp << "]" << endl;
}

int main(int argc, char **argv)
{
	// 随便构造的数据
    const char *str = 
        "HTTP/1.0 207 Multi-Status\r\n"
        "Connection: keep-alive\r\n"
        "Content-Length: 5299\r\n"
        "Content-Type: text/html\r\n"
        "Server: eular/httpd v1.0\r\n\r\n"
        "{\"code\":2,\"message\":\"/home.html\"}\0";
    
    string temp = str;

    http_response_parser parser;
    http_response_parser_init(&parser, nullptr);
    parser.http_field = on_http_field;
    parser.http_status = on_http_status;
    parser.http_reason = on_http_reason;
    parser.http_version = on_http_version;
    parser.http_data = on_http_data;

    printf("strlen: %zu\n", strlen(str));

    http_response_parser_execute(&parser, temp.c_str(), temp.length(), 0);
    if (http_response_parser_has_error(&parser)) {
        printf("has error\n");
    }

    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值