In Libevent (vi) HTTP server, as a single-threaded HTTP server, listen not only to the arrival of each connection, but also to the I/O events on each connection.
Check the source, in Evhttp_bind_socket set the Accept callback function:ACCEPT_SOCKET_CB.
/**/staticvoidACCEPT_SOCKET_CB (structstruct intvoid *arg) { struct evhttp *http = arg; Evhttp_get_request (http, NFD, Peer_sa, Peer_socklen);}
Static voidEvhttp_get_request (structEvhttp *http, evutil_socket_t FD,structSOCKADDR *SA, ev_socklen_t Salen) { structEvhttp_connection *Evcon; Evcon=evhttp_get_request_connection (http, FD, SA, salen); if(Evcon = =NULL) {Event_sock_warn (FD,"%s:cannot get connection on"ev_sock_fmt, __func__, Ev_sock_arg (FD)); Evutil_closesocket (FD); return; } /*The timeout can be used by the server to close idle connections*/ if(Http->timeout! =-1) evhttp_connection_set_timeout (Evcon, HTTP-timeout); /** If we want to accept more than one request in a connection, * we need to know which HTTP server it belongs To. */Evcon->http_server =http; Tailq_insert_tail (&http->connections, Evcon, next); if(Evhttp_associate_new_request_with_connection (evcon) = =-1) Evhttp_connection_free (Evcon);}
Two important functions: evhttp_get_request_connection,evhttp_associate_new_request_with_connection .
1. evhttp_get_request_connection
/** Takes A file descriptor to read a request from. * The callback was executed once the whole request has been read. /c11>*/Static structevhttp_connection*Evhttp_get_request_connection (structevhttp*http, evutil_socket_t FD,structSOCKADDR *SA, ev_socklen_t Salen) { structEvhttp_connection *Evcon; Char*hostname = NULL, *portname =NULL; NAME_FROM_ADDR (SA, Salen,&hostname, &portname); if(hostname = = NULL | | portname = =NULL) { if(hostname) Mm_free (hostname); if(PortName) mm_free (portname); return(NULL); } event_debug (("%s:new request from%s:%s on"Ev_sock_fmt"\ n", __FUNC__, hostname, portname, Ev_sock_arg (FD)); /*We need a connection object to put the HTTP request on*/Evcon=evhttp_connection_base_new (http-Base, NULL, hostname, atoi (portname)); Mm_free (hostname); Mm_free (PortName); if(Evcon = =NULL)return(NULL); Evcon->max_headers_size = http->default_max_headers_size; Evcon->max_body_size = http->default_max_body_size; Evcon->flags |=evhttp_con_incoming; Evcon->state =Evcon_reading_firstline; Evcon->FD =FD; bufferevent_setfd (Evcon ->bufev, FD); return(Evcon);}
Note the point:
1. bufferevent READCD is set through Evhttp_connection_base_new: EVHTTP_READ_CB,
WRITECB: EVHTTP_WRITE_CB.
2. Call BUFFEREVENT_SETFD
The BUFFEREVENT_SETFD code is as follows:
intBufferevent_setfd (structBufferevent *Bev, evutil_socket_t FD) {Union Bufferevent_ctrl_data D; intres =-1; D.FD=FD; Bev_lock (BEV); if(bev->be_ops->ctrl) Res= Bev->be_ops->ctrl (Bev, BEV_CTRL_SET_FD, &d); Bev_unlock (BEV); returnRes;}Static intBe_socket_ctrl (structBufferevent *bev,enumbufferevent_ctrl_op op, Union bufferevent_ctrl_data*data) { Switch(OP) { Casebev_ctrl_set_fd:be_socket_setfd (BEV, Data-FD); return 0; CaseBev_ctrl_get_fd:data->FD = EVENT_GET_FD (&bev->ev_read); return 0; Casebev_ctrl_get_underlying: CaseBev_ctrl_cancel_all:default: return-1; }}Static voidBe_socket_setfd (structBufferevent *Bufev, evutil_socket_t FD) {Bev_lock (BUFEV); Evutil_assert (Bufev->be_ops = = &bufferevent_ops_socket); Event_del (&bufev->ev_read); Event_del (&bufev->ev_write); Event_assign (&bufev->ev_read, bufev->ev_base, FD, Ev_read|ev_persist, BUFFEREVENT_READCB, Bufev); Event_assign (&bufev->ev_write, bufev->ev_base, FD, Ev_write|ev_persist, BUFFEREVENT_WRITECB, Bufev); if(FD >=0) bufferevent_enable (Bufev, Bufev-enabled); Bev_unlock (Bufev);}
Here the main set of FD read event callback BUFFEREVENT_READCB, write event callback BUFFEREVENT_WRITECB.
( here the bufferevent_enable can not be cared for, the back will be reset.) )
2. evhttp_associate_new_request_with_connection
Static intEvhttp_associate_new_request_with_connection (structEvhttp_connection *Evcon) { structEvhttp *http = evcon->Http_server; structEvhttp_request *req; if((req = evhttp_request_new (evhttp_handle_request, http)) = =NULL)return(-1); if((Req->remote_host = Mm_strdup (evcon->address)) = =NULL) {Event_warn ("%s:strdup", __func__); Evhttp_request_free (req); return(-1); } req->remote_port = evcon->Port; Req->evcon = Evcon;/*The request ends up owning the connection*/req->flags |=evhttp_req_own_connection; /*We didn't present the request to the user user yet, so treat it as * if the user is done with the request. This allows us to free the * request on a persistent connection if the client drops it without * sending a request . */req->userdone =1; Tailq_insert_tail (&evcon->requests, req, next); Req->kind =evhttp_request; Evhttp_start_read (Evcon); return(0);}
The first step is to set Evhttp_request's callback function evhttp_handle_request, and the second step calls Evhttp_start_read:
/** Reads data from file descriptor into request structure * Request structure needs to BES set up correctly.*/voidEvhttp_start_read (structEvhttp_connection *Evcon) { /*Set up a event to read the headers*/bufferevent_disable (Evcon-Bufev, Ev_write); Bufferevent_enable (Evcon-Bufev, Ev_read); Evcon->state =Evcon_reading_firstline; /*Reset the bufferevent callbacks*/BUFFEREVENT_SETCB (Evcon-Bufev, EVHTTP_READ_CB, EVHTTP_WRITE_CB, EVHTTP_ERROR_CB, Evcon); /*If There ' s still data pending, process it next time through the * loop. Don ' t do it now; That could get recusive. */ if(Evbuffer_get_length (Bufferevent_get_input (evcon->Bufev))) {Event_deferred_cb_schedule (Get_deferred_queue (Evcon),&evcon->READ_MORE_DEFERRED_CB); }}
As you can see, the read events of FD are added to the event loop.
Finally comb the next read event invocation process:
1. A read event occurred on FD
2. BUFFEREVENT_READCB
3. EVHTTP_READ_CB
4. Evhttp_connection_done
5. Evhttp_handle_request
6. Call the user-defined evhttp callback function
About the flow of data
When a read event occurs on the FD, the data on the FD is first read into the Evhttp_connection bufferevent, and then the data in the bufferevent is read into the input buffer of the evhttp_request.
When we use evhttp_send_reply to send data, the data is first written to the evhttp_request output buffer, then to the bufferevent of evhttp_connection, and the output buffer to the FD is written last.
Libevent (13) Evhttp Event Processing flow