2 * uhttpd - Tiny single-threaded httpd
4 * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
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.
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.
19 #include <libubox/blobmsg.h>
20 #include <libubox/blobmsg_json.h>
21 #include <libubox/json_script.h>
26 struct list_head list
;
28 struct json_script_file
*request
;
29 struct json_script_file
*fallback
;
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
;
41 handle_redirect(struct json_script_ctx
*ctx
, struct blob_attr
*data
)
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
},
49 struct blob_attr
*tb
[3];
50 const char *status
= "Found";
53 blobmsg_parse_array(policy
, ARRAY_SIZE(policy
), tb
, blobmsg_data(data
), blobmsg_data_len(data
));
58 code
= blobmsg_get_u32(tb
[1]);
60 status
= blobmsg_get_string(tb
[2]);
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]));
72 json_script_abort(ctx
);
76 handle_set_uri(struct json_script_ctx
*ctx
, struct blob_attr
*data
)
78 struct client
*cl
= cur_client
;
79 static struct blobmsg_policy policy
= {
80 .type
= BLOBMSG_TYPE_STRING
,
83 struct blob_attr
*old_url
= blob_data(cl
->hdr
.head
);
85 blobmsg_parse_array(&policy
, 1, &tb
, blobmsg_data(data
), blobmsg_data_len(data
));
90 blob_put_raw(&b
, blob_next(old_url
), blob_len(cl
->hdr
.head
) - blob_pad_len(old_url
));
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
));
102 json_script_abort(ctx
);
106 handle_add_header(struct json_script_ctx
*ctx
, struct blob_attr
*data
)
108 struct client
*cl
= cur_client
;
109 static struct blobmsg_policy policy
[2] = {
110 { .type
= BLOBMSG_TYPE_STRING
},
111 { .type
= BLOBMSG_TYPE_STRING
},
113 struct blob_attr
*tb
[2];
115 blobmsg_parse_array(policy
, ARRAY_SIZE(tb
), tb
, blobmsg_data(data
), blobmsg_data_len(data
));
116 if (!tb
[0] || !tb
[1])
119 blobmsg_add_string(&cl
->hdr_response
, blobmsg_get_string(tb
[0]),
120 blobmsg_get_string(tb
[1]));
124 handle_command(struct json_script_ctx
*ctx
, const char *name
,
125 struct blob_attr
*data
, struct blob_attr
*vars
)
127 static const struct {
129 void (*func
)(struct json_script_ctx
*ctx
, struct blob_attr
*data
);
131 { "redirect", handle_redirect
},
132 { "rewrite", handle_set_uri
},
133 { "add-header", handle_add_header
},
137 for (i
= 0; i
< ARRAY_SIZE(cmds
); i
++) {
138 if (!strcmp(cmds
[i
].name
, name
)) {
139 cmds
[i
].func(ctx
, data
);
146 handle_var(struct json_script_ctx
*ctx
, const char *name
,
147 struct blob_attr
*vars
)
149 struct client
*cl
= cur_client
;
151 static struct path_info empty_path
;
154 struct path_info
*p
= uh_path_lookup(cl
, *cur_url
);
159 cur_vars
= uh_get_process_vars(cl
, p
);
162 for (cur
= cur_vars
; cur
->name
; cur
++) {
163 if (!strcmp(cur
->name
, name
))
172 if (handler_ctx
.handle_command
)
175 json_script_init(&handler_ctx
);
176 handler_ctx
.handle_command
= handle_command
;
177 handler_ctx
.handle_var
= handle_var
;
180 static bool set_handler(struct json_script_file
**dest
, struct blob_attr
*data
)
185 *dest
= json_script_file_from_blobmsg(NULL
, blobmsg_data(data
), blobmsg_data_len(data
));
189 int uh_handler_add(const char *file
)
196 struct blobmsg_policy policy
[__H_MAX
] = {
197 [H_REQUEST
] = { "request", BLOBMSG_TYPE_ARRAY
},
198 [H_FALLBACK
] = { "fallback", BLOBMSG_TYPE_ARRAY
},
200 struct blob_attr
*tb
[__H_MAX
];
204 blob_buf_init(&b
, 0);
206 if (!blobmsg_add_json_from_file(&b
, file
))
209 blobmsg_parse(policy
, __H_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
210 if (!tb
[H_REQUEST
] && !tb
[H_FALLBACK
])
213 h
= calloc(1, sizeof(*h
));
214 if (!set_handler(&h
->request
, tb
[H_REQUEST
]) ||
215 !set_handler(&h
->fallback
, tb
[H_FALLBACK
])) {
222 list_add_tail(&h
->list
, &handlers
);
226 int uh_handler_run(struct client
*cl
, char **url
, bool fallback
)
228 struct json_script_file
*f
;
237 list_for_each_entry(h
, &handlers
, list
) {
238 f
= fallback
? h
->fallback
: h
->request
;
242 blob_buf_init(&b
, 0);
243 json_script_run_file(&handler_ctx
, f
, b
.head
);
244 if (handler_ctx
.abort
)