On Fri, Jun 24, 2011 at 8:22 PM, Mina Naguib <mina.nag...@bloomdigital.com> wrote: > > Hi everyone > > I'm trying to keep a hostile client from causing a libevent server from > consuming too much memory in buffered data. > > Using bufferevents, I let libevent keep the input buffer within limits using > its watermark mechanism, and I added a simple mechanism in the server to, > essentially, stop draining the input buffer if the output buffer is past a > certain size. > > The problem I'm observing using this pattern (and various variations I've > tried), is that at some point libevent stops calling the write-ready callback > set on the bufferevent. > > I've simplified the server code to the bare essentials needed to demonstrate > the problem, source posted here: > http://pastebin.com/CzpVRRAy
Tentatively, I'm going to blame this one on some kind of buffering or stalling issue in netcat or in your terminal, not on libevent. When I write a trivial libevent-based client program to hammer your server program, it works fine. See the attached file -- I used your server program as a template. The underlying pattern you're using here is reasonable, though you'd probably want to polish it a little. Instead of handling as much as possible from the inbuf whenever the outbuf starts out less-than-full, I'd suggest draining the inbuf and adding to the outbuf only until the outbuf becomes full. Also, for a situation like this one, I'd suggest setting the low-watermark for write to something nonzero: In some cases like this you don't want to let your write buffer drop to 0 either. yrs, -- Nick
#include <stdlib.h> #include <unistd.h> #include <errno.h> #include <stdint.h> #include <string.h> #include <time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <event.h> #include <event2/bufferevent.h> #define PORT 6565 unsigned long long bytes_read = 0; struct timeval start_time; void timer_cb(evutil_socket_t fd, short events, void *arg) { struct timeval now; double seconds_elapsed, bytes_per_second; gettimeofday(&now, NULL); seconds_elapsed = now.tv_sec - start_time.tv_sec; seconds_elapsed += (now.tv_usec - start_time.tv_usec) / 1.0e6; bytes_per_second = bytes_read / seconds_elapsed; printf("%f bytes per second\n", bytes_per_second); } void handle_client_buffer_in(struct bufferevent *bev, void *hint) { struct evbuffer *input = bufferevent_get_input(bev); size_t inlen = evbuffer_get_length(input); evbuffer_drain(input, inlen); bytes_read += inlen; } void handle_client_buffer_out(struct bufferevent *bev, void *hint) { struct evbuffer *output = bufferevent_get_output(bev); size_t outlen = evbuffer_get_length(output); int i, n; n = 64*1024; for (i=outlen / 4; i < n; ++i) bufferevent_write(bev, "hi\r\n", 4); } void handle_client_buffer_evt(struct bufferevent *bev, short revents, void *hint) { if (revents & BEV_EVENT_CONNECTED) { printf("Connected\n"); bufferevent_write(bev, "hi\r\nhi\r\n", 8); } else if (revents & BEV_EVENT_ERROR) { printf("Client error: %s\n", strerror(errno)); bufferevent_free(bev); } else if (revents & BEV_EVENT_TIMEOUT) { printf("Client timed out\n"); bufferevent_free(bev); } else if (revents & BEV_EVENT_EOF) { printf("Client disconnected\n"); bufferevent_free(bev); } else { printf("Spurious event from client: %d\n", revents); bufferevent_free(bev); } } int main(int argc, char ** argv) { struct bufferevent *bev; struct event_base *eb = NULL; struct sockaddr_in sa; struct timeval five_seconds = { 5, 0 }; eb = event_base_new(); struct event timer; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(0x7f000001); sa.sin_port = htons(PORT); bev = bufferevent_socket_new(eb, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, handle_client_buffer_in, handle_client_buffer_out, handle_client_buffer_evt, NULL ); bufferevent_enable(bev, EV_READ|EV_WRITE); bufferevent_socket_connect(bev, (struct sockaddr*)&sa, sizeof(sa)); event_assign(&timer, eb, -1, EV_PERSIST, timer_cb, NULL); gettimeofday(&start_time, NULL); event_add(&timer, &five_seconds); event_base_dispatch(eb); return 0; }