4 #include "mod_cml_funcs.h"
8 #include "stat_cache.h"
17 typedef unsigned char HASH
[HASHLEN
];
19 typedef char HASHHEX
[HASHHEXLEN
+1];
33 static int lua_to_c_get_string(lua_State
*L
, const char *varname
, buffer
*b
) {
34 int curelem
= lua_gettop(L
);
37 lua_getglobal(L
, varname
);
39 if (lua_isstring(L
, curelem
)) {
40 buffer_copy_string(b
, lua_tostring(L
, curelem
));
47 force_assert(curelem
== lua_gettop(L
));
51 static int lua_to_c_is_table(lua_State
*L
, const char *varname
) {
52 int curelem
= lua_gettop(L
);
55 lua_getglobal(L
, varname
);
57 result
= lua_istable(L
, curelem
) ? 1 : 0;
60 force_assert(curelem
== lua_gettop(L
));
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
);
72 static int cache_export_get_params(lua_State
*L
, int tbl
, buffer
*qrystr
) {
75 char *key
= NULL
, *val
= NULL
;
80 len
= buffer_string_length(qrystr
);
81 for (i
= 0; i
<= len
; i
++) {
82 switch(qrystr
->ptr
[i
]) {
85 val
= qrystr
->ptr
+ i
+ 1;
87 qrystr
->ptr
[i
] = '\0';
94 case '\0': /* fin symbol */
96 /* we need at least a = since the last & */
98 /* terminate the value */
99 qrystr
->ptr
[i
] = '\0';
101 c_to_lua_push(L
, tbl
,
106 key
= qrystr
->ptr
+ i
+ 1;
116 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {
122 /* push the lua file to the interpreter and see what happends */
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
);
134 lua_pushlightuserdata(L
, p
->conf
.memc
);
135 lua_pushcclosure(L
, f_memcache_get_long
, 1);
136 lua_setglobal(L
, "memcache_get_long");
138 lua_pushlightuserdata(L
, p
->conf
.memc
);
139 lua_pushcclosure(L
, f_memcache_get_string
, 1);
140 lua_setglobal(L
, "memcache_get_string");
142 lua_pushlightuserdata(L
, p
->conf
.memc
);
143 lua_pushcclosure(L
, f_memcache_exists
, 1);
144 lua_setglobal(L
, "memcache_exists");
147 /* register CGI environment */
150 int header_tbl
= lua_gettop(L
);
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
));
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
));
163 lua_setglobal(L
, "request");
165 /* register GET parameter */
168 int get_tbl
= lua_gettop(L
);
170 buffer_copy_buffer(b
, con
->uri
.query
);
171 cache_export_get_params(L
, get_tbl
, b
);
174 lua_setglobal(L
, "get");
176 /* 2 default constants */
177 lua_pushinteger(L
, 0);
178 lua_setglobal(L
, "CACHE_HIT");
180 lua_pushinteger(L
, 1);
181 lua_setglobal(L
, "CACHE_MISS");
183 /* load lua program */
184 ret
= luaL_loadfile(L
, fn
->ptr
);
186 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
187 "failed loading cml_lua script",
190 lua_tostring(L
, -1));
194 if (lua_pcall(L
, 0, 1, 0)) {
195 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
196 "failed running cml_lua script",
199 lua_tostring(L
, -1));
203 /* get return value */
204 ret
= (int)lua_tointeger(L
, -1);
207 /* fetch the data from lua */
208 lua_to_c_get_string(L
, "trigger_handler", p
->trigger_handler
);
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
));
215 /* up to now it is a cache-hit, check if all files exist */
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");
228 lua_getglobal(L
, "output_include");
229 curelem
= lua_gettop(L
);
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
236 * The mtime of the content is the mtime of the freshest base file
240 lua_pushnil(L
); /* first key */
241 while (lua_next(L
, curelem
) != 0) {
242 /* key' is at index -2 and value' at index -1 */
244 if (lua_isstring(L
, -1)) {
245 const char *s
= lua_tostring(L
, -1);
249 /* the file is relative, make it absolute */
251 buffer_copy_buffer(b
, p
->basedir
);
252 buffer_append_string(b
, lua_tostring(L
, -1));
254 buffer_copy_string(b
, lua_tostring(L
, -1));
257 fd
= stat_cache_open_rdonly_fstat(srv
, con
, b
, &st
);
263 /* a file is missing, call the handler to generate it */
264 if (!buffer_string_is_empty(p
->trigger_handler
)) {
265 ret
= 1; /* cache-miss */
267 log_error_write(srv
, __FILE__
, __LINE__
, "s",
268 "a file is missing, calling handler");
272 /* handler not set -> 500 */
275 log_error_write(srv
, __FILE__
, __LINE__
, "s",
276 "a file missing and no handler set");
285 chunkqueue_append_file_fd(con
->write_queue
, b
, fd
, 0, st
.st_size
);
286 if (st
.st_mtime
> mtime
) mtime
= st
.st_mtime
;
291 log_error_write(srv
, __FILE__
, __LINE__
, "s",
296 lua_pop(L
, 1); /* removes value'; keeps key' for next iteration */
299 lua_settop(L
, curelem
- 1);
303 char timebuf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
305 con
->file_finished
= 1;
307 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
308 if (0 == mtime
) mtime
= time(NULL
); /* default last-modified to now */
310 /* no Last-Modified specified */
313 strftime(timebuf
, sizeof(timebuf
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime
));
315 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), timebuf
, sizeof(timebuf
) - 1);
316 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
317 force_assert(NULL
!= ds
);
320 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, ds
->value
)) {
321 /* ok, the client already has our content,
322 * no need to send it again */
324 chunkqueue_reset(con
->write_queue
);
325 ret
= 0; /* cache-hit */
328 chunkqueue_reset(con
->write_queue
);
332 if (ret
== 1 && !buffer_string_is_empty(p
->trigger_handler
)) {
334 buffer_copy_buffer(con
->uri
.path
, p
->baseurl
);
335 buffer_append_string_buffer(con
->uri
.path
, p
->trigger_handler
);
337 buffer_copy_buffer(con
->physical
.path
, p
->basedir
);
338 buffer_append_string_buffer(con
->physical
.path
, p
->trigger_handler
);
340 chunkqueue_reset(con
->write_queue
);
348 return ret
/* cache-error */;
351 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {