Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 1.1.1.2
1.1 misho 1: #include "mod_cml.h"
2: #include "mod_cml_funcs.h"
3: #include "log.h"
4: #include "stream.h"
5:
6: #include "stat_cache.h"
7:
8: #include <assert.h>
9: #include <stdio.h>
10: #include <errno.h>
11: #include <time.h>
12: #include <string.h>
13:
14: #define HASHLEN 16
15: typedef unsigned char HASH[HASHLEN];
16: #define HASHHEXLEN 32
17: typedef char HASHHEX[HASHHEXLEN+1];
18: #ifdef USE_OPENSSL
19: #define IN const
20: #else
21: #define IN
22: #endif
23: #define OUT
24:
25: #ifdef HAVE_LUA_H
26:
27: #include <lua.h>
28: #include <lualib.h>
29: #include <lauxlib.h>
30:
31: typedef struct {
32: stream st;
33: int done;
34: } readme;
35:
36: static const char * load_file(lua_State *L, void *data, size_t *size) {
37: readme *rm = data;
38:
39: UNUSED(L);
40:
41: if (rm->done) return 0;
42:
43: *size = rm->st.size;
44: rm->done = 1;
45: return rm->st.start;
46: }
47:
48: static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
49: int curelem;
50:
51: lua_pushstring(L, varname);
52:
53: curelem = lua_gettop(L);
54: lua_gettable(L, LUA_GLOBALSINDEX);
55:
56: /* it should be a table */
57: if (!lua_isstring(L, curelem)) {
58: lua_settop(L, curelem - 1);
59:
60: return -1;
61: }
62:
63: buffer_copy_string(b, lua_tostring(L, curelem));
64:
65: lua_pop(L, 1);
66:
1.1.1.2 ! misho 67: force_assert(curelem - 1 == lua_gettop(L));
1.1 misho 68:
69: return 0;
70: }
71:
72: static int lua_to_c_is_table(lua_State *L, const char *varname) {
73: int curelem;
74:
75: lua_pushstring(L, varname);
76:
77: curelem = lua_gettop(L);
78: lua_gettable(L, LUA_GLOBALSINDEX);
79:
80: /* it should be a table */
81: if (!lua_istable(L, curelem)) {
82: lua_settop(L, curelem - 1);
83:
84: return 0;
85: }
86:
87: lua_settop(L, curelem - 1);
88:
1.1.1.2 ! misho 89: force_assert(curelem - 1 == lua_gettop(L));
1.1 misho 90:
91: return 1;
92: }
93:
94: 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) {
95: lua_pushlstring(L, key, key_len);
96: lua_pushlstring(L, val, val_len);
97: lua_settable(L, tbl);
98:
99: return 0;
100: }
101:
102:
103: static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
104: size_t is_key = 1;
105: size_t i;
106: char *key = NULL, *val = NULL;
107:
108: key = qrystr->ptr;
109:
110: /* we need the \0 */
111: for (i = 0; i < qrystr->used; i++) {
112: switch(qrystr->ptr[i]) {
113: case '=':
114: if (is_key) {
115: val = qrystr->ptr + i + 1;
116:
117: qrystr->ptr[i] = '\0';
118:
119: is_key = 0;
120: }
121:
122: break;
123: case '&':
124: case '\0': /* fin symbol */
125: if (!is_key) {
126: /* we need at least a = since the last & */
127:
128: /* terminate the value */
129: qrystr->ptr[i] = '\0';
130:
131: c_to_lua_push(L, tbl,
132: key, strlen(key),
133: val, strlen(val));
134: }
135:
136: key = qrystr->ptr + i + 1;
137: val = NULL;
138: is_key = 1;
139: break;
140: }
141: }
142:
143: return 0;
144: }
145: #if 0
146: int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) {
147: data_unset *d;
148:
149: UNUSED(srv);
150:
151: if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
152: data_string *ds = (data_string *)d;
153: size_t key = 0, value = 0;
154: size_t is_key = 1, is_sid = 0;
155: size_t i;
156:
157: /* found COOKIE */
158: if (!DATA_IS_STRING(d)) return -1;
159: if (ds->value->used == 0) return -1;
160:
161: if (ds->value->ptr[0] == '\0' ||
162: ds->value->ptr[0] == '=' ||
163: ds->value->ptr[0] == ';') return -1;
164:
165: buffer_reset(p->session_id);
166: for (i = 0; i < ds->value->used; i++) {
167: switch(ds->value->ptr[i]) {
168: case '=':
169: if (is_key) {
170: if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
171: /* found PHP-session-id-key */
172: is_sid = 1;
173: }
174: value = i + 1;
175:
176: is_key = 0;
177: }
178:
179: break;
180: case ';':
181: if (is_sid) {
182: buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
183: }
184:
185: is_sid = 0;
186: key = i + 1;
187: value = 0;
188: is_key = 1;
189: break;
190: case ' ':
191: if (is_key == 1 && key == i) key = i + 1;
192: if (is_key == 0 && value == i) value = i + 1;
193: break;
194: case '\0':
195: if (is_sid) {
196: buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
197: }
198: /* fin */
199: break;
200: }
201: }
202: }
203:
204: return 0;
205: }
206: #endif
207:
208: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
209: lua_State *L;
210: readme rm;
211: int ret = -1;
1.1.1.2 ! misho 212: buffer *b;
1.1 misho 213: int header_tbl = 0;
214:
215: rm.done = 0;
1.1.1.2 ! misho 216: if (-1 == stream_open(&rm.st, fn)) {
! 217: log_error_write(srv, __FILE__, __LINE__, "sbss",
! 218: "opening lua cml file ", fn, "failed:", strerror(errno));
! 219: return -1;
! 220: }
1.1 misho 221:
1.1.1.2 ! misho 222: b = buffer_init();
1.1 misho 223: /* push the lua file to the interpreter and see what happends */
224: L = luaL_newstate();
225: luaL_openlibs(L);
226:
227: /* register functions */
228: lua_register(L, "md5", f_crypto_md5);
229: lua_register(L, "file_mtime", f_file_mtime);
230: lua_register(L, "file_isreg", f_file_isreg);
231: lua_register(L, "file_isdir", f_file_isreg);
232: lua_register(L, "dir_files", f_dir_files);
233:
234: #ifdef HAVE_MEMCACHE_H
235: lua_pushliteral(L, "memcache_get_long");
236: lua_pushlightuserdata(L, p->conf.mc);
237: lua_pushcclosure(L, f_memcache_get_long, 1);
238: lua_settable(L, LUA_GLOBALSINDEX);
239:
240: lua_pushliteral(L, "memcache_get_string");
241: lua_pushlightuserdata(L, p->conf.mc);
242: lua_pushcclosure(L, f_memcache_get_string, 1);
243: lua_settable(L, LUA_GLOBALSINDEX);
244:
245: lua_pushliteral(L, "memcache_exists");
246: lua_pushlightuserdata(L, p->conf.mc);
247: lua_pushcclosure(L, f_memcache_exists, 1);
248: lua_settable(L, LUA_GLOBALSINDEX);
249: #endif
250: /* register CGI environment */
251: lua_pushliteral(L, "request");
252: lua_newtable(L);
253: lua_settable(L, LUA_GLOBALSINDEX);
254:
255: lua_pushliteral(L, "request");
256: header_tbl = lua_gettop(L);
257: lua_gettable(L, LUA_GLOBALSINDEX);
258:
259: c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
260: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
261: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
262: c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
263: if (!buffer_is_empty(con->request.pathinfo)) {
264: c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
265: }
266:
267: c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
268: c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
269:
270: /* register GET parameter */
271: lua_pushliteral(L, "get");
272: lua_newtable(L);
273: lua_settable(L, LUA_GLOBALSINDEX);
274:
275: lua_pushliteral(L, "get");
276: header_tbl = lua_gettop(L);
277: lua_gettable(L, LUA_GLOBALSINDEX);
278:
279: buffer_copy_string_buffer(b, con->uri.query);
280: cache_export_get_params(L, header_tbl, b);
281: buffer_reset(b);
282:
283: /* 2 default constants */
284: lua_pushliteral(L, "CACHE_HIT");
285: lua_pushnumber(L, 0);
286: lua_settable(L, LUA_GLOBALSINDEX);
287:
288: lua_pushliteral(L, "CACHE_MISS");
289: lua_pushnumber(L, 1);
290: lua_settable(L, LUA_GLOBALSINDEX);
291:
292: /* load lua program */
293: if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
294: log_error_write(srv, __FILE__, __LINE__, "s",
295: lua_tostring(L,-1));
296:
297: goto error;
298: }
299:
300: /* get return value */
301: ret = (int)lua_tonumber(L, -1);
302: lua_pop(L, 1);
303:
304: /* fetch the data from lua */
305: lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
306:
307: if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
308: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
309: }
310:
311: if (ret == 0) {
312: /* up to now it is a cache-hit, check if all files exist */
313:
314: int curelem;
315: time_t mtime = 0;
316:
317: if (!lua_to_c_is_table(L, "output_include")) {
318: log_error_write(srv, __FILE__, __LINE__, "s",
319: "output_include is missing or not a table");
320: ret = -1;
321:
322: goto error;
323: }
324:
325: lua_pushstring(L, "output_include");
326:
327: curelem = lua_gettop(L);
328: lua_gettable(L, LUA_GLOBALSINDEX);
329:
330: /* HOW-TO build a etag ?
331: * as we don't just have one file we have to take the stat()
332: * from all base files, merge them and build the etag from
333: * it later.
334: *
335: * The mtime of the content is the mtime of the freshest base file
336: *
337: * */
338:
339: lua_pushnil(L); /* first key */
340: while (lua_next(L, curelem) != 0) {
341: stat_cache_entry *sce = NULL;
342: /* key' is at index -2 and value' at index -1 */
343:
344: if (lua_isstring(L, -1)) {
345: const char *s = lua_tostring(L, -1);
346:
347: /* the file is relative, make it absolute */
348: if (s[0] != '/') {
349: buffer_copy_string_buffer(b, p->basedir);
350: buffer_append_string(b, lua_tostring(L, -1));
351: } else {
352: buffer_copy_string(b, lua_tostring(L, -1));
353: }
354:
355: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
356: /* stat failed */
357:
358: switch(errno) {
359: case ENOENT:
360: /* a file is missing, call the handler to generate it */
361: if (!buffer_is_empty(p->trigger_handler)) {
362: ret = 1; /* cache-miss */
363:
364: log_error_write(srv, __FILE__, __LINE__, "s",
365: "a file is missing, calling handler");
366:
367: break;
368: } else {
369: /* handler not set -> 500 */
370: ret = -1;
371:
372: log_error_write(srv, __FILE__, __LINE__, "s",
373: "a file missing and no handler set");
374:
375: break;
376: }
377: break;
378: default:
379: break;
380: }
381: } else {
382: chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
383: if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
384: }
385: } else {
386: /* not a string */
387: ret = -1;
388: log_error_write(srv, __FILE__, __LINE__, "s",
389: "not a string");
390: break;
391: }
392:
393: lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
394: }
395:
396: lua_settop(L, curelem - 1);
397:
398: if (ret == 0) {
399: data_string *ds;
400: char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
401: buffer tbuf;
402:
403: con->file_finished = 1;
404:
405: ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
1.1.1.2 ! misho 406: if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
1.1 misho 407:
408: /* no Last-Modified specified */
1.1.1.2 ! misho 409: if (NULL == ds) {
1.1 misho 410:
411: strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
412:
413: response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
414:
415: tbuf.ptr = timebuf;
416: tbuf.used = sizeof(timebuf);
417: tbuf.size = sizeof(timebuf);
1.1.1.2 ! misho 418: } else {
1.1 misho 419: tbuf.ptr = ds->value->ptr;
420: tbuf.used = ds->value->used;
421: tbuf.size = ds->value->size;
422: }
423:
424: if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
425: /* ok, the client already has our content,
426: * no need to send it again */
427:
428: chunkqueue_reset(con->write_queue);
429: ret = 0; /* cache-hit */
430: }
431: } else {
432: chunkqueue_reset(con->write_queue);
433: }
434: }
435:
436: if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
437: /* cache-miss */
438: buffer_copy_string_buffer(con->uri.path, p->baseurl);
439: buffer_append_string_buffer(con->uri.path, p->trigger_handler);
440:
441: buffer_copy_string_buffer(con->physical.path, p->basedir);
442: buffer_append_string_buffer(con->physical.path, p->trigger_handler);
443:
444: chunkqueue_reset(con->write_queue);
445: }
446:
447: error:
448: lua_close(L);
449:
450: stream_close(&rm.st);
451: buffer_free(b);
452:
453: return ret /* cache-error */;
454: }
455: #else
456: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
457: UNUSED(srv);
458: UNUSED(con);
459: UNUSED(p);
460: UNUSED(fn);
461: /* error */
462: return -1;
463: }
464: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>