用libevent构建一个http server非常方便,可参考libevent(六)http server。
主要涉及的一个结构体是evhttp:
struct evhttp {
/* Next vhost, if this is a vhost. */
TAILQ_ENTRY(evhttp) next_vhost; /* All listeners for this host */
TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; /* All live connections on this host. */
struct evconq connections; TAILQ_HEAD(vhostsq, evhttp) virtualhosts; TAILQ_HEAD(aliasq, evhttp_server_alias) aliases; /* NULL if this server is not a vhost */
char *vhost_pattern; int timeout; size_t default_max_headers_size;
ev_uint64_t default_max_body_size; /* Bitmask of all HTTP methods that we accept and pass to user
* callbacks. */
ev_uint16_t allowed_methods; /* Fallback callback if all the other callbacks for this connection
don't match. */
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg; struct event_base *base;
};
值得关注的有两个成员:
callbacks,一个链表,存放用户定义的回调函数
connections,一个链表,存放所有连接,每个连接对应一个evhttp_connection
evhttp_connection结构如下:
/* A client or server connection. */
struct evhttp_connection {
/* we use this tailq only if this connection was created for an http
* server */
TAILQ_ENTRY(evhttp_connection) next; evutil_socket_t fd;
struct bufferevent *bufev; struct event retry_ev; /* for retrying connects */ char *bind_address; /* address to use for binding the src */
u_short bind_port; /* local port for binding the src */ char *address; /* address to connect to */
u_short port; size_t max_headers_size;
ev_uint64_t max_body_size; int flags;
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */ int timeout; /* timeout in seconds for events */
int retry_cnt; /* retry count */
int retry_max; /* maximum number of retries */ enum evhttp_connection_state state; /* for server connections, the http server they are connected with */
struct evhttp *http_server; TAILQ_HEAD(evcon_requestq, evhttp_request) requests; void (*cb)(struct evhttp_connection *, void *);
void *cb_arg; void (*closecb)(struct evhttp_connection *, void *);
void *closecb_arg; struct deferred_cb read_more_deferred_cb; struct event_base *base;
struct evdns_base *dns_base;
};
值得关注的成员有两个:
bufev,对应一个bufferevent
requests,一个链表,存放该连接上的所有请求,每个请求对应evhttp_request
evhttp_request结构如下:
struct evhttp_request {
#if defined(TAILQ_ENTRY)
TAILQ_ENTRY(evhttp_request) next;
#else
struct {
struct evhttp_request *tqe_next;
struct evhttp_request **tqe_prev;
} next;
#endif /* the connection object that this request belongs to */
struct evhttp_connection *evcon;
int flags;
/** The request obj owns the evhttp connection and needs to free it */
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
/** Request was made via a proxy */
#define EVHTTP_PROXY_REQUEST 0x0002
/** The request object is owned by the user; the user must free it */
#define EVHTTP_USER_OWNED 0x0004
/** The request will be used again upstack; freeing must be deferred */
#define EVHTTP_REQ_DEFER_FREE 0x0008
/** The request should be freed upstack */
#define EVHTTP_REQ_NEEDS_FREE 0x0010 struct evkeyvalq *input_headers;
struct evkeyvalq *output_headers; /* address of the remote host and the port connection came from */
char *remote_host;
ev_uint16_t remote_port; /* cache of the hostname for evhttp_request_get_host */
char *host_cache; enum evhttp_request_kind kind;
enum evhttp_cmd_type type; size_t headers_size;
size_t body_size; char *uri; /* uri after HTTP request was parsed */
struct evhttp_uri *uri_elems; /* uri elements */ char major; /* HTTP Major number */
char minor; /* HTTP Minor number */ int response_code; /* HTTP Response code */
char *response_code_line; /* Readable response */ struct evbuffer *input_buffer; /* read data */
ev_int64_t ntoread;
unsigned chunked:, /* a chunked request */
userdone:; /* the user has sent all data */ struct evbuffer *output_buffer; /* outgoing post or data */ /* Callback */
void (*cb)(struct evhttp_request *, void *);
void *cb_arg; /*
* Chunked data callback - call for each completed chunk if
* specified. If not specified, all the data is delivered via
* the regular callback.
*/
void (*chunk_cb)(struct evhttp_request *, void *);
};
值得注意的是:
每个请求有自己的输入缓冲input_buffer、输出缓冲output_buffer。
总结一下evhttp:
1. 一个evhttp使用一个链表存放多个evhttp_connection,每个evhttp_connection使用链表存放多个evhttp_request。
2. 每个evhttp_connection包含一个bufferevent,每个evhttp_request包含两个evbuffer,用于输入输出缓冲。
说了半天,好像没看见同步机制,可见evhttp不适合多线程。
参考资料: