最近学习了下http_parse解析库,是nginx的一个解析http库, 在解析的过程中,它不会调用任何系统调用,不会在HEAP上申请内存,不会缓存数据,并且可以在任意时刻打断解析过程,而不会产生任何影响。在解析http的时候使用http_parse可以大幅度提高效率(Ps:当然一些开源的库也是比较好用的。如果你写的是基础框架类,那么使用这个库是再好不过了)。
本文主要分为两部分学习。第一部分简单了解http的一些基本知识。第二部分简单学习http_parse库使用
第一部分:http基本知识
1.http的请求格式包括 request line、Request header、空行、Body。如果是get方法则不包含body。
2.url解释
schema://host[:port#]/path/.../[?query-string][#anchor]
scheme 指定低层使用的协议(例如:http, https, ftp)
host HTTP服务器的IP地址或者域名
port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path 访问资源的路径
query-string 发送给http服务器的数据
anchor- 锚
举例:
http://www.mywebsite.com/sj/test/test.aspx?name=sviergn&x=true#stuff
Schema: http
host: www.mywebsite.com
path: /sj/test/test.aspx
Query String: name=sviergn&x=true
Anchor: stuff
网上有很多http相关的学习博客或者文档。这里就不多余的阐述了
第二部分 http_parse使用
//设置回调函数
http_parser_settings settings_null;
settings_null.on_message_begin = on_message_begin;
settings_null.on_header_field = on_header_field;
settings_null.on_header_value = on_header_value;
settings_null.on_url = on_url;
settings_null.on_status = 0;
settings_null.on_body = on_body;
settings_null.on_headers_complete = on_headers_complete;
settings_null.on_message_complete = on_message_complete;
// 为结构体申请内存
http_parser *parser = malloc(sizeof(http_parser));
// 初始化解析器
http_parser_init(parser, HTTP_REQUEST);
//开始解析
parsed = http_parser_execute(parser, &settings_null, buf, strlen(buf));
在回调中可以进行处理。如果是解析回包设置HTTP_RESPONSE。使用还是很简单的。
下面看看http_parse结构。存放一些用于管理得状态
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
看看http_parser_settings,里面的每一个变量其实都是回调地址
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
在调用http_parser_execute解析的时候,根据enum state来进行阶段性处理(有点类似状态机,把解析http看做一个流程。一步一步扭转状态以及解析)
http_parse中还有一个结构可以方便的进行url格式解析。先上代码
struct http_parser_url u;
if(0 == http_parser_parse_url(url, strlen(url), 0, &u))
{
if(u.field_set & (1 << UF_PORT))
{
httpc->port = u.port;
}
else
{
httpc->port = 80;
}
if(httpc->host) free(httpc->host);
if(u.field_set & (1 << UF_HOST) )
{
httpc->host = (char*)malloc(u.field_data[UF_HOST].len+1);
strncpy(httpc->host, url+u.field_data[UF_HOST].off, u.field_data[UF_HOST].len);
httpc->host[u.field_data[UF_HOST].len] = 0;
}
if(httpc->path) free(httpc->path);
if(u.field_set & (1 << UF_PATH))
{
httpc->path = (char*)malloc(u.field_data[UF_PATH].len+1);
strncpy(httpc->path, url+u.field_data[UF_PATH].off, u.field_data[UF_PATH].len);
httpc->path[u.field_data[UF_PATH].len] = 0;
}
return 0;
}
struct http_parser_url结构其实很简单
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
通过field_set来可以判断field是否被设置。field有如下bit
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
使用u.field_set & (1 << UF_PORT或者u.field_set & (1 << UF_HOST)或者。。。可以判断字段是否设置取得字段可以使用field_data+off来得到。比如u.field_data[UF_HOST].off。http_parser_parse_url的解析就是纯字符串解析了。有兴趣可以看看源码。
总结:http_parse主要使用c来写的。下面可以再接着研究下fast-http的源码以及使用。好记性不如烂笔头。如有不对大家可以一起探讨