1 /* $NetBSD: lua-bozo.c,v 1.12 2015/07/04 22:39:23 christos Exp $ */
4 * Copyright (c) 2013 Marc Balmer <marc@msys.ch>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 /* this code implements dynamic content generation using Lua for bozohttpd */
33 #ifndef NO_LUA_SUPPORT
35 #include <sys/param.h>
44 #include "bozohttpd.h"
46 /* Lua binding for bozohttp */
48 #if LUA_VERSION_NUM < 502
49 #define LUA_HTTPDLIBNAME "httpd"
52 #define FORM "application/x-www-form-urlencoded"
55 lua_flush(lua_State
*L
)
59 lua_pushstring(L
, "bozohttpd");
60 lua_gettable(L
, LUA_REGISTRYINDEX
);
61 httpd
= lua_touserdata(L
, -1);
64 bozo_flush(httpd
, stdout
);
69 lua_print(lua_State
*L
)
73 lua_pushstring(L
, "bozohttpd");
74 lua_gettable(L
, LUA_REGISTRYINDEX
);
75 httpd
= lua_touserdata(L
, -1);
78 bozo_printf(httpd
, "%s\r\n", lua_tostring(L
, -1));
83 lua_read(lua_State
*L
)
89 lua_pushstring(L
, "bozohttpd");
90 lua_gettable(L
, LUA_REGISTRYINDEX
);
91 httpd
= lua_touserdata(L
, -1);
94 len
= luaL_checkinteger(L
, -1);
95 data
= bozomalloc(httpd
, len
+ 1);
96 n
= bozo_read(httpd
, STDIN_FILENO
, data
, len
);
99 lua_pushstring(L
, data
);
107 lua_register_handler(lua_State
*L
)
109 lua_state_map_t
*map
;
110 lua_handler_t
*handler
;
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);
121 luaL_checkstring(L
, 1);
122 luaL_checktype(L
, 2, LUA_TFUNCTION
);
124 handler
= bozomalloc(httpd
, sizeof(lua_handler_t
));
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;
134 lua_write(lua_State
*L
)
139 lua_pushstring(L
, "bozohttpd");
140 lua_gettable(L
, LUA_REGISTRYINDEX
);
141 httpd
= lua_touserdata(L
, -1);
144 data
= luaL_checkstring(L
, -1);
145 lua_pushinteger(L
, bozo_write(httpd
, STDIN_FILENO
, data
, strlen(data
)));
150 luaopen_httpd(lua_State
*L
)
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
},
160 #if LUA_VERSION_NUM >= 502
161 luaL_newlib(L
, functions
);
163 luaL_register(L
, LUA_HTTPDLIBNAME
, functions
);
165 lua_pushstring(L
, "httpd 1.0.0");
166 lua_setfield(L
, -2, "_VERSION");
170 #if LUA_VERSION_NUM < 502
172 lua_openlib(lua_State
*L
, const char *name
, lua_CFunction fn
)
174 lua_pushcfunction(L
, fn
);
175 lua_pushstring(L
, name
);
180 /* bozohttpd integration */
182 bozo_add_lua_map(bozohttpd_t
*httpd
, const char *prefix
, const char *script
)
184 lua_state_map_t
*map
;
186 map
= bozomalloc(httpd
, sizeof(lua_state_map_t
));
187 map
->prefix
= bozostrdup(httpd
, prefix
);
189 map
->script
= bozostrdup(httpd
, script
);
191 char cwd
[MAXPATHLEN
], *path
;
193 getcwd(cwd
, sizeof(cwd
) - 1);
194 asprintf(&path
, "%s/%s", cwd
, script
);
197 map
->L
= luaL_newstate();
199 bozo_err(httpd
, 1, "can't create Lua state");
200 SIMPLEQ_INIT(&map
->handlers
);
202 #if LUA_VERSION_NUM >= 502
203 luaL_openlibs(map
->L
);
204 lua_getglobal(map
->L
, "package");
205 lua_getfield(map
->L
, -1, "preload");
206 lua_pushcfunction(map
->L
, luaopen_httpd
);
207 lua_setfield(map
->L
, -2, "httpd");
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
);
219 lua_pushstring(map
->L
, "lua_state_map");
220 lua_pushlightuserdata(map
->L
, map
);
221 lua_settable(map
->L
, LUA_REGISTRYINDEX
);
223 lua_pushstring(map
->L
, "bozohttpd");
224 lua_pushlightuserdata(map
->L
, httpd
);
225 lua_settable(map
->L
, LUA_REGISTRYINDEX
);
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
);
237 lua_env(lua_State
*L
, const char *name
, const char *value
)
239 lua_pushstring(L
, value
);
240 lua_setfield(L
, -2, name
);
243 /* decode query string */
245 lua_url_decode(lua_State
*L
, char *s
)
247 char *v
, *p
, *val
, *q
;
255 val
= malloc(strlen(v
) + 1);
259 for (p
= v
, q
= val
; *p
; p
++) {
262 if (*(p
+ 1) == '\0' || *(p
+ 2) == '\0') {
269 sscanf(buf
, "%2x", &c
);
280 lua_pushstring(L
, val
);
281 lua_setfield(L
, -2, s
);
286 lua_decode_query(lua_State
*L
, char *query
)
290 s
= strtok(query
, "&");
292 lua_url_decode(L
, s
);
293 s
= strtok(NULL
, "&");
298 bozo_process_lua(bozo_httpreq_t
*request
)
300 bozohttpd_t
*httpd
= request
->hr_httpd
;
301 lua_state_map_t
*map
;
302 lua_handler_t
*hndlr
;
305 bozoheaders_t
*headp
;
306 char *s
, *query
, *uri
, *file
, *command
, *info
, *content
;
307 const char *type
, *clen
;
308 char *prefix
, *handler
, *p
;
311 if (!httpd
->process_lua
)
317 uri
= request
->hr_oldfile
? request
->hr_oldfile
: request
->hr_file
;
320 file
= bozostrdup(httpd
, uri
);
323 prefix
= bozostrdup(httpd
, &uri
[1]);
325 if (asprintf(&file
, "/%s", uri
) < 0)
327 prefix
= bozostrdup(httpd
, uri
);
332 if (request
->hr_query
&& request
->hr_query
[0])
333 query
= bozostrdup(httpd
, request
->hr_query
);
335 p
= strchr(prefix
, '/');
342 p
= strchr(handler
, '/');
347 if ((s
= strchr(command
, '/')) != NULL
) {
348 info
= bozostrdup(httpd
, s
);
352 type
= request
->hr_content_type
;
353 clen
= request
->hr_content_length
;
355 SIMPLEQ_FOREACH(map
, &httpd
->lua_states
, s_next
) {
356 if (strcmp(map
->prefix
, prefix
))
359 SIMPLEQ_FOREACH(hndlr
, &map
->handlers
, h_next
) {
360 if (strcmp(hndlr
->name
, handler
))
363 lua_rawgeti(map
->L
, LUA_REGISTRYINDEX
, hndlr
->ref
);
365 /* Create the "environment" */
366 lua_newtable(map
->L
);
367 lua_env(map
->L
, "SERVER_NAME",
368 BOZOHOST(httpd
, request
));
369 lua_env(map
->L
, "GATEWAY_INTERFACE", "Luigi/1.0");
370 lua_env(map
->L
, "SERVER_PROTOCOL", request
->hr_proto
);
371 lua_env(map
->L
, "REQUEST_METHOD",
372 request
->hr_methodstr
);
373 lua_env(map
->L
, "SCRIPT_PREFIX", map
->prefix
);
374 lua_env(map
->L
, "SCRIPT_NAME", file
);
375 lua_env(map
->L
, "HANDLER_NAME", hndlr
->name
);
376 lua_env(map
->L
, "SCRIPT_FILENAME", map
->script
);
377 lua_env(map
->L
, "SERVER_SOFTWARE",
378 httpd
->server_software
);
379 lua_env(map
->L
, "REQUEST_URI", uri
);
380 lua_env(map
->L
, "DATE_GMT",
381 bozo_http_date(date
, sizeof(date
)));
383 lua_env(map
->L
, "QUERY_STRING", query
);
385 lua_env(map
->L
, "PATH_INFO", info
);
387 lua_env(map
->L
, "CONTENT_TYPE", type
);
389 lua_env(map
->L
, "CONTENT_LENGTH", clen
);
390 if (request
->hr_serverport
&& *request
->hr_serverport
)
391 lua_env(map
->L
, "SERVER_PORT",
392 request
->hr_serverport
);
393 if (request
->hr_remotehost
&& *request
->hr_remotehost
)
394 lua_env(map
->L
, "REMOTE_HOST",
395 request
->hr_remotehost
);
396 if (request
->hr_remoteaddr
&& *request
->hr_remoteaddr
)
397 lua_env(map
->L
, "REMOTE_ADDR",
398 request
->hr_remoteaddr
);
400 /* Pass the headers in a separate table */
401 lua_newtable(map
->L
);
402 SIMPLEQ_FOREACH(headp
, &request
->hr_headers
, h_next
)
403 lua_env(map
->L
, headp
->h_header
,
406 /* Pass the query variables */
407 if ((query
&& *query
) ||
408 (type
&& *type
&& !strcmp(type
, FORM
))) {
409 lua_newtable(map
->L
);
411 lua_decode_query(map
->L
, query
);
412 if (type
&& *type
&& !strcmp(type
, FORM
)) {
413 if (clen
&& *clen
&& atol(clen
) > 0) {
415 content
= bozomalloc(httpd
,
418 STDIN_FILENO
, content
,
422 lua_decode_query(map
->L
,
434 ret
= lua_pcall(map
->L
, 3, 0, 0);
436 printf("<br>Lua error: %s\n",
437 lua_tostring(map
->L
, -1));
438 bozo_flush(httpd
, stdout
);
452 #endif /* NO_LUA_SUPPORT */