+ list_add_tail(&d->list, &dispatch_handlers);
+}
+
+static struct dispatch_handler *
+dispatch_find(const char *url, struct path_info *pi)
+{
+ struct dispatch_handler *d;
+
+ list_for_each_entry(d, &dispatch_handlers, list) {
+ if (pi) {
+ if (d->check_url)
+ continue;
+
+ if (d->check_path(pi, url))
+ return d;
+ } else {
+ if (d->check_path)
+ continue;
+
+ if (d->check_url(url))
+ return d;
+ }
+ }
+
+ 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);
+
+ cl = dr->cl;
+ dr->called = true;
+ cl->dispatch.data_blocked = false;
+ uh_invoke_script(cl, dr->d, dr->path ? &dr->pi : NULL);
+ client_poll_post_data(cl);
+ }
+}
+
+
+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)
+
+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;
+
+ 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;
+ cl->dispatch.data_blocked = true;
+ 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] = {
+ [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING },
+ [HDR_IF_MODIFIED_SINCE] = { "if-modified-since", BLOBMSG_TYPE_STRING },
+ [HDR_IF_UNMODIFIED_SINCE] = { "if-unmodified-since", BLOBMSG_TYPE_STRING },
+ [HDR_IF_MATCH] = { "if-match", BLOBMSG_TYPE_STRING },
+ [HDR_IF_NONE_MATCH] = { "if-none-match", BLOBMSG_TYPE_STRING },
+ [HDR_IF_RANGE] = { "if-range", BLOBMSG_TYPE_STRING },
+ };
+ struct dispatch_handler *d;
+ struct blob_attr *tb[__HDR_MAX];