[BACK]Return to lua-bozo.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / httpd

Annotation of src/libexec/httpd/lua-bozo.c, Revision 1.6

1.6     ! christos    1: /*     $NetBSD: lua-bozo.c,v 1.5 2013/10/17 08:07:54 mbalmer Exp $     */
1.1       mbalmer     2:
                      3: /*
                      4:  * Copyright (c) 2013 Marc Balmer <marc@msys.ch>
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer and
                     14:  *    dedication in the documentation and/or other materials provided
                     15:  *    with the distribution.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     18:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     19:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     20:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     21:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
                     22:  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
                     23:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
                     24:  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     25:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     27:  * SUCH DAMAGE.
                     28:  *
                     29:  */
                     30:
                     31: /* this code implements dynamic content generation using Lua for bozohttpd */
                     32:
                     33: #ifndef NO_LUA_SUPPORT
                     34:
                     35: #include <lua.h>
                     36: #include <lauxlib.h>
                     37: #include <lualib.h>
                     38: #include <stdlib.h>
                     39: #include <string.h>
                     40: #include <unistd.h>
                     41:
                     42: #include "bozohttpd.h"
                     43:
                     44: /* Lua binding for bozohttp */
                     45:
                     46: #if LUA_VERSION_NUM < 502
                     47: #define LUA_HTTPDLIBNAME "httpd"
                     48: #endif
                     49:
                     50: #define FORM   "application/x-www-form-urlencoded"
                     51:
                     52: static int
                     53: lua_flush(lua_State *L)
                     54: {
                     55:        bozohttpd_t *httpd;
                     56:
                     57:        lua_pushstring(L, "bozohttpd");
                     58:        lua_gettable(L, LUA_REGISTRYINDEX);
                     59:        httpd = lua_touserdata(L, -1);
                     60:        lua_pop(L, 1);
                     61:
                     62:        bozo_flush(httpd, stdout);
                     63:        return 0;
                     64: }
                     65:
                     66: static int
                     67: lua_print(lua_State *L)
                     68: {
                     69:        bozohttpd_t *httpd;
                     70:
                     71:        lua_pushstring(L, "bozohttpd");
                     72:        lua_gettable(L, LUA_REGISTRYINDEX);
                     73:        httpd = lua_touserdata(L, -1);
                     74:        lua_pop(L, 1);
                     75:
                     76:        bozo_printf(httpd, "%s\r\n", lua_tostring(L, -1));
                     77:        return 0;
                     78: }
                     79:
                     80: static int
                     81: lua_read(lua_State *L)
                     82: {
                     83:        bozohttpd_t *httpd;
1.4       mbalmer    84:        int n, len;
1.1       mbalmer    85:        char *data;
                     86:
                     87:        lua_pushstring(L, "bozohttpd");
                     88:        lua_gettable(L, LUA_REGISTRYINDEX);
                     89:        httpd = lua_touserdata(L, -1);
                     90:        lua_pop(L, 1);
                     91:
                     92:        len = luaL_checkinteger(L, -1);
                     93:        data = bozomalloc(httpd, len + 1);
1.4       mbalmer    94:        n = bozo_read(httpd, STDIN_FILENO, data, len);
                     95:        if (n >= 0) {
                     96:                data[n] = '\0';
                     97:                lua_pushstring(L, data);
                     98:        } else
                     99:                lua_pushnil(L);
1.1       mbalmer   100:        free(data);
                    101:        return 1;
                    102: }
                    103:
                    104: static int
                    105: lua_register_handler(lua_State *L)
                    106: {
                    107:        lua_state_map_t *map;
                    108:        lua_handler_t *handler;
                    109:        bozohttpd_t *httpd;
                    110:
                    111:        lua_pushstring(L, "lua_state_map");
                    112:        lua_gettable(L, LUA_REGISTRYINDEX);
                    113:        map = lua_touserdata(L, -1);
                    114:        lua_pushstring(L, "bozohttpd");
                    115:        lua_gettable(L, LUA_REGISTRYINDEX);
                    116:        httpd = lua_touserdata(L, -1);
                    117:        lua_pop(L, 2);
                    118:
                    119:        luaL_checkstring(L, 1);
                    120:        luaL_checktype(L, 2, LUA_TFUNCTION);
                    121:
                    122:        handler = bozomalloc(httpd, sizeof(lua_handler_t));
                    123:
                    124:        handler->name = bozostrdup(httpd, lua_tostring(L, 1));
                    125:        handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
                    126:        SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
                    127:        httpd->process_lua = 1;
                    128:        return 0;
                    129: }
                    130:
                    131: static int
                    132: lua_write(lua_State *L)
                    133: {
                    134:        bozohttpd_t *httpd;
                    135:        const char *data;
                    136:
                    137:        lua_pushstring(L, "bozohttpd");
                    138:        lua_gettable(L, LUA_REGISTRYINDEX);
                    139:        httpd = lua_touserdata(L, -1);
                    140:        lua_pop(L, 1);
                    141:
                    142:        data = luaL_checkstring(L, -1);
                    143:        lua_pushinteger(L, bozo_write(httpd, STDIN_FILENO, data, strlen(data)));
                    144:        return 1;
                    145: }
                    146:
                    147: static int
                    148: luaopen_httpd(lua_State *L)
                    149: {
                    150:        struct luaL_Reg functions[] = {
                    151:                { "flush",              lua_flush },
                    152:                { "print",              lua_print },
                    153:                { "read",               lua_read },
                    154:                { "register_handler",   lua_register_handler },
                    155:                { "write",              lua_write },
                    156:                { NULL, NULL }
                    157:        };
                    158: #if LUA_VERSION_NUM >= 502
                    159:        luaL_newlib(L, functions);
                    160: #else
                    161:        luaL_register(L, LUA_HTTPDLIBNAME, functions);
                    162: #endif
                    163:        lua_pushstring(L, "httpd 1.0.0");
                    164:        lua_setfield(L, -2, "_VERSION");
                    165:        return 1;
                    166: }
                    167:
                    168: #if LUA_VERSION_NUM < 502
                    169: static void
                    170: lua_openlib(lua_State *L, const char *name, lua_CFunction fn)
                    171: {
                    172:        lua_pushcfunction(L, fn);
                    173:        lua_pushstring(L, name);
                    174:        lua_call(L, 1, 0);
                    175: }
                    176: #endif
                    177:
                    178: /* bozohttpd integration */
                    179: void
                    180: bozo_add_lua_map(bozohttpd_t *httpd, const char *prefix, const char *script)
                    181: {
                    182:        lua_state_map_t *map;
                    183:        char *cwd, *path;
                    184:
                    185:        map = bozomalloc(httpd, sizeof(lua_state_map_t));
                    186:        map->prefix = bozostrdup(httpd, prefix);
                    187:        if (*script == '/')
                    188:                map->script = bozostrdup(httpd, script);
                    189:        else {
                    190:                cwd = getwd(NULL);
                    191:                asprintf(&path, "%s/%s", cwd, script);
                    192:                map->script = path;
                    193:                free(cwd);
                    194:        }
                    195:        map->L = luaL_newstate();
                    196:        if (map->L == NULL)
                    197:                bozo_err(httpd, 1, "can't create Lua state");
                    198:        SIMPLEQ_INIT(&map->handlers);
                    199:
                    200: #if LUA_VERSION_NUM >= 502
                    201:        luaL_openlibs(map->L);
                    202:        lua_getglobal(L, "package");
                    203:        lua_getfield(L, -1, "preload");
                    204:        lua_pushcfunction(L, luaopen_httpd);
                    205:        lua_setfield(L, -2, "httpd");
                    206:        lua_pop(L, 2);
                    207: #else
                    208:        lua_openlib(map->L, "", luaopen_base);
                    209:        lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package);
                    210:        lua_openlib(map->L, LUA_TABLIBNAME, luaopen_table);
                    211:        lua_openlib(map->L, LUA_STRLIBNAME, luaopen_string);
                    212:        lua_openlib(map->L, LUA_MATHLIBNAME, luaopen_math);
                    213:        lua_openlib(map->L, LUA_OSLIBNAME, luaopen_os);
                    214:        lua_openlib(map->L, LUA_IOLIBNAME, luaopen_io);
                    215:        lua_openlib(map->L, LUA_HTTPDLIBNAME, luaopen_httpd);
                    216: #endif
                    217:        lua_pushstring(map->L, "lua_state_map");
                    218:        lua_pushlightuserdata(map->L, map);
                    219:        lua_settable(map->L, LUA_REGISTRYINDEX);
                    220:
                    221:        lua_pushstring(map->L, "bozohttpd");
                    222:        lua_pushlightuserdata(map->L, httpd);
                    223:        lua_settable(map->L, LUA_REGISTRYINDEX);
                    224:
                    225:        if (luaL_loadfile(map->L, script))
                    226:                bozo_err(httpd, 1, "failed to load script %s: %s", script,
                    227:                    lua_tostring(map->L, -1));
                    228:        if (lua_pcall(map->L, 0, 0, 0))
                    229:                bozo_err(httpd, 1, "failed to execute script %s: %s", script,
                    230:                    lua_tostring(map->L, -1));
                    231:        SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
                    232: }
                    233:
                    234: static void
                    235: lua_env(lua_State *L, const char *name, const char *value)
                    236: {
                    237:        lua_pushstring(L, value);
                    238:        lua_setfield(L, -2, name);
                    239: }
                    240:
                    241: /* decode query string */
                    242: static void
                    243: lua_url_decode(lua_State *L, char *s)
                    244: {
                    245:        char *v, *p, *val, *q;
                    246:        char buf[3];
                    247:        int c;
                    248:
                    249:        v = strchr(s, '=');
                    250:        if (v == NULL)
                    251:                return;
                    252:        *v++ = '\0';
                    253:        val = malloc(strlen(v) + 1);
                    254:        if (val == NULL)
                    255:                return;
                    256:
                    257:        for (p = v, q = val; *p; p++) {
                    258:                switch (*p) {
                    259:                case '%':
1.3       mbalmer   260:                        if (*(p + 1) == '\0' || *(p + 2) == '\0') {
                    261:                                free(val);
1.1       mbalmer   262:                                return;
1.3       mbalmer   263:                        }
1.1       mbalmer   264:                        buf[0] = *++p;
                    265:                        buf[1] = *++p;
                    266:                        buf[2] = '\0';
                    267:                        sscanf(buf, "%2x", &c);
                    268:                        *q++ = (char)c;
                    269:                        break;
                    270:                case '+':
                    271:                        *q++ = ' ';
                    272:                        break;
                    273:                default:
                    274:                        *q++ = *p;
                    275:                }
                    276:        }
                    277:        lua_pushstring(L, val);
                    278:        lua_setfield(L, -2, s);
                    279:        free(val);
                    280: }
                    281:
                    282: static void
                    283: lua_decode_query(lua_State *L, char *query)
                    284: {
                    285:        char *s;
                    286:
                    287:        s = strtok(query, "&");
                    288:        while (s) {
                    289:                lua_url_decode(L, s);
                    290:                s = strtok(NULL, "&");
                    291:        }
                    292: }
                    293:
                    294: int
                    295: bozo_process_lua(bozo_httpreq_t *request)
                    296: {
                    297:        bozohttpd_t *httpd = request->hr_httpd;
                    298:        lua_state_map_t *map;
                    299:        lua_handler_t *hndlr;
1.4       mbalmer   300:        int n, ret, length;
1.1       mbalmer   301:        char date[40];
                    302:        bozoheaders_t *headp;
                    303:        char *s, *query, *uri, *file, *command, *info, *content;
                    304:        const char *type, *clen;
                    305:        char *prefix, *handler, *p;
                    306:
                    307:        if (!httpd->process_lua)
                    308:                return 0;
                    309:
                    310:        uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
                    311:
                    312:        if (*uri == '/') {
                    313:                file = bozostrdup(httpd, uri);
                    314:                prefix = bozostrdup(httpd, &uri[1]);
                    315:        } else {
                    316:                prefix = bozostrdup(httpd, uri);
                    317:                asprintf(&file, "/%s", uri);
                    318:        }
                    319:        if (file == NULL) {
                    320:                free(prefix);
                    321:                return 0;
                    322:        }
                    323:
                    324:        if (request->hr_query && strlen(request->hr_query))
                    325:                query = bozostrdup(httpd, request->hr_query);
                    326:        else
                    327:                query = NULL;
                    328:
                    329:        p = strchr(prefix, '/');
                    330:        if (p == NULL){
                    331:                free(prefix);
                    332:                return 0;
                    333:        }
                    334:        *p++ = '\0';
                    335:        handler = p;
                    336:        if (!*handler) {
                    337:                free(prefix);
                    338:                return 0;
                    339:        }
                    340:        p = strchr(handler, '/');
                    341:        if (p != NULL)
                    342:                *p++ = '\0';
                    343:
                    344:        info = NULL;
                    345:        command = file + 1;
                    346:        if ((s = strchr(command, '/')) != NULL) {
                    347:                info = bozostrdup(httpd, s);
                    348:                *s = '\0';
                    349:        }
                    350:
                    351:        type = request->hr_content_type;
                    352:        clen = request->hr_content_length;
                    353:
                    354:        SIMPLEQ_FOREACH(map, &httpd->lua_states, s_next) {
                    355:                if (strcmp(map->prefix, prefix))
                    356:                        continue;
                    357:
                    358:                SIMPLEQ_FOREACH(hndlr, &map->handlers, h_next) {
                    359:                        if (strcmp(hndlr->name, handler))
                    360:                                continue;
                    361:
                    362:                        lua_rawgeti(map->L, LUA_REGISTRYINDEX, hndlr->ref);
                    363:
                    364:                        /* Create the "environment" */
                    365:                        lua_newtable(map->L);
                    366:                        lua_env(map->L, "SERVER_NAME",
                    367:                            BOZOHOST(httpd, request));
                    368:                        lua_env(map->L, "GATEWAY_INTERFACE", "Luigi/1.0");
                    369:                        lua_env(map->L, "SERVER_PROTOCOL", request->hr_proto);
                    370:                        lua_env(map->L, "REQUEST_METHOD",
                    371:                            request->hr_methodstr);
                    372:                        lua_env(map->L, "SCRIPT_PREFIX", map->prefix);
                    373:                        lua_env(map->L, "SCRIPT_NAME", file);
                    374:                        lua_env(map->L, "HANDLER_NAME", hndlr->name);
                    375:                        lua_env(map->L, "SCRIPT_FILENAME", map->script);
                    376:                        lua_env(map->L, "SERVER_SOFTWARE",
                    377:                            httpd->server_software);
                    378:                        lua_env(map->L, "REQUEST_URI", uri);
                    379:                        lua_env(map->L, "DATE_GMT",
                    380:                            bozo_http_date(date, sizeof(date)));
                    381:                        if (query && *query)
                    382:                                lua_env(map->L, "QUERY_STRING", query);
                    383:                        if (info && *info)
                    384:                                lua_env(map->L, "PATH_INFO", info);
                    385:                        if (type && *type)
                    386:                                lua_env(map->L, "CONTENT_TYPE", type);
                    387:                        if (clen && *clen)
                    388:                                lua_env(map->L, "CONTENT_LENGTH", clen);
                    389:                        if (request->hr_serverport && *request->hr_serverport)
                    390:                                lua_env(map->L, "SERVER_PORT",
                    391:                                    request->hr_serverport);
                    392:                        if (request->hr_remotehost && *request->hr_remotehost)
                    393:                                lua_env(map->L, "REMOTE_HOST",
                    394:                                    request->hr_remotehost);
                    395:                        if (request->hr_remoteaddr && *request->hr_remoteaddr)
                    396:                                lua_env(map->L, "REMOTE_ADDR",
                    397:                                    request->hr_remoteaddr);
                    398:
                    399:                        /* Pass the headers in a separate table */
                    400:                        lua_newtable(map->L);
                    401:                        SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next)
                    402:                                lua_env(map->L, headp->h_header,
                    403:                                    headp->h_value);
                    404:
                    405:                        /* Pass the query variables */
1.5       mbalmer   406:                        if ((query && *query) ||
                    407:                            (type && *type && !strcmp(type, FORM))) {
1.1       mbalmer   408:                                lua_newtable(map->L);
                    409:                                if (query && *query)
                    410:                                        lua_decode_query(map->L, query);
                    411:                                if (type && *type && !strcmp(type, FORM)) {
                    412:                                        if (clen && *clen && atol(clen) > 0) {
                    413:                                                length = atol(clen);
                    414:                                                content = bozomalloc(httpd,
1.2       mbalmer   415:                                                    length + 1);
1.4       mbalmer   416:                                                n = bozo_read(httpd,
                    417:                                                    STDIN_FILENO, content,
                    418:                                                    length);
                    419:                                                if (n >= 0) {
                    420:                                                        content[n] = '\0';
                    421:                                                        lua_decode_query(map->L,
                    422:                                                            content);
1.5       mbalmer   423:                                                } else {
                    424:                                                        lua_pop(map->L, 1);
                    425:                                                        lua_pushnil(map->L);
1.4       mbalmer   426:                                                }
1.1       mbalmer   427:                                                free(content);
                    428:                                        }
                    429:                                }
                    430:                        } else
                    431:                                lua_pushnil(map->L);
                    432:
                    433:                        ret = lua_pcall(map->L, 3, 0, 0);
                    434:                        if (ret)
                    435:                                printf("<br>Lua error: %s\n",
                    436:                                    lua_tostring(map->L, -1));
                    437:                        bozo_flush(httpd, stdout);
                    438:                        free(prefix);
                    439:                        free(uri);
                    440:                        free(info);
1.6     ! christos  441:                        free(query);
1.1       mbalmer   442:                        return 1;
                    443:                }
                    444:        }
                    445:        free(prefix);
                    446:        free(uri);
                    447:        free(info);
1.6     ! christos  448:        free(query);
1.1       mbalmer   449:        return 0;
                    450: }
                    451:
                    452: #endif /* NO_LUA_SUPPORT */

CVSweb <webmaster@jp.NetBSD.org>