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>