mod_duktape.inl 7.4 KB

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