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

Reply via email to