11 #include "mod_cml_funcs.h"
15 #include "stat_cache.h"
18 typedef unsigned char HASH
[HASHLEN
];
20 typedef char HASHHEX
[HASHHEXLEN
+1];
22 static int lua_to_c_get_string(lua_State
*L
, const char *varname
, buffer
*b
) {
23 int curelem
= lua_gettop(L
);
26 lua_getglobal(L
, varname
);
28 if (lua_isstring(L
, curelem
)) {
29 buffer_copy_string(b
, lua_tostring(L
, curelem
));
36 force_assert(curelem
== lua_gettop(L
));
40 static int lua_to_c_is_table(lua_State
*L
, const char *varname
) {
41 int curelem
= lua_gettop(L
);
44 lua_getglobal(L
, varname
);
46 result
= lua_istable(L
, curelem
) ? 1 : 0;
49 force_assert(curelem
== lua_gettop(L
));
53 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
) {
54 lua_pushlstring(L
, key
, key_len
);
55 lua_pushlstring(L
, val
, val_len
);
61 static int cache_export_get_params(lua_State
*L
, int tbl
, buffer
*qrystr
) {
64 char *key
= NULL
, *val
= NULL
;
69 len
= buffer_string_length(qrystr
);
70 for (i
= 0; i
<= len
; i
++) {
71 switch(qrystr
->ptr
[i
]) {
74 val
= qrystr
->ptr
+ i
+ 1;
76 qrystr
->ptr
[i
] = '\0';
83 case '\0': /* fin symbol */
85 /* we need at least a = since the last & */
87 /* terminate the value */
88 qrystr
->ptr
[i
] = '\0';
95 key
= qrystr
->ptr
+ i
+ 1;
105 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {
111 /* push the lua file to the interpreter and see what happends */
115 /* register functions */
116 lua_register(L
, "md5", f_crypto_md5
);
117 lua_register(L
, "file_mtime", f_file_mtime
);
118 lua_register(L
, "file_isreg", f_file_isreg
);
119 lua_register(L
, "file_isdir", f_file_isreg
);
120 lua_register(L
, "dir_files", f_dir_files
);
123 lua_pushlightuserdata(L
, p
->conf
.memc
);
124 lua_pushcclosure(L
, f_memcache_get_long
, 1);
125 lua_setglobal(L
, "memcache_get_long");
127 lua_pushlightuserdata(L
, p
->conf
.memc
);
128 lua_pushcclosure(L
, f_memcache_get_string
, 1);
129 lua_setglobal(L
, "memcache_get_string");
131 lua_pushlightuserdata(L
, p
->conf
.memc
);
132 lua_pushcclosure(L
, f_memcache_exists
, 1);
133 lua_setglobal(L
, "memcache_exists");
136 /* register CGI environment */
139 int header_tbl
= lua_gettop(L
);
141 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con
->request
.orig_uri
));
142 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con
->uri
.path
));
143 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con
->physical
.path
));
144 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con
->physical
.basedir
));
145 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
146 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con
->request
.pathinfo
));
149 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p
->basedir
));
150 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p
->baseurl
));
152 lua_setglobal(L
, "request");
154 /* register GET parameter */
157 int get_tbl
= lua_gettop(L
);
159 buffer_copy_buffer(b
, con
->uri
.query
);
160 cache_export_get_params(L
, get_tbl
, b
);
163 lua_setglobal(L
, "get");
165 /* 2 default constants */
166 lua_pushinteger(L
, 0);
167 lua_setglobal(L
, "CACHE_HIT");
169 lua_pushinteger(L
, 1);
170 lua_setglobal(L
, "CACHE_MISS");
172 /* load lua program */
173 ret
= luaL_loadfile(L
, fn
->ptr
);
175 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
176 "failed loading cml_lua script",
179 lua_tostring(L
, -1));
183 if (lua_pcall(L
, 0, 1, 0)) {
184 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
185 "failed running cml_lua script",
188 lua_tostring(L
, -1));
192 /* get return value */
193 ret
= (int)lua_tointeger(L
, -1);
196 /* fetch the data from lua */
197 lua_to_c_get_string(L
, "trigger_handler", p
->trigger_handler
);
199 if (0 == lua_to_c_get_string(L
, "output_contenttype", b
)) {
200 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b
));
204 /* up to now it is a cache-hit, check if all files exist */
209 if (!lua_to_c_is_table(L
, "output_include")) {
210 log_error_write(srv
, __FILE__
, __LINE__
, "s",
211 "output_include is missing or not a table");
217 lua_getglobal(L
, "output_include");
218 curelem
= lua_gettop(L
);
220 /* HOW-TO build a etag ?
221 * as we don't just have one file we have to take the stat()
222 * from all base files, merge them and build the etag from
225 * The mtime of the content is the mtime of the freshest base file
229 lua_pushnil(L
); /* first key */
230 while (lua_next(L
, curelem
) != 0) {
231 /* key' is at index -2 and value' at index -1 */
233 if (lua_isstring(L
, -1)) {
234 const char *s
= lua_tostring(L
, -1);
238 /* the file is relative, make it absolute */
240 buffer_copy_buffer(b
, p
->basedir
);
241 buffer_append_string(b
, lua_tostring(L
, -1));
243 buffer_copy_string(b
, lua_tostring(L
, -1));
246 fd
= stat_cache_open_rdonly_fstat(srv
, con
, b
, &st
);
252 /* a file is missing, call the handler to generate it */
253 if (!buffer_string_is_empty(p
->trigger_handler
)) {
254 ret
= 1; /* cache-miss */
256 log_error_write(srv
, __FILE__
, __LINE__
, "s",
257 "a file is missing, calling handler");
261 /* handler not set -> 500 */
264 log_error_write(srv
, __FILE__
, __LINE__
, "s",
265 "a file missing and no handler set");
274 chunkqueue_append_file_fd(con
->write_queue
, b
, fd
, 0, st
.st_size
);
275 if (st
.st_mtime
> mtime
) mtime
= st
.st_mtime
;
280 log_error_write(srv
, __FILE__
, __LINE__
, "s",
285 lua_pop(L
, 1); /* removes value'; keeps key' for next iteration */
288 lua_settop(L
, curelem
- 1);
292 char timebuf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
294 con
->file_finished
= 1;
296 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
297 if (0 == mtime
) mtime
= time(NULL
); /* default last-modified to now */
299 /* no Last-Modified specified */
302 strftime(timebuf
, sizeof(timebuf
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime
));
304 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), timebuf
, sizeof(timebuf
) - 1);
305 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
306 force_assert(NULL
!= ds
);
309 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, ds
->value
)) {
310 /* ok, the client already has our content,
311 * no need to send it again */
313 chunkqueue_reset(con
->write_queue
);
314 ret
= 0; /* cache-hit */
317 chunkqueue_reset(con
->write_queue
);
321 if (ret
== 1 && !buffer_string_is_empty(p
->trigger_handler
)) {
323 buffer_copy_buffer(con
->uri
.path
, p
->baseurl
);
324 buffer_append_string_buffer(con
->uri
.path
, p
->trigger_handler
);
326 buffer_copy_buffer(con
->physical
.path
, p
->basedir
);
327 buffer_append_string_buffer(con
->physical
.path
, p
->trigger_handler
);
329 chunkqueue_reset(con
->write_queue
);
337 return ret
/* cache-error */;