1/* This file is part of the CivetWeb web server. 2 * See https://github.com/civetweb/civetweb/ 3 * (C) 2015-2018 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 16static const char *const civetweb_conn_id = "\xFF" 17 "civetweb_conn"; 18static const char *const civetweb_ctx_id = "\xFF" 19 "civetweb_ctx"; 20 21 22static void * 23mg_duk_mem_alloc(void *udata, duk_size_t size) 24{ 25 return mg_malloc_ctx(size, (struct mg_context *)udata); 26} 27 28 29static void * 30mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize) 31{ 32 return mg_realloc_ctx(ptr, newsize, (struct mg_context *)udata); 33} 34 35 36static void 37mg_duk_mem_free(void *udata, void *ptr) 38{ 39 (void)udata; 40 mg_free(ptr); 41} 42 43static void 44mg_duk_fatal_handler(duk_context *duk_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(duk_ctx); 52 duk_get_prop_string(duk_ctx, -1, civetweb_conn_id); 53 conn = (struct mg_connection *)duk_to_pointer(duk_ctx, -1); 54 55 mg_cry_internal(conn, "JavaScript fatal (%u): %s", (unsigned)code, msg); 56} 57 58#if DUK_VERSION >= 20000L 59/* Dropped from interface */ 60duk_int_t duk_peval_file(duk_context *duk_ctx, const char *script); 61 62static void 63mg_duk_v2_fatal(void *udata, const char *msg) 64{ 65 ; /* TODO: How to get "conn" without duk_ctx */ 66} 67 68static void 69push_file_as_string(duk_context *ctx, const char *filename) 70{ 71 FILE *f; 72 struct stat fst; 73 void *buf; 74 size_t len; 75 76 if (0 != stat(filename, &fst)) { 77 duk_push_undefined(ctx); 78 return; 79 } 80 81 f = fopen(filename, "rb"); 82 if (!f) { 83 duk_push_undefined(ctx); 84 return; 85 } 86 87 buf = mg_malloc(fst.st_size); 88 if (!f) { 89 fclose(f); 90 duk_push_undefined(ctx); 91 return; 92 } 93 94 len = fread(buf, 1, fst.st_size, f); 95 fclose(f); 96 97 duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len); 98 mg_free(buf); 99} 100 101duk_int_t 102duk_peval_file(duk_context *duk_ctx, const char *script) 103{ 104 push_file_as_string(duk_ctx, script); 105 return duk_peval(duk_ctx); 106} 107#endif 108 109static duk_ret_t 110duk_itf_write(duk_context *duk_ctx) 111{ 112 struct mg_connection *conn; 113 duk_double_t ret; 114 duk_size_t len = 0; 115 const char *val = duk_require_lstring(duk_ctx, -1, &len); 116 117 /* 118 duk_push_global_stash(duk_ctx); 119 duk_get_prop_string(duk_ctx, -1, civetweb_conn_id); 120 conn = (struct mg_connection *)duk_to_pointer(duk_ctx, -1); 121 */ 122 duk_push_current_function(duk_ctx); 123 duk_get_prop_string(duk_ctx, -1, civetweb_conn_id); 124 conn = (struct mg_connection *)duk_to_pointer(duk_ctx, -1); 125 126 if (!conn) { 127 duk_error(duk_ctx, 128 DUK_ERR_ERROR, 129 "function not available without connection object"); 130 return DUK_RET_ERROR; 131 } 132 133 ret = mg_write(conn, val, len); 134 135 duk_push_number(duk_ctx, ret); 136 return 1; 137} 138 139 140static duk_ret_t 141duk_itf_read(duk_context *duk_ctx) 142{ 143 struct mg_connection *conn; 144 char buf[1024]; 145 int len; 146 147 duk_push_current_function(duk_ctx); 148 duk_get_prop_string(duk_ctx, -1, civetweb_conn_id); 149 conn = (struct mg_connection *)duk_to_pointer(duk_ctx, -1); 150 151 if (!conn) { 152 duk_error(duk_ctx, 153 DUK_ERR_ERROR, 154 "function not available without connection object"); 155 return DUK_RET_ERROR; 156 } 157 158 len = mg_read(conn, buf, sizeof(buf)); 159 160 duk_push_lstring(duk_ctx, buf, len); 161 return 1; 162} 163 164 165static duk_ret_t 166duk_itf_getoption(duk_context *duk_ctx) 167{ 168 struct mg_connection *conn; 169 const char *ret; 170 int optidx; 171 duk_size_t len = 0; 172 const char *val = duk_require_lstring(duk_ctx, -1, &len); 173 174 duk_push_current_function(duk_ctx); 175 duk_get_prop_string(duk_ctx, -1, civetweb_conn_id); 176 conn = (struct mg_connection *)duk_to_pointer(duk_ctx, -1); 177 178 if (!conn) { 179 duk_error(duk_ctx, 180 DUK_ERR_ERROR, 181 "function not available without context object"); 182 return DUK_RET_ERROR; 183 } 184 185 optidx = get_option_index(val); 186 if (optidx >= 0) { 187 ret = conn->dom_ctx->config[optidx]; 188 } else { 189 ret = NULL; 190 } 191 if (ret) { 192 duk_push_string(duk_ctx, ret); 193 } else { 194 duk_push_null(duk_ctx); 195 } 196 197 return 1; 198} 199 200 201static void 202mg_exec_duktape_script(struct mg_connection *conn, const char *script_name) 203{ 204 int i; 205 duk_context *duk_ctx = NULL; 206 207 conn->must_close = 1; 208 209 /* Create Duktape interpreter state */ 210 duk_ctx = duk_create_heap(mg_duk_mem_alloc, 211 mg_duk_mem_realloc, 212 mg_duk_mem_free, 213 (void *)conn->phys_ctx, 214#if DUK_VERSION >= 20000L 215 mg_duk_v2_fatal 216#else 217 mg_duk_fatal_handler 218#endif 219 ); 220 if (!duk_ctx) { 221 mg_cry_internal(conn, "%s", "Failed to create a Duktape heap."); 222 goto exec_duktape_finished; 223 } 224 225 /* Add "conn" object */ 226 duk_push_global_object(duk_ctx); 227 duk_push_object(duk_ctx); /* create a new table/object ("conn") */ 228 229 /* add function conn.write */ 230 duk_push_c_function(duk_ctx, duk_itf_write, 1 /* 1 = nargs */); 231 duk_push_pointer(duk_ctx, (void *)conn); 232 duk_put_prop_string(duk_ctx, -2, civetweb_conn_id); 233 duk_put_prop_string(duk_ctx, -2, "write"); 234 235 /* add function conn.read */ 236 duk_push_c_function(duk_ctx, duk_itf_read, 0 /* 0 = nargs */); 237 duk_push_pointer(duk_ctx, (void *)conn); 238 duk_put_prop_string(duk_ctx, -2, civetweb_conn_id); 239 duk_put_prop_string(duk_ctx, -2, "read"); 240 241 /* add request_method object */ 242 duk_push_string(duk_ctx, conn->request_info.request_method); 243 duk_put_prop_string(duk_ctx, 244 -2, 245 "request_method"); /* add string conn.r... */ 246 247 duk_push_string(duk_ctx, conn->request_info.request_uri); 248 duk_put_prop_string(duk_ctx, -2, "request_uri"); 249 250 duk_push_string(duk_ctx, conn->request_info.local_uri); 251 duk_put_prop_string(duk_ctx, -2, "uri"); 252 253 duk_push_string(duk_ctx, conn->request_info.http_version); 254 duk_put_prop_string(duk_ctx, -2, "http_version"); 255 256 duk_push_string(duk_ctx, conn->request_info.query_string); 257 duk_put_prop_string(duk_ctx, -2, "query_string"); 258 259 duk_push_string(duk_ctx, conn->request_info.remote_addr); 260 duk_put_prop_string(duk_ctx, -2, "remote_addr"); 261 262 duk_push_int(duk_ctx, conn->request_info.remote_port); 263 duk_put_prop_string(duk_ctx, -2, "remote_port"); 264 265 duk_push_int(duk_ctx, ntohs(conn->client.lsa.sin.sin_port)); 266 duk_put_prop_string(duk_ctx, -2, "server_port"); 267 268 duk_push_object(duk_ctx); /* subfolder "conn.http_headers" */ 269 for (i = 0; i < conn->request_info.num_headers; i++) { 270 duk_push_string(duk_ctx, conn->request_info.http_headers[i].value); 271 duk_put_prop_string(duk_ctx, 272 -2, 273 conn->request_info.http_headers[i].name); 274 } 275 duk_put_prop_string(duk_ctx, -2, "http_headers"); 276 277 duk_put_prop_string(duk_ctx, -2, "conn"); /* call the table "conn" */ 278 279 /* Add "civetweb" object */ 280 duk_push_global_object(duk_ctx); 281 duk_push_object(duk_ctx); /* create a new table/object ("conn") */ 282 283 duk_push_string(duk_ctx, CIVETWEB_VERSION); 284 duk_put_prop_string(duk_ctx, -2, "version"); 285 286 duk_push_string(duk_ctx, script_name); 287 duk_put_prop_string(duk_ctx, -2, "script_name"); 288 289 /* add function civetweb.getoption */ 290 duk_push_c_function(duk_ctx, duk_itf_getoption, 1 /* 1 = nargs */); 291 duk_push_pointer(duk_ctx, (void *)conn); 292 duk_put_prop_string(duk_ctx, -2, civetweb_conn_id); 293 duk_put_prop_string(duk_ctx, -2, "getoption"); 294 295 if (conn->phys_ctx != NULL) { 296 /* add system name */ 297 if (conn->phys_ctx->systemName != NULL) { 298 duk_push_string(duk_ctx, conn->phys_ctx->systemName); 299 duk_put_prop_string(duk_ctx, -2, "system"); 300 } 301 } 302 303 duk_put_prop_string(duk_ctx, 304 -2, 305 "civetweb"); /* call the table "civetweb" */ 306 307 duk_push_global_stash(duk_ctx); 308 duk_push_pointer(duk_ctx, (void *)conn); 309 duk_put_prop_string(duk_ctx, -2, civetweb_conn_id); 310 311 if (duk_peval_file(duk_ctx, script_name) != 0) { 312 mg_cry_internal(conn, "%s", duk_safe_to_string(duk_ctx, -1)); 313 goto exec_duktape_finished; 314 } 315 duk_pop(duk_ctx); /* ignore result */ 316 317exec_duktape_finished: 318 duk_destroy_heap(duk_ctx); 319} 320 321 322/* End of mod_duktape.inl */ 323