From 78f9f35e22c60d5748f0d69a202ca541c517f0bb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 19 Jan 2013 18:30:23 +0100 Subject: [PATCH] add support for deferring script requests, limit maximum number of script calls to 3, maximum number of connections to 100 Signed-off-by: Felix Fietkau --- cgi.c | 1 + client.c | 2 ++ file.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- listen.c | 8 ++--- lua.c | 1 + main.c | 14 +++++--- uhttpd.h | 8 ++++- 7 files changed, 139 insertions(+), 15 deletions(-) diff --git a/cgi.c b/cgi.c index 7b47e7d..c4438b0 100644 --- a/cgi.c +++ b/cgi.c @@ -107,6 +107,7 @@ static bool check_cgi_path(struct path_info *pi, const char *url) } struct dispatch_handler cgi_dispatch = { + .script = true, .check_path = check_cgi_path, .handle_request = cgi_handle_request, }; diff --git a/client.c b/client.c index e1fdca4..7604d7e 100644 --- a/client.c +++ b/client.c @@ -73,6 +73,8 @@ static void uh_dispatch_done(struct client *cl) { if (cl->dispatch.free) cl->dispatch.free(cl); + if (cl->dispatch.req_free) + cl->dispatch.req_free(cl); } static void client_timeout(struct uloop_timeout *timeout) diff --git a/file.c b/file.c index 3ac863c..f16b893 100644 --- a/file.c +++ b/file.c @@ -34,6 +34,16 @@ static LIST_HEAD(index_files); static LIST_HEAD(dispatch_handlers); +static LIST_HEAD(pending_requests); +static int n_requests; + +struct deferred_request { + struct list_head list; + struct dispatch_handler *d; + struct client *cl; + struct path_info pi; + bool called, path; +}; struct index_file { struct list_head list; @@ -650,6 +660,106 @@ dispatch_find(const char *url, struct path_info *pi) return NULL; } +static void +uh_invoke_script(struct client *cl, struct dispatch_handler *d, struct path_info *pi) +{ + char *url = blobmsg_data(blob_data(cl->hdr.head)); + + n_requests++; + d->handle_request(cl, url, pi); +} + +static void uh_complete_request(struct client *cl) +{ + struct deferred_request *dr; + + n_requests--; + + while (!list_empty(&pending_requests)) { + if (n_requests >= conf.max_script_requests) + return; + + dr = list_first_entry(&pending_requests, struct deferred_request, list); + list_del(&dr->list); + + dr->called = true; + uh_invoke_script(dr->cl, dr->d, dr->path ? &dr->pi : NULL); + } +} + + +static void +uh_free_pending_request(struct client *cl) +{ + struct deferred_request *dr = cl->dispatch.req_data; + + if (dr->called) + uh_complete_request(cl); + else + list_del(&dr->list); + free(dr); +} + +static int field_len(const char *ptr) +{ + if (!ptr) + return 0; + + return strlen(ptr) + 1; +} + +#define path_info_fields \ + _field(root) \ + _field(phys) \ + _field(name) \ + _field(info) \ + _field(query) \ + _field(auth) + +static void +uh_defer_script(struct client *cl, struct dispatch_handler *d, struct path_info *pi) +{ + struct deferred_request *dr; + char *_root, *_phys, *_name, *_info, *_query, *_auth; + + cl->dispatch.req_free = uh_free_pending_request; + + if (pi) { + /* allocate enough memory to duplicate all path_info strings in one block */ +#undef _field +#define _field(_name) &_##_name, field_len(pi->_name), + dr = calloc_a(sizeof(*dr), path_info_fields NULL); + + memcpy(&dr->pi, pi, sizeof(*pi)); + dr->path = true; + + /* copy all path_info strings */ +#undef _field +#define _field(_name) if (pi->_name) dr->pi._name = strcpy(_##_name, pi->_name); + path_info_fields + } else { + dr = calloc(1, sizeof(*dr)); + } + + cl->dispatch.req_data = dr; + dr->cl = cl; + dr->d = d; + list_add(&dr->list, &pending_requests); +} + +static void +uh_invoke_handler(struct client *cl, struct dispatch_handler *d, char *url, struct path_info *pi) +{ + if (!d->script) + return d->handle_request(cl, url, pi); + + if (n_requests >= conf.max_script_requests) + return uh_defer_script(cl, d, pi); + + cl->dispatch.req_free = uh_complete_request; + uh_invoke_script(cl, d, pi); +} + static bool __handle_file_request(struct client *cl, char *url) { static const struct blobmsg_policy hdr_policy[__HDR_MAX] = { @@ -680,7 +790,7 @@ static bool __handle_file_request(struct client *cl, char *url) d = dispatch_find(url, pi); if (d) - d->handle_request(cl, url, pi); + uh_invoke_handler(cl, d, url, pi); else uh_file_request(cl, url, pi, tb); @@ -691,15 +801,13 @@ void uh_handle_request(struct client *cl) { struct http_request *req = &cl->request; struct dispatch_handler *d; - char *url = blobmsg_data(blob_data(cl->hdr.head));; + char *url = blobmsg_data(blob_data(cl->hdr.head)); char *error_handler; req->redirect_status = 200; d = dispatch_find(url, NULL); - if (d) { - d->handle_request(cl, url, NULL); - return; - } + if (d) + return uh_invoke_handler(cl, d, url, NULL); if (__handle_file_request(cl, url)) return; diff --git a/listen.c b/listen.c index 37fc4bb..adf8b16 100644 --- a/listen.c +++ b/listen.c @@ -56,8 +56,8 @@ static void uh_poll_listeners(struct uloop_timeout *timeout) { struct listener *l; - if ((!n_blocked && conf.max_requests) || - n_clients >= conf.max_requests) + if ((!n_blocked && conf.max_connections) || + n_clients >= conf.max_connections) return; list_for_each_entry(l, &listeners, list) { @@ -65,7 +65,7 @@ static void uh_poll_listeners(struct uloop_timeout *timeout) continue; l->fd.cb(&l->fd, ULOOP_READ); - if (n_clients >= conf.max_requests) + if (n_clients >= conf.max_connections) break; n_blocked--; @@ -92,7 +92,7 @@ static void listener_cb(struct uloop_fd *fd, unsigned int events) break; } - if (conf.max_requests && n_clients >= conf.max_requests) + if (conf.max_connections && n_clients >= conf.max_connections) uh_block_listener(l); } diff --git a/lua.c b/lua.c index 6bf5c24..c6f27e9 100644 --- a/lua.c +++ b/lua.c @@ -280,6 +280,7 @@ static bool check_lua_url(const char *url) } static struct dispatch_handler lua_dispatch = { + .script = true, .check_url = check_lua_url, .handle_request = lua_handle_request, }; diff --git a/main.c b/main.c index 56cff80..31700c7 100644 --- a/main.c +++ b/main.c @@ -132,7 +132,8 @@ static int usage(const char *name) " -S Do not follow symbolic links outside of the docroot\n" " -D Do not allow directory listings, send 403 instead\n" " -R Enable RFC1918 filter\n" - " -n count Maximum allowed number of concurrent requests\n" + " -n count Maximum allowed number of concurrent script requests\n" + " -N count Maximum allowed number of concurrent connections\n" #ifdef HAVE_LUA " -l string URL prefix for Lua handler, default is '/lua'\n" " -L file Lua handler script, omit to disable Lua\n" @@ -159,7 +160,8 @@ static void init_defaults(void) conf.script_timeout = 60; conf.network_timeout = 30; conf.http_keepalive = 20; - conf.max_requests = 3; + conf.max_script_requests = 3; + conf.max_connections = 100; conf.realm = "Protected Area"; conf.cgi_prefix = "/cgi-bin"; conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin"; @@ -201,7 +203,7 @@ int main(int argc, char **argv) init_defaults(); signal(SIGPIPE, SIG_IGN); - while ((ch = getopt(argc, argv, "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:n:x:i:t:k:T:A:u:U:")) != -1) { + while ((ch = getopt(argc, argv, "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:")) != -1) { bool tls = false; switch(ch) { @@ -253,7 +255,11 @@ int main(int argc, char **argv) break; case 'n': - conf.max_requests = atoi(optarg); + conf.max_script_requests = atoi(optarg); + break; + + case 'N': + conf.max_connections = atoi(optarg); break; case 'x': diff --git a/uhttpd.h b/uhttpd.h index 38f60d3..807070c 100644 --- a/uhttpd.h +++ b/uhttpd.h @@ -62,7 +62,8 @@ struct config { int network_timeout; int rfc1918_filter; int tcp_keepalive; - int max_requests; + int max_script_requests; + int max_connections; int http_keepalive; int script_timeout; }; @@ -165,6 +166,7 @@ struct dispatch_proc { struct dispatch_handler { struct list_head list; + bool script; bool (*check_url)(const char *url); bool (*check_path)(struct path_info *pi, const char *url); @@ -198,6 +200,10 @@ struct dispatch { void (*write_cb)(struct client *cl); void (*close_fds)(struct client *cl); void (*free)(struct client *cl); + + void *req_data; + void (*req_free)(struct client *cl); + bool data_blocked; union { -- 2.11.0