Annotation of src/libexec/httpd/lua-bozo.c, Revision 1.9.6.2
1.9.6.2 ! msaitoh 1: /* $NetBSD: lua-bozo.c,v 1.9 2014/01/02 08:21:38 mrg Exp $ */
! 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 <sys/param.h>
! 36:
! 37: #include <lua.h>
! 38: #include <lauxlib.h>
! 39: #include <lualib.h>
! 40: #include <stdlib.h>
! 41: #include <string.h>
! 42: #include <unistd.h>
! 43:
! 44: #include "bozohttpd.h"
! 45:
! 46: /* Lua binding for bozohttp */
! 47:
! 48: #if LUA_VERSION_NUM < 502
! 49: #define LUA_HTTPDLIBNAME "httpd"
! 50: #endif
! 51:
! 52: #define FORM "application/x-www-form-urlencoded"
! 53:
! 54: static int
! 55: lua_flush(lua_State *L)
! 56: {
! 57: bozohttpd_t *httpd;
! 58:
! 59: lua_pushstring(L, "bozohttpd");
! 60: lua_gettable(L, LUA_REGISTRYINDEX);
! 61: httpd = lua_touserdata(L, -1);
! 62: lua_pop(L, 1);
! 63:
! 64: bozo_flush(httpd, stdout);
! 65: return 0;
! 66: }
! 67:
! 68: static int
! 69: lua_print(lua_State *L)
! 70: {
! 71: bozohttpd_t *httpd;
! 72:
! 73: lua_pushstring(L, "bozohttpd");
! 74: lua_gettable(L, LUA_REGISTRYINDEX);
! 75: httpd = lua_touserdata(L, -1);
! 76: lua_pop(L, 1);
! 77:
! 78: bozo_printf(httpd, "%s\r\n", lua_tostring(L, -1));
! 79: return 0;
! 80: }
! 81:
! 82: static int
! 83: lua_read(lua_State *L)
! 84: {
! 85: bozohttpd_t *httpd;
! 86: int n, len;
! 87: char *data;
! 88:
! 89: lua_pushstring(L, "bozohttpd");
! 90: lua_gettable(L, LUA_REGISTRYINDEX);
! 91: httpd = lua_touserdata(L, -1);
! 92: lua_pop(L, 1);
! 93:
! 94: len = luaL_checkinteger(L, -1);
! 95: data = bozomalloc(httpd, len + 1);
! 96: n = bozo_read(httpd, STDIN_FILENO, data, len);
! 97: if (n >= 0) {
! 98: data[n] = '\0';
! 99: lua_pushstring(L, data);
! 100: } else
! 101: lua_pushnil(L);
! 102: free(data);
! 103: return 1;
! 104: }
! 105:
! 106: static int
! 107: lua_register_handler(lua_State *L)
! 108: {
! 109: lua_state_map_t *map;
! 110: lua_handler_t *handler;
! 111: bozohttpd_t *httpd;
! 112:
! 113: lua_pushstring(L, "lua_state_map");
! 114: lua_gettable(L, LUA_REGISTRYINDEX);
! 115: map = lua_touserdata(L, -1);
! 116: lua_pushstring(L, "bozohttpd");
! 117: lua_gettable(L, LUA_REGISTRYINDEX);
! 118: httpd = lua_touserdata(L, -1);
! 119: lua_pop(L, 2);
! 120:
! 121: luaL_checkstring(L, 1);
! 122: luaL_checktype(L, 2, LUA_TFUNCTION);
! 123:
! 124: handler = bozomalloc(httpd, sizeof(lua_handler_t));
! 125:
! 126: handler->name = bozostrdup(httpd, lua_tostring(L, 1));
! 127: handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
! 128: SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
! 129: httpd->process_lua = 1;
! 130: return 0;
! 131: }
! 132:
! 133: static int
! 134: lua_write(lua_State *L)
! 135: {
! 136: bozohttpd_t *httpd;
! 137: const char *data;
! 138:
! 139: lua_pushstring(L, "bozohttpd");
! 140: lua_gettable(L, LUA_REGISTRYINDEX);
! 141: httpd = lua_touserdata(L, -1);
! 142: lua_pop(L, 1);
! 143:
! 144: data = luaL_checkstring(L, -1);
! 145: lua_pushinteger(L, bozo_write(httpd, STDIN_FILENO, data, strlen(data)));
! 146: return 1;
! 147: }
! 148:
! 149: static int
! 150: luaopen_httpd(lua_State *L)
! 151: {
! 152: struct luaL_Reg functions[] = {
! 153: { "flush", lua_flush },
! 154: { "print", lua_print },
! 155: { "read", lua_read },
! 156: { "register_handler", lua_register_handler },
! 157: { "write", lua_write },
! 158: { NULL, NULL }
! 159: };
! 160: #if LUA_VERSION_NUM >= 502
! 161: luaL_newlib(L, functions);
! 162: #else
! 163: luaL_register(L, LUA_HTTPDLIBNAME, functions);
! 164: #endif
! 165: lua_pushstring(L, "httpd 1.0.0");
! 166: lua_setfield(L, -2, "_VERSION");
! 167: return 1;
! 168: }
! 169:
! 170: #if LUA_VERSION_NUM < 502
! 171: static void
! 172: lua_openlib(lua_State *L, const char *name, lua_CFunction fn)
! 173: {
! 174: lua_pushcfunction(L, fn);
! 175: lua_pushstring(L, name);
! 176: lua_call(L, 1, 0);
! 177: }
! 178: #endif
! 179:
! 180: /* bozohttpd integration */
! 181: void
! 182: bozo_add_lua_map(bozohttpd_t *httpd, const char *prefix, const char *script)
! 183: {
! 184: lua_state_map_t *map;
! 185:
! 186: map = bozomalloc(httpd, sizeof(lua_state_map_t));
! 187: map->prefix = bozostrdup(httpd, prefix);
! 188: if (*script == '/')
! 189: map->script = bozostrdup(httpd, script);
! 190: else {
! 191: char cwd[MAXPATHLEN], *path;
! 192:
! 193: getcwd(cwd, sizeof(cwd) - 1);
! 194: asprintf(&path, "%s/%s", cwd, script);
! 195: map->script = path;
! 196: }
! 197: map->L = luaL_newstate();
! 198: if (map->L == NULL)
! 199: bozo_err(httpd, 1, "can't create Lua state");
! 200: SIMPLEQ_INIT(&map->handlers);
! 201:
! 202: #if LUA_VERSION_NUM >= 502
! 203: luaL_openlibs(map->L);
! 204: lua_getglobal(L, "package");
! 205: lua_getfield(L, -1, "preload");
! 206: lua_pushcfunction(L, luaopen_httpd);
! 207: lua_setfield(L, -2, "httpd");
! 208: lua_pop(L, 2);
! 209: #else
! 210: lua_openlib(map->L, "", luaopen_base);
! 211: lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package);
! 212: lua_openlib(map->L, LUA_TABLIBNAME, luaopen_table);
! 213: lua_openlib(map->L, LUA_STRLIBNAME, luaopen_string);
! 214: lua_openlib(map->L, LUA_MATHLIBNAME, luaopen_math);
! 215: lua_openlib(map->L, LUA_OSLIBNAME, luaopen_os);
! 216: lua_openlib(map->L, LUA_IOLIBNAME, luaopen_io);
! 217: lua_openlib(map->L, LUA_HTTPDLIBNAME, luaopen_httpd);
! 218: #endif
! 219: lua_pushstring(map->L, "lua_state_map");
! 220: lua_pushlightuserdata(map->L, map);
! 221: lua_settable(map->L, LUA_REGISTRYINDEX);
! 222:
! 223: lua_pushstring(map->L, "bozohttpd");
! 224: lua_pushlightuserdata(map->L, httpd);
! 225: lua_settable(map->L, LUA_REGISTRYINDEX);
! 226:
! 227: if (luaL_loadfile(map->L, script))
! 228: bozo_err(httpd, 1, "failed to load script %s: %s", script,
! 229: lua_tostring(map->L, -1));
! 230: if (lua_pcall(map->L, 0, 0, 0))
! 231: bozo_err(httpd, 1, "failed to execute script %s: %s", script,
! 232: lua_tostring(map->L, -1));
! 233: SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
! 234: }
! 235:
! 236: static void
! 237: lua_env(lua_State *L, const char *name, const char *value)
! 238: {
! 239: lua_pushstring(L, value);
! 240: lua_setfield(L, -2, name);
! 241: }
! 242:
! 243: /* decode query string */
! 244: static void
! 245: lua_url_decode(lua_State *L, char *s)
! 246: {
! 247: char *v, *p, *val, *q;
! 248: char buf[3];
! 249: int c;
! 250:
! 251: v = strchr(s, '=');
! 252: if (v == NULL)
! 253: return;
! 254: *v++ = '\0';
! 255: val = malloc(strlen(v) + 1);
! 256: if (val == NULL)
! 257: return;
! 258:
! 259: for (p = v, q = val; *p; p++) {
! 260: switch (*p) {
! 261: case '%':
! 262: if (*(p + 1) == '\0' || *(p + 2) == '\0') {
! 263: free(val);
! 264: return;
! 265: }
! 266: buf[0] = *++p;
! 267: buf[1] = *++p;
! 268: buf[2] = '\0';
! 269: sscanf(buf, "%2x", &c);
! 270: *q++ = (char)c;
! 271: break;
! 272: case '+':
! 273: *q++ = ' ';
! 274: break;
! 275: default:
! 276: *q++ = *p;
! 277: }
! 278: }
! 279: lua_pushstring(L, val);
! 280: lua_setfield(L, -2, s);
! 281: free(val);
! 282: }
! 283:
! 284: static void
! 285: lua_decode_query(lua_State *L, char *query)
! 286: {
! 287: char *s;
! 288:
! 289: s = strtok(query, "&");
! 290: while (s) {
! 291: lua_url_decode(L, s);
! 292: s = strtok(NULL, "&");
! 293: }
! 294: }
! 295:
! 296: int
! 297: bozo_process_lua(bozo_httpreq_t *request)
! 298: {
! 299: bozohttpd_t *httpd = request->hr_httpd;
! 300: lua_state_map_t *map;
! 301: lua_handler_t *hndlr;
! 302: int n, ret, length;
! 303: char date[40];
! 304: bozoheaders_t *headp;
! 305: char *s, *query, *uri, *file, *command, *info, *content;
! 306: const char *type, *clen;
! 307: char *prefix, *handler, *p;
! 308: int rv = 0;
! 309:
! 310: if (!httpd->process_lua)
! 311: return 0;
! 312:
! 313: uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
! 314:
! 315: if (*uri == '/') {
! 316: file = bozostrdup(httpd, uri);
! 317: prefix = bozostrdup(httpd, &uri[1]);
! 318: } else {
! 319: prefix = bozostrdup(httpd, uri);
! 320: asprintf(&file, "/%s", uri);
! 321: }
! 322: if (file == NULL) {
! 323: free(prefix);
! 324: return 0;
! 325: }
! 326:
! 327: if (request->hr_query && strlen(request->hr_query))
! 328: query = bozostrdup(httpd, request->hr_query);
! 329: else
! 330: query = NULL;
! 331:
! 332: p = strchr(prefix, '/');
! 333: if (p == NULL){
! 334: free(prefix);
! 335: return 0;
! 336: }
! 337: *p++ = '\0';
! 338: handler = p;
! 339: if (!*handler) {
! 340: free(prefix);
! 341: return 0;
! 342: }
! 343: p = strchr(handler, '/');
! 344: if (p != NULL)
! 345: *p++ = '\0';
! 346:
! 347: info = NULL;
! 348: command = file + 1;
! 349: if ((s = strchr(command, '/')) != NULL) {
! 350: info = bozostrdup(httpd, s);
! 351: *s = '\0';
! 352: }
! 353:
! 354: type = request->hr_content_type;
! 355: clen = request->hr_content_length;
! 356:
! 357: SIMPLEQ_FOREACH(map, &httpd->lua_states, s_next) {
! 358: if (strcmp(map->prefix, prefix))
! 359: continue;
! 360:
! 361: SIMPLEQ_FOREACH(hndlr, &map->handlers, h_next) {
! 362: if (strcmp(hndlr->name, handler))
! 363: continue;
! 364:
! 365: lua_rawgeti(map->L, LUA_REGISTRYINDEX, hndlr->ref);
! 366:
! 367: /* Create the "environment" */
! 368: lua_newtable(map->L);
! 369: lua_env(map->L, "SERVER_NAME",
! 370: BOZOHOST(httpd, request));
! 371: lua_env(map->L, "GATEWAY_INTERFACE", "Luigi/1.0");
! 372: lua_env(map->L, "SERVER_PROTOCOL", request->hr_proto);
! 373: lua_env(map->L, "REQUEST_METHOD",
! 374: request->hr_methodstr);
! 375: lua_env(map->L, "SCRIPT_PREFIX", map->prefix);
! 376: lua_env(map->L, "SCRIPT_NAME", file);
! 377: lua_env(map->L, "HANDLER_NAME", hndlr->name);
! 378: lua_env(map->L, "SCRIPT_FILENAME", map->script);
! 379: lua_env(map->L, "SERVER_SOFTWARE",
! 380: httpd->server_software);
! 381: lua_env(map->L, "REQUEST_URI", uri);
! 382: lua_env(map->L, "DATE_GMT",
! 383: bozo_http_date(date, sizeof(date)));
! 384: if (query && *query)
! 385: lua_env(map->L, "QUERY_STRING", query);
! 386: if (info && *info)
! 387: lua_env(map->L, "PATH_INFO", info);
! 388: if (type && *type)
! 389: lua_env(map->L, "CONTENT_TYPE", type);
! 390: if (clen && *clen)
! 391: lua_env(map->L, "CONTENT_LENGTH", clen);
! 392: if (request->hr_serverport && *request->hr_serverport)
! 393: lua_env(map->L, "SERVER_PORT",
! 394: request->hr_serverport);
! 395: if (request->hr_remotehost && *request->hr_remotehost)
! 396: lua_env(map->L, "REMOTE_HOST",
! 397: request->hr_remotehost);
! 398: if (request->hr_remoteaddr && *request->hr_remoteaddr)
! 399: lua_env(map->L, "REMOTE_ADDR",
! 400: request->hr_remoteaddr);
! 401:
! 402: /* Pass the headers in a separate table */
! 403: lua_newtable(map->L);
! 404: SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next)
! 405: lua_env(map->L, headp->h_header,
! 406: headp->h_value);
! 407:
! 408: /* Pass the query variables */
! 409: if ((query && *query) ||
! 410: (type && *type && !strcmp(type, FORM))) {
! 411: lua_newtable(map->L);
! 412: if (query && *query)
! 413: lua_decode_query(map->L, query);
! 414: if (type && *type && !strcmp(type, FORM)) {
! 415: if (clen && *clen && atol(clen) > 0) {
! 416: length = atol(clen);
! 417: content = bozomalloc(httpd,
! 418: length + 1);
! 419: n = bozo_read(httpd,
! 420: STDIN_FILENO, content,
! 421: length);
! 422: if (n >= 0) {
! 423: content[n] = '\0';
! 424: lua_decode_query(map->L,
! 425: content);
! 426: } else {
! 427: lua_pop(map->L, 1);
! 428: lua_pushnil(map->L);
! 429: }
! 430: free(content);
! 431: }
! 432: }
! 433: } else
! 434: lua_pushnil(map->L);
! 435:
! 436: ret = lua_pcall(map->L, 3, 0, 0);
! 437: if (ret)
! 438: printf("<br>Lua error: %s\n",
! 439: lua_tostring(map->L, -1));
! 440: bozo_flush(httpd, stdout);
! 441: rv = 1;
! 442: goto out;
! 443: }
! 444: }
! 445: out:
! 446: free(prefix);
! 447: free(uri);
! 448: free(info);
! 449: free(query);
! 450: free(file);
! 451: return rv;
! 452: }
! 453:
! 454: #endif /* NO_LUA_SUPPORT */
CVSweb <webmaster@jp.NetBSD.org>