Hi The meat. Benchmarks at http://candgsoc.host56.com/2012/06/cgi-plugin-moving-to-events/
5 files changed, 298 insertions(+), 48 deletions(-) - Lauri
>From 906b19cb877b8a401b0ad44ced6e4d16cadb29ce Mon Sep 17 00:00:00 2001 From: Lauri Kasanen <[email protected]> Date: Mon, 18 Jun 2012 12:08:11 +0300 Subject: [PATCH 3/3] cgi: Update to an event model Signed-off-by: Lauri Kasanen <[email protected]> --- plugins/cgi/Makefile.in | 2 +- plugins/cgi/cgi.c | 93 +++++++++++++++++++------------------- plugins/cgi/cgi.h | 88 ++++++++++++++++++++++++++++++++++++ plugins/cgi/event.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++ plugins/cgi/request.c | 48 +++++++++++++++++++ 5 files changed, 298 insertions(+), 48 deletions(-) create mode 100644 plugins/cgi/cgi.h create mode 100644 plugins/cgi/event.c create mode 100644 plugins/cgi/request.c diff --git a/plugins/cgi/Makefile.in b/plugins/cgi/Makefile.in index 97a9cd8..fb2c1d3 100644 --- a/plugins/cgi/Makefile.in +++ b/plugins/cgi/Makefile.in @@ -5,7 +5,7 @@ CFLAGS = $CFLAGS LDFLAGS = $LDFLAGS DEFS = $DEFS INCDIR = ../../src/include -OBJECTS = cgi.o +OBJECTS = cgi.o request.o event.o all: monkey-cgi.so diff --git a/plugins/cgi/cgi.c b/plugins/cgi/cgi.c index 0d68fcb..c0a47b4 100644 --- a/plugins/cgi/cgi.c +++ b/plugins/cgi/cgi.c @@ -17,29 +17,19 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define _GNU_SOURCE +#include <sys/time.h> +#include <sys/resource.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <regex.h> -#include <unistd.h> -#include <signal.h> -#include <libgen.h> - -#include "MKPlugin.h" +#include "cgi.h" MONKEY_PLUGIN("cgi", /* shortname */ "CGI handler", /* name */ VERSION, /* version */ - MK_PLUGIN_STAGE_30); /* hooks */ + MK_PLUGIN_STAGE_30 | MK_PLUGIN_CORE_THCTX); /* hooks */ static regex_t match_regex; -enum { - PATHLEN = 1024, - SHORTLEN = 36 -}; +struct cgi_request **requests_by_socket; struct post_t { int fd; @@ -47,7 +37,7 @@ struct post_t { unsigned long len; }; -static int swrite(const int fd, const void *buf, const size_t count) +int swrite(const int fd, const void *buf, const size_t count) { ssize_t pos = count, ret = 0; @@ -72,7 +62,9 @@ static void cgi_write_post(void *p) } static int do_cgi(const char * const __restrict__ file, const char * const __restrict__ url, - int socket, const struct session_request * const sr) + int socket, struct session_request * const sr, + struct client_session * const cs, + struct plugin * const plugin) { char *env[30]; @@ -217,8 +209,6 @@ static int do_cgi(const char * const __restrict__ file, const char * const __res close(writepipe[0]); close(readpipe[1]); - mk_api->socket_cork_flag(socket, TCP_CORK_ON); - /* If we have POST data to write, spawn a thread to do that */ if (sr->data.len) { struct post_t p; @@ -228,37 +218,22 @@ static int do_cgi(const char * const __restrict__ file, const char * const __res mk_api->worker_spawn(cgi_write_post, &p); } + else { + close(writepipe[1]); + } - char buf[PATHLEN], *outptr; - long count; - char headers_done = 0; - - while ((count = read(readpipe[0], buf, PATHLEN)) > 0) { - outptr = buf; - /* We have to check the headers are OK */ - if (!headers_done) { - if (count >= 8 && memcmp(buf, "Status: ", 8) == 0) { - swrite(socket, "HTTP/1.0 ", 9); - outptr += 8; - count -= 8; - headers_done = 1; - } - else if (count >= 4) { - if (memcmp(buf, "HTTP", 4) != 0) { - swrite(socket, "HTTP/1.0 200 OK\r\n", sizeof("HTTP/1.0 200 OK\r\n") - 1); - } - headers_done = 1; - } - } + struct cgi_request *r = cgi_req_create(readpipe[0], socket); + if (!r) return 403; - swrite(socket, outptr, count); - } + cgi_req_add(r); + mk_api->event_add(readpipe[0], MK_EPOLL_READ, plugin, cs, sr, MK_EPOLL_LEVEL_TRIGGERED); - close(writepipe[1]); - close(readpipe[0]); + /* XXX Fixme: this needs to be atomic */ + requests_by_socket[socket] = r; - mk_api->socket_cork_flag(socket, TCP_CORK_OFF); + /* We have nothing to write yet */ + mk_api->event_socket_change_mode(socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED); return 200; } @@ -304,6 +279,13 @@ int _mkp_init(struct plugin_api **api, char *confdir) mk_api = *api; cgi_read_config(confdir); + pthread_key_create(&_mkp_data, NULL); + + struct rlimit lim; + getrlimit(RLIMIT_NOFILE, &lim); +// printf("Allocating a cache for %lu fds\n", lim.rlim_cur); + requests_by_socket = mk_api->mem_alloc_z(sizeof(struct cgi_request *) * lim.rlim_cur); + /* Make sure we act good if the child dies */ signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); @@ -314,6 +296,7 @@ int _mkp_init(struct plugin_api **api, char *confdir) void _mkp_exit() { regfree(&match_regex); + free(requests_by_socket); } int _mkp_stage_30(struct plugin *plugin, struct client_session *cs, @@ -333,12 +316,28 @@ int _mkp_stage_30(struct plugin *plugin, struct client_session *cs, if (regexec(&match_regex, url, 0, NULL, 0)) return MK_PLUGIN_RET_NOT_ME; - int status = do_cgi(file, url, cs->socket, sr); + if (cgi_req_get(cs->socket)) { + printf("Error, someone tried to retry\n"); + return MK_PLUGIN_RET_CONTINUE; + } + + int status = do_cgi(file, url, cs->socket, sr, cs, plugin); /* These are just for the other plugins, such as logger; bogus data */ mk_api->header_set_http_status(sr, status); + if (status != 200) + return MK_PLUGIN_RET_END; + sr->close_now = MK_TRUE; - return MK_PLUGIN_RET_END; + return MK_PLUGIN_RET_CONTINUE; +} + +void _mkp_core_thctx() +{ + struct mk_list *list = mk_api->mem_alloc_z(sizeof(struct mk_list)); + + mk_list_init(list); + pthread_setspecific(_mkp_data, list); } diff --git a/plugins/cgi/cgi.h b/plugins/cgi/cgi.h new file mode 100644 index 0000000..a8b896a --- /dev/null +++ b/plugins/cgi/cgi.h @@ -0,0 +1,88 @@ +/* Monkey HTTP Daemon + * ------------------ + * Copyright (C) 2012, Lauri Kasanen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CGI_H +#define CGI_H + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <regex.h> +#include <unistd.h> +#include <signal.h> +#include <libgen.h> + +#include "MKPlugin.h" + +enum { + PATHLEN = 1024, + SHORTLEN = 36 +}; + +struct cgi_request { + + char in_buf[PATHLEN]; + + struct mk_list _head; + + unsigned int in_len; + unsigned int out_len; + + int fd; /* From the CGI app */ + int socket; + + unsigned char headers_done; +}; + +extern struct cgi_request **requests_by_socket; + +int swrite(const int fd, const void *buf, const size_t count); + +struct cgi_request *cgi_req_create(int fd, int socket); +void cgi_req_add(struct cgi_request *r); +int cgi_req_del(struct cgi_request *r); + +static inline struct cgi_request *cgi_req_get(int socket) +{ + struct cgi_request *r = requests_by_socket[socket]; + + return r; +} + +static inline struct cgi_request *cgi_req_get_by_fd(int fd) +{ + struct mk_list *list, *node; + struct cgi_request *r; + + list = pthread_getspecific(_mkp_data); + if (mk_list_is_empty(list) == 0) + return NULL; + + mk_list_foreach(node, list) { + r = mk_list_entry(node, struct cgi_request, _head); + if (r->fd == fd) + return r; + } + + return NULL; +} + +#endif diff --git a/plugins/cgi/event.c b/plugins/cgi/event.c new file mode 100644 index 0000000..f14c12f --- /dev/null +++ b/plugins/cgi/event.c @@ -0,0 +1,115 @@ +/* Monkey HTTP Daemon + * ------------------ + * Copyright (C) 2012, Lauri Kasanen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "cgi.h" + +static int hangup(int socket) +{ + struct cgi_request *r = cgi_req_get_by_fd(socket); + + if (r) { + /* If the CGI app is fast, we might get a hangup event before + * a write event. Try to write things out first. */ + _mkp_event_write(r->socket); + + mk_api->event_del(r->fd); + + mk_api->http_request_end(r->socket); + mk_api->socket_close(r->fd); + + /* XXX Fixme: this needs to be atomic */ + requests_by_socket[r->socket] = NULL; + + cgi_req_del(r); + return MK_PLUGIN_RET_EVENT_OWNED; + } + + return MK_PLUGIN_RET_EVENT_CONTINUE; +} + +int _mkp_event_write(int socket) +{ + struct cgi_request *r = cgi_req_get(socket); + if (!r) return MK_PLUGIN_RET_EVENT_CONTINUE; + + if (r->in_len > 0) { + + mk_api->socket_cork_flag(socket, TCP_CORK_ON); + + const char *buf = r->in_buf, *outptr = r->in_buf; + + if (!r->headers_done) { + if (r->in_len >= 8 && memcmp(buf, "Status: ", 8) == 0) { + swrite(socket, "HTTP/1.0 ", 9); + outptr += 8; + r->in_len -= 8; + r->headers_done = 1; + } + else if (r->in_len >= 4) { + if (memcmp(buf, "HTTP", 4) != 0) { + swrite(socket, "HTTP/1.0 200 OK\r\n", sizeof("HTTP/1.0 200 OK\r\n") - 1); + } + r->headers_done = 1; + } + } + + swrite(socket, outptr, r->in_len); + r->in_len = 0; + mk_api->event_socket_change_mode(socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED); + + mk_api->socket_cork_flag(socket, TCP_CORK_OFF); + } + + return MK_PLUGIN_RET_EVENT_OWNED; +} + +int _mkp_event_read(int fd) +{ + struct cgi_request *r = cgi_req_get_by_fd(fd); + if (!r) return MK_PLUGIN_RET_EVENT_NEXT; + + size_t count = PATHLEN - r->in_len; + + /* Too much to read? Start writing. */ + if (count < 1) + goto out; + + int n = read(r->fd, r->in_buf + r->in_len, count); + + if (n <=0) + return MK_PLUGIN_RET_EVENT_CLOSE; + + r->in_len += n; + +out: + /* Now we do have something to write */ + mk_api->event_socket_change_mode(r->socket, MK_EPOLL_WRITE, MK_EPOLL_LEVEL_TRIGGERED); + + return MK_PLUGIN_RET_EVENT_OWNED; +} + +int _mkp_event_close(int socket) +{ + return hangup(socket); +} + +int _mkp_event_error(int socket) +{ + return hangup(socket); +} diff --git a/plugins/cgi/request.c b/plugins/cgi/request.c new file mode 100644 index 0000000..614df1d --- /dev/null +++ b/plugins/cgi/request.c @@ -0,0 +1,48 @@ +/* Monkey HTTP Daemon + * ------------------ + * Copyright (C) 2012, Lauri Kasanen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "cgi.h" + +struct cgi_request *cgi_req_create(int fd, int socket) +{ + struct cgi_request *newcgi = mk_api->mem_alloc_z(sizeof(struct cgi_request)); + if (!newcgi) return NULL; + + newcgi->fd = fd; + newcgi->socket = socket; + + return newcgi; +} + +void cgi_req_add(struct cgi_request *r) +{ + struct mk_list *list = pthread_getspecific(_mkp_data); + + mk_list_add(&r->_head, list); +} + +int cgi_req_del(struct cgi_request *r) +{ + if (!r) return 1; + + mk_list_del(&r->_head); + free(r); + + return 0; +} -- 1.7.2.1
_______________________________________________ Monkey mailing list [email protected] http://lists.monkey-project.com/listinfo/monkey
