file: poke ustream after starting deferred program
[project/uhttpd.git] / handler.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <libubox/blobmsg.h>
20 #include <libubox/blobmsg_json.h>
21 #include <libubox/json_script.h>
22
23 #include "uhttpd.h"
24
25 struct handler {
26 struct list_head list;
27
28 struct json_script_file *request;
29 struct json_script_file *fallback;
30 };
31
32 static LIST_HEAD(handlers);
33 static struct json_script_ctx handler_ctx;
34 static struct env_var *cur_vars;
35 static struct blob_buf b;
36 static int handler_ret;
37 static struct client *cur_client;
38 static char **cur_url;
39
40 static void
41 handle_redirect(struct json_script_ctx *ctx, struct blob_attr *data)
42 {
43 struct client *cl = cur_client;
44 static struct blobmsg_policy policy[3] = {
45 { .type = BLOBMSG_TYPE_STRING },
46 { .type = BLOBMSG_TYPE_INT32 },
47 { .type = BLOBMSG_TYPE_STRING },
48 };
49 struct blob_attr *tb[3];
50 const char *status = "Found";
51 int code = 302;
52
53 blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(data), blobmsg_data_len(data));
54 if (!tb[0])
55 return;
56
57 if (tb[1]) {
58 code = blobmsg_get_u32(tb[1]);
59 if (tb[2])
60 status = blobmsg_get_string(tb[2]);
61 }
62
63 uh_http_header(cl, code, status);
64 if (!uh_use_chunked(cl))
65 ustream_printf(cl->us, "Content-Length: 0\r\n");
66 ustream_printf(cl->us, "Location: %s\r\n\r\n",
67 blobmsg_get_string(tb[0]));
68 uh_request_done(cl);
69 *cur_url = NULL;
70
71 handler_ret = 1;
72 json_script_abort(ctx);
73 }
74
75 static void
76 handle_set_uri(struct json_script_ctx *ctx, struct blob_attr *data)
77 {
78 struct client *cl = cur_client;
79 static struct blobmsg_policy policy = {
80 .type = BLOBMSG_TYPE_STRING,
81 };
82 struct blob_attr *tb;
83 struct blob_attr *old_url = blob_data(cl->hdr.head);
84
85 blobmsg_parse_array(&policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data));
86 if (!tb)
87 return;
88
89 blob_buf_init(&b, 0);
90 blob_put_raw(&b, blob_next(old_url), blob_len(cl->hdr.head) - blob_pad_len(old_url));
91
92 /* replace URL in client header cache */
93 blob_buf_init(&cl->hdr, 0);
94 blobmsg_add_string(&cl->hdr, "URL", blobmsg_get_string(tb));
95 blob_put_raw(&cl->hdr, blob_data(b.head), blob_len(b.head));
96 *cur_url = blobmsg_data(blob_data(cl->hdr.head));
97 cur_vars = NULL;
98
99 blob_buf_init(&b, 0);
100
101 handler_ret = 1;
102 json_script_abort(ctx);
103 }
104
105 static void
106 handle_add_header(struct json_script_ctx *ctx, struct blob_attr *data)
107 {
108 struct client *cl = cur_client;
109 static struct blobmsg_policy policy[2] = {
110 { .type = BLOBMSG_TYPE_STRING },
111 { .type = BLOBMSG_TYPE_STRING },
112 };
113 struct blob_attr *tb[2];
114
115 blobmsg_parse_array(policy, ARRAY_SIZE(tb), tb, blobmsg_data(data), blobmsg_data_len(data));
116 if (!tb[0] || !tb[1])
117 return;
118
119 blobmsg_add_string(&cl->hdr_response, blobmsg_get_string(tb[0]),
120 blobmsg_get_string(tb[1]));
121 }
122
123 static void
124 handle_no_cache(struct json_script_ctx *ctx, struct blob_attr *data)
125 {
126 struct client *cl = cur_client;
127
128 cl->dispatch.no_cache = true;
129 }
130
131 static void
132 handle_command(struct json_script_ctx *ctx, const char *name,
133 struct blob_attr *data, struct blob_attr *vars)
134 {
135 static const struct {
136 const char *name;
137 void (*func)(struct json_script_ctx *ctx, struct blob_attr *data);
138 } cmds[] = {
139 { "redirect", handle_redirect },
140 { "rewrite", handle_set_uri },
141 { "add-header", handle_add_header },
142 { "no-cache", handle_no_cache },
143 };
144 int i;
145
146 for (i = 0; i < ARRAY_SIZE(cmds); i++) {
147 if (!strcmp(cmds[i].name, name)) {
148 cmds[i].func(ctx, data);
149 return;
150 }
151 }
152 }
153
154 static const char *
155 handle_var(struct json_script_ctx *ctx, const char *name,
156 struct blob_attr *vars)
157 {
158 struct client *cl = cur_client;
159 struct env_var *cur;
160 static struct path_info empty_path;
161
162 if (!cur_vars) {
163 struct path_info *p = uh_path_lookup(cl, *cur_url);
164
165 if (!p)
166 p = &empty_path;
167
168 cur_vars = uh_get_process_vars(cl, p);
169 }
170
171 for (cur = cur_vars; cur->name; cur++) {
172 if (!strcmp(cur->name, name))
173 return cur->value;
174 }
175 return NULL;
176 }
177
178 static void
179 handler_init(void)
180 {
181 if (handler_ctx.handle_command)
182 return;
183
184 json_script_init(&handler_ctx);
185 handler_ctx.handle_command = handle_command;
186 handler_ctx.handle_var = handle_var;
187 }
188
189 static bool set_handler(struct json_script_file **dest, struct blob_attr *data)
190 {
191 if (!data)
192 return true;
193
194 *dest = json_script_file_from_blobmsg(NULL, blobmsg_data(data), blobmsg_data_len(data));
195 return *dest;
196 }
197
198 int uh_handler_add(const char *file)
199 {
200 enum {
201 H_REQUEST,
202 H_FALLBACK,
203 __H_MAX,
204 };
205 struct blobmsg_policy policy[__H_MAX] = {
206 [H_REQUEST] = { "request", BLOBMSG_TYPE_ARRAY },
207 [H_FALLBACK] = { "fallback", BLOBMSG_TYPE_ARRAY },
208 };
209 struct blob_attr *tb[__H_MAX];
210 struct handler *h;
211
212 handler_init();
213 blob_buf_init(&b, 0);
214
215 if (!blobmsg_add_json_from_file(&b, file))
216 return -1;
217
218 blobmsg_parse(policy, __H_MAX, tb, blob_data(b.head), blob_len(b.head));
219 if (!tb[H_REQUEST] && !tb[H_FALLBACK])
220 return -1;
221
222 h = calloc(1, sizeof(*h));
223 if (!set_handler(&h->request, tb[H_REQUEST]) ||
224 !set_handler(&h->fallback, tb[H_FALLBACK])) {
225 free(h->request);
226 free(h->fallback);
227 free(h);
228 return -1;
229 }
230
231 list_add_tail(&h->list, &handlers);
232 return 0;
233 }
234
235 int uh_handler_run(struct client *cl, char **url, bool fallback)
236 {
237 struct json_script_file *f;
238 struct handler *h;
239
240 cur_client = cl;
241 cur_url = url;
242 cur_vars = NULL;
243
244 handler_ret = 0;
245
246 list_for_each_entry(h, &handlers, list) {
247 f = fallback ? h->fallback : h->request;
248 if (!f)
249 continue;
250
251 blob_buf_init(&b, 0);
252 json_script_run_file(&handler_ctx, f, b.head);
253 if (handler_ctx.abort)
254 break;
255 }
256
257 return handler_ret;
258 }