diff -rp -U 5 libevent-1.4.14-stable/event.c libevent-1.4.14-stable-fb/event.c --- libevent-1.4.14-stable/event.c 2010-06-07 14:40:35.000000000 -0700 +++ libevent-1.4.14-stable-fb/event.c 2010-06-18 16:30:57.000000000 -0700 @@ -136,14 +136,16 @@ detect_monotonic(void) } static int gettime(struct event_base *base, struct timeval *tp) { +/* if (base->tv_cache.tv_sec) { *tp = base->tv_cache; return (0); } +*/ #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) if (use_monotonic) { struct timespec ts; @@ -479,11 +481,11 @@ event_base_loop(struct event_base *base, struct timeval tv; struct timeval *tv_p; int res, done; /* clear time cache */ - base->tv_cache.tv_sec = 0; + /* base->tv_cache.tv_sec = 0; */ if (base->sig.ev_signal_added) evsignal_base = base; done = 0; while (!done) { @@ -531,17 +533,17 @@ event_base_loop(struct event_base *base, /* update last old time */ gettime(base, &base->event_tv); /* clear time cache */ - base->tv_cache.tv_sec = 0; + /* base->tv_cache.tv_sec = 0; */ res = evsel->dispatch(base, evbase, tv_p); if (res == -1) return (-1); - gettime(base, &base->tv_cache); + /* gettime(base, &base->tv_cache); */ timeout_process(base); if (base->event_count_active) { event_process_active(base); @@ -550,11 +552,11 @@ event_base_loop(struct event_base *base, } else if (flags & EVLOOP_NONBLOCK) done = 1; } /* clear time cache */ - base->tv_cache.tv_sec = 0; + /* base->tv_cache.tv_sec = 0; */ event_debug(("%s: asked to terminate loop.", __func__)); return (0); } Only in libevent-1.4.14-stable-fb: event.c.orig diff -rp -U 5 libevent-1.4.14-stable/evhttp.h libevent-1.4.14-stable-fb/evhttp.h --- libevent-1.4.14-stable/evhttp.h 2010-06-07 14:40:35.000000000 -0700 +++ libevent-1.4.14-stable-fb/evhttp.h 2010-06-18 16:36:24.000000000 -0700 @@ -79,16 +79,54 @@ struct evhttp *evhttp_new(struct event_b * to multiple different ports. * * @param http a pointer to an evhttp object * @param address a string containing the IP address to listen(2) on * @param port the port number to listen on - * @return a newly allocated evhttp struct + * @return 0 on success, -1 on error * @see evhttp_free() */ int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port); /** + * Binds an HTTP server on the specified address and port, using backlog. + * + * Can be called multiple times to bind the same http server + * to multiple different ports. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @param backlog the backlog value for listen(2) + * @return 0 on success, -1 on error + * @see evhttp_free() + */ +int evhttp_bind_socket_backlog(struct evhttp *http, const char *address, u_short port, int backlog); + +/** + * Like evhttp_bind_socket(), but returns the socket file descriptor. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return Socket file descriptor on success, -1 on failure + * @see evhttp_bind_socket() + */ +int evhttp_bind_socket_with_fd(struct evhttp *http, const char *address, u_short port); + +/** + * Like evhttp_bind_socket(), but returns the socket file descriptor. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @param backlog the backlog value for listen(2) + * @return Socket file descriptor on success, -1 on failure + * @see evhttp_bind_socket() + */ +int evhttp_bind_socket_backlog_fd(struct evhttp *http, const char *address, u_short port, int backlog); + +/** * Makes an HTTP server accept connections on the specified socket * * This may be useful to create a socket and then fork multiple instances * of an http server, or when a socket has been communicated via file * descriptor passing in situations where an http servers does not have @@ -103,10 +141,25 @@ int evhttp_bind_socket(struct evhttp *ht * @see evhttp_free(), evhttp_bind_socket() */ int evhttp_accept_socket(struct evhttp *http, int fd); /** + * Makes an HTTP server stop accepting connections on the specified socket + * + * This may be useful when a socket has been sent via file descriptor passing + * and is no longer needed by the current process. + * + * This function does not close the socket. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is currently accepting connections + * @return 0 on success, -1 on failure. + * @see evhttp_accept_socket() + */ +int evhttp_del_accept_socket(struct evhttp *http, int fd); + +/** * Free the previously created HTTP server. * * Works only if no requests are currently being served. * * @param http the evhttp server object to be freed @@ -132,10 +185,25 @@ void evhttp_set_gencb(struct evhttp *, * @param http an evhttp object * @param timeout_in_secs the timeout, in seconds */ void evhttp_set_timeout(struct evhttp *, int timeout_in_secs); +/** + * Limit the number of simultaneous connections via this http instance. + * + * @param http an evhttp object + * @param nlimit the maximum number of connections, zero is unlimited + */ +int evhttp_set_connection_limit(struct evhttp *http, int nlimit); + +/** + * Return the maximum number of connections allowed for this instance. + * + * @param http an evhttp object + */ +int evhttp_get_connection_limit(struct evhttp *http); + /* Request/Response functionality */ /** * Send an HTML error message to the client. * @@ -155,10 +223,23 @@ void evhttp_send_error(struct evhttp_req * @param databuf the body of the response */ void evhttp_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *databuf); +/** + * Send an HTML reply synchronously as much as possible by calling _begin(). + * Great for a worker thread to send the reply immediately without queuing up + * events back to the loop. Call _end() to send the rest of the packet from + * event loop. + * + * When _begin() returns needs to be fed into _end() as the 1st parameter + * "nwritten". + */ +int evhttp_send_reply_sync_begin(struct evhttp_request *req, int code, + const char *reason, struct evbuffer *databuf); +void evhttp_send_reply_sync_end(int nwritten, struct evhttp_request *req); + /* Low-level response interface, for streaming/chunked replies */ void evhttp_send_reply_start(struct evhttp_request *, int, const char *); void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); void evhttp_send_reply_end(struct evhttp_request *); @@ -208,10 +289,11 @@ struct { char *remote_host; u_short remote_port; enum evhttp_request_kind kind; enum evhttp_cmd_type type; + char *ext_method; /* webdav methods, for example */ char *uri; /* uri after HTTP request was parsed */ char major; /* HTTP Major number */ char minor; /* HTTP Minor number */ @@ -222,10 +304,12 @@ struct { struct evbuffer *input_buffer; /* read data */ ev_int64_t ntoread; int chunked:1, /* a chunked request */ userdone:1; /* the user has sent all data */ + int referenced; + struct evbuffer *output_buffer; /* outgoing post or data */ /* Callback */ void (*cb)(struct evhttp_request *, void *); void *cb_arg; diff -rp -U 5 libevent-1.4.14-stable/http-internal.h libevent-1.4.14-stable-fb/http-internal.h --- libevent-1.4.14-stable/http-internal.h 2010-06-07 14:40:35.000000000 -0700 +++ libevent-1.4.14-stable-fb/http-internal.h 2010-06-18 16:30:57.000000000 -0700 @@ -114,10 +114,13 @@ struct evhttp { TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; struct evconq connections; + int connection_count; + int connection_limit; + int timeout; void (*gencb)(struct evhttp_request *req, void *); void *gencbarg; diff -rp -U 5 libevent-1.4.14-stable/http.c libevent-1.4.14-stable-fb/http.c --- libevent-1.4.14-stable/http.c 2010-06-07 14:40:35.000000000 -0700 +++ libevent-1.4.14-stable-fb/http.c 2010-06-18 16:35:23.000000000 -0700 @@ -217,10 +217,17 @@ static int evhttp_decode_uri_internal(co char *ret, int always_decode_plus); void evhttp_read(int, short, void *); void evhttp_write(int, short, void *); + +void evhttp_server_drop_connection(struct evhttp_connection *evcon); +void evhttp_server_add_connection(struct evhttp *http, + struct evhttp_connection *evcon); +void evhttp_pause(struct evhttp *http); +void evhttp_resume(struct evhttp *http); + #ifndef HAVE_STRSEP /* strsep replacement for platforms that lack it. Only works if * del is one character long. */ static char * strsep(char **s, const char *del) @@ -476,21 +483,19 @@ evhttp_make_header_response(struct evhtt */ if (req->minor == 0 && is_keepalive) evhttp_add_header(req->output_headers, "Connection", "keep-alive"); - if (req->minor == 1 || is_keepalive) { /* * we need to add the content length if the * user did not give it, this is required for * persistent connections to work. */ evhttp_maybe_add_content_length_header( req->output_headers, (long)EVBUFFER_LENGTH(req->output_buffer)); } - } /* Potentially add headers for unidentified content. */ if (EVBUFFER_LENGTH(req->output_buffer)) { if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) { @@ -685,18 +690,18 @@ evhttp_connection_fail(struct evhttp_con void evhttp_write(int fd, short what, void *arg) { struct evhttp_connection *evcon = arg; - int n; if (what == EV_TIMEOUT) { evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); return; } - n = evbuffer_write(evcon->output_buffer, fd); + if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) { + int n = evbuffer_write(evcon->output_buffer, fd); if (n == -1) { event_debug(("%s: evbuffer_write", __func__)); evhttp_connection_fail(evcon, EVCON_HTTP_EOF); return; } @@ -704,10 +709,11 @@ evhttp_write(int fd, short what, void *a if (n == 0) { event_debug(("%s: write nothing", __func__)); evhttp_connection_fail(evcon, EVCON_HTTP_EOF); return; } + } if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) { evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT); return; @@ -1010,15 +1016,13 @@ evhttp_connection_free(struct evhttp_con */ while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) { TAILQ_REMOVE(&evcon->requests, req, next); evhttp_request_free(req); } - - if (evcon->http_server != NULL) { - struct evhttp *http = evcon->http_server; - TAILQ_REMOVE(&http->connections, evcon, next); - } + + if (evcon->http_server != NULL) + evhttp_server_drop_connection(evcon); if (event_initialized(&evcon->close_ev)) event_del(&evcon->close_ev); if (event_initialized(&evcon->ev)) @@ -1099,14 +1103,20 @@ evhttp_connection_reset(struct evhttp_co EVUTIL_CLOSESOCKET(evcon->fd); evcon->fd = -1; } evcon->state = EVCON_DISCONNECTED; - evbuffer_drain(evcon->input_buffer, - EVBUFFER_LENGTH(evcon->input_buffer)); - evbuffer_drain(evcon->output_buffer, - EVBUFFER_LENGTH(evcon->output_buffer)); + /* + * These can grow quite large if processing a large photo or video + * upload/download. Instead of keeping the buffers around, just + * free and allocate new. + */ + evbuffer_free(evcon->input_buffer); + evcon->input_buffer = evbuffer_new(); + + evbuffer_free(evcon->output_buffer); + evcon->output_buffer = evbuffer_new(); } static void evhttp_detect_close_cb(int fd, short what, void *arg) { @@ -1276,23 +1286,56 @@ evhttp_parse_request_line(struct evhttp_ /* Parse the request line */ method = strsep(&line, " "); if (line == NULL) return (-1); uri = strsep(&line, " "); - if (line == NULL) - return (-1); + if (line == NULL) { + version = "HTTP/1.0"; + } else { version = strsep(&line, " "); if (line != NULL) return (-1); + } /* First line */ + req->ext_method = NULL; if (strcmp(method, "GET") == 0) { req->type = EVHTTP_REQ_GET; } else if (strcmp(method, "POST") == 0) { req->type = EVHTTP_REQ_POST; } else if (strcmp(method, "HEAD") == 0) { req->type = EVHTTP_REQ_HEAD; + } else if (strcmp(method, "OPTIONS") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "OPTIONS"; + } else if (strcmp(method, "REPORT") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "REPORT"; + } else if (strcmp(method, "PROPFIND") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "PROPFIND"; + } else if (strcmp(method, "PROPPATH") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "PROPPATH"; + } else if (strcmp(method, "MKCOL") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "MKCOL"; + } else if (strcmp(method, "MKCALENDAR") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "MKCALENDAR"; + } else if (strcmp(method, "PUT") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "PUT"; + } else if (strcmp(method, "DELETE") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "DELETE"; + } else if (strcmp(method, "LOCK") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "LOCK"; + } else if (strcmp(method, "UNLOCK") == 0) { + req->type = EVHTTP_REQ_POST; + req->ext_method = "UNLOCK"; } else { event_debug(("%s: bad method %s on request %p from %s", __func__, method, req, req->remote_host)); return (-1); } @@ -1961,14 +2004,48 @@ evhttp_send_reply(struct evhttp_request evhttp_response_code(req, code, reason); evhttp_send(req, databuf); } +int +evhttp_send_reply_sync_begin(struct evhttp_request *req, int code, + const char *reason, struct evbuffer *databuf) { + evhttp_response_code(req, code, reason); + struct evhttp_connection *evcon = req->evcon; + + assert(TAILQ_FIRST(&evcon->requests) == req); + + /* xxx: not sure if we really should expose the data buffer this way */ + if (databuf != NULL) + evbuffer_add_buffer(req->output_buffer, databuf); + + /* Adds headers to the response */ + evhttp_make_header(evcon, req); + + return evbuffer_write(evcon->output_buffer, evcon->fd); +} + +void +evhttp_send_reply_sync_end(int nwritten, struct evhttp_request *req) { + struct evhttp_connection *evcon = req->evcon; + + if (nwritten <= 0) { + evhttp_connection_fail(evcon, EVCON_HTTP_EOF); + } else if (EVBUFFER_LENGTH(evcon->output_buffer) == 0) { + evhttp_send_done(evcon, NULL); + } else { + evhttp_write_buffer(evcon, evhttp_send_done, NULL); + } +} + + void evhttp_send_reply_start(struct evhttp_request *req, int code, const char *reason) { + req->referenced = 1; + evhttp_response_code(req, code, reason); if (req->major == 1 && req->minor == 1) { /* use chunked encoding for HTTP/1.1 */ evhttp_add_header(req->output_headers, "Transfer-Encoding", "chunked"); @@ -1984,10 +2061,12 @@ evhttp_send_reply_chunk(struct evhttp_re struct evhttp_connection *evcon = req->evcon; if (evcon == NULL) return; + if (req->referenced < 0) return; + if (req->chunked) { evbuffer_add_printf(evcon->output_buffer, "%x\r\n", (unsigned)EVBUFFER_LENGTH(databuf)); } evbuffer_add_buffer(evcon->output_buffer, databuf); @@ -2005,11 +2084,18 @@ evhttp_send_reply_end(struct evhttp_requ if (evcon == NULL) { evhttp_request_free(req); return; } - /* we expect no more calls form the user on this request */ + if (req->referenced < 0) { + req->referenced = 0; + evhttp_request_free(req); + return; + } + req->referenced = 0; + + /* we expect no more calls form the user on this request */ req->userdone = 1; if (req->chunked) { evbuffer_add(req->evcon->output_buffer, "0\r\n\r\n", 5); evhttp_write_buffer(req->evcon, evhttp_send_done, NULL); @@ -2291,33 +2377,63 @@ accept_socket(int fd, short what, void * evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen); } int -evhttp_bind_socket(struct evhttp *http, const char *address, u_short port) +evhttp_bind_socket_backlog_fd(struct evhttp *http, const char *address, + u_short port, int backlog) { int fd; int res; if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1) return (-1); - if (listen(fd, 128) == -1) { + if (listen(fd, backlog) == -1) { event_warn("%s: listen", __func__); EVUTIL_CLOSESOCKET(fd); return (-1); } res = evhttp_accept_socket(http, fd); - if (res != -1) + if (res != -1) { event_debug(("Bound to port %d - Awaiting connections ... ", port)); + return (fd); + } return (res); } +static int +mask_fd(int fd) +{ + return fd > 0 ? 0 : fd; +} + +int +evhttp_bind_socket(struct evhttp *http, const char *address, u_short port) +{ + return mask_fd(evhttp_bind_socket_backlog_fd(http, address, port, 128)); +} + +int +evhttp_bind_socket_with_fd(struct evhttp *http, const char *address, + u_short port) +{ + return evhttp_bind_socket_backlog_fd(http, address, port, 128); +} + +int +evhttp_bind_socket_backlog(struct evhttp *http, const char *address, + u_short port, int backlog) +{ + return mask_fd( + evhttp_bind_socket_backlog_fd(http, address, port, backlog)); +} + int evhttp_accept_socket(struct evhttp *http, int fd) { struct evhttp_bound_socket *bound; struct event *ev; @@ -2343,10 +2459,29 @@ evhttp_accept_socket(struct evhttp *http TAILQ_INSERT_TAIL(&http->sockets, bound, next); return (0); } +int +evhttp_del_accept_socket(struct evhttp *http, int fd) +{ + struct evhttp_bound_socket *bound; + TAILQ_FOREACH(bound, &http->sockets, next) { + if (bound->bind_ev.ev_fd == fd) + break; + } + + if (bound == NULL) + return (-1); + + TAILQ_REMOVE(&http->sockets, bound, next); + event_del(&bound->bind_ev); + free(bound); + + return (0); +} + static struct evhttp* evhttp_new_object(void) { struct evhttp *http = NULL; @@ -2525,10 +2660,15 @@ evhttp_request_new(void (*cb)(struct evh } void evhttp_request_free(struct evhttp_request *req) { + if (req->referenced) { + req->referenced = -1; + return; + } + if (req->remote_host != NULL) free(req->remote_host); if (req->uri != NULL) free(req->uri); if (req->response_code_line != NULL) @@ -2655,17 +2795,76 @@ evhttp_get_request(struct evhttp *http, /* * if we want to accept more than one request on a connection, * we need to know which http server it belongs to. */ - evcon->http_server = http; - TAILQ_INSERT_TAIL(&http->connections, evcon, next); + + evhttp_server_add_connection(http, evcon); if (evhttp_associate_new_request_with_connection(evcon) == -1) evhttp_connection_free(evcon); } +void +evhttp_pause(struct evhttp *http) +{ + struct evhttp_bound_socket *bound; + TAILQ_FOREACH(bound, &http->sockets, next) { + event_del(&bound->bind_ev); + } +} + +void +evhttp_resume(struct evhttp *http) +{ + struct evhttp_bound_socket *bound; + TAILQ_FOREACH(bound, &http->sockets, next) { + event_add(&bound->bind_ev, 0); + } +} + +int +evhttp_get_connection_limit(struct evhttp *http) +{ + return http->connection_limit; +} + +int +evhttp_set_connection_limit(struct evhttp *http, int nlimit) +{ + int olimit = http->connection_limit; + http->connection_limit = nlimit; + return olimit; +} + +void +evhttp_server_add_connection(struct evhttp *http, + struct evhttp_connection *evcon) +{ + evcon->http_server = http; + TAILQ_INSERT_TAIL(&http->connections, evcon, next); + + http->connection_count++; + if (http->connection_limit > 0 + && http->connection_count >= http->connection_limit) + { + evhttp_pause(http); + } +} + +void +evhttp_server_drop_connection(struct evhttp_connection *evcon) +{ + struct evhttp *http = evcon->http_server; + TAILQ_REMOVE(&http->connections, evcon, next); + http->connection_count--; + if (http->connection_limit > 0 + && http->connection_count < http->connection_limit) + { + evhttp_resume(http); + } +} /* * Network helper functions that we do not want to export to the rest of * the world. */