http-parser使用简介

Comments(39)


Posted on 2014-03-08 12:55:57 http


概括

http-parser是一个用C代码编写的HTTP消息解析器。可以解析HTTP请求或者回应消息。 这个解析器常常在高性能的HTTP应用中使用。 在解析的过程中,它不会调用任何系统调用,不会在HEAP上申请内存,不会缓存数据,并且可以在任意时刻打断解析过程,而不会产生任何影响。 对于每个HTTP消息(在WEB服务器中就是每个请求),它只需要40字节的内存占用(解析器本身的基本数据结构),不过最终的要看你实际的代码架构。

特性:

  • 无第三方依赖
  • 可以处理持久消息(keep-alive)
  • 支持解码chunk编码的消息
  • 支持Upgrade协议升级(如无例外就是WebSocket)
  • 可以防御缓冲区溢出攻击

解析器可以处理以下类型的HTTP消息:

  • 头部的字段和值
  • Content-Length
  • 请求方法
  • 返回的HTTP代码
  • Transfer-Encoding
  • HTTP版本
  • 请求的URL
  • HTTP消息主体

简单使用:

每个HTTP请求使用一个http_parser对象。使用http_parser_init来初始化结构体,并且设置解析时的回调。下面的代码可能看起来像是解析HTTP请求:

// 设置回调
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */

// 为结构体申请内存
http_parser *parser = malloc(sizeof(http_parser));
// 初始化解析器
http_parser_init(parser, HTTP_REQUEST);
// 设置保存调用者的数据,用于在callback内使用
parser->data = my_socket;

当接收到数据后,解析器开始执行,并检查错误:

size_t len = 80*1024;   // 需要接受的数据大小80K
size_t nparsed;         // 已经解析完成的数据大小
char buf[len];          // 接收缓存
ssize_t recved;         // 实际接收到的数据大小

// 接受数据
recved = recv(fd, buf, len, 0);

// 如果接收到的字节数小于0,说明从socket读取出错
if (recved < 0) {
  /* Handle error. */
}

/* Start up / continue the parser.
 * Note we pass recved==0 to signal that EOF has been recieved.
 */
// 开始解析
// @parser 解析器对象
// @&settings 解析时的回调函数
// @buf 要解析的数据
// @receved 要解析的数据大小
nparsed = http_parser_execute(parser, &settings, buf, recved);

// 如果解析到websocket请求
if (parser->upgrade) {
  /* handle new protocol */
// 如果解析出错,即解析完成的数据大小不等于传递给http_parser_execute的大小
} else if (nparsed != recved) {
  /* Handle error. Usually just close the connection. */
}

HTTP需要知道数据流在那里结束。

举个例子,一些服务器发送响应数据的时候,HTTP头部不带有Content-Length字段,希望客户端持续从socket中读取数据,知道遇到EOF为止。 在调用http_parser_execute时,传递最后一个参数为0,用来通知http_parser,解析已经结束。 在http_parser遇到EOF并处理的过程中,仍然可能会遇到错误,所以应该在callback中处理这些错误。

注意: 上面的意思是说,如果需要多次调用http_parser_execute的时候,就是因为无法一次完成对HTTP服务器/客户端数据的接收。 所以需要在每次接收到一些数据之后,调用一次http_parser_execute,当从socket接收到EOF时,应该结束解析,同时通知http_parser解析结束。

一些可扩展的信息字段,例如status_codemethodHTTP版本号,它们都存储在解析器的数据结构中。 这些数据被临时的存储在http_parser中,并且会在每个连接到来后被重置(当多个连接的HTTP数据使用同一个解析器时); 如果需要保留这些数据,必须要在on_headers_complete返回之前保存它門。

注意: 应该为每个HTTP连接的数据,单独初始化一个解析器的时候,不会存在上述问题.

解析器会解析HTTP请求和相应中的transfer-encoding字段。 就是说,chunked编码会在调用on_body之前被解析。

关于Upgrade协议的问题

HTTP支持将连接升级为不同的协议. 例如目前日益普遍的WebSocket协议的请求数据:

GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample

在WebSocket请求头部传输完毕后,就下来传输的数据是非HTTP协议的数据了。

关于WebSocket协议的详细内容见: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75

要支持这种类似与WebSocket的协议,解析器会把它当作一个不带HTTP主体数据的包(只含有头部).然后调用on_headers_completeon_message_complete回调。所以不论怎样,当检测到HTTP头部的数据结束时,http_parser_execute会停止解析,并且返回。

建议用户在http_parser_execute函数返回后,检查parset->upgrade字段,是否被设置为1.在http_parset_execute的返回值中,非HTTP类型的数据(除去HTTP头部的数据)的范围,会被设置为从一个offset参数处开始。

回调函数

当调用http_parser_execute时,在http_parset_settings中设置的回调会执行。 解析器维护了自身状态数据,并且这些数据不会被保存,所以没有必要将这些状态数据缓存。 如果你真需要保存这些状态数据,可以在回调中保存。

有两种类型的回调:

  • 通知 typedef int (*http_cb) (http_parser *); 包括:on_message_begin, on_headers_complete, on_message_complete

  • 数据 typedef int (*http_data_cb) (http_parser *, const char at, size_t length); 包括;(只限与请求) on_uri, (通用) on_header_field, on_header_value, on_body

用户的回调函数应该返回0表示成功。返回非0的值,会告诉解析器发生了错误,解析器会立刻退出。

如果你解析chunks编码的HTTP消息(例如:从socket中读read()HTTP请求行,解析,然后再次读到一半的头部消息后,再次解析,等等),你的数据类型的回调就会被调用不止一次。 HTTP解析器保证,参数中传递的数据指针,只在回调函数内有效(即回调调用结束,数据指针无效). 因为http-parser返回解析结果的方式为:在需要解析的数据中,依靠指针和数据长度来供用户代码读取 如果可以的话,你也可以将read()到的数据,保存到在HEAP上申请的内存中,以避免非必要的数据拷贝。

比较笨的方法是:每读取一次将读取到的数据传递给http_parset_execute函数.

注意:对于将一个完整的HTTP报文分开多次解析,应该使用同一个parser对象!

但是实际上的情况更复杂:

  • 首先根据HTTP协议头部的规则,应该持续从socket读取数据,直到读到了\r\n\r\n,表示头部报文结束。 这时可以传递给http_parser解析,或者根据下面的规则,继续读取实体部分的数据。

  • 如果报文中使用Content-Length指定传输实体的大小,接下来不论HTTP客户/服务器都因该根据读取到Content-Length指定的实体大小

  • 对于分块传输的实体,传输编码为chunked。即Transfer-Encoding: chunked。 分快传输的编码,一般只适用于HTTP内容响应(HTTP请求也可以指定传输编码为chunked,但不是所有HTTP服务器都支持)。 这时可以读取定量的数据(如4096字节) ,交给parser解析。然后重复此过程,直到chunk编码结束。

内容参考: HTTP权威指南

前一篇: Golang中的各种坑(持续更新) 后一篇: http-parser实际解析过程

Captcha:
验证码

Email:

Content: (Support Markdown Syntax)


Pq5hSOzJPyOi  2015-08-08 08:20:20 From 54.154.58.224

I’d venurte that this article has saved me more time than any other.


SlireeSop  2021-07-22 12:19:07 From 127.0.0.1

where to buy cialis cheap


JoshuaDIari  2021-08-22 09:32:45 From 127.0.0.1

sildenafil 20 generic for viagra canadian drugstore viagra how viagra works viagra alternative 100mg viagra cost


HenryCealp  2021-09-08 13:52:18 From 127.0.0.1

gay teen dating forums gay movie about online dating japanese gay dating sim


plaquenil and hair loss  2021-09-20 09:21:10 From 127.0.0.1

Vente De Propecia Generique


Aninevunc  2022-02-05 04:07:07 From 127.0.0.1

can i buy levitra at walgreens Lsrfey plaquenil eye


DgwiHerne  2022-03-31 13:01:07 From 127.0.0.1

buy ivermectin stromectol ivermectin 0.5


aweryjjw  2022-05-15 06:30:01 From 127.0.0.1

modafinil usa provigil pill


JtbnHerne  2022-06-07 02:19:43 From 127.0.0.1

viagra buy online female viagra online canada https://viagranameed.com/


vsuwlofl  2022-06-17 17:29:09 From 127.0.0.1

stromectol 3 mg tablet generic stromectol 12 mg stromectol 12 mg tablets