evhttp处理POST请求的技巧
evhttp是libevent提供的一个轻量级的基于消息驱动的HTTP Server,详细的资料可以参考libevent的主页:http://monkey.org/~provos/libevent/ ,本文主要描述如何处理POST请求。
美中不足:evhttp不支持POST?
evhttp在evhttp_request接口中包含一个请求类型type,用来表示HTTP的操作(EVHTTP_REQ_GET,EVHTTP_REQ_POST),但evhttp接口并没有区分GET和POST操作,我们来看具体的接口实现:
evhttp_request_uri: 解析HTTP请求中的ur;
evhttp_parse_query: 解析名值对;
对于GET操作来说,这两个函数已经满足要求,例如:处理如下GET请求:
http://foo.com/?q=test&s=some+thing
evhttp_request_uri: 解析HTTP请求中的ur,得到/?q=test&s=some+thing
evhttp_parse_query: 解析名值对,得到一个evkeyvalq结构,里面包含了key/value
的数组.
但如果这个请求是用POST发送的,那么这两个函数就不适应了:
evhttp_request_uri: 解析HTTP请求中的ur,得到/
evhttp_parse_query: 解析名值对,q=test&s=some+thing是保存在消息体中而不是uri中,因此解析后是一个空的结构。
除此之外,evhttp并没有提供接口访问POST的消息体中的数据。
曲径通幽:如何处理POST请求?
既然这样,难道用evhttp就不能使用POST请求了么?当然不是,只不过相对GET来说,我们要多费一些周折。下面就介绍如何在evhttp中处理POST请求。
1. 访问缓冲区获取POST消息体数据
evhttp_request结构中包含input_buf结构,input_buffer中的buffer就是存放消息体数据的缓冲区,input_buffer还有一个orig_buffer,实际上和buffer指向的是同一块内存空间,不知道为什么这样设计,但我们用的时候直接用buffer就可以了。
但这里的buffer并不是全部存放消息体中的数据,buffer的大小是2的N次方,而且总是和实际数据长度最接近的2的N次方,个人猜测evhttp可能使用了slab之类的内存预分配机制,所以这样处理。
那么我们如何取到真正的数据呢?其实很简单:input_buffer有一个offset字段,就是用来指明具体的数据长度的。实际使用的时候,你只要memcpy一把就可以了。
2. 模拟GET请求参数
如果你的POST请求消息体中存放的是标准的key=value&key=value.....这种格式的数据,你当然可以自己去解析它,但更聪明的办法是模拟GET请求,再调用evhttp_parse_query函数来帮你解析。
具体步骤如下:
1) 参照“访问缓冲区获取POST消息体数据”的指导获取数据;
2) 将第一步获取的数据解码成普通的字符串;
3) evhttp_request_uri解析出uri
4) 组装成/?q=test&s=some+thing格式的字符串,注意要自己加上问号”?”
5) 调用evhttp_parse_query解析组装的字符串
需要注意的是:如果你的POST请求消息体中的数据不是这种key=value&key=value.....的格式,那么就只能自己处理了,这应该也是evhttp不提供POST请求通用处理的原因吧。