evhttp

来源:互联网 发布:苹果扫描软件下载 编辑:程序博客网 时间:2024/05/29 15:54

libevent库使得高并发响应HTTP Server的编写变得很容易。
因此实际中,使用libevent可以为任何应用(如数据库)提供一个HTTP based的网络接口,方便多个clients采用任何支持HTTP protocol的语言与server进行交互

最近将之前用gsoap写的webserver改为使用libevent库。gsoap是为了实现soap协议,如果客户端只调用restful风格的接口的话,用gsoap实现http server有很多多余的东西了,而且处理起来也不太方便。

虽然之前使用过libevent,不过evhttp还是第一次用,还是一如既往的好用。再此简单记录下,方便以后取用。
代码(https://github.com/xuleicode/lb,单线程,多线程。供自己参考)

整个过程包括如下几步:

  1. 初始化
  2. 创建HTTP Server
  3. 指定回调函数(根据情况可指定多个),
  4. 进入事件循环
  5. 在回调函数中,可以获取客户端请求(request的HTTP
    Header和参数等),进行相应处理后,再将结果发送给客户端(response的HTTP Header和内容)。
#include <evhttp.h>void httpd_handler(struct evhttp_request *req, void *arg);void specific_handler(struct evhttp_request *req, void *arg);int starthttp(){        /* 使用libevent创建HTTP Server */        //初始化event API        event_init();        //创建一个http server        struct evhttp *httpd;         //默认参数        char *httpd_option_listen = "0.0.0.0";        int httpd_option_port = 8080;        int httpd_option_daemon = 0;        int httpd_option_timeout = 120; //in seconds        httpd = evhttp_start(httpd_option_listen, httpd_option_port);        evhttp_set_timeout(httpd, httpd_option_timeout);        //        //evhttp_set_allowed_methods( httpd , EVHTTP_REQ_GET);        //指定generic callback        evhttp_set_gencb(httpd, httpd_handler, NULL);        //Set a callback for a specified URI        evhttp_set_cb(httpd, "/spec", specific_handler, NULL);        //循环处理events        event_dispatch();        evhttp_free(httpd);        return 0;}
//linux编译:需要levent库g++ -o myhttpd -Wall -levent myhttpd.cpp
//windwos 下需要添加依赖库ws2_32.libwsock32.liblibevent.liblibevent_core.liblibevent_extras.lib//并且windows下使用libevnet网络时需要//初始化winsocket#ifdef WIN32   WSADATA wsaData;    if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0)     {        return -1;    }#endif//和释放#ifdef WIN32    WSACleanup();  #endif   

主要结构体
evhttp_request *req 中包含了http请求的所有内容

struct evkeyvalq *input_headers;    //保存客户端请求的HTTP headers(key-value pairs)struct evkeyvalq *output_headers;   //保存将要发送到客户端的HTTP headers(key-value pairs)//客户端的ip和portchar *remote_host;u_short remote_port;enum evhttp_request_kind kind;  //可以是EVHTTP_REQUEST或EVHTTP_RESPONSE//enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };                  enum evhttp_cmd_type type;  //可以是EVHTTP_REQ_GET, EVHTTP_REQ_POST或EVHTTP_REQ_HEAD等,详见最后char *uri;          //客户端请求的urichar major;         //HTTP major numberchar minor;         //HTTP major numberint response_code;      //HTTP response codechar *response_code_line;   //readable responsestruct evbuffer *input_buffer;  //客户端POST的数据struct evbuffer *output_buffer; //输出到客户端的数据...//还有一些回调函数指针

保存参数的结构体

 /* * Key-Value pairs.  Can be used for HTTP headers but also for * query argument parsing. */struct evkeyval {    TAILQ_ENTRY(evkeyval) next;    char *key;    char *value;};
宏TAILQ_ENTRY(evkeyval)被定义为:#define TAILQ_ENTRY(type)    struct {        struct type *tqe_next;      //next element        struct type **tqe_prev;     //address of previous next element    }

buffer结构体

//buffer.hstruct evbuffer#ifdef EVENT_IN_DOXYGEN_{}#endif;//evbuffer-internal.h文件  struct evbuffer_chain;  struct evbuffer {      struct evbuffer_chain *first;      struct evbuffer_chain *last;      //二级指针。使用*last_with_datap时,指向的是链表中最后一个有数据的evbuffer_chain。      //所以last_with_datap存储的是倒数第二个evbuffer_chain的next成员地址。      //一开始buffer->last_with_datap = &buffer->first;此时first为NULL。所以当链表没有节点时      //*last_with_datap为NULL。当只有一个节点时*last_with_datap就是first。          struct evbuffer_chain **last_with_datap;      size_t total_len;//链表中所有chain的总字节数      ...  };  struct evbuffer_chain {      struct evbuffer_chain *next;      size_t buffer_len;//buffer的大小      //错开不使用的空间。该成员的值一般等于0      ev_off_t misalign;      //evbuffer_chain已存数据的字节数      //所以要从buffer + misalign + off的位置开始写入数据      size_t off;      ...         unsigned char *buffer;  };  

另外定义宏方便获取evbuffer中保存的内容和大小:

  #define EVBUFFER_LENGTH(x)      (x)->off  #define EVBUFFER_DATA(x)        (x)->buffer

例如,获取客户端POST数据的内容和大小:

    EVBUFFER_DATA(res->input_buffer);    EVBUFFER_LENGTH(res->input_buffer);//这个和res->body_size相同

另外struct evbuffer用如下函数创建添加和释放:

    struct evbuffer *buf;    buf = evbuffer_new();    //往buffer中添加内容    evbuffer_add_printf(buf, "I got it! you just requested: %s\n", req->uri);   //Append a formatted string to the end of an evbuffer.    //将内容输出到客户端    evhttp_send_reply(req, HTTP_OK, "OK", buf);    //释放掉buf    evbuffer_free(buf);

关键函数

获取客户端请求的URI

//使用req->uri//或使用函数const char *evhttp_request_uri(struct evhttp_request *req);//即(evhttp_request_uri(req);)。

对获取的URI进行解析和其他操作

使用函数

void evhttp_parse_query(const char *uri, struct evkeyvalq *args);

可对uri的参数进行解析,结果保存在struct evkeyvalq的key-value pairs中,例如:

    char *uri = "http://foo.com/?id=1&infor=hello";    struct evkeyvalq args;    evhttp_parse_query(uri, &args);    //然后通过evhttp_find_header等函数获取各个参数及对应的值    evhttp_find_header(&args, "id"); //得到test    evhttp_find_header(&args, "infor"); //得到some thing  

如下两个函数对URI进行encode和decode:

    char *evhttp_encode_uri(const char *uri);    char *evhttp_decode_uri(const char *uri);

URI encode的结果是所有非alphanumeric及-_的字符都被类似于%和一个2位16进制字符替换(其中空格被+号替换)。如上两个函数返回的字符串需要free掉。
Escape特殊的HTML字符

char *evhttp_htmlescape(const char *html);

特殊字符:&被替换为&;”被替换为";’被替换为'; <被替换为<;>被替换为>。该函数返回的字符串需要free掉。

处理HTTP headers相关的函数
HTTP headers保存在req的input_headers中,这个是struct evkeyvalq 的结构体(key-value pairs),使用如下函数可对其进行修改:

const char *evhttp_find_header(const struct evkeyvalq *, const char *);int evhttp_remove_header(struct evkeyvalq *, const char *);int evhttp_add_header(struct evkeyvalq *, const char *, const char *);void evhttp_clear_headers(struct evkeyvalq *);

设定只识别get请求,

evhttp_set_allowed_methods( httpd , EVHTTP_REQ_GET);

设置后,只处理get请求,其他请求返回405 Method not allowed,我自己使用的时候发现返回的是501 Not Implemented
请求类型如下:

enum evhttp_cmd_type {    EVHTTP_REQ_GET     = 1 << 0,    EVHTTP_REQ_POST    = 1 << 1,    EVHTTP_REQ_HEAD    = 1 << 2,    EVHTTP_REQ_PUT     = 1 << 3,    EVHTTP_REQ_DELETE  = 1 << 4,    EVHTTP_REQ_OPTIONS = 1 << 5,    EVHTTP_REQ_TRACE   = 1 << 6,    EVHTTP_REQ_CONNECT = 1 << 7,    EVHTTP_REQ_PATCH   = 1 << 8};
原创粉丝点击