Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 1.1.1.3
1.1.1.3 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: #include "mod_cml.h"
4: #include "mod_cml_funcs.h"
5: #include "log.h"
6: #include "stream.h"
7:
8: #include "stat_cache.h"
9:
10: #include <assert.h>
11: #include <stdio.h>
12: #include <errno.h>
13: #include <time.h>
14: #include <string.h>
15:
16: #define HASHLEN 16
17: typedef unsigned char HASH[HASHLEN];
18: #define HASHHEXLEN 32
19: typedef char HASHHEX[HASHHEXLEN+1];
20: #ifdef USE_OPENSSL
21: #define IN const
22: #else
23: #define IN
24: #endif
25: #define OUT
26:
27: #ifdef HAVE_LUA_H
28:
29: #include <lua.h>
30: #include <lualib.h>
31: #include <lauxlib.h>
32:
33: static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
1.1.1.3 ! misho 34: int curelem = lua_gettop(L);
! 35: int result;
1.1 misho 36:
1.1.1.3 ! misho 37: lua_getglobal(L, varname);
1.1 misho 38:
1.1.1.3 ! misho 39: if (lua_isstring(L, curelem)) {
! 40: buffer_copy_string(b, lua_tostring(L, curelem));
! 41: result = 0;
! 42: } else {
! 43: result = -1;
1.1 misho 44: }
45:
46: lua_pop(L, 1);
1.1.1.3 ! misho 47: force_assert(curelem == lua_gettop(L));
! 48: return result;
1.1 misho 49: }
50:
51: static int lua_to_c_is_table(lua_State *L, const char *varname) {
1.1.1.3 ! misho 52: int curelem = lua_gettop(L);
! 53: int result;
1.1 misho 54:
1.1.1.3 ! misho 55: lua_getglobal(L, varname);
1.1 misho 56:
1.1.1.3 ! misho 57: result = lua_istable(L, curelem) ? 1 : 0;
1.1 misho 58:
1.1.1.3 ! misho 59: lua_pop(L, 1);
! 60: force_assert(curelem == lua_gettop(L));
! 61: return result;
1.1 misho 62: }
63:
64: static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
65: lua_pushlstring(L, key, key_len);
66: lua_pushlstring(L, val, val_len);
67: lua_settable(L, tbl);
68:
69: return 0;
70: }
71:
72: static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
73: size_t is_key = 1;
1.1.1.3 ! misho 74: size_t i, len;
1.1 misho 75: char *key = NULL, *val = NULL;
76:
77: key = qrystr->ptr;
78:
79: /* we need the \0 */
1.1.1.3 ! misho 80: len = buffer_string_length(qrystr);
! 81: for (i = 0; i <= len; i++) {
1.1 misho 82: switch(qrystr->ptr[i]) {
83: case '=':
84: if (is_key) {
85: val = qrystr->ptr + i + 1;
86:
87: qrystr->ptr[i] = '\0';
88:
89: is_key = 0;
90: }
91:
92: break;
93: case '&':
94: case '\0': /* fin symbol */
95: if (!is_key) {
96: /* we need at least a = since the last & */
97:
98: /* terminate the value */
99: qrystr->ptr[i] = '\0';
100:
101: c_to_lua_push(L, tbl,
1.1.1.3 ! misho 102: key, strlen(key),
! 103: val, strlen(val));
1.1 misho 104: }
105:
106: key = qrystr->ptr + i + 1;
107: val = NULL;
108: is_key = 1;
109: break;
110: }
111: }
112:
113: return 0;
114: }
115:
116: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
117: lua_State *L;
118: int ret = -1;
1.1.1.2 misho 119: buffer *b;
1.1 misho 120:
1.1.1.2 misho 121: b = buffer_init();
1.1 misho 122: /* push the lua file to the interpreter and see what happends */
123: L = luaL_newstate();
124: luaL_openlibs(L);
125:
126: /* register functions */
127: lua_register(L, "md5", f_crypto_md5);
128: lua_register(L, "file_mtime", f_file_mtime);
129: lua_register(L, "file_isreg", f_file_isreg);
130: lua_register(L, "file_isdir", f_file_isreg);
131: lua_register(L, "dir_files", f_dir_files);
132:
1.1.1.3 ! misho 133: #ifdef USE_MEMCACHED
! 134: lua_pushlightuserdata(L, p->conf.memc);
1.1 misho 135: lua_pushcclosure(L, f_memcache_get_long, 1);
1.1.1.3 ! misho 136: lua_setglobal(L, "memcache_get_long");
1.1 misho 137:
1.1.1.3 ! misho 138: lua_pushlightuserdata(L, p->conf.memc);
1.1 misho 139: lua_pushcclosure(L, f_memcache_get_string, 1);
1.1.1.3 ! misho 140: lua_setglobal(L, "memcache_get_string");
1.1 misho 141:
1.1.1.3 ! misho 142: lua_pushlightuserdata(L, p->conf.memc);
1.1 misho 143: lua_pushcclosure(L, f_memcache_exists, 1);
1.1.1.3 ! misho 144: lua_setglobal(L, "memcache_exists");
1.1 misho 145: #endif
1.1.1.3 ! misho 146:
1.1 misho 147: /* register CGI environment */
148: lua_newtable(L);
1.1.1.3 ! misho 149: {
! 150: int header_tbl = lua_gettop(L);
1.1 misho 151:
1.1.1.3 ! misho 152: c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
! 153: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
! 154: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
! 155: c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
! 156: if (!buffer_string_is_empty(con->request.pathinfo)) {
! 157: c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
! 158: }
1.1 misho 159:
1.1.1.3 ! misho 160: c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
! 161: c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
! 162: }
! 163: lua_setglobal(L, "request");
1.1 misho 164:
165: /* register GET parameter */
166: lua_newtable(L);
1.1.1.3 ! misho 167: {
! 168: int get_tbl = lua_gettop(L);
1.1 misho 169:
1.1.1.3 ! misho 170: buffer_copy_buffer(b, con->uri.query);
! 171: cache_export_get_params(L, get_tbl, b);
! 172: buffer_reset(b);
! 173: }
! 174: lua_setglobal(L, "get");
1.1 misho 175:
176: /* 2 default constants */
1.1.1.3 ! misho 177: lua_pushinteger(L, 0);
! 178: lua_setglobal(L, "CACHE_HIT");
! 179:
! 180: lua_pushinteger(L, 1);
! 181: lua_setglobal(L, "CACHE_MISS");
1.1 misho 182:
183: /* load lua program */
1.1.1.3 ! misho 184: ret = luaL_loadfile(L, fn->ptr);
! 185: if (0 != ret) {
! 186: log_error_write(srv, __FILE__, __LINE__, "sbsS",
! 187: "failed loading cml_lua script",
! 188: fn,
! 189: ":",
! 190: lua_tostring(L, -1));
! 191: goto error;
! 192: }
1.1 misho 193:
1.1.1.3 ! misho 194: if (lua_pcall(L, 0, 1, 0)) {
! 195: log_error_write(srv, __FILE__, __LINE__, "sbsS",
! 196: "failed running cml_lua script",
! 197: fn,
! 198: ":",
! 199: lua_tostring(L, -1));
1.1 misho 200: goto error;
201: }
202:
203: /* get return value */
1.1.1.3 ! misho 204: ret = (int)lua_tointeger(L, -1);
1.1 misho 205: lua_pop(L, 1);
206:
207: /* fetch the data from lua */
208: lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
209:
210: if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
211: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
212: }
213:
214: if (ret == 0) {
215: /* up to now it is a cache-hit, check if all files exist */
216:
217: int curelem;
218: time_t mtime = 0;
219:
220: if (!lua_to_c_is_table(L, "output_include")) {
221: log_error_write(srv, __FILE__, __LINE__, "s",
222: "output_include is missing or not a table");
223: ret = -1;
224:
225: goto error;
226: }
227:
1.1.1.3 ! misho 228: lua_getglobal(L, "output_include");
1.1 misho 229: curelem = lua_gettop(L);
230:
231: /* HOW-TO build a etag ?
232: * as we don't just have one file we have to take the stat()
233: * from all base files, merge them and build the etag from
234: * it later.
235: *
236: * The mtime of the content is the mtime of the freshest base file
237: *
238: * */
239:
240: lua_pushnil(L); /* first key */
241: while (lua_next(L, curelem) != 0) {
242: /* key' is at index -2 and value' at index -1 */
243:
244: if (lua_isstring(L, -1)) {
245: const char *s = lua_tostring(L, -1);
1.1.1.3 ! misho 246: struct stat st;
! 247: int fd;
1.1 misho 248:
249: /* the file is relative, make it absolute */
250: if (s[0] != '/') {
1.1.1.3 ! misho 251: buffer_copy_buffer(b, p->basedir);
1.1 misho 252: buffer_append_string(b, lua_tostring(L, -1));
253: } else {
254: buffer_copy_string(b, lua_tostring(L, -1));
255: }
256:
1.1.1.3 ! misho 257: fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
! 258: if (fd < 0) {
1.1 misho 259: /* stat failed */
260:
261: switch(errno) {
262: case ENOENT:
263: /* a file is missing, call the handler to generate it */
1.1.1.3 ! misho 264: if (!buffer_string_is_empty(p->trigger_handler)) {
1.1 misho 265: ret = 1; /* cache-miss */
266:
267: log_error_write(srv, __FILE__, __LINE__, "s",
268: "a file is missing, calling handler");
269:
270: break;
271: } else {
272: /* handler not set -> 500 */
273: ret = -1;
274:
275: log_error_write(srv, __FILE__, __LINE__, "s",
276: "a file missing and no handler set");
277:
278: break;
279: }
280: break;
281: default:
282: break;
283: }
284: } else {
1.1.1.3 ! misho 285: chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
! 286: if (st.st_mtime > mtime) mtime = st.st_mtime;
1.1 misho 287: }
288: } else {
289: /* not a string */
290: ret = -1;
291: log_error_write(srv, __FILE__, __LINE__, "s",
292: "not a string");
293: break;
294: }
295:
296: lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
297: }
298:
299: lua_settop(L, curelem - 1);
300:
301: if (ret == 0) {
302: data_string *ds;
303: char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
304:
305: con->file_finished = 1;
306:
307: ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
1.1.1.2 misho 308: if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
1.1 misho 309:
310: /* no Last-Modified specified */
1.1.1.2 misho 311: if (NULL == ds) {
1.1 misho 312:
313: strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
314:
315: response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
1.1.1.3 ! misho 316: ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
! 317: force_assert(NULL != ds);
1.1 misho 318: }
319:
1.1.1.3 ! misho 320: if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) {
1.1 misho 321: /* ok, the client already has our content,
322: * no need to send it again */
323:
324: chunkqueue_reset(con->write_queue);
325: ret = 0; /* cache-hit */
326: }
327: } else {
328: chunkqueue_reset(con->write_queue);
329: }
330: }
331:
1.1.1.3 ! misho 332: if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
1.1 misho 333: /* cache-miss */
1.1.1.3 ! misho 334: buffer_copy_buffer(con->uri.path, p->baseurl);
1.1 misho 335: buffer_append_string_buffer(con->uri.path, p->trigger_handler);
336:
1.1.1.3 ! misho 337: buffer_copy_buffer(con->physical.path, p->basedir);
1.1 misho 338: buffer_append_string_buffer(con->physical.path, p->trigger_handler);
339:
340: chunkqueue_reset(con->write_queue);
341: }
342:
343: error:
344: lua_close(L);
345:
346: buffer_free(b);
347:
348: return ret /* cache-error */;
349: }
350: #else
351: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
352: UNUSED(srv);
353: UNUSED(con);
354: UNUSED(p);
355: UNUSED(fn);
356: /* error */
357: return -1;
358: }
359: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>