From cb23152c39c23166c987bbc8711642940b01117e Mon Sep 17 00:00:00 2001 From: mbalmer Date: Sat, 12 Oct 2013 17:24:06 +0000 Subject: [PATCH] add Lua scripting support to bozohttpd, see httpd(8) for details --- libexec/httpd/Makefile | 7 +- libexec/httpd/bozohttpd.8 | 30 ++- libexec/httpd/bozohttpd.c | 8 +- libexec/httpd/bozohttpd.h | 34 ++- libexec/httpd/lua-bozo.c | 437 +++++++++++++++++++++++++++++++++++++ libexec/httpd/main.c | 20 +- libexec/httpd/printenv.lua | 83 +++++++ 7 files changed, 610 insertions(+), 9 deletions(-) create mode 100644 libexec/httpd/lua-bozo.c create mode 100644 libexec/httpd/printenv.lua diff --git a/libexec/httpd/Makefile b/libexec/httpd/Makefile index eeb8f933f304..44e10895e3ca 100644 --- a/libexec/httpd/Makefile +++ b/libexec/httpd/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.15 2013/10/12 07:49:40 mbalmer Exp $ +# $NetBSD: Makefile,v 1.16 2013/10/12 17:24:06 mbalmer Exp $ # # $eterna: Makefile,v 1.30 2010/07/11 00:34:27 mrg Exp $ # @@ -13,6 +13,7 @@ # NO_DYNAMIC_CONTENT /* don't support dynamic content updates */ # NO_SSL_SUPPORT /* don't support ssl (https) */ # DO_HTPASSWD /* support .htpasswd files */ +# NO_LUA_SUPPORT /* don't support Lua for dynamic content */ # # these are usually set via the "COPTS" variable, or some other method # for setting CFLAGS relevant to your make, eg @@ -23,10 +24,10 @@ PROG= httpd MAN= httpd.8 BUILDSYMLINKS+=bozohttpd.8 httpd.8 SRCS= bozohttpd.c ssl-bozo.c auth-bozo.c cgi-bozo.c daemon-bozo.c \ - tilde-luzah-bozo.c dir-index-bozo.c content-bozo.c + tilde-luzah-bozo.c dir-index-bozo.c content-bozo.c lua-bozo.c SRCS+= main.c -LDADD= -lcrypt +LDADD= -lcrypt -llua DPADD= ${LIBCRYPT} WARNS?= 4 diff --git a/libexec/httpd/bozohttpd.8 b/libexec/httpd/bozohttpd.8 index 22dd1df9b786..b4ddfbfa58ef 100644 --- a/libexec/httpd/bozohttpd.8 +++ b/libexec/httpd/bozohttpd.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: bozohttpd.8,v 1.38 2013/07/11 08:19:56 wiz Exp $ +.\" $NetBSD: bozohttpd.8,v 1.39 2013/10/12 17:24:06 mbalmer Exp $ .\" .\" $eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $ .\" @@ -26,7 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 11, 2013 +.Dd October 12, 2013 .Dt HTTPD 8 .Os .Sh NAME @@ -36,6 +36,7 @@ .Nm .Op Fl CIMPSZciptvx .Op Fl C Ar suffix cgihandler +.Op Fl L Ar prefix script .Op Fl I Ar port .Op Fl M Ar suffix type encoding encoding11 .Op Fl P Ar pidfile @@ -95,6 +96,27 @@ outside of the cgibin directory to be executed. Multiple .Fl C options may be passed. +.It Fl L Ar prefix script +Adds a new Lua script for a particular prefix. +The +.Ar prefix +should be an arbitrary text, and the +.Ar script +should be a full path to a Lua script. +Multiple +.Fl L +options may be passed. +A separate Lua state is created for each prefix. +The Lua script can register callbacks using the +httpd.register_handler('', function) Lua function, +which will trigger the execution of the Lua function +.Em function +when a URL in the form +.Em http://// +is being accessed. +The function is passed three tables as arguments, the server +environment, the request headers, and the decoded query string +plus any data that was send as application/x-www-form-urlencoded. .It Fl c Ar cgibin Enables the CGI/1.1 interface. The @@ -494,6 +516,10 @@ was written by Matthew R. Green The large list of contributors includes: .Bl -dash .It +Marc Balmer +.Aq mbalmer@NetBSD.org +added Lua support for dynamic content creation +.It Arnaud Lacombe .Aq alc@NetBSD.org provided some clean up for memory leaks diff --git a/libexec/httpd/bozohttpd.c b/libexec/httpd/bozohttpd.c index 629383650811..a66016547105 100644 --- a/libexec/httpd/bozohttpd.c +++ b/libexec/httpd/bozohttpd.c @@ -1,4 +1,4 @@ -/* $NetBSD: bozohttpd.c,v 1.42 2013/10/12 07:49:40 mbalmer Exp $ */ +/* $NetBSD: bozohttpd.c,v 1.43 2013/10/12 17:24:06 mbalmer Exp $ */ /* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $ */ @@ -1425,6 +1425,9 @@ transform_request(bozo_httpreq_t *request, int *isindex) if (bozo_process_cgi(request)) return 0; + if (bozo_process_lua(request)) + return 0; + debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); return 1; bad_done: @@ -2086,6 +2089,9 @@ bozo_init_httpd(bozohttpd_t *httpd) "bozohttpd: memory_allocation failure\n"); return 0; } +#ifndef NO_LUA_SUPPORT + SIMPLEQ_INIT(&httpd->lua_states); +#endif return 1; } diff --git a/libexec/httpd/bozohttpd.h b/libexec/httpd/bozohttpd.h index 8f08d9c74ca5..13c528b1777a 100644 --- a/libexec/httpd/bozohttpd.h +++ b/libexec/httpd/bozohttpd.h @@ -1,4 +1,4 @@ -/* $NetBSD: bozohttpd.h,v 1.29 2013/10/12 07:49:40 mbalmer Exp $ */ +/* $NetBSD: bozohttpd.h,v 1.30 2013/10/12 17:24:07 mbalmer Exp $ */ /* $eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $ */ @@ -36,6 +36,9 @@ #include +#ifndef NO_LUA_SUPPORT +#include +#endif #include /* lots of "const" but gets free()'ed etc at times, sigh */ @@ -47,6 +50,22 @@ typedef struct bozoheaders { SIMPLEQ_ENTRY(bozoheaders) h_next; } bozoheaders_t; +#ifndef NO_LUA_SUPPORT +typedef struct lua_handler { + const char *name; + int ref; + SIMPLEQ_ENTRY(lua_handler) h_next; +} lua_handler_t; + +typedef struct lua_state_map { + const char *script; + const char *prefix; + lua_State *L; + SIMPLEQ_HEAD(, lua_handler) handlers; + SIMPLEQ_ENTRY(lua_state_map) s_next; +} lua_state_map_t; +#endif + typedef struct bozo_content_map_t { const char *name; /* postfix of file */ size_t namelen; /* length of postfix */ @@ -94,6 +113,10 @@ typedef struct bozohttpd_t { int hide_dots; /* hide .* */ int process_cgi; /* use the cgi handler */ char *cgibin; /* cgi-bin directory */ +#ifndef NO_LUA_SUPPORT + int process_lua; /* use the Lua handler */ + SIMPLEQ_HEAD(, lua_state_map) lua_states; +#endif void *sslinfo; /* pointer to ssl struct */ int dynamic_content_map_size;/* size of dyn cont map */ bozo_content_map_t *dynamic_content_map;/* dynamic content map */ @@ -252,6 +275,15 @@ void bozo_add_content_map_cgi(bozohttpd_t *, const char *, const char *); #endif /* NO_CGIBIN_SUPPORT */ +/* lua-bozo.c */ +#ifdef NO_LUA_SUPPORT +#define bozo_process_lua(h) 0 +#else +void bozo_add_lua_map(bozohttpd_t *, const char *, const char *); +int bozo_process_lua(bozo_httpreq_t *); +#endif /* NO_LUA_SUPPORT */ + + /* daemon-bozo.c */ #ifdef NO_DAEMON_MODE #define bozo_daemon_init(x) do { /* nothing */ } while (0) diff --git a/libexec/httpd/lua-bozo.c b/libexec/httpd/lua-bozo.c new file mode 100644 index 000000000000..9138c207b03f --- /dev/null +++ b/libexec/httpd/lua-bozo.c @@ -0,0 +1,437 @@ +/* $NetBSD: lua-bozo.c,v 1.1 2013/10/12 17:24:07 mbalmer Exp $ */ + +/* + * Copyright (c) 2013 Marc Balmer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer and + * dedication in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* this code implements dynamic content generation using Lua for bozohttpd */ + +#ifndef NO_LUA_SUPPORT + +#include +#include +#include +#include +#include +#include + +#include "bozohttpd.h" + +/* Lua binding for bozohttp */ + +#if LUA_VERSION_NUM < 502 +#define LUA_HTTPDLIBNAME "httpd" +#endif + +#define FORM "application/x-www-form-urlencoded" + +static int +lua_flush(lua_State *L) +{ + bozohttpd_t *httpd; + + lua_pushstring(L, "bozohttpd"); + lua_gettable(L, LUA_REGISTRYINDEX); + httpd = lua_touserdata(L, -1); + lua_pop(L, 1); + + bozo_flush(httpd, stdout); + return 0; +} + +static int +lua_print(lua_State *L) +{ + bozohttpd_t *httpd; + + lua_pushstring(L, "bozohttpd"); + lua_gettable(L, LUA_REGISTRYINDEX); + httpd = lua_touserdata(L, -1); + lua_pop(L, 1); + + bozo_printf(httpd, "%s\r\n", lua_tostring(L, -1)); + return 0; +} + +static int +lua_read(lua_State *L) +{ + bozohttpd_t *httpd; + int len; + char *data; + + lua_pushstring(L, "bozohttpd"); + lua_gettable(L, LUA_REGISTRYINDEX); + httpd = lua_touserdata(L, -1); + lua_pop(L, 1); + + len = luaL_checkinteger(L, -1); + data = bozomalloc(httpd, len + 1); + bozo_read(httpd, STDIN_FILENO, data, len); + lua_pushstring(L, data); + free(data); + return 1; +} + +static int +lua_register_handler(lua_State *L) +{ + lua_state_map_t *map; + lua_handler_t *handler; + bozohttpd_t *httpd; + + lua_pushstring(L, "lua_state_map"); + lua_gettable(L, LUA_REGISTRYINDEX); + map = lua_touserdata(L, -1); + lua_pushstring(L, "bozohttpd"); + lua_gettable(L, LUA_REGISTRYINDEX); + httpd = lua_touserdata(L, -1); + lua_pop(L, 2); + + luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + handler = bozomalloc(httpd, sizeof(lua_handler_t)); + + handler->name = bozostrdup(httpd, lua_tostring(L, 1)); + handler->ref = luaL_ref(L, LUA_REGISTRYINDEX); + SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next); + httpd->process_lua = 1; + return 0; +} + +static int +lua_write(lua_State *L) +{ + bozohttpd_t *httpd; + const char *data; + + lua_pushstring(L, "bozohttpd"); + lua_gettable(L, LUA_REGISTRYINDEX); + httpd = lua_touserdata(L, -1); + lua_pop(L, 1); + + data = luaL_checkstring(L, -1); + lua_pushinteger(L, bozo_write(httpd, STDIN_FILENO, data, strlen(data))); + return 1; +} + +static int +luaopen_httpd(lua_State *L) +{ + struct luaL_Reg functions[] = { + { "flush", lua_flush }, + { "print", lua_print }, + { "read", lua_read }, + { "register_handler", lua_register_handler }, + { "write", lua_write }, + { NULL, NULL } + }; +#if LUA_VERSION_NUM >= 502 + luaL_newlib(L, functions); +#else + luaL_register(L, LUA_HTTPDLIBNAME, functions); +#endif + lua_pushstring(L, "httpd 1.0.0"); + lua_setfield(L, -2, "_VERSION"); + return 1; +} + +#if LUA_VERSION_NUM < 502 +static void +lua_openlib(lua_State *L, const char *name, lua_CFunction fn) +{ + lua_pushcfunction(L, fn); + lua_pushstring(L, name); + lua_call(L, 1, 0); +} +#endif + +/* bozohttpd integration */ +void +bozo_add_lua_map(bozohttpd_t *httpd, const char *prefix, const char *script) +{ + lua_state_map_t *map; + char *cwd, *path; + + map = bozomalloc(httpd, sizeof(lua_state_map_t)); + map->prefix = bozostrdup(httpd, prefix); + if (*script == '/') + map->script = bozostrdup(httpd, script); + else { + cwd = getwd(NULL); + asprintf(&path, "%s/%s", cwd, script); + map->script = path; + free(cwd); + } + map->L = luaL_newstate(); + if (map->L == NULL) + bozo_err(httpd, 1, "can't create Lua state"); + SIMPLEQ_INIT(&map->handlers); + +#if LUA_VERSION_NUM >= 502 + luaL_openlibs(map->L); + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushcfunction(L, luaopen_httpd); + lua_setfield(L, -2, "httpd"); + lua_pop(L, 2); +#else + lua_openlib(map->L, "", luaopen_base); + lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package); + lua_openlib(map->L, LUA_TABLIBNAME, luaopen_table); + lua_openlib(map->L, LUA_STRLIBNAME, luaopen_string); + lua_openlib(map->L, LUA_MATHLIBNAME, luaopen_math); + lua_openlib(map->L, LUA_OSLIBNAME, luaopen_os); + lua_openlib(map->L, LUA_IOLIBNAME, luaopen_io); + lua_openlib(map->L, LUA_HTTPDLIBNAME, luaopen_httpd); +#endif + lua_pushstring(map->L, "lua_state_map"); + lua_pushlightuserdata(map->L, map); + lua_settable(map->L, LUA_REGISTRYINDEX); + + lua_pushstring(map->L, "bozohttpd"); + lua_pushlightuserdata(map->L, httpd); + lua_settable(map->L, LUA_REGISTRYINDEX); + + if (luaL_loadfile(map->L, script)) + bozo_err(httpd, 1, "failed to load script %s: %s", script, + lua_tostring(map->L, -1)); + if (lua_pcall(map->L, 0, 0, 0)) + bozo_err(httpd, 1, "failed to execute script %s: %s", script, + lua_tostring(map->L, -1)); + SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next); +} + +static void +lua_env(lua_State *L, const char *name, const char *value) +{ + lua_pushstring(L, value); + lua_setfield(L, -2, name); +} + +/* decode query string */ +static void +lua_url_decode(lua_State *L, char *s) +{ + char *v, *p, *val, *q; + char buf[3]; + int c; + + v = strchr(s, '='); + if (v == NULL) + return; + *v++ = '\0'; + val = malloc(strlen(v) + 1); + if (val == NULL) + return; + + for (p = v, q = val; *p; p++) { + switch (*p) { + case '%': + if (*(p + 1) == '\0' || *(p + 2) == '\0') + return; + buf[0] = *++p; + buf[1] = *++p; + buf[2] = '\0'; + sscanf(buf, "%2x", &c); + *q++ = (char)c; + break; + case '+': + *q++ = ' '; + break; + default: + *q++ = *p; + } + } + lua_pushstring(L, val); + lua_setfield(L, -2, s); + free(val); +} + +static void +lua_decode_query(lua_State *L, char *query) +{ + char *s; + + s = strtok(query, "&"); + while (s) { + lua_url_decode(L, s); + s = strtok(NULL, "&"); + } +} + +int +bozo_process_lua(bozo_httpreq_t *request) +{ + bozohttpd_t *httpd = request->hr_httpd; + lua_state_map_t *map; + lua_handler_t *hndlr; + int ret, length; + char date[40]; + bozoheaders_t *headp; + char *s, *query, *uri, *file, *command, *info, *content; + const char *type, *clen; + char *prefix, *handler, *p; + + if (!httpd->process_lua) + return 0; + + uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file; + + if (*uri == '/') { + file = bozostrdup(httpd, uri); + prefix = bozostrdup(httpd, &uri[1]); + } else { + prefix = bozostrdup(httpd, uri); + asprintf(&file, "/%s", uri); + } + if (file == NULL) { + free(prefix); + return 0; + } + + if (request->hr_query && strlen(request->hr_query)) + query = bozostrdup(httpd, request->hr_query); + else + query = NULL; + + p = strchr(prefix, '/'); + if (p == NULL){ + free(prefix); + return 0; + } + *p++ = '\0'; + handler = p; + if (!*handler) { + free(prefix); + return 0; + } + p = strchr(handler, '/'); + if (p != NULL) + *p++ = '\0'; + + info = NULL; + command = file + 1; + if ((s = strchr(command, '/')) != NULL) { + info = bozostrdup(httpd, s); + *s = '\0'; + } + + type = request->hr_content_type; + clen = request->hr_content_length; + + SIMPLEQ_FOREACH(map, &httpd->lua_states, s_next) { + if (strcmp(map->prefix, prefix)) + continue; + + SIMPLEQ_FOREACH(hndlr, &map->handlers, h_next) { + if (strcmp(hndlr->name, handler)) + continue; + + lua_rawgeti(map->L, LUA_REGISTRYINDEX, hndlr->ref); + + /* Create the "environment" */ + lua_newtable(map->L); + lua_env(map->L, "SERVER_NAME", + BOZOHOST(httpd, request)); + lua_env(map->L, "GATEWAY_INTERFACE", "Luigi/1.0"); + lua_env(map->L, "SERVER_PROTOCOL", request->hr_proto); + lua_env(map->L, "REQUEST_METHOD", + request->hr_methodstr); + lua_env(map->L, "SCRIPT_PREFIX", map->prefix); + lua_env(map->L, "SCRIPT_NAME", file); + lua_env(map->L, "HANDLER_NAME", hndlr->name); + lua_env(map->L, "SCRIPT_FILENAME", map->script); + lua_env(map->L, "SERVER_SOFTWARE", + httpd->server_software); + lua_env(map->L, "REQUEST_URI", uri); + lua_env(map->L, "DATE_GMT", + bozo_http_date(date, sizeof(date))); + if (query && *query) + lua_env(map->L, "QUERY_STRING", query); + if (info && *info) + lua_env(map->L, "PATH_INFO", info); + if (type && *type) + lua_env(map->L, "CONTENT_TYPE", type); + if (clen && *clen) + lua_env(map->L, "CONTENT_LENGTH", clen); + if (request->hr_serverport && *request->hr_serverport) + lua_env(map->L, "SERVER_PORT", + request->hr_serverport); + if (request->hr_remotehost && *request->hr_remotehost) + lua_env(map->L, "REMOTE_HOST", + request->hr_remotehost); + if (request->hr_remoteaddr && *request->hr_remoteaddr) + lua_env(map->L, "REMOTE_ADDR", + request->hr_remoteaddr); + + /* Pass the headers in a separate table */ + lua_newtable(map->L); + SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next) + lua_env(map->L, headp->h_header, + headp->h_value); + + /* Pass the query variables */ + if ((query && *query) || (type && *type + && !strcmp(type, FORM))) { + lua_newtable(map->L); + if (query && *query) + lua_decode_query(map->L, query); + if (type && *type && !strcmp(type, FORM)) { + if (clen && *clen && atol(clen) > 0) { + length = atol(clen); + content = bozomalloc(httpd, + length); + bozo_read(httpd, STDIN_FILENO, + content, length); + lua_decode_query(map->L, + content); + free(content); + } + } + } else + lua_pushnil(map->L); + + ret = lua_pcall(map->L, 3, 0, 0); + if (ret) + printf("
Lua error: %s\n", + lua_tostring(map->L, -1)); + bozo_flush(httpd, stdout); + free(prefix); + free(uri); + free(info); + return 1; + } + } + free(prefix); + free(uri); + free(info); + return 0; +} + +#endif /* NO_LUA_SUPPORT */ diff --git a/libexec/httpd/main.c b/libexec/httpd/main.c index 210134e9aebb..bd8c783dc307 100644 --- a/libexec/httpd/main.c +++ b/libexec/httpd/main.c @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.5 2011/11/18 09:51:31 mrg Exp $ */ +/* $NetBSD: main.c,v 1.6 2013/10/12 17:24:07 mbalmer Exp $ */ /* $eterna: main.c,v 1.6 2011/11/18 09:21:15 mrg Exp $ */ /* from: eterna: bozohttpd.c,v 1.159 2009/05/23 02:14:30 mrg Exp */ @@ -78,6 +78,9 @@ usage(bozohttpd_t *httpd, char *progname) #endif bozo_warn(httpd, " -c cgibin\t\tenable cgi-bin support in this directory"); +#endif +#ifndef NO_LUA_SUPPORT + bozo_warn(httpd, " -L arg script\tadd this Lua script"); #endif bozo_warn(httpd, " -I port\t\tbind or use on this port"); #ifndef NO_DAEMON_MODE @@ -138,9 +141,22 @@ main(int argc, char **argv) bozo_set_defaults(&httpd, &prefs); while ((c = getopt(argc, argv, - "C:HI:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) { + "C:HI:L:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) { switch(c) { + case 'L': +#ifdef NO_LUA_SUPPORT + bozo_err(&httpd, 1, + "Lua support is not enabled"); + /* NOTREACHED */ +#else + /* make sure there's two argument */ + if (argc - optind < 1) + usage(&httpd, progname); + bozo_add_lua_map(&httpd, optarg, argv[optind]); + optind++; + break; +#endif /* NO_LUA_SUPPORT */ case 'M': #ifdef NO_DYNAMIC_CONTENT bozo_err(&httpd, 1, diff --git a/libexec/httpd/printenv.lua b/libexec/httpd/printenv.lua new file mode 100644 index 000000000000..5ef8305293d9 --- /dev/null +++ b/libexec/httpd/printenv.lua @@ -0,0 +1,83 @@ +-- this small Lua script demonstrates the use of Lua in (bozo)httpd +-- it will simply output the "environment" + +-- Keep in mind that bozohttpd forks for each request when started in +-- daemon mode, you can set global veriables here, but they will have +-- the same value on each invocation. You can not keep state between +-- two calls. + +local httpd = require 'httpd' + +function printenv(env, headers, query) + + -- we get the "environment" in the env table, the values are more + -- or less the same as the variable for a CGI program + + if count == nil then + count = 1 + end + + -- output a header + print([[ + + + Bozotic Lua Environment + + +

Bozotic Lua Environment

+ ]]) + + print('module version: ' .. httpd._VERSION .. '
') + + print('

Server Environment

') + -- print the list of "environment" variables + for k, v in pairs(env) do + print(k .. '=' .. v .. '
') + end + + print('

Request Headers

') + for k, v in pairs(headers) do + print(k .. '=' .. v .. '
') + end + + if query ~= nil then + print('

Query Variables

') + for k, v in pairs(query) do + print(k .. '=' .. v .. '
') + end + end + + print('

Form Test

') + + print([[ +
+ + +
+ ]]) + -- output a footer + print([[ + + + ]]) +end + +function form(env, header, query) + if query ~= nil then + print('

Form Variables

') + + if env.CONTENT_TYPE ~= nil then + print('Content-type: ' .. env.CONTENT_TYPE .. '
') + end + + for k, v in pairs(query) do + print(k .. '=' .. v .. '
') + end + else + print('No values') + end +end + +-- register this handler for http:////printenv +httpd.register_handler('printenv', printenv) +httpd.register_handler('form', form)