diff options
author | Felix Fietkau <nbd@openwrt.org> | 2013-01-19 18:30:23 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2013-01-19 18:30:23 +0100 |
commit | 78f9f35e22c60d5748f0d69a202ca541c517f0bb (patch) | |
tree | e9be1da7f35f96d8b3bb3232cedc9a8324e3e709 | |
parent | 9cfe019ff4b6637e2bb7fbf5fc8db37abdd3c569 (diff) |
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 <nbd@openwrt.org>
-rw-r--r-- | cgi.c | 1 | ||||
-rw-r--r-- | client.c | 2 | ||||
-rw-r--r-- | file.c | 120 | ||||
-rw-r--r-- | listen.c | 8 | ||||
-rw-r--r-- | lua.c | 1 | ||||
-rw-r--r-- | main.c | 14 | ||||
-rw-r--r-- | uhttpd.h | 8 |
7 files changed, 139 insertions, 15 deletions
@@ -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, }; @@ -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) @@ -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; @@ -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); } @@ -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, }; @@ -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': @@ -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 { |