1 Ragel Ragel是一个状态机编译器,类似Lex,主要是用来处理字符输入,用于语法解析。简单的文本处理工作一般用正则表达式,或者用awk/sed这些工具就可以处理了,之所以使用Ragel是为了当你的代码的核心任务是解析文本,而且需要高效地处理数据,比如一个SMTP引擎,HTTP引擎,那么Ragel可以按你定义好的语法,生成一个状态机嵌入到你的代码中。因为这个状态机是专门针对你预定义的语法,且以你的原生代码执行,效率自然比正则表达式,awk这些通用工具高的多(据说媲美汇编)。Ragel支持生成C/C++/Java/Ruby/D/C#等各种语言。 Ragel构造状态机是以字符匹配和正则表达式为基础的。从模型上说,有3中状态机构造模型:第一种时传统的Regular Expression,也就是正则表达式,第二种Scanner是扩展的模式,不知道怎么翻译,大家看看就行,最后一种也是扩展的模式,State Chart也就是状态图。这里主要用到的是第一种模式,基于正则表达式 不了解Rangel的可以查看CSDN的这篇文章   不了解有限状态机的可以查看CSDN的这篇文章 不了解正则表达式的可以查看这篇文章) Ubuntu下Rangel需要安装:sudo apt-get install ragel %%{ 表示Ragel代码块开始machine 定义一个状态机action mark {表示一个action动作,可以在指定位置解析完成调用该动作main := ("+" | “-” %save_symbol)? (digit %save_number)+ 定义正则表达式,main是个关键字,表示状态机入口write data 指示Ragel在代码的这个位置写入ragel运行需要的静态数据}%% 表示Ragel代码块结束   write init 指示Ragel在代码的这个位置写入Ragel运行需要的初始化代码变量p和pe表示状态机处理的buffer起始和终止地址,状态机运行时就从这个buffer依次读入字符,变量名很重要,必须为p和pe,因为Ragel生成的代码里面使用这两个变量名write exec 指示Ragel在代码的这个位置写入运行状态机的代码   本项目解析的时候复用了github的一个开源Web服务器项目Mongrel2中对于HTTP解析的处理,特别是里面的http11_common.h、http11_parser.h 、httpclient_parser.h、http11_parser.rl、httpclient_parsre.rl 2 约定优于配置 使用配置系统Config为HTTP请求/响应报文解析的一些参数做配置 //配置项 规定一个请求报文首部字段数据长度阈值默认4KB  来规避大数据发包攻击static sylar::ConfigVaruint64_t>::ptr g_http_request_buffer_size =        sylar::Config::Lookup("http.request.buffer_size", (uint64_t)(4 * 1024), "http request buffer size");//配置项 规定一个请求报文报文实体数据长度阈值默认64MBstatic sylar::ConfigVaruint64_t>::ptr g_http_request_max_body_size =        sylar::Config::Lookup("http.request.max_body_size", (uint64_t)(64 * 1024 * 1024), "http request max body size");//配置项 规定一个响应报文首部字段数据长度阈值默认4KB  来规避大数据发包攻击static sylar::ConfigVaruint64_t>::ptr g_http_response_buffer_size =        sylar::Config::Lookup("http.response.buffer_size", (uint64_t)(4 * 1024), "http response buffer size");//配置项 规定一个响应报文报文实体数据长度阈值默认64MBstatic sylar::ConfigVaruint64_t>::ptr g_http_response_max_body_size =        sylar::Config::Lookup("http.response.max_body_size", (uint64_t)(64 * 1024 * 1024), "http response max body size");static uint64_t s_http_request_buffer_size = 0;    //当前请求报文首部字段长度static uint64_t s_http_request_max_body_size = 0;  //当前请求报文实体数据长度static uint64_t s_http_response_buffer_size = 0;   //当前响应报文首部字段长度static uint64_t s_http_response_max_body_size = 0; //当前响应报文实体数据长度 需要这些参数配置先于程序进入main( )函数之前,就要进行配置操作。并且给这些配置项设置回调,一旦发生修改就自动适应新值 //于main函数之前初始化配置项,并给这些配置项设置回调,一旦发生修改就自动适应新值namespace { //初始化结构放在匿名空间 防止污染    struct _RequestSizeIniter {        _RequestSizeIniter() {            s_http_request_buffer_size = g_http_request_buffer_size->getValue();            s_http_request_max_body_size = g_http_request_max_body_size->getValue();            s_http_response_buffer_size = g_http_response_buffer_size->getValue();            s_http_response_max_body_size = g_http_response_max_body_size->getValue();            g_http_request_buffer_size->addListener(                        [](const uint64_t& ov, const uint64_t& nv){ s_http_request_buffer_size = nv; }                    );            g_http_request_max_body_size->addListener(                        [](const uint64_t& ov, const uint64_t& nv){ s_http_request_max_body_size = nv; }                    );            g_http_response_buffer_size->addListener(                        [](const uint64_t& ov, const uint64_t& nv){ s_http_response_buffer_size = nv; }                    );            g_http_response_max_body_size->addListener(                        [](const uint64_t& ov, const uint64_t& nv){ s_http_response_max_body_size = nv; }                    );        }    };    static _RequestSizeIniter _init;} 3 HTTP请求报文解析类HttpRequestParser 3.1 成员变量 class HttpRequestParser {public:    typedef std::shared_ptrHttpRequestParser> ptr;    … …private:    http_parser m_parser; //http_parser,解析请求报文的结构体    HttpRequest::ptr m_data; //HttpRequest结构,请求报文对象智能指针    //错误码    //1000: invalid method    //1001: invalid version    //1002: invalid field    int m_error;}; 3.2 构造函数 struct http_parser的成员变量包含普通变量和函数指针,http_parser_init初始化普通成员,其他都设置为回调函数 //对解析请求报文的结构体httpclient_parser进行初始化,需要对报文每一部分的解析指定对应的一个回调函数HttpRequestParser::HttpRequestParser()    :m_error(0) {    m_data.reset(new sylar::http::HttpRequest);    //初始化http_parser m_parser的所有成员    http_parser_init(&m_parser);    m_parser.request_method = on_request_method;    m_parser.request_uri = on_request_uri;    m_parser.fragment = on_request_fragment;    m_parser.request_path = on_request_path;    m_parser.query_string = on_request_query;    m_parser.http_version = on_request_version;    m_parser.header_done = on_request_header_done;    m_parser.http_field = on_request_http_field;    m_parser.data = this;} 3.3 回调函数 /*根据HTTP请求报文不同部分,调用相应的回调函数处理。 仿照开源项目给出的函数签名形式,定义我们自己的解析回调函数● http11_common.h:该头文件中给出了回调函数所需的函数签名,有两种类型    typedef void (*element_cb)(void *data, const char *at, size_t length);    typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);*///解析HTTP请求方法回调函数void on_request_method(void *data, const char *at, size_t length) {    HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    HttpMethod m = CharsToHttpMethod(at);    if(m == HttpMethod::INVALID_METHOD) {        SYLAR_LOG_WARN(g_logger)  "invalid http request method: "  std::string(at, length);        parser->setError(1000);        return;    }    parser->getData()->setMethod(m);}//解析URI回调函数 要自定义URI的解析故不使用void on_request_uri(void *data, const char *at, size_t length) {}//解析分段标识符回调函数void on_request_fragment(void *data, const char *at, size_t length) {    //SYLAR_LOG_INFO(g_logger)     HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    parser->getData()->setFragment(std::string(at, length));}//解析资源路径回调函数void on_request_path(void *data, const char *at, size_t length) {    HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    parser->getData()->setPath(std::string(at, length));}//解析查询参数回调函数void on_request_query(void *data, const char *at, size_t length) {    HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    parser->getData()->setQuery(std::string(at, length));}//解析HTTP协议版本回调函数void on_request_version(void *data, const char *at, size_t length) {    HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    uint8_t v = 0;    if(strncmp(at, "HTTP/1.1", length) == 0) {        v = 0x11;    } else if(strncmp(at, "HTTP/1.0", length) == 0) {        v = 0x10;    }else{        SYLAR_LOG_WARN(g_logger)  "invalid http request version: "             std::string(at, length);        parser->setError(1001);        return;    }    parser->getData()->setVersion(v);}void on_request_header_done(void *data, const char *at, size_t length) {    //HttpRequestParser* parser = static_cast(data);}//解析一系列首部字段的回调函数void on_request_http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) {    HttpRequestParser* parser = static_castHttpRequestParser*>(data); //拿到this指针    if(flen == 0) {        SYLAR_LOG_WARN(g_logger)  "invalid http request field length == 0";        //parser->setError(1002); //invalid field        return;    }    parser->getData()->setHeader(std::string(field, flen), std::string(value, vlen));} 3.4 接口 uint64_t HttpRequestParser::getContentLength() {    return m_data->getHeaderAs("content-length", 0);}/*核心函数:执行解析动作,进行一次对HTTP请求报文的解析。    这个借口比较特殊,使用了开源项目中的http_parser_execute(),该函数是一种状态机的调用形式:    解析不同报文部分,会自动调用不同的回调函数去进行自动的解析。//1: 成功//-1: 有错误//>0: 已处理的字节数,且data有效数据为len - v;    */size_t HttpRequestParser::execute(char* data, size_t len) {    size_t offset = http_parser_execute(&m_parser, data, len, 0); //power: 解析,这里是关键    //先将解析过的空间挪走 防止缓存不够 但是仍然有数据位解析完成的情况    memmove(data, data + offset, (len - offset));    //返回实际解析过的字节数    return offset;}int HttpRequestParser::isFinished() {    return http_parser_finish(&m_parser);}int HttpRequestParser::hasError() {    return m_error || http_parser_has_error(&m_parser);} 4 HTTP响应报文解析类HttpResponseParser 4.1 成员变量 struct httpclient_parser的成员变量包含普通变量和函数指针,http_parser_init初始化普通成员,其他都设置为回调函数 class HttpResponseParser {public:    typedef std::shared_ptrHttpResponseParser> ptr;... ...private:    httpclient_parser m_parser; //解析响应报文的结构体    HttpResponse::ptr m_data; //响应报文对象    //错误码    //1001: invalid version    //1002: invalid field    int m_error;}; 4.2 构造函数 HttpResponseParser::HttpResponseParser()    :m_error(0) {    m_data.reset(new sylar::http::HttpResponse);    httpclient_parser_init(&m_parser);    m_parser.reason_phrase = on_response_reason;    m_parser.status_code = on_response_status;    m_parser.chunk_size = on_response_chunk;    m_parser.http_version = on_response_version;    m_parser.header_done = on_response_header_done;    m_parser.last_chunk = on_response_last_chunk;    m_parser.http_field = on_response_http_field;    m_parser.data = this; //this指针放入} 4.3 回调函数 /*根据HTTP响应报文不同部分,调用相应的回调函数处理。 仿照开源项目给出的函数签名形式,定义我们自己的解析回调函数*///解析状态原因短语回调函数void on_response_reason(void *data, const char *at, size_t length) {    HttpResponseParser* parser = static_castHttpResponseParser*>(data);    parser->getData()->setReason(std::string(at, length));}//解析状态码回调函数void on_response_status(void *data, const char *at, size_t length) {    HttpResponseParser* parser = static_castHttpResponseParser*>(data);    HttpStatus status = (HttpStatus)(atoi(at));    parser->getData()->setStatus(status);}//解析HTTP协议版本回调函数void on_response_chunk(void *data, const char *at, size_t length) {}void on_response_version(void *data, const char *at, size_t length) {    HttpResponseParser* parser = static_castHttpResponseParser*>(data);    uint8_t v = 0;    if(strncmp(at, "HTTP/1.1", length) == 0) {        v = 0x11;    } else if(strncmp(at, "HTTP/1.0", length) == 0) {        v = 0x10;    }else{        SYLAR_LOG_WARN(g_logger)  "invalid http response version: "             std::string(at, length);        parser->setError(1001);        return;    }    parser->getData()->setVersion(v);}void on_response_header_done(void *data, const char *at, size_t length) {}void on_response_last_chunk(void *data, const char *at, size_t length) {}//解析一系列首部字段回调函数void on_response_http_field(void *data, const char *field, size_t flen                        ,const char *value, size_t vlen) {    HttpResponseParser* parser = static_castHttpResponseParser*>(data);    if(flen == 0) {        SYLAR_LOG_WARN(g_logger)  "invalid http response field length == 0";        //parser->setError(1002); //invalid field        return;    }    parser->getData()->setHeader(std::string(field, flen), std::string(value, vlen));} 4.4 接口 /*执行解析动作,进行一次对HTTP响应报文的解析。这个借口比较特殊,使用了开源项目中的httpclient_parser_execute(),该函数是一种状态机的调用形式:    解析不同报文部分,会自动调用不同的回调函数去进行自动的解析,然后判断这一段是解析成功还是解析失败。和HTTP请求报文解析不同的点在于,响应报文不一定一次就全部发送完毕。可能存在分块发送的情况,即:chunck形式。    但是复用的开源项目里并不支持对报文的分段解析,需要我们特殊处理:    如果为chunck发包形式的话,每一次解析就要重置解析结构体的状态,防止记录上一个包的状态。 */size_t HttpResponseParser::execute(char* data, size_t len, bool chunck) {    if(chunck) { //如果为chunck包需要重新初始化一下解析包        httpclient_parser_init(&m_parser);    }    //每一次都是从头开始解析    size_t offset = httpclient_parser_execute(&m_parser, data, len, 0); //power: 这里是关键    //先将解析过的空间挪走    memmove(data, data + offset, (len - offset));    //返回实际解析过的字节数    return offset;}int HttpResponseParser::isFinished() {    return httpclient_parser_finish(&m_parser);}int HttpResponseParser::hasError() {    return m_error || httpclient_parser_has_error(&m_parser);}uint64_t HttpResponseParser::getContentLength() {    return m_data->getHeaderAs("content-length", 0);}
点赞 0
评论 0
全部评论

相关推荐

竟然收到了测评听说是双机位
投递拼多多集团-PDD等公司10个岗位
点赞 评论 收藏
分享
头像
07-24 13:05
已编辑
西南大学 Java
点赞 评论 收藏
分享
半解316:内容充实,细节需要修改一下。 1,整体压缩为一页。所有内容顶格。 2,项目描述删除,直接写个人工作量 修改完之后还需要建议,可以私聊
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务