Hi,
I am using libevent to implement a HTTP server. I am
observing a crash & connection disconnect in libevent in the following scenario.
1.
The process starts a libevent HTTP server &
registers for callbacks for different URLs.
2.
For one of the GET request from the client, the
server needs to keep the connection open & send chunk replies as when data
from some other source arrive.
3.
So it does a *_reply_start() & then *_reply_chunk()
when data is available
Everything works fine at the beginning , but after a while
(random could in 30 mins or couple of hours) observe the following issues
1. 1. The libevent crashes. Checked the core dump
analysis of multiple crashes & it points to the following code.
a. 0x0042b0a8
in evbuffer_add_buffer (outbuf=0x4924b8, inbuf=0x496428) at buffer.c:774
2. Sometimes
the connection between server & client gets disconnected. When checked
through wireshark, observed a FIN ACK coming from the HTTP server.
Below is a simplified code snippet showcasing the implementation
& use case.
I would appreciate if someone could let know, if I am
doing something wrong in the usage of the libevent APIs & what could be the
cause of the crash.
#define KEY_STRING "CHECK"
struct evhttp_request *req; //Structure holds the req, on which chunk reply is sent
pthread_t SenderThread;
bool startSend = false;
void connectionClosed(struct
evhttp_connection* evcon, void* key)
{
printf("Connection
closed by client");
}
//****** Function to send some data every 5 secs************/
void sendChunk(struct evhttp_connection*
evcon, void* key)
{
int
counter = 0;
printf("Start
sending data everyy 5 secs");
while(1)
{
if(startSend)
//startSend is set to true when client does a GET & SSEEvents_cb() is
invoked
{
evbuffer
*databuf = evbuffer_new();
if
(databuf != NULL)
{
evbuffer_add(databuf,"event:remoteEvent\n",strlen("event:remoteEvent\n"));
evbuffer_add_printf(databuf,"data:TestDATA%d\n",counter);
evhttp_send_reply_chunk(req,
databuf);
evbuffer_free(databuf);
counter++;
if(10000 == counter)
counter = 0;
}
sleep(5);
}
else
{
sleep(2); //Sleep for 2 secs & then check if the flag is set or not
}
}
}
///******Callback when Client does a GET on URL "/SSEEvents"********
void SSEEvents_cb(struct evhttp_request
*aReq, void *key)
{
if
(EVHTTP_REQ_GET == aReq->type)
{
const
char * getUri = evhttp_request_get_uri(aReq);
if(
NULL == getUri )
{
evhttp_send_reply(aReq,
400, "Bad request", NULL); //Response
back to client
}
req
= (evhttp_request*) aReq;
evhttp_request_own(aReq);
evhttp_connection_set_closecb(evhttp_request_get_connection(aReq),&connectionClosed,
KEY_STRING);
evhttp_add_header(aReq->output_headers,"Content-Type","text/event-stream");
//Mandatory
evhttp_send_reply_start(aReq,
200, "OK"); ///Start the reply & then set the flag so that chunk reply can be sent every 5 secs
//
Set this flag to true so that the sender thread can start sending chunk data
startSend
= true;
}
else
{
evhttp_send_reply(aReq,
501, " SET Not Implemented", NULL);
}
}
int StartServer(char *aIPAddress, char
*aPort)
{
base
= event_base_new();
if
(!base)
{
printf("Couldn't
create an event_base: So Exiting");
return
1;
}
/*
Create a new evhttp object to handle requests. */
http
= evhttp_new(base);
if
(!http)
{
printf("Couldn't
create evhttp: So Exiting....");
return
1;
}
//Register
for callback for any HTTP request for url "SSEEvents"
evhttp_set_cb(http,
"/SSEEvents", SSEEvents_cb, this);
/*
Now we tell the evhttp what port to listen on */
uint16_t
port = atoi(aPort);
handle
= evhttp_bind_socket_with_handle(http, aIPAddress, port);
if
(!handle)
{
printf("couldn't
bind to IP & port %d. Exiting.",(uint16_t)port);
return
-1;
}
///**
Launch Thread to send chunk reply after http client makes request
pthread_attr_t
attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr,
(128 * 1024)); //128kbs
int
threadCreate = -1;
threadCreate
= pthread_create(&SenderThread, NULL, &sendChunk, NULL);
event_base_dispatch(base);
return
0;
}
int main(int argc , char *argv[])
{
StartServer(argv[1],
argv[2]);
}
Regards,
Alap