1 #include "civetweb_lua.h"
2 #include "civetweb_private_lua.h"
6 mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
8 /* TODO (low): This is an incomplete implementation of mmap for windows.
9 * Currently it is sufficient, but there are a lot of unused parameters.
10 * Better use a function "mg_map" which only has the required parameters,
11 * and implement it using mmap in Linux and CreateFileMapping in Windows.
12 * Noone should expect a full mmap for Windows here.
14 HANDLE fh = (HANDLE)_get_osfhandle(fd);
15 HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
16 void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t)len);
19 /* unused parameters */
29 munmap(void *addr, int64_t length)
31 /* unused parameters */
34 UnmapViewOfFile(addr);
37 #define MAP_FAILED (NULL)
38 #define MAP_PRIVATE (0)
44 static const char *LUASOCKET = "luasocket";
45 static const char lua_regkey_ctx = 1;
46 static const char lua_regkey_connlist = 2;
48 /* Forward declarations */
49 static void handle_request(struct mg_connection *);
50 static int handle_lsp_request(struct mg_connection *,
56 reg_string(struct lua_State *L, const char *name, const char *val)
58 if (name != NULL && val != NULL) {
59 lua_pushstring(L, name);
60 lua_pushstring(L, val);
66 reg_int(struct lua_State *L, const char *name, int val)
69 lua_pushstring(L, name);
70 lua_pushinteger(L, val);
76 reg_boolean(struct lua_State *L, const char *name, int val)
79 lua_pushstring(L, name);
80 lua_pushboolean(L, val != 0);
86 reg_conn_function(struct lua_State *L,
89 struct mg_connection *conn)
91 if (name != NULL && func != NULL && conn != NULL) {
92 lua_pushstring(L, name);
93 lua_pushlightuserdata(L, conn);
94 lua_pushcclosure(L, func, 1);
100 reg_function(struct lua_State *L, const char *name, lua_CFunction func)
102 if (name != NULL && func != NULL) {
103 lua_pushstring(L, name);
104 lua_pushcclosure(L, func, 0);
110 lua_cry(struct mg_connection *conn,
113 const char *lua_title,
114 const char *lua_operation)
122 "%s: %s failed: runtime error: %s",
125 lua_tostring(L, -1));
129 "%s: %s failed: syntax error: %s",
132 lua_tostring(L, -1));
135 mg_cry(conn, "%s: %s failed: out of memory", lua_title, lua_operation);
139 "%s: %s failed: error during garbage collection",
145 "%s: %s failed: error in error handling: %s",
148 lua_tostring(L, -1));
151 mg_cry(conn, "%s: %s failed: error %i", lua_title, lua_operation, err);
157 lsp_sock_close(lua_State *L)
159 int num_args = lua_gettop(L);
160 if ((num_args == 1) && lua_istable(L, -1)) {
161 lua_getfield(L, -1, "sock");
162 closesocket((SOCKET)lua_tonumber(L, -1));
164 return luaL_error(L, "invalid :close() call");
170 lsp_sock_recv(lua_State *L)
172 int num_args = lua_gettop(L);
176 if ((num_args == 1) && lua_istable(L, -1)) {
177 lua_getfield(L, -1, "sock");
178 n = recv((SOCKET)lua_tonumber(L, -1), buf, sizeof(buf), 0);
182 lua_pushlstring(L, buf, n);
185 return luaL_error(L, "invalid :close() call");
191 lsp_sock_send(lua_State *L)
193 int num_args = lua_gettop(L);
195 size_t len, sent = 0;
198 if ((num_args == 2) && lua_istable(L, -2) && lua_isstring(L, -1)) {
199 buf = lua_tolstring(L, -1, &len);
200 lua_getfield(L, -2, "sock");
201 sock = (int)lua_tonumber(L, -1);
203 if ((n = send(sock, buf + sent, (int)(len - sent), 0)) <= 0) {
208 lua_pushnumber(L, n);
210 return luaL_error(L, "invalid :close() call");
215 static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
216 {"send", lsp_sock_send},
217 {"recv", lsp_sock_recv},
221 lsp_connect(lua_State *L)
223 int num_args = lua_gettop(L);
229 if ((num_args == 3) && lua_isstring(L, -3) && lua_isnumber(L, -2)
230 && lua_isnumber(L, -1)) {
231 ok = connect_socket(NULL,
233 (int)lua_tonumber(L, -2),
234 (int)lua_tonumber(L, -1),
240 return luaL_error(L, ebuf);
243 reg_int(L, "sock", (int)sock);
244 reg_string(L, "host", lua_tostring(L, -4));
245 luaL_getmetatable(L, LUASOCKET);
246 lua_setmetatable(L, -2);
247 /* TODO (high): The metatable misses a _gc method to free the
248 * sock object -> currently lsp_connect is a resource leak. */
252 L, "connect(host,port,is_ssl): invalid parameter given.");
258 lsp_error(lua_State *L)
260 lua_getglobal(L, "mg");
261 lua_getfield(L, -1, "onerror");
262 lua_pushvalue(L, -3);
263 lua_pcall(L, 1, 0, 0);
267 /* Silently stop processing chunks. */
269 lsp_abort(lua_State *L)
271 int top = lua_gettop(L);
272 lua_getglobal(L, "mg");
274 lua_setfield(L, -2, "onerror");
276 lua_pushstring(L, "aborting");
280 struct lsp_var_reader_data {
288 lsp_var_reader(lua_State *L, void *ud, size_t *sz)
290 struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud;
292 (void)(L); /* unused */
294 switch (reader->state) {
318 lsp(struct mg_connection *conn,
324 int i, j, pos = 0, lines = 1, lualines = 0, is_var, lua_ok;
325 char chunkname[MG_BUF_LEN];
326 struct lsp_var_reader_data data;
328 for (i = 0; i < len; i++) {
331 if ((i + 1) < len && p[i] == '<' && p[i + 1] == '?') {
333 /* <?= ?> means a variable is enclosed and its value should be
335 is_var = ((i + 2) < len && p[i + 2] == '=');
345 if ((j + 1) < len && p[j] == '?' && p[j + 1] == '>') {
346 mg_write(conn, p + pos, i - pos);
349 NULL, /* name only used for debugging */
355 lua_pushlightuserdata(L, conn);
356 lua_pushcclosure(L, lsp_error, 1);
359 data.begin = p + (i + 3);
360 data.len = j - (i + 3);
362 lua_ok = mg_lua_load(
363 L, lsp_var_reader, &data, chunkname, NULL);
365 lua_ok = luaL_loadbuffer(L,
372 /* Syntax error or OOM. Error message is pushed on
374 lua_pcall(L, 1, 0, 0);
376 /* Success loading chunk. Call it. */
377 lua_pcall(L, 0, 0, 1);
395 mg_write(conn, p + pos, i - pos);
402 /* mg.write: Send data to the client */
404 lsp_write(lua_State *L)
406 struct mg_connection *conn =
407 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
408 int num_args = lua_gettop(L);
413 for (i = 1; i <= num_args; i++) {
414 if (lua_isstring(L, i)) {
415 str = lua_tolstring(L, i, &size);
416 mg_write(conn, str, size);
424 /* mg.read: Read data from the client (e.g., from a POST request) */
426 lsp_read(lua_State *L)
428 struct mg_connection *conn =
429 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
431 int len = mg_read(conn, buf, sizeof(buf));
435 lua_pushlstring(L, buf, len);
441 /* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
443 lsp_keep_alive(lua_State *L)
445 struct mg_connection *conn =
446 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
447 int num_args = lua_gettop(L);
449 /* This function may be called with one parameter (boolean) to set the
451 Or without a parameter to just query the current keep_alive state. */
452 if ((num_args == 1) && lua_isboolean(L, 1)) {
453 conn->must_close = !lua_toboolean(L, 1);
454 } else if (num_args != 0) {
456 return luaL_error(L, "invalid keep_alive() call");
459 /* Return the current "keep_alive" state. This may be false, even it
460 * keep_alive(true) has been called. */
461 lua_pushboolean(L, should_keep_alive(conn));
466 /* mg.include: Include another .lp file */
468 lsp_include(lua_State *L)
470 struct mg_connection *conn =
471 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
472 int num_args = lua_gettop(L);
473 struct file file = STRUCT_FILE_INITIALIZER;
474 const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
477 if (handle_lsp_request(conn, filename, &file, L)) {
478 /* handle_lsp_request returned an error code, meaning an error
480 the included page and mg.onerror returned non-zero. Stop processing.
486 return luaL_error(L, "invalid include() call");
492 /* mg.cry: Log an error. Default value for mg.onerror. */
494 lsp_cry(lua_State *L)
496 struct mg_connection *conn =
497 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
498 int num_args = lua_gettop(L);
499 const char *text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
502 mg_cry(conn, "%s", lua_tostring(L, -1));
505 return luaL_error(L, "invalid cry() call");
511 /* mg.redirect: Redirect the request (internally). */
513 lsp_redirect(lua_State *L)
515 struct mg_connection *conn =
516 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
517 int num_args = lua_gettop(L);
518 const char *target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
521 conn->request_info.local_uri = target;
522 handle_request(conn);
526 return luaL_error(L, "invalid redirect() call");
534 lsp_send_file(lua_State *L)
536 struct mg_connection *conn =
537 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
538 int num_args = lua_gettop(L);
539 const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
542 mg_send_file(conn, filename);
545 return luaL_error(L, "invalid send_file() call");
553 lsp_get_time(lua_State *L)
555 int num_args = lua_gettop(L);
556 int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0;
560 clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts);
561 d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9);
562 lua_pushnumber(L, d);
569 lsp_get_var(lua_State *L)
571 int num_args = lua_gettop(L);
572 const char *data, *var_name;
573 size_t data_len, occurrence;
577 if (num_args >= 2 && num_args <= 3) {
578 data = lua_tolstring(L, 1, &data_len);
579 var_name = lua_tostring(L, 2);
580 occurrence = (num_args > 2) ? (long)lua_tonumber(L, 3) : 0;
583 mg_get_var2(data, data_len, var_name, dst, sizeof(dst), occurrence);
585 /* Variable found: return value to Lua */
586 lua_pushstring(L, dst);
588 /* Variable not found (TODO (mid): may be string too long) */
593 return luaL_error(L, "invalid get_var() call");
599 /* mg.get_mime_type */
601 lsp_get_mime_type(lua_State *L)
603 int num_args = lua_gettop(L);
604 struct vec mime_type = {0, 0};
605 struct mg_context *ctx;
608 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
609 lua_gettable(L, LUA_REGISTRYINDEX);
610 ctx = (struct mg_context *)lua_touserdata(L, -1);
613 text = lua_tostring(L, 1);
616 get_mime_type(ctx, text, &mime_type);
617 lua_pushlstring(L, mime_type.ptr, mime_type.len);
619 text = mg_get_builtin_mime_type(text);
620 lua_pushstring(L, text);
624 return luaL_error(L, "invalid argument for get_mime_type() call");
628 return luaL_error(L, "invalid get_mime_type() call");
636 lsp_get_cookie(lua_State *L)
638 int num_args = lua_gettop(L);
640 const char *var_name;
645 cookie = lua_tostring(L, 1);
646 var_name = lua_tostring(L, 2);
647 if (cookie != NULL && var_name != NULL) {
648 ret = mg_get_cookie(cookie, var_name, dst, sizeof(dst));
654 lua_pushlstring(L, dst, ret);
660 return luaL_error(L, "invalid get_cookie() call");
668 lsp_md5(lua_State *L)
670 int num_args = lua_gettop(L);
678 text = lua_tolstring(L, 1, &text_len);
681 md5_append(&ctx, (const md5_byte_t *)text, text_len);
682 md5_finish(&ctx, hash);
683 bin2str(buf, hash, sizeof(hash));
684 lua_pushstring(L, buf);
690 return luaL_error(L, "invalid md5() call");
698 lsp_url_encode(lua_State *L)
700 int num_args = lua_gettop(L);
706 text = lua_tolstring(L, 1, &text_len);
708 mg_url_encode(text, dst, sizeof(dst));
709 lua_pushstring(L, dst);
715 return luaL_error(L, "invalid url_encode() call");
723 lsp_url_decode(lua_State *L)
725 int num_args = lua_gettop(L);
731 if (num_args == 1 || (num_args == 2 && lua_isboolean(L, 2))) {
732 text = lua_tolstring(L, 1, &text_len);
733 is_form = (num_args == 2) ? lua_isboolean(L, 2) : 0;
735 mg_url_decode(text, text_len, dst, (int)sizeof(dst), is_form);
736 lua_pushstring(L, dst);
742 return luaL_error(L, "invalid url_decode() call");
748 /* mg.base64_encode */
750 lsp_base64_encode(lua_State *L)
752 int num_args = lua_gettop(L);
758 text = lua_tolstring(L, 1, &text_len);
760 dst = (char *)mg_malloc(text_len * 8 / 6 + 4);
762 base64_encode((const unsigned char *)text, (int)text_len, dst);
763 lua_pushstring(L, dst);
766 return luaL_error(L, "out of memory in base64_encode() call");
773 return luaL_error(L, "invalid base64_encode() call");
779 /* mg.base64_encode */
781 lsp_base64_decode(lua_State *L)
783 int num_args = lua_gettop(L);
785 size_t text_len, dst_len;
790 text = lua_tolstring(L, 1, &text_len);
792 dst = (char *)mg_malloc(text_len);
794 ret = base64_decode((const unsigned char *)text,
801 L, "illegal character in lsp_base64_decode() call");
803 lua_pushlstring(L, dst, dst_len);
808 "out of memory in lsp_base64_decode() call");
815 return luaL_error(L, "invalid lsp_base64_decode() call");
821 /* mg.get_response_code_text */
823 lsp_get_response_code_text(lua_State *L)
825 int num_args = lua_gettop(L);
831 type1 = lua_type(L, 1);
832 if (type1 == LUA_TNUMBER) {
833 /* If the first argument is a number,
834 convert it to the corresponding text. */
835 code = lua_tonumber(L, 1);
836 text = mg_get_response_code_text(NULL, (int)code);
838 lua_pushstring(L, text);
844 return luaL_error(L, "invalid get_response_code_text() call");
848 /* mg.random - might be better than math.random on some systems */
850 lsp_random(lua_State *L)
852 int num_args = lua_gettop(L);
854 /* The civetweb internal random number generator will generate
855 * a 64 bit random number. */
856 uint64_t r = get_random();
857 /* Lua "number" is a IEEE 754 double precission float:
858 * https://en.wikipedia.org/wiki/Double-precision_floating-point_format
859 * Thus, mask with 2^53-1 to get an integer with the maximum
860 * precission available. */
861 r &= ((((uint64_t)1) << 53) - 1);
862 lua_pushnumber(L, (double)r);
867 return luaL_error(L, "invalid random() call");
873 void (*f)(unsigned char uuid[16]);
879 lsp_uuid(lua_State *L)
882 unsigned char uuid_array[16];
883 struct uuid_struct_type {
892 int num_args = lua_gettop(L);
894 memset(&uuid, 0, sizeof(uuid));
895 memset(uuid_str, 0, sizeof(uuid_str));
899 pf_uuid_generate.f(uuid.uuid_array);
902 "{%08lX-%04X-%04X-%02X%02X-"
903 "%02X%02X%02X%02X%02X%02X}",
904 (unsigned long)uuid.uuid_struct.data1,
905 (unsigned)uuid.uuid_struct.data2,
906 (unsigned)uuid.uuid_struct.data3,
907 (unsigned)uuid.uuid_struct.data4[0],
908 (unsigned)uuid.uuid_struct.data4[1],
909 (unsigned)uuid.uuid_struct.data4[2],
910 (unsigned)uuid.uuid_struct.data4[3],
911 (unsigned)uuid.uuid_struct.data4[4],
912 (unsigned)uuid.uuid_struct.data4[5],
913 (unsigned)uuid.uuid_struct.data4[6],
914 (unsigned)uuid.uuid_struct.data4[7]);
916 lua_pushstring(L, uuid_str);
921 return luaL_error(L, "invalid random() call");
926 struct lua_websock_data {
930 struct mg_connection *conn[MAX_WORKER_THREADS];
931 pthread_mutex_t ws_mutex;
936 /* mg.write for websockets */
938 lwebsock_write(lua_State *L)
941 int num_args = lua_gettop(L);
942 struct lua_websock_data *ws;
947 struct mg_connection *client = NULL;
949 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
950 lua_gettable(L, LUA_REGISTRYINDEX);
951 ws = (struct lua_websock_data *)lua_touserdata(L, -1);
953 (void)pthread_mutex_lock(&(ws->ws_mutex));
956 /* just one text: send it to all client */
957 if (lua_isstring(L, 1)) {
958 opcode = WEBSOCKET_OPCODE_TEXT;
960 } else if (num_args == 2) {
961 if (lua_isnumber(L, 1)) {
962 /* opcode number and message text */
963 opcode = (int)lua_tointeger(L, 1);
964 } else if (lua_isstring(L, 1)) {
965 /* opcode string and message text */
966 str = lua_tostring(L, 1);
967 if (!mg_strncasecmp(str, "text", 4))
968 opcode = WEBSOCKET_OPCODE_TEXT;
969 else if (!mg_strncasecmp(str, "bin", 3))
970 opcode = WEBSOCKET_OPCODE_BINARY;
971 else if (!mg_strncasecmp(str, "close", 5))
972 opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
973 else if (!mg_strncasecmp(str, "ping", 4))
974 opcode = WEBSOCKET_OPCODE_PING;
975 else if (!mg_strncasecmp(str, "pong", 4))
976 opcode = WEBSOCKET_OPCODE_PONG;
977 else if (!mg_strncasecmp(str, "cont", 4))
978 opcode = WEBSOCKET_OPCODE_CONTINUATION;
979 } else if (lua_isuserdata(L, 1)) {
980 /* client id and message text */
981 client = (struct mg_connection *)lua_touserdata(L, 1);
982 opcode = WEBSOCKET_OPCODE_TEXT;
984 } else if (num_args == 3) {
985 if (lua_isuserdata(L, 1)) {
986 client = (struct mg_connection *)lua_touserdata(L, 1);
987 if (lua_isnumber(L, 2)) {
988 /* client id, opcode number and message text */
989 opcode = (int)lua_tointeger(L, 2);
990 } else if (lua_isstring(L, 2)) {
991 /* client id, opcode string and message text */
992 str = lua_tostring(L, 2);
993 if (!mg_strncasecmp(str, "text", 4))
994 opcode = WEBSOCKET_OPCODE_TEXT;
995 else if (!mg_strncasecmp(str, "bin", 3))
996 opcode = WEBSOCKET_OPCODE_BINARY;
997 else if (!mg_strncasecmp(str, "close", 5))
998 opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
999 else if (!mg_strncasecmp(str, "ping", 4))
1000 opcode = WEBSOCKET_OPCODE_PING;
1001 else if (!mg_strncasecmp(str, "pong", 4))
1002 opcode = WEBSOCKET_OPCODE_PONG;
1003 else if (!mg_strncasecmp(str, "cont", 4))
1004 opcode = WEBSOCKET_OPCODE_CONTINUATION;
1009 if (opcode >= 0 && opcode < 16 && lua_isstring(L, num_args)) {
1010 str = lua_tolstring(L, num_args, &size);
1012 for (i = 0; i < ws->references; i++) {
1013 if (client == ws->conn[i]) {
1014 mg_websocket_write(ws->conn[i], opcode, str, size);
1018 for (i = 0; i < ws->references; i++) {
1019 mg_websocket_write(ws->conn[i], opcode, str, size);
1023 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1024 return luaL_error(L, "invalid websocket write() call");
1027 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1030 (void)(L); /* unused */
1036 struct laction_arg {
1039 pthread_mutex_t *pmutex;
1045 lua_action(struct laction_arg *arg)
1048 struct mg_context *ctx;
1050 (void)pthread_mutex_lock(arg->pmutex);
1052 lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx);
1053 lua_gettable(arg->state, LUA_REGISTRYINDEX);
1054 ctx = (struct mg_context *)lua_touserdata(arg->state, -1);
1056 err = luaL_loadstring(arg->state, arg->txt);
1058 lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
1059 (void)pthread_mutex_unlock(arg->pmutex);
1063 err = lua_pcall(arg->state, 0, 1, 0);
1065 lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
1066 (void)pthread_mutex_unlock(arg->pmutex);
1071 ok = lua_type(arg->state, -1);
1072 if (lua_isboolean(arg->state, -1)) {
1073 ok = lua_toboolean(arg->state, -1);
1077 lua_pop(arg->state, 1);
1079 (void)pthread_mutex_unlock(arg->pmutex);
1089 lua_action_free(struct laction_arg *arg)
1091 if (lua_action(arg)) {
1099 lwebsocket_set_timer(lua_State *L, int is_periodic)
1101 #if defined(USE_TIMERS) && defined(USE_WEBSOCKET)
1102 int num_args = lua_gettop(L);
1103 struct lua_websock_data *ws;
1104 int type1, type2, ok = 0;
1106 struct mg_context *ctx;
1107 struct laction_arg *arg;
1111 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
1112 lua_gettable(L, LUA_REGISTRYINDEX);
1113 ctx = (struct mg_context *)lua_touserdata(L, -1);
1115 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
1116 lua_gettable(L, LUA_REGISTRYINDEX);
1117 ws = (struct lua_websock_data *)lua_touserdata(L, -1);
1120 return luaL_error(L,
1121 "not enough arguments for set_timer/interval() call");
1124 type1 = lua_type(L, 1);
1125 type2 = lua_type(L, 2);
1127 if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) {
1128 timediff = (double)lua_tonumber(L, 2);
1129 txt = lua_tostring(L, 1);
1130 txt_len = strlen(txt);
1131 arg = (struct laction_arg *)mg_malloc(sizeof(struct laction_arg)
1134 arg->script = ws->script;
1135 arg->pmutex = &(ws->ws_mutex);
1136 memcpy(arg->txt, "return(", 7);
1137 memcpy(arg->txt + 7, txt, txt_len);
1138 arg->txt[txt_len + 7] = ')';
1139 arg->txt[txt_len + 8] = 0;
1146 (taction)(is_periodic ? lua_action : lua_action_free),
1148 } else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) {
1149 /* TODO (mid): not implemented yet */
1150 return luaL_error(L, "invalid arguments for set_timer/interval() call");
1152 return luaL_error(L, "invalid arguments for set_timer/interval() call");
1155 lua_pushboolean(L, ok);
1159 (void)(L); /* unused */
1160 (void)(is_periodic); /* unused */
1166 /* mg.set_timeout for websockets */
1168 lwebsocket_set_timeout(lua_State *L)
1170 return lwebsocket_set_timer(L, 0);
1174 /* mg.set_interval for websockets */
1176 lwebsocket_set_interval(lua_State *L)
1178 return lwebsocket_set_timer(L, 1);
1182 LUA_ENV_TYPE_LUA_SERVER_PAGE = 0,
1183 LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,
1184 LUA_ENV_TYPE_LUA_WEBSOCKET = 2,
1189 prepare_lua_request_info(struct mg_connection *conn, lua_State *L)
1194 /* Export mg.request_info */
1195 lua_pushstring(L, "request_info");
1197 reg_string(L, "request_method", conn->request_info.request_method);
1198 reg_string(L, "request_uri", conn->request_info.request_uri);
1199 reg_string(L, "uri", conn->request_info.local_uri);
1200 reg_string(L, "http_version", conn->request_info.http_version);
1201 reg_string(L, "query_string", conn->request_info.query_string);
1202 #if defined(MG_LEGACY_INTERFACE)
1203 reg_int(L, "remote_ip", conn->request_info.remote_ip); /* remote_ip is
1208 reg_string(L, "remote_addr", conn->request_info.remote_addr);
1209 /* TODO (high): ip version */
1210 reg_int(L, "remote_port", conn->request_info.remote_port);
1211 reg_int(L, "num_headers", conn->request_info.num_headers);
1212 reg_int(L, "server_port", ntohs(conn->client.lsa.sin.sin_port));
1214 if (conn->request_info.content_length >= 0) {
1215 /* reg_int64: content_length */
1216 lua_pushstring(L, "content_length");
1219 (lua_Number)conn->request_info
1220 .content_length); /* lua_Number may be used as 52 bit integer */
1223 if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
1224 reg_string(L, "content_type", s);
1227 if (conn->request_info.remote_user != NULL) {
1228 reg_string(L, "remote_user", conn->request_info.remote_user);
1229 reg_string(L, "auth_type", "Digest");
1232 reg_boolean(L, "https", conn->ssl != NULL);
1234 if (conn->status_code > 0) {
1235 /* Lua error handler should show the status code */
1236 reg_int(L, "status", conn->status_code);
1239 lua_pushstring(L, "http_headers");
1241 for (i = 0; i < conn->request_info.num_headers; i++) {
1243 conn->request_info.http_headers[i].name,
1244 conn->request_info.http_headers[i].value);
1253 civetweb_open_lua_libs(lua_State *L)
1256 extern void luaL_openlibs(lua_State *);
1260 #ifdef USE_LUA_SQLITE3
1262 extern int luaopen_lsqlite3(lua_State *);
1263 luaopen_lsqlite3(L);
1266 #ifdef USE_LUA_LUAXML
1268 extern int luaopen_LuaXML_lib(lua_State *);
1269 luaopen_LuaXML_lib(L);
1272 #ifdef USE_LUA_FILE_SYSTEM
1274 extern int luaopen_lfs(lua_State *);
1278 #ifdef USE_LUA_BINARY
1280 /* TODO (low): Test if this could be used as a replacement for bit32.
1281 * Check again with Lua 5.3 later. */
1282 extern int luaopen_binary(lua_State *);
1284 luaL_requiref(L, "binary", luaopen_binary, 1);
1292 prepare_lua_environment(struct mg_context *ctx,
1293 struct mg_connection *conn,
1294 struct lua_websock_data *ws_conn_list,
1296 const char *script_name,
1299 civetweb_open_lua_libs(L);
1301 #if LUA_VERSION_NUM == 502
1302 /* Keep the "connect" method for compatibility,
1303 * but do not backport it to Lua 5.1.
1304 * TODO: Redesign the interface.
1306 luaL_newmetatable(L, LUASOCKET);
1307 lua_pushliteral(L, "__index");
1308 luaL_newlib(L, luasocket_methods);
1311 lua_register(L, "connect", lsp_connect);
1314 /* Store context in the registry */
1316 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
1317 lua_pushlightuserdata(L, (void *)ctx);
1318 lua_settable(L, LUA_REGISTRYINDEX);
1320 if (ws_conn_list != NULL) {
1321 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
1322 lua_pushlightuserdata(L, (void *)ws_conn_list);
1323 lua_settable(L, LUA_REGISTRYINDEX);
1326 /* Register mg module */
1329 switch (lua_env_type) {
1330 case LUA_ENV_TYPE_LUA_SERVER_PAGE:
1331 reg_string(L, "lua_type", "page");
1333 case LUA_ENV_TYPE_PLAIN_LUA_PAGE:
1334 reg_string(L, "lua_type", "script");
1336 case LUA_ENV_TYPE_LUA_WEBSOCKET:
1337 reg_string(L, "lua_type", "websocket");
1341 if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE
1342 || lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) {
1343 reg_conn_function(L, "cry", lsp_cry, conn);
1344 reg_conn_function(L, "read", lsp_read, conn);
1345 reg_conn_function(L, "write", lsp_write, conn);
1346 reg_conn_function(L, "keep_alive", lsp_keep_alive, conn);
1347 reg_conn_function(L, "send_file", lsp_send_file, conn);
1350 if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) {
1351 reg_conn_function(L, "include", lsp_include, conn);
1352 reg_conn_function(L, "redirect", lsp_redirect, conn);
1355 if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
1356 reg_function(L, "write", lwebsock_write);
1358 reg_function(L, "set_timeout", lwebsocket_set_timeout);
1359 reg_function(L, "set_interval", lwebsocket_set_interval);
1361 /* reg_conn_function(L, "send_file", lsp_send_file, conn); */
1364 reg_function(L, "time", lsp_get_time);
1365 reg_function(L, "get_var", lsp_get_var);
1366 reg_function(L, "get_mime_type", lsp_get_mime_type);
1367 reg_function(L, "get_cookie", lsp_get_cookie);
1368 reg_function(L, "md5", lsp_md5);
1369 reg_function(L, "url_encode", lsp_url_encode);
1370 reg_function(L, "url_decode", lsp_url_decode);
1371 reg_function(L, "base64_encode", lsp_base64_encode);
1372 reg_function(L, "base64_decode", lsp_base64_decode);
1373 reg_function(L, "get_response_code_text", lsp_get_response_code_text);
1374 reg_function(L, "random", lsp_random);
1375 if (pf_uuid_generate.f) {
1376 reg_function(L, "uuid", lsp_uuid);
1379 reg_string(L, "version", CIVETWEB_VERSION);
1381 reg_string(L, "script_name", script_name);
1384 reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
1385 reg_string(L, "auth_domain", ctx->config[AUTHENTICATION_DOMAIN]);
1386 #if defined(USE_WEBSOCKET)
1387 reg_string(L, "websocket_root", ctx->config[WEBSOCKET_ROOT]);
1390 if (ctx->systemName != NULL) {
1391 reg_string(L, "system", ctx->systemName);
1395 /* Export connection specific info */
1397 prepare_lua_request_info(conn, L);
1400 lua_setglobal(L, "mg");
1402 /* Register default mg.onerror function */
1403 IGNORE_UNUSED_RESULT(
1405 "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
1406 "debug.traceback(e, 1)) end"));
1410 if (ctx->config[LUA_PRELOAD_FILE] != NULL) {
1411 IGNORE_UNUSED_RESULT(luaL_dofile(L, ctx->config[LUA_PRELOAD_FILE]));
1414 if (ctx->callbacks.init_lua != NULL) {
1415 ctx->callbacks.init_lua(conn, L);
1422 lua_error_handler(lua_State *L)
1424 const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
1426 lua_getglobal(L, "mg");
1427 if (!lua_isnil(L, -1)) {
1428 lua_getfield(L, -1, "write"); /* call mg.write() */
1429 lua_pushstring(L, error_msg);
1430 lua_pushliteral(L, "\n");
1432 IGNORE_UNUSED_RESULT(
1433 luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"));
1435 printf("Lua error: [%s]\n", error_msg);
1436 IGNORE_UNUSED_RESULT(
1437 luaL_dostring(L, "print(debug.traceback(), '\\n')"));
1439 /* TODO(lsm, low): leave the stack balanced */
1446 lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
1450 (void)osize; /* not used */
1456 return mg_realloc(ptr, nsize);
1461 mg_exec_lua_script(struct mg_connection *conn,
1463 const void **exports)
1468 /* Assume the script does not support keep_alive. The script may change this
1469 * by calling mg.keep_alive(true). */
1470 conn->must_close = 1;
1472 /* Execute a plain Lua script. */
1473 if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) {
1474 prepare_lua_environment(
1475 conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
1476 lua_pushcclosure(L, &lua_error_handler, 0);
1478 if (exports != NULL) {
1479 #if LUA_VERSION_NUM > 501
1480 lua_pushglobaltable(L);
1481 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
1483 lua_pushstring(L, (const char *)(exports[i]));
1484 *(const void **)(&func) = exports[i + 1];
1485 lua_pushcclosure(L, func, 0);
1489 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
1491 const char *name = (const char *)(exports[i]);
1492 *(const void **)(&func) = exports[i + 1];
1493 lua_register(L, name, func);
1498 if (luaL_loadfile(L, path) != 0) {
1499 lua_error_handler(L);
1501 lua_pcall(L, 0, 0, -2);
1508 handle_lsp_request(struct mg_connection *conn,
1511 struct lua_State *ls)
1514 lua_State *L = NULL;
1516 struct file filesize = STRUCT_FILE_INITIALIZER;
1518 /* Assume the script does not support keep_alive. The script may change this
1519 * by calling mg.keep_alive(true). */
1520 conn->must_close = 1;
1522 /* We need both mg_stat to get file size, and mg_fopen to get fd */
1523 if (!mg_stat(conn, path, &filesize)) {
1525 /* File not found */
1527 send_http_error(conn, 500, "Error: File %s not found", path);
1529 luaL_error(ls, "File [%s] not found", path);
1532 goto cleanup_handle_lsp_request;
1535 if (!mg_fopen(conn, path, "r", filep)) {
1537 /* File not found or not accessible */
1539 send_http_error(conn,
1541 "Error: Cannot open script file %s",
1544 luaL_error(ls, "Cannot [%s] not found", path);
1547 goto cleanup_handle_lsp_request;
1550 /* TODO: Operations mg_fopen and mg_stat should do what their names
1551 * indicate. They should not fill in different members of the same
1553 * See Github issue #225 */
1554 filep->size = filesize.size;
1556 if (filep->membuf == NULL
1558 (size_t)filep->size,
1562 0)) == MAP_FAILED) {
1569 "Error: Cannot open script\nFile %s can not be mapped",
1573 "mmap(%s, %zu, %d): %s",
1575 (size_t)filep->size,
1580 goto cleanup_handle_lsp_request;
1586 L = lua_newstate(lua_allocator, NULL);
1592 "Error: Cannot execute script\nlua_newstate failed");
1594 goto cleanup_handle_lsp_request;
1596 prepare_lua_environment(
1597 conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
1600 /* Lua state is ready to use */
1601 /* We're not sending HTTP headers here, Lua page must do it. */
1604 (filep->membuf == NULL) ? (const char *)p
1605 : (const char *)filep->membuf,
1610 cleanup_handle_lsp_request:
1612 if (L != NULL && ls == NULL)
1615 munmap(p, filep->size);
1622 #ifdef USE_WEBSOCKET
1623 struct mg_shared_lua_websocket_list {
1624 struct lua_websock_data ws;
1625 struct mg_shared_lua_websocket_list *next;
1630 lua_websocket_new(const char *script, struct mg_connection *conn)
1632 struct mg_shared_lua_websocket_list **shared_websock_list =
1633 &(conn->ctx->shared_lua_websockets);
1634 struct lua_websock_data *ws;
1637 assert(conn->lua_websocket_state == NULL);
1639 /* lock list (mg_context global) */
1640 mg_lock_context(conn->ctx);
1641 while (*shared_websock_list) {
1642 /* check if ws already in list */
1643 if (0 == strcmp(script, (*shared_websock_list)->ws.script)) {
1646 shared_websock_list = &((*shared_websock_list)->next);
1649 if (*shared_websock_list == NULL) {
1650 /* add ws to list */
1651 *shared_websock_list = (struct mg_shared_lua_websocket_list *)
1652 mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
1653 if (*shared_websock_list == NULL) {
1654 mg_unlock_context(conn->ctx);
1655 mg_cry(conn, "Cannot create shared websocket struct, OOM");
1658 /* init ws list element */
1659 ws = &(*shared_websock_list)->ws;
1660 ws->script = mg_strdup(script); /* TODO (low): handle OOM */
1661 pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr);
1662 (void)pthread_mutex_lock(&(ws->ws_mutex));
1663 ws->state = lua_newstate(lua_allocator, NULL);
1666 prepare_lua_environment(
1667 conn->ctx, NULL, ws, ws->state, script, LUA_ENV_TYPE_LUA_WEBSOCKET);
1668 err = luaL_loadfile(ws->state, script);
1670 lua_cry(conn, err, ws->state, script, "load");
1672 err = lua_pcall(ws->state, 0, 0, 0);
1674 lua_cry(conn, err, ws->state, script, "init");
1678 ws = &(*shared_websock_list)->ws;
1679 (void)pthread_mutex_lock(&(ws->ws_mutex));
1680 (*shared_websock_list)->ws.conn[(ws->references)++] = conn;
1682 mg_unlock_context(conn->ctx);
1685 lua_getglobal(ws->state, "open");
1686 lua_newtable(ws->state);
1687 prepare_lua_request_info(conn, ws->state);
1688 lua_pushstring(ws->state, "client");
1689 lua_pushlightuserdata(ws->state, (void *)conn);
1690 lua_rawset(ws->state, -3);
1692 err = lua_pcall(ws->state, 1, 1, 0);
1694 lua_cry(conn, err, ws->state, script, "open handler");
1696 if (lua_isboolean(ws->state, -1)) {
1697 ok = lua_toboolean(ws->state, -1);
1699 lua_pop(ws->state, 1);
1702 /* Remove from ws connection list. */
1703 /* TODO (mid): Check if list entry and Lua state needs to be deleted
1704 * (see websocket_close). */
1705 (*shared_websock_list)->ws.conn[--(ws->references)] = 0;
1708 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1710 return ok ? (void *)ws : NULL;
1715 lua_websocket_data(struct mg_connection *conn,
1721 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
1725 assert(ws->state != NULL);
1727 (void)pthread_mutex_lock(&(ws->ws_mutex));
1729 lua_getglobal(ws->state, "data");
1730 lua_newtable(ws->state);
1731 lua_pushstring(ws->state, "client");
1732 lua_pushlightuserdata(ws->state, (void *)conn);
1733 lua_rawset(ws->state, -3);
1734 lua_pushstring(ws->state, "bits"); /* TODO: dont use "bits" but fields with
1735 a meaning according to
1736 http://tools.ietf.org/html/rfc6455,
1738 lua_pushnumber(ws->state, bits);
1739 lua_rawset(ws->state, -3);
1740 lua_pushstring(ws->state, "data");
1741 lua_pushlstring(ws->state, data, data_len);
1742 lua_rawset(ws->state, -3);
1744 err = lua_pcall(ws->state, 1, 1, 0);
1746 lua_cry(conn, err, ws->state, ws->script, "data handler");
1748 if (lua_isboolean(ws->state, -1)) {
1749 ok = lua_toboolean(ws->state, -1);
1751 lua_pop(ws->state, 1);
1753 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1760 lua_websocket_ready(struct mg_connection *conn, void *ws_arg)
1762 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
1766 assert(ws->state != NULL);
1768 (void)pthread_mutex_lock(&(ws->ws_mutex));
1770 lua_getglobal(ws->state, "ready");
1771 lua_newtable(ws->state);
1772 lua_pushstring(ws->state, "client");
1773 lua_pushlightuserdata(ws->state, (void *)conn);
1774 lua_rawset(ws->state, -3);
1775 err = lua_pcall(ws->state, 1, 1, 0);
1777 lua_cry(conn, err, ws->state, ws->script, "ready handler");
1779 if (lua_isboolean(ws->state, -1)) {
1780 ok = lua_toboolean(ws->state, -1);
1782 lua_pop(ws->state, 1);
1785 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1792 lua_websocket_close(struct mg_connection *conn, void *ws_arg)
1794 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
1795 struct mg_shared_lua_websocket_list **shared_websock_list =
1796 &(conn->ctx->shared_lua_websockets);
1801 assert(ws->state != NULL);
1803 (void)pthread_mutex_lock(&(ws->ws_mutex));
1805 lua_getglobal(ws->state, "close");
1806 lua_newtable(ws->state);
1807 lua_pushstring(ws->state, "client");
1808 lua_pushlightuserdata(ws->state, (void *)conn);
1809 lua_rawset(ws->state, -3);
1811 err = lua_pcall(ws->state, 1, 0, 0);
1813 lua_cry(conn, err, ws->state, ws->script, "close handler");
1815 for (i = 0; i < ws->references; i++) {
1816 if (ws->conn[i] == conn) {
1818 ws->conn[i] = ws->conn[ws->references];
1821 /* TODO: Delete lua_websock_data and remove it from the websocket list.
1822 This must only be done, when all connections are closed, and all
1823 asynchronous operations and timers are completed/expired. */
1824 (void)shared_websock_list; /* shared_websock_list unused (see open TODO) */
1826 (void)pthread_mutex_unlock(&(ws->ws_mutex));
1832 lua_init_optional_libraries(void)
1834 #if !defined(_WIN32)
1835 void *dll_handle = dlopen("libuuid.so", RTLD_LAZY);
1836 pf_uuid_generate.p = dlsym(dll_handle, "uuid_generate");
1838 pf_uuid_generate.p = 0;