]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* This file is part of the CivetWeb web server. |
2 | * See https://github.com/civetweb/civetweb/ | |
3 | * (C) 2015 by the CivetWeb authors, MIT license. | |
4 | */ | |
5 | ||
6 | #include "duktape.h" | |
7 | ||
8 | /* TODO: the mg context should be added to duktape as well */ | |
9 | /* Alternative: redefine a new, clean API from scratch (instead of using mg), | |
10 | * or at least do not add problematic functions. */ | |
11 | /* For evaluation purposes, currently only "send" is supported. | |
12 | * All other ~50 functions will be added later. */ | |
13 | ||
14 | /* Note: This is only experimental support, so the API may still change. */ | |
15 | ||
16 | static const char *civetweb_conn_id = "\xFF" | |
17 | "civetweb_conn"; | |
18 | static const char *civetweb_ctx_id = "\xFF" | |
19 | "civetweb_ctx"; | |
20 | ||
21 | ||
22 | static void * | |
23 | mg_duk_mem_alloc(void *udata, duk_size_t size) | |
24 | { | |
25 | return mg_malloc(size); | |
26 | } | |
27 | ||
28 | ||
29 | static void * | |
30 | mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize) | |
31 | { | |
32 | return mg_realloc(ptr, newsize); | |
33 | } | |
34 | ||
35 | ||
36 | static void | |
37 | mg_duk_mem_free(void *udata, void *ptr) | |
38 | { | |
39 | mg_free(ptr); | |
40 | } | |
41 | ||
42 | ||
43 | static void | |
44 | mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) | |
45 | { | |
46 | /* Script is called "protected" (duk_peval_file), so script errors should | |
47 | * never yield in a call to this function. Maybe calls prior to executing | |
48 | * the script could raise a fatal error. */ | |
49 | struct mg_connection *conn; | |
50 | ||
51 | duk_push_global_stash(ctx); | |
52 | duk_get_prop_string(ctx, -1, civetweb_conn_id); | |
53 | conn = (struct mg_connection *)duk_to_pointer(ctx, -1); | |
54 | ||
55 | mg_cry(conn, "%s", msg); | |
56 | } | |
57 | ||
58 | ||
59 | static duk_ret_t | |
60 | duk_itf_write(duk_context *ctx) | |
61 | { | |
62 | struct mg_connection *conn; | |
63 | duk_double_t ret; | |
64 | duk_size_t len = 0; | |
65 | const char *val = duk_require_lstring(ctx, -1, &len); | |
66 | ||
67 | /* | |
68 | duk_push_global_stash(ctx); | |
69 | duk_get_prop_string(ctx, -1, civetweb_conn_id); | |
70 | conn = (struct mg_connection *)duk_to_pointer(ctx, -1); | |
71 | */ | |
72 | duk_push_current_function(ctx); | |
73 | duk_get_prop_string(ctx, -1, civetweb_conn_id); | |
74 | conn = (struct mg_connection *)duk_to_pointer(ctx, -1); | |
75 | ||
76 | if (!conn) { | |
77 | duk_error(ctx, | |
78 | DUK_ERR_INTERNAL_ERROR, | |
79 | "function not available without connection object"); | |
80 | /* probably never reached, but satisfies static code analysis */ | |
81 | return DUK_RET_INTERNAL_ERROR; | |
82 | } | |
83 | ||
84 | ret = mg_write(conn, val, len); | |
85 | ||
86 | duk_push_number(ctx, ret); | |
87 | return 1; | |
88 | } | |
89 | ||
90 | ||
91 | static duk_ret_t | |
92 | duk_itf_read(duk_context *ctx) | |
93 | { | |
94 | struct mg_connection *conn; | |
95 | char buf[1024]; | |
96 | int len; | |
97 | ||
98 | duk_push_global_stash(ctx); | |
99 | duk_get_prop_string(ctx, -1, civetweb_conn_id); | |
100 | conn = (struct mg_connection *)duk_to_pointer(ctx, -1); | |
101 | ||
102 | if (!conn) { | |
103 | duk_error(ctx, | |
104 | DUK_ERR_INTERNAL_ERROR, | |
105 | "function not available without connection object"); | |
106 | /* probably never reached, but satisfies static code analysis */ | |
107 | return DUK_RET_INTERNAL_ERROR; | |
108 | } | |
109 | ||
110 | len = mg_read(conn, buf, sizeof(buf)); | |
111 | ||
112 | duk_push_lstring(ctx, buf, len); | |
113 | return 1; | |
114 | } | |
115 | ||
116 | ||
117 | static duk_ret_t | |
118 | duk_itf_getoption(duk_context *ctx) | |
119 | { | |
120 | struct mg_context *cv_ctx; | |
121 | const char *ret; | |
122 | duk_size_t len = 0; | |
123 | const char *val = duk_require_lstring(ctx, -1, &len); | |
124 | ||
125 | duk_push_current_function(ctx); | |
126 | duk_get_prop_string(ctx, -1, civetweb_ctx_id); | |
127 | cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1); | |
128 | ||
129 | if (!cv_ctx) { | |
130 | duk_error(ctx, | |
131 | DUK_ERR_INTERNAL_ERROR, | |
132 | "function not available without connection object"); | |
133 | /* probably never reached, but satisfies static code analysis */ | |
134 | return DUK_RET_INTERNAL_ERROR; | |
135 | } | |
136 | ||
137 | ret = mg_get_option(cv_ctx, val); | |
138 | if (ret) { | |
139 | duk_push_string(ctx, ret); | |
140 | } else { | |
141 | duk_push_null(ctx); | |
142 | } | |
143 | ||
144 | return 1; | |
145 | } | |
146 | ||
147 | ||
148 | static void | |
149 | mg_exec_duktape_script(struct mg_connection *conn, const char *script_name) | |
150 | { | |
151 | int i; | |
152 | duk_context *ctx = NULL; | |
153 | ||
154 | conn->must_close = 1; | |
155 | ||
156 | /* Create Duktape interpreter state */ | |
157 | ctx = duk_create_heap(mg_duk_mem_alloc, | |
158 | mg_duk_mem_realloc, | |
159 | mg_duk_mem_free, | |
160 | NULL, | |
161 | mg_duk_fatal_handler); | |
162 | if (!ctx) { | |
163 | mg_cry(conn, "Failed to create a Duktape heap."); | |
164 | goto exec_duktape_finished; | |
165 | } | |
166 | ||
167 | /* Add "conn" object */ | |
168 | duk_push_global_object(ctx); | |
169 | duk_push_object(ctx); /* create a new table/object ("conn") */ | |
170 | ||
171 | duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */); | |
172 | duk_push_pointer(ctx, (void *)conn); | |
173 | duk_put_prop_string(ctx, -2, civetweb_conn_id); | |
174 | duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */ | |
175 | ||
176 | duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */); | |
177 | duk_push_pointer(ctx, (void *)conn); | |
178 | duk_put_prop_string(ctx, -2, civetweb_conn_id); | |
179 | duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */ | |
180 | ||
181 | duk_push_string(ctx, conn->request_info.request_method); | |
182 | duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */ | |
183 | ||
184 | duk_push_string(ctx, conn->request_info.request_uri); | |
185 | duk_put_prop_string(ctx, -2, "request_uri"); | |
186 | ||
187 | duk_push_string(ctx, conn->request_info.local_uri); | |
188 | duk_put_prop_string(ctx, -2, "uri"); | |
189 | ||
190 | duk_push_string(ctx, conn->request_info.http_version); | |
191 | duk_put_prop_string(ctx, -2, "http_version"); | |
192 | ||
193 | duk_push_string(ctx, conn->request_info.query_string); | |
194 | duk_put_prop_string(ctx, -2, "query_string"); | |
195 | ||
196 | duk_push_string(ctx, conn->request_info.remote_addr); | |
197 | duk_put_prop_string(ctx, -2, "remote_addr"); | |
198 | ||
199 | duk_push_int(ctx, conn->request_info.remote_port); | |
200 | duk_put_prop_string(ctx, -2, "remote_port"); | |
201 | ||
202 | duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port)); | |
203 | duk_put_prop_string(ctx, -2, "server_port"); | |
204 | ||
205 | duk_push_object(ctx); /* subfolder "conn.http_headers" */ | |
206 | for (i = 0; i < conn->request_info.num_headers; i++) { | |
207 | duk_push_string(ctx, conn->request_info.http_headers[i].value); | |
208 | duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name); | |
209 | } | |
210 | duk_put_prop_string(ctx, -2, "http_headers"); | |
211 | ||
212 | duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */ | |
213 | ||
214 | /* Add "civetweb" object */ | |
215 | duk_push_global_object(ctx); | |
216 | duk_push_object(ctx); /* create a new table/object ("conn") */ | |
217 | ||
218 | duk_push_string(ctx, CIVETWEB_VERSION); | |
219 | duk_put_prop_string(ctx, -2, "version"); | |
220 | ||
221 | duk_push_string(ctx, script_name); | |
222 | duk_put_prop_string(ctx, -2, "script_name"); | |
223 | ||
224 | if (conn->ctx != NULL) { | |
225 | duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */); | |
226 | duk_push_pointer(ctx, (void *)(conn->ctx)); | |
227 | duk_put_prop_string(ctx, -2, civetweb_ctx_id); | |
228 | duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */ | |
229 | ||
230 | if (conn->ctx->systemName != NULL) { | |
231 | duk_push_string(ctx, conn->ctx->systemName); | |
232 | duk_put_prop_string(ctx, -2, "system"); | |
233 | } | |
234 | } | |
235 | ||
236 | duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */ | |
237 | ||
238 | duk_push_global_stash(ctx); | |
239 | duk_push_pointer(ctx, (void *)conn); | |
240 | duk_put_prop_string(ctx, -2, civetweb_conn_id); | |
241 | ||
242 | if (duk_peval_file(ctx, script_name) != 0) { | |
243 | mg_cry(conn, "%s", duk_safe_to_string(ctx, -1)); | |
244 | goto exec_duktape_finished; | |
245 | } | |
246 | duk_pop(ctx); /* ignore result */ | |
247 | ||
248 | exec_duktape_finished: | |
249 | duk_destroy_heap(ctx); | |
250 | } |