13 #include "mod_cml_funcs.h"
17 #include "stat_cache.h"
20 typedef unsigned char HASH
[HASHLEN
];
22 typedef char HASHHEX
[HASHHEXLEN
+1];
24 static int lua_to_c_get_string(lua_State
*L
, const char *varname
, buffer
*b
) {
25 int curelem
= lua_gettop(L
);
28 lua_getglobal(L
, varname
);
30 if (lua_isstring(L
, curelem
)) {
31 buffer_copy_string(b
, lua_tostring(L
, curelem
));
38 force_assert(curelem
== lua_gettop(L
));
42 static int lua_to_c_is_table(lua_State
*L
, const char *varname
) {
43 int curelem
= lua_gettop(L
);
46 lua_getglobal(L
, varname
);
48 result
= lua_istable(L
, curelem
) ? 1 : 0;
51 force_assert(curelem
== lua_gettop(L
));
55 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
) {
56 lua_pushlstring(L
, key
, key_len
);
57 lua_pushlstring(L
, val
, val_len
);
63 static int cache_export_get_params(lua_State
*L
, int tbl
, buffer
*qrystr
) {
66 char *key
= NULL
, *val
= NULL
;
71 len
= buffer_string_length(qrystr
);
72 for (i
= 0; i
<= len
; i
++) {
73 switch(qrystr
->ptr
[i
]) {
76 val
= qrystr
->ptr
+ i
+ 1;
78 qrystr
->ptr
[i
] = '\0';
85 case '\0': /* fin symbol */
87 /* we need at least a = since the last & */
89 /* terminate the value */
90 qrystr
->ptr
[i
] = '\0';
97 key
= qrystr
->ptr
+ i
+ 1;
107 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {
113 /* push the lua file to the interpreter and see what happends */
117 /* register functions */
118 lua_register(L
, "md5", f_crypto_md5
);
119 lua_register(L
, "file_mtime", f_file_mtime
);
120 lua_register(L
, "file_isreg", f_file_isreg
);
121 lua_register(L
, "file_isdir", f_file_isreg
);
122 lua_register(L
, "dir_files", f_dir_files
);
125 lua_pushlightuserdata(L
, p
->conf
.memc
);
126 lua_pushcclosure(L
, f_memcache_get_long
, 1);
127 lua_setglobal(L
, "memcache_get_long");
129 lua_pushlightuserdata(L
, p
->conf
.memc
);
130 lua_pushcclosure(L
, f_memcache_get_string
, 1);
131 lua_setglobal(L
, "memcache_get_string");
133 lua_pushlightuserdata(L
, p
->conf
.memc
);
134 lua_pushcclosure(L
, f_memcache_exists
, 1);
135 lua_setglobal(L
, "memcache_exists");
138 /* register CGI environment */
141 int header_tbl
= lua_gettop(L
);
143 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con
->request
.orig_uri
));
144 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con
->uri
.path
));
145 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con
->physical
.path
));
146 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con
->physical
.basedir
));
147 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
148 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con
->request
.pathinfo
));
151 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p
->basedir
));
152 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p
->baseurl
));
154 lua_setglobal(L
, "request");
156 /* register GET parameter */
159 int get_tbl
= lua_gettop(L
);
161 buffer_copy_buffer(b
, con
->uri
.query
);
162 cache_export_get_params(L
, get_tbl
, b
);
165 lua_setglobal(L
, "get");
167 /* 2 default constants */
168 lua_pushinteger(L
, 0);
169 lua_setglobal(L
, "CACHE_HIT");
171 lua_pushinteger(L
, 1);
172 lua_setglobal(L
, "CACHE_MISS");
174 /* load lua program */
175 ret
= luaL_loadfile(L
, fn
->ptr
);
177 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
178 "failed loading cml_lua script",
181 lua_tostring(L
, -1));
185 if (lua_pcall(L
, 0, 1, 0)) {
186 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
187 "failed running cml_lua script",
190 lua_tostring(L
, -1));
194 /* get return value */
195 ret
= (int)lua_tointeger(L
, -1);
198 /* fetch the data from lua */
199 lua_to_c_get_string(L
, "trigger_handler", p
->trigger_handler
);
201 if (0 == lua_to_c_get_string(L
, "output_contenttype", b
)) {
202 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b
));
206 /* up to now it is a cache-hit, check if all files exist */
211 if (!lua_to_c_is_table(L
, "output_include")) {
212 log_error_write(srv
, __FILE__
, __LINE__
, "s",
213 "output_include is missing or not a table");
219 lua_getglobal(L
, "output_include");
220 curelem
= lua_gettop(L
);
222 /* HOW-TO build a etag ?
223 * as we don't just have one file we have to take the stat()
224 * from all base files, merge them and build the etag from
227 * The mtime of the content is the mtime of the freshest base file
231 lua_pushnil(L
); /* first key */
232 while (lua_next(L
, curelem
) != 0) {
233 /* key' is at index -2 and value' at index -1 */
235 if (lua_isstring(L
, -1)) {
236 const char *s
= lua_tostring(L
, -1);
240 /* the file is relative, make it absolute */
242 buffer_copy_buffer(b
, p
->basedir
);
243 buffer_append_string(b
, lua_tostring(L
, -1));
245 buffer_copy_string(b
, lua_tostring(L
, -1));
248 fd
= stat_cache_open_rdonly_fstat(srv
, con
, b
, &st
);
254 /* a file is missing, call the handler to generate it */
255 if (!buffer_string_is_empty(p
->trigger_handler
)) {
256 ret
= 1; /* cache-miss */
258 log_error_write(srv
, __FILE__
, __LINE__
, "s",
259 "a file is missing, calling handler");
263 /* handler not set -> 500 */
266 log_error_write(srv
, __FILE__
, __LINE__
, "s",
267 "a file missing and no handler set");
276 chunkqueue_append_file_fd(con
->write_queue
, b
, fd
, 0, st
.st_size
);
277 if (st
.st_mtime
> mtime
) mtime
= st
.st_mtime
;
282 log_error_write(srv
, __FILE__
, __LINE__
, "s",
287 lua_pop(L
, 1); /* removes value'; keeps key' for next iteration */
290 lua_settop(L
, curelem
- 1);
294 char timebuf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
296 con
->file_finished
= 1;
298 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
299 if (0 == mtime
) mtime
= time(NULL
); /* default last-modified to now */
301 /* no Last-Modified specified */
304 strftime(timebuf
, sizeof(timebuf
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime
));
306 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), timebuf
, sizeof(timebuf
) - 1);
307 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
308 force_assert(NULL
!= ds
);
311 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, ds
->value
)) {
312 /* ok, the client already has our content,
313 * no need to send it again */
315 chunkqueue_reset(con
->write_queue
);
316 ret
= 0; /* cache-hit */
319 chunkqueue_reset(con
->write_queue
);
323 if (ret
== 1 && !buffer_string_is_empty(p
->trigger_handler
)) {
325 buffer_copy_buffer(con
->uri
.path
, p
->baseurl
);
326 buffer_append_string_buffer(con
->uri
.path
, p
->trigger_handler
);
328 buffer_copy_buffer(con
->physical
.path
, p
->basedir
);
329 buffer_append_string_buffer(con
->physical
.path
, p
->trigger_handler
);
331 chunkqueue_reset(con
->write_queue
);
339 return ret
/* cache-error */;