From 2e232b330412d38bf6ffba6fd0d5ac5f04d33df7 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka <valenok@gmail.com> Date: Wed, 15 May 2013 17:42:34 +0100 Subject: [PATCH] Lua functionality change: print renamed to mg.write, read renamed to mg.read. Expanded user manual, fixed example pages. --- UserManual.md | 31 ++-- examples/lua/prime_numbers.lp | 24 ++- mod_lua.c | 259 ++++++++++++++++++++++++++++++ mongoose.c | 290 +--------------------------------- test/page.lp | 27 ++-- test/unit_test.c | 14 +- 6 files changed, 311 insertions(+), 334 deletions(-) create mode 100644 mod_lua.c diff --git a/UserManual.md b/UserManual.md index 20b1f3242..ac4583673 100644 --- a/UserManual.md +++ b/UserManual.md @@ -302,35 +302,40 @@ print current weekday name, one can write: <p> <span>Today is:</span> - <? print(os.date("%A")) ?> + <? mg.write(os.date("%A")) ?> </p> -Note that this example uses function `print()`, which prints data to the -web page. Using function `print()` is the way to generate web content from -inside Lua code. In addition to `print()`, all standard library functions +Note that this example uses function `mg.write()`, which prints data to the +web page. Using function `mg.write()` is the way to generate web content from +inside Lua code. In addition to `mg.write()`, all standard library functions are accessible from the Lua code (please check reference manual for details), -and also information about the request is available in `request_info` object, +and also information about the request is available in `mg.request_info` object, like request method, all headers, etcetera. Please refer to `struct mg_request_info` definition in [mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h) -to see what kind of information is present in `request_info` object. Also, +to see what kind of information is present in `mg.request_info` object. Also, [page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) and [prime_numbers.lp](https://github.com/valenok/mongoose/blob/master/examples/lua/prime_numbers.lp) contains some example code that uses `request_info` and other functions(form submitting for example). +Mongoose exports the following to the Lua server page: -One substantial difference of mongoose's Lua Pages and PHP is that Mongoose -expects Lua page to output HTTP headers. Therefore, **at the very beginning of -every Lua Page must be a Lua block that outputs HTTP headers**, like this: + mg.read() -- reads a chunk from POST data, returns it as a string + mg.write(str) -- writes string to the client + mg.include(path) -- sources another Lua file + mg.redirect(uri) -- internal redirect to a given URI + mg.onerror(msg) -- error handler, can be overridden + mg.version -- a string that holds Mongoose version + mg.request_info -- a table with request information + + +**IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore, +every Lua Page must begin with HTTP reply line and headers**, like this: <? print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?> <html><body> ... the rest of the web page ... -It is easy to do things like redirects, for example: - - <? print('HTTP/1.0 302 Found\r\nLocation: http://google.com\r\n\r\n') ?> - To serve Lua Page, mongoose creates Lua context. That context is used for all Lua blocks within the page. That means, all Lua blocks on the same page share the same context. If one block defines a variable, for example, that diff --git a/examples/lua/prime_numbers.lp b/examples/lua/prime_numbers.lp index b3edc129e..a162f242f 100644 --- a/examples/lua/prime_numbers.lp +++ b/examples/lua/prime_numbers.lp @@ -1,8 +1,5 @@ -<? - -- Lua server pages have full control over the output, including HTTP - -- headers they send to the client. Send HTTP headers: - print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') -?> +HTTP/1.0 200 OK +Content-Type: text/html <html> <p>Prime numbers from 0 to 100, calculated by Lua:</p> @@ -18,7 +15,7 @@ end for i = 1, 100 do - if is_prime(i) then print('<span>' .. i .. '</span> ') end + if is_prime(i) then mg.write('<span>' .. i .. '</span> ') end end ?> @@ -27,15 +24,16 @@ <form method="POST" ><input type="text" name="t1"/><input type="submit"></form> <pre> - POST data: [<? print(read())?>] - request method: [<? print(request_info.request_method) ?>] - IP/port: [<? print(request_info.remote_ip, ':', request_info.remote_port) ?>] - URI: [<? print(request_info.uri) ?>] - HTTP version [<? print(request_info.http_version) ?>] + POST data: [<? mg.write(mg.read())?>] + request method: [<? mg.write(mg.request_info.request_method) ?>] + IP/port: [<? mg.write(mg.request_info.remote_ip, ':', + mg.request_info.remote_port) ?>] + URI: [<? mg.write(mg.request_info.uri) ?>] + HTTP version [<? mg.write(mg.request_info.http_version) ?>] HEADERS: <? - for name, value in pairs(request_info.http_headers) do - print(name, ':', value, '\n') + for name, value in pairs(mg.request_info.http_headers) do + mg.write(name, ':', value, '\n') end ?> </pre> diff --git a/mod_lua.c b/mod_lua.c new file mode 100644 index 000000000..248ce0b34 --- /dev/null +++ b/mod_lua.c @@ -0,0 +1,259 @@ +#include <lua.h> +#include <lauxlib.h> + +#ifdef _WIN32 +static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, + int offset) { + HANDLE fh = (HANDLE) _get_osfhandle(fd); + HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); + void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len); + CloseHandle(mh); + return p; +} +#define munmap(x, y) UnmapViewOfFile(x) +#define MAP_FAILED NULL +#define MAP_PRIVATE 0 +#define PROT_READ 0 +#else +#include <sys/mman.h> +#endif + +static void handle_request(struct mg_connection *); + +static int handle_lsp_request(struct mg_connection *, const char *, + struct file *, struct lua_State *); + +static int lsp_error(lua_State *L) { + lua_getglobal(L, "mg"); + lua_getfield(L, -1, "onerror"); + lua_pushvalue(L, -3); + lua_pcall(L, 1, 0, 0); + return 0; +} + +// Silently stop processing chunks. +static void lsp_abort(lua_State *L) { + int top = lua_gettop(L); + lua_getglobal(L, "mg"); + lua_pushnil(L); + lua_setfield(L, -2, "onerror"); + lua_settop(L, top); + lua_pushstring(L, "aborting"); + lua_error(L); +} + +static int lsp(struct mg_connection *conn, const char *path, + const char *p, int64_t len, lua_State *L) { + int i, j, pos = 0, lines = 1, lualines = 0; + char chunkname[MG_BUF_LEN]; + + for (i = 0; i < len; i++) { + if (p[i] == '\n') lines++; + if (p[i] == '<' && p[i + 1] == '?') { + for (j = i + 1; j < len ; j++) { + if (p[j] == '\n') lualines++; + if (p[j] == '?' && p[j + 1] == '>') { + mg_write(conn, p + pos, i - pos); + + snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines); + lua_pushlightuserdata(L, conn); + lua_pushcclosure(L, lsp_error, 1); + if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) { + // Syntax error or OOM. Error message is pushed on stack. + lua_pcall(L, 1, 0, 0); + } else { + // Success loading chunk. Call it. + lua_pcall(L, 0, 0, 1); + } + + pos = j + 2; + i = pos - 1; + break; + } + } + if (lualines > 0) { + lines += lualines; + lualines = 0; + } + } + } + + if (i > pos) { + mg_write(conn, p + pos, i - pos); + } + + return 0; +} + +static int lsp_write(lua_State *L) { + int i, num_args; + const char *str; + size_t size; + struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); + + num_args = lua_gettop(L); + for (i = 1; i <= num_args; i++) { + if (lua_isstring(L, i)) { + str = lua_tolstring(L, i, &size); + mg_write(conn, str, size); + } + } + + return 0; +} + +static int lsp_read(lua_State *L) { + struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); + char buf[1024]; + int len = mg_read(conn, buf, sizeof(buf)); + + if (!len) return 0; + lua_pushlstring(L, buf, len); + + return 1; +} + +// mg.include: Include another .lp file +static int lsp_include(lua_State *L) { + struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); + struct file file = STRUCT_FILE_INITIALIZER; + if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) { + // handle_lsp_request returned an error code, meaning an error occured in + // the included page and mg.onerror returned non-zero. Stop processing. + lsp_abort(L); + } + return 0; +} + +// mg.cry: Log an error. Default value for mg.onerror. +static int lsp_cry(lua_State *L){ + struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); + cry(conn, "%s", lua_tostring(L, -1)); + return 0; +} + +// mg.redirect: Redirect the request (internally). +static int lsp_redirect(lua_State *L) { + struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); + conn->request_info.uri = lua_tostring(L, -1); + handle_request(conn); + lsp_abort(L); + return 0; +} + +static void reg_string(struct lua_State *L, const char *name, const char *val) { + lua_pushstring(L, name); + lua_pushstring(L, val); + lua_rawset(L, -3); +} + +static void reg_int(struct lua_State *L, const char *name, int val) { + lua_pushstring(L, name); + lua_pushinteger(L, val); + lua_rawset(L, -3); +} + +static void reg_function(struct lua_State *L, const char *name, + lua_CFunction func, struct mg_connection *conn) { + lua_pushstring(L, name); + lua_pushlightuserdata(L, conn); + lua_pushcclosure(L, func, 1); + lua_rawset(L, -3); +} + +static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) { + const struct mg_request_info *ri = mg_get_request_info(conn); + extern void luaL_openlibs(lua_State *); + int i; + + luaL_openlibs(L); +#ifdef USE_LUA_SQLITE3 + { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); } +#endif + + // Register mg module + lua_newtable(L); + + reg_function(L, "read", lsp_read, conn); + reg_function(L, "write", lsp_write, conn); + reg_function(L, "cry", lsp_cry, conn); + reg_function(L, "include", lsp_include, conn); + reg_function(L, "redirect", lsp_redirect, conn); + reg_string(L, "version", MONGOOSE_VERSION); + + // Export request_info + lua_pushstring(L, "request_info"); + lua_newtable(L); + reg_string(L, "request_method", ri->request_method); + reg_string(L, "uri", ri->uri); + reg_string(L, "http_version", ri->http_version); + reg_string(L, "query_string", ri->query_string); + reg_int(L, "remote_ip", ri->remote_ip); + reg_int(L, "remote_port", ri->remote_port); + reg_int(L, "num_headers", ri->num_headers); + lua_pushstring(L, "http_headers"); + lua_newtable(L); + for (i = 0; i < ri->num_headers; i++) { + reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value); + } + lua_rawset(L, -3); + lua_rawset(L, -3); + + lua_setglobal(L, "mg"); + + // Register default mg.onerror function + luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', " + "debug.traceback(e, 1)) end"); +} + +static void lsp_send_err(struct mg_connection *conn, struct lua_State *L, + const char *fmt, ...) { + char buf[MG_BUF_LEN]; + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (L == NULL) { + send_http_error(conn, 500, http_500_error, "%s", buf); + } else { + lua_pushstring(L, buf); + lua_error(L); + } +} + +static int handle_lsp_request(struct mg_connection *conn, const char *path, + struct file *filep, struct lua_State *ls) { + void *p = NULL; + lua_State *L = NULL; + int error = 1; + + // We need both mg_stat to get file size, and mg_fopen to get fd + if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) { + lsp_send_err(conn, ls, "File [%s] not found", path); + } else if (filep->membuf == NULL && + (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE, + fileno(filep->fp), 0)) == MAP_FAILED) { + lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size, + fileno(filep->fp), strerror(errno)); + } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) { + send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed"); + } else { + // We're not sending HTTP headers here, Lua page must do it. + if (ls == NULL) { + prepare_lua_environment(conn, L); + if (conn->ctx->callbacks.init_lua != NULL) { + conn->ctx->callbacks.init_lua(conn, L); + } + } + error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf, + filep->size, L); + } + + if (L != NULL && ls == NULL) lua_close(L); + if (p != NULL) munmap(p, filep->size); + mg_fclose(filep); + return error; +} diff --git a/mongoose.c b/mongoose.c index 49bfd6729..c0ef119a0 100644 --- a/mongoose.c +++ b/mongoose.c @@ -248,11 +248,6 @@ typedef int SOCKET; #include "mongoose.h" -#ifdef USE_LUA -#include <lua.h> -#include <lauxlib.h> -#endif - #define MONGOOSE_VERSION "3.8" #define PASSWORDS_FILE_NAME ".htpasswd" #define CGI_ENVIRONMENT_SIZE 4096 @@ -3983,290 +3978,7 @@ static uint32_t get_remote_ip(const struct mg_connection *conn) { } #ifdef USE_LUA - -#ifdef _WIN32 -static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, - int offset) { - HANDLE fh = (HANDLE) _get_osfhandle(fd); - HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); - void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len); - CloseHandle(mh); - return p; -} -#define munmap(x, y) UnmapViewOfFile(x) -#define MAP_FAILED NULL -#define MAP_PRIVATE 0 -#define PROT_READ 0 -#else -#include <sys/mman.h> -#endif - -static void handle_request(struct mg_connection *); - -static int handle_lsp_request(struct mg_connection *, const char *, - struct file *, struct lua_State *); - -static int lsp_mg_error(lua_State *L) { - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - int top = lua_gettop(L); - if (top < 1) lua_pushstring(L, "unknown error"); - // Get mg.onerror. - lua_getglobal(L, "mg"); - lua_getfield(L, -1, "onerror"); - // If mg.onerror is nil, silently stop processing chunks. - if (lua_isnil(L, -1)) { - lua_pushinteger(L, 1); - return 1; - } - // Call mg.onerror. - lua_pushvalue(L, top); - lua_remove(L, top); - if (lua_pcall(L, 1, 1, 0)) { - // If mg.onerror fails, cry the error message and stop processing chunks. - cry(conn, "mg.onerror failed: %s", lua_tostring(L, -1)); - lua_pushinteger(L, 1); - return 1; - } - // Return the return value from mg.onerror. Non-0 = stop processing chunks. - return 1; -} - -// Silently stop processing chunks. -static void lsp_abort(lua_State *L) { - int top = lua_gettop(L); - lua_getglobal(L, "mg"); - lua_pushnil(L); - lua_setfield(L, -2, "onerror"); - lua_settop(L, top); - lua_pushstring(L, "aborting"); - lua_error(L); -} - -static int lsp(struct mg_connection *conn, const char *path, - const char *p, int64_t len, lua_State *L) { - int i, j, result, pos = 0, lines = 1, lualines = 0; - char chunkname [MG_BUF_LEN]; - - for (i = 0; i < len; i++) { - if (p[i] == '\n') ++lines; - if (p[i] == '<' && p[i + 1] == '?') { - for (j = i + 1; j < len ; j++) { - if (p[j] == '\n') ++lualines; - if (p[j] == '?' && p[j + 1] == '>') { - mg_write(conn, p + pos, i - pos); - lua_pushlightuserdata(L, conn); - lua_pushcclosure(L, lsp_mg_error, 1); - snprintf (chunkname, sizeof(chunkname), "@%s+%i", path, lines); - if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) { - lua_pcall(L, 1, 1, 0); - result = lua_tointeger(L, -1); - if (result) return result; - } else { - lua_pcall(L, 0, 0, 1); - result = lua_tointeger(L, -1); - if (result) return result; - } - pos = j + 2; - i = pos - 1; - break; - } - } - if (lualines > 0) { - lines += lualines; - lualines = 0; - } - } - } - - if (i > pos) { - mg_write(conn, p + pos, i - pos); - } - - return 0; -} - -static int lsp_mg_print(lua_State *L) { - int i, num_args; - const char *str; - size_t size; - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - - num_args = lua_gettop(L); - for (i = 1; i <= num_args; i++) { - if (lua_isstring(L, i)) { - str = lua_tolstring(L, i, &size); - mg_write(conn, str, size); - } - } - - return 0; -} - -static int lsp_mg_read(lua_State *L) { - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - char buf[1024]; - int len = mg_read(conn, buf, sizeof(buf)); - - if (!len) return 0; - lua_pushlstring(L, buf, len); - - return 1; -} - -// mg.include: Include another .lp file -static int lsp_mod_include(lua_State *L) { - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - struct file file = STRUCT_FILE_INITIALIZER; - if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) { - // handle_lsp_request returned an error code, meaning an error occured in - // the included page and mg.onerror returned non-zero. Stop processing. - lsp_abort(L); - } - return 0; -} - -// mg.cry: Log an error. Default value for mg.onerror. -static int lsp_mod_cry(lua_State *L){ - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - cry(conn, "%s", lua_tostring(L, -1)); - return 0; -} - -// mg.redirect: Redirect the request (internally). -static int lsp_mod_redirect(lua_State *L) { - struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); - conn->request_info.uri = lua_tostring(L, -1); - handle_request(conn); - lsp_abort(L); - return 0; -} - -static void reg_string(struct lua_State *L, const char *name, const char *val) { - lua_pushstring(L, name); - lua_pushstring(L, val); - lua_rawset(L, -3); -} - -static void reg_int(struct lua_State *L, const char *name, int val) { - lua_pushstring(L, name); - lua_pushinteger(L, val); - lua_rawset(L, -3); -} - -static void reg_function(struct lua_State *L, const char *name, - lua_CFunction func, struct mg_connection *conn) { - lua_pushstring(L, name); - lua_pushlightuserdata(L, conn); - lua_pushcclosure(L, func, 1); - lua_rawset(L, -3); -} - -static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) { - const struct mg_request_info *ri = mg_get_request_info(conn); - extern void luaL_openlibs(lua_State *); - int i; - - luaL_openlibs(L); -#ifdef USE_LUA_SQLITE3 - { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); } -#endif - - // Register "print" function which calls mg_write() - lua_pushlightuserdata(L, conn); - lua_pushcclosure(L, lsp_mg_print, 1); - lua_setglobal(L, "print"); - - // Register mg_read() - lua_pushlightuserdata(L, conn); - lua_pushcclosure(L, lsp_mg_read, 1); - lua_setglobal(L, "read"); - - // Register mg module - lua_newtable(L); - reg_function(L, "cry", lsp_mod_cry, conn); - reg_function(L, "include", lsp_mod_include, conn); - reg_function(L, "onerror", lsp_mod_cry, conn); - reg_function(L, "redirect", lsp_mod_redirect, conn); - reg_string(L, "version", MONGOOSE_VERSION); - lua_setglobal(L, "mg"); - - // Export request_info - lua_newtable(L); - reg_string(L, "request_method", ri->request_method); - reg_string(L, "uri", ri->uri); - reg_string(L, "http_version", ri->http_version); - reg_string(L, "query_string", ri->query_string); - reg_int(L, "remote_ip", ri->remote_ip); - reg_int(L, "remote_port", ri->remote_port); - reg_int(L, "num_headers", ri->num_headers); - lua_pushstring(L, "http_headers"); - lua_newtable(L); - for (i = 0; i < ri->num_headers; i++) { - reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value); - } - lua_rawset(L, -3); - lua_setglobal(L, "request_info"); -} - -// Throw a lua error. Called from handle_lsp_request via mg.include -static void send_lua_error(struct lua_State *L, - PRINTF_FORMAT_STRING(const char *fmt), ...) - PRINTF_ARGS(2, 3); - -static void send_lua_error(struct lua_State *L, const char *fmt, ...) { - char buf[MG_BUF_LEN]; - va_list ap; - int len = 0; - - va_start(ap, fmt); - len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); - va_end(ap); - - lua_pushstring(L, buf); - lua_error(L); -} - -static int handle_lsp_request(struct mg_connection *conn, const char *path, - struct file *filep, struct lua_State *ls) { - void *p = NULL; - lua_State *L = NULL; - int error = 1; - - if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) { - if (ls == NULL) { - send_http_error(conn, 404, "Not Found", "%s", "File not found"); - } else { - send_lua_error(ls, "File not found: %s", path); - } - } else if (filep->membuf == NULL && - (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE, - fileno(filep->fp), 0)) == MAP_FAILED) { - if (ls == NULL) { - send_http_error(conn, 500, http_500_error, "mmap(%s, %zu, %d): %s", path, - (size_t) filep->size, fileno(filep->fp), strerror(errno)); - } else { - send_lua_error(ls, "mmap(%s, %zu, %d): %s", path, - (size_t) filep->size, fileno(filep->fp), strerror(errno)); - } - } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) { - send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed"); - } else { - // We're not sending HTTP headers here, Lua page must do it. - if (ls == NULL) { - prepare_lua_environment(conn, L); - if (conn->ctx->callbacks.init_lua != NULL) { - conn->ctx->callbacks.init_lua(conn, L); - } - } - error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf, - filep->size, L); - } - - if (L && ls == NULL) lua_close(L); - if (p) munmap(p, filep->size); - mg_fclose(filep); - return error; -} +#include "mod_lua.c" #endif // USE_LUA int mg_upload(struct mg_connection *conn, const char *destination_dir) { diff --git a/test/page.lp b/test/page.lp index b2ebee8b1..c38cfc415 100644 --- a/test/page.lp +++ b/test/page.lp @@ -1,24 +1,25 @@ -<? - -- Lua server pages have full control over the output, including HTTP - -- headers they send to the client. Send HTTP headers: - print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') +HTTP/1.0 200 OK +Content-Type: text/html + +<html><body> -?><html><body> <p>This is an example Lua server page served by <a href="http://code.google.com/p/mongoose">Mongoose web server</a>. Mongoose has Lua, Sqlite, and other functionality built in the binary. This example page stores the request in the Sqlite database, and shows all requests done previously.</p> -<p> Today is <? print(os.date("%A")) ?> +<p> Today is <? mg.write(os.date("%A")) ?> <pre> <? + -- for k,v in pairs(_G) do mg.write(k, '\n') end + -- Open database local db = sqlite3.open('requests.db') -- Setup a trace callback, to show SQL statements we'll be executing. - -- db:trace(function(data, sql) print('Executing: ' .. sql .. '\n') end, nil) + -- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil) -- Create a table if it is not created already db:exec([[ @@ -27,24 +28,26 @@ all requests done previously.</p> timestamp NOT NULL, method NOT NULL, uri NOT NULL, - user_agent + addr ); ]]) + -- Add entry about this request local stmt = db:prepare( 'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);'); - stmt:bind_values(request_info.request_method, request_info.uri, - request_info.http_headers['User-Agent']) + stmt:bind_values(mg.request_info.request_method, + mg.request_info.uri, + mg.request_info.remote_port) stmt:step() stmt:finalize() -- Show all previous records - print('Previous requests:\n') + mg.write('Previous requests:\n') stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;') while stmt:step() == sqlite3.ROW do local v = stmt:get_values() - print(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' ' + mg.write(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' ' .. v[4] .. ' ' .. v[5] .. '\n') end diff --git a/test/unit_test.c b/test/unit_test.c index 096d9dccf..40c907452 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -467,13 +467,13 @@ static void test_lua(void) { ASSERT(lua_gettop(L) == 0); check_lua_expr(L, "'hi'", "hi"); - check_lua_expr(L, "request_info.request_method", "POST"); - check_lua_expr(L, "request_info.uri", "/foo/bar"); - check_lua_expr(L, "request_info.num_headers", "2"); - check_lua_expr(L, "request_info.remote_ip", "0"); - check_lua_expr(L, "request_info.http_headers['Content-Length']", "12"); - check_lua_expr(L, "request_info.http_headers['Connection']", "close"); - (void) luaL_dostring(L, "post = read()"); + check_lua_expr(L, "mg.request_info.request_method", "POST"); + check_lua_expr(L, "mg.request_info.uri", "/foo/bar"); + check_lua_expr(L, "mg.request_info.num_headers", "2"); + check_lua_expr(L, "mg.request_info.remote_ip", "0"); + check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12"); + check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close"); + (void) luaL_dostring(L, "post = mg.read()"); check_lua_expr(L, "# post", "12"); check_lua_expr(L, "post", "hello world!"); lua_close(L); -- GitLab