4 #include "mod_cml_funcs.h"
8 #include "stat_cache.h"
17 typedef unsigned char HASH
[HASHLEN
];
19 typedef char HASHHEX
[HASHHEXLEN
+1];
31 static int lua_to_c_get_string(lua_State
*L
, const char *varname
, buffer
*b
) {
32 int curelem
= lua_gettop(L
);
35 lua_getglobal(L
, varname
);
37 if (lua_isstring(L
, curelem
)) {
38 buffer_copy_string(b
, lua_tostring(L
, curelem
));
45 force_assert(curelem
== lua_gettop(L
));
49 static int lua_to_c_is_table(lua_State
*L
, const char *varname
) {
50 int curelem
= lua_gettop(L
);
53 lua_getglobal(L
, varname
);
55 result
= lua_istable(L
, curelem
) ? 1 : 0;
58 force_assert(curelem
== lua_gettop(L
));
62 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
) {
63 lua_pushlstring(L
, key
, key_len
);
64 lua_pushlstring(L
, val
, val_len
);
70 static int cache_export_get_params(lua_State
*L
, int tbl
, buffer
*qrystr
) {
73 char *key
= NULL
, *val
= NULL
;
78 len
= buffer_string_length(qrystr
);
79 for (i
= 0; i
<= len
; i
++) {
80 switch(qrystr
->ptr
[i
]) {
83 val
= qrystr
->ptr
+ i
+ 1;
85 qrystr
->ptr
[i
] = '\0';
92 case '\0': /* fin symbol */
94 /* we need at least a = since the last & */
96 /* terminate the value */
97 qrystr
->ptr
[i
] = '\0';
104 key
= qrystr
->ptr
+ i
+ 1;
114 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {
120 /* push the lua file to the interpreter and see what happends */
124 /* register functions */
125 lua_register(L
, "md5", f_crypto_md5
);
126 lua_register(L
, "file_mtime", f_file_mtime
);
127 lua_register(L
, "file_isreg", f_file_isreg
);
128 lua_register(L
, "file_isdir", f_file_isreg
);
129 lua_register(L
, "dir_files", f_dir_files
);
132 lua_pushlightuserdata(L
, p
->conf
.memc
);
133 lua_pushcclosure(L
, f_memcache_get_long
, 1);
134 lua_setglobal(L
, "memcache_get_long");
136 lua_pushlightuserdata(L
, p
->conf
.memc
);
137 lua_pushcclosure(L
, f_memcache_get_string
, 1);
138 lua_setglobal(L
, "memcache_get_string");
140 lua_pushlightuserdata(L
, p
->conf
.memc
);
141 lua_pushcclosure(L
, f_memcache_exists
, 1);
142 lua_setglobal(L
, "memcache_exists");
145 /* register CGI environment */
148 int header_tbl
= lua_gettop(L
);
150 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con
->request
.orig_uri
));
151 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con
->uri
.path
));
152 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con
->physical
.path
));
153 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con
->physical
.basedir
));
154 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
155 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con
->request
.pathinfo
));
158 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p
->basedir
));
159 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p
->baseurl
));
161 lua_setglobal(L
, "request");
163 /* register GET parameter */
166 int get_tbl
= lua_gettop(L
);
168 buffer_copy_buffer(b
, con
->uri
.query
);
169 cache_export_get_params(L
, get_tbl
, b
);
172 lua_setglobal(L
, "get");
174 /* 2 default constants */
175 lua_pushinteger(L
, 0);
176 lua_setglobal(L
, "CACHE_HIT");
178 lua_pushinteger(L
, 1);
179 lua_setglobal(L
, "CACHE_MISS");
181 /* load lua program */
182 ret
= luaL_loadfile(L
, fn
->ptr
);
184 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
185 "failed loading cml_lua script",
188 lua_tostring(L
, -1));
192 if (lua_pcall(L
, 0, 1, 0)) {
193 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
194 "failed running cml_lua script",
197 lua_tostring(L
, -1));
201 /* get return value */
202 ret
= (int)lua_tointeger(L
, -1);
205 /* fetch the data from lua */
206 lua_to_c_get_string(L
, "trigger_handler", p
->trigger_handler
);
208 if (0 == lua_to_c_get_string(L
, "output_contenttype", b
)) {
209 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b
));
213 /* up to now it is a cache-hit, check if all files exist */
218 if (!lua_to_c_is_table(L
, "output_include")) {
219 log_error_write(srv
, __FILE__
, __LINE__
, "s",
220 "output_include is missing or not a table");
226 lua_getglobal(L
, "output_include");
227 curelem
= lua_gettop(L
);
229 /* HOW-TO build a etag ?
230 * as we don't just have one file we have to take the stat()
231 * from all base files, merge them and build the etag from
234 * The mtime of the content is the mtime of the freshest base file
238 lua_pushnil(L
); /* first key */
239 while (lua_next(L
, curelem
) != 0) {
240 /* key' is at index -2 and value' at index -1 */
242 if (lua_isstring(L
, -1)) {
243 const char *s
= lua_tostring(L
, -1);
247 /* the file is relative, make it absolute */
249 buffer_copy_buffer(b
, p
->basedir
);
250 buffer_append_string(b
, lua_tostring(L
, -1));
252 buffer_copy_string(b
, lua_tostring(L
, -1));
255 fd
= stat_cache_open_rdonly_fstat(srv
, con
, b
, &st
);
261 /* a file is missing, call the handler to generate it */
262 if (!buffer_string_is_empty(p
->trigger_handler
)) {
263 ret
= 1; /* cache-miss */
265 log_error_write(srv
, __FILE__
, __LINE__
, "s",
266 "a file is missing, calling handler");
270 /* handler not set -> 500 */
273 log_error_write(srv
, __FILE__
, __LINE__
, "s",
274 "a file missing and no handler set");
283 chunkqueue_append_file_fd(con
->write_queue
, b
, fd
, 0, st
.st_size
);
284 if (st
.st_mtime
> mtime
) mtime
= st
.st_mtime
;
289 log_error_write(srv
, __FILE__
, __LINE__
, "s",
294 lua_pop(L
, 1); /* removes value'; keeps key' for next iteration */
297 lua_settop(L
, curelem
- 1);
301 char timebuf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
303 con
->file_finished
= 1;
305 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
306 if (0 == mtime
) mtime
= time(NULL
); /* default last-modified to now */
308 /* no Last-Modified specified */
311 strftime(timebuf
, sizeof(timebuf
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime
));
313 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), timebuf
, sizeof(timebuf
) - 1);
314 ds
= (data_string
*)array_get_element(con
->response
.headers
, "Last-Modified");
315 force_assert(NULL
!= ds
);
318 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, ds
->value
)) {
319 /* ok, the client already has our content,
320 * no need to send it again */
322 chunkqueue_reset(con
->write_queue
);
323 ret
= 0; /* cache-hit */
326 chunkqueue_reset(con
->write_queue
);
330 if (ret
== 1 && !buffer_string_is_empty(p
->trigger_handler
)) {
332 buffer_copy_buffer(con
->uri
.path
, p
->baseurl
);
333 buffer_append_string_buffer(con
->uri
.path
, p
->trigger_handler
);
335 buffer_copy_buffer(con
->physical
.path
, p
->basedir
);
336 buffer_append_string_buffer(con
->physical
.path
, p
->trigger_handler
);
338 chunkqueue_reset(con
->write_queue
);
346 return ret
/* cache-error */;