11 #include "mod_cml_funcs.h"
17 #include "http_header.h"
19 #include "stat_cache.h"
22 typedef unsigned char HASH
[HASHLEN
];
24 typedef char HASHHEX
[HASHHEXLEN
+1];
26 static int lua_to_c_get_string(lua_State
*L
, const char *varname
, buffer
*b
) {
27 int curelem
= lua_gettop(L
);
30 lua_getglobal(L
, varname
);
32 if (lua_isstring(L
, curelem
)) {
33 buffer_copy_string(b
, lua_tostring(L
, curelem
));
40 force_assert(curelem
== lua_gettop(L
));
44 static int lua_to_c_is_table(lua_State
*L
, const char *varname
) {
45 int curelem
= lua_gettop(L
);
48 lua_getglobal(L
, varname
);
50 result
= lua_istable(L
, curelem
) ? 1 : 0;
53 force_assert(curelem
== lua_gettop(L
));
57 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
) {
58 lua_pushlstring(L
, key
, key_len
);
59 lua_pushlstring(L
, val
, val_len
);
65 static int cache_export_get_params(lua_State
*L
, int tbl
, buffer
*qrystr
) {
67 size_t i
, len
, klen
= 0;
68 char *key
= NULL
, *val
= NULL
;
70 if (buffer_string_is_empty(qrystr
)) return 0;
74 len
= buffer_string_length(qrystr
);
75 for (i
= 0; i
<= len
; i
++) {
76 switch(qrystr
->ptr
[i
]) {
79 val
= qrystr
->ptr
+ i
+ 1;
80 klen
= (size_t)(val
- key
- 1);
86 case '\0': /* fin symbol */
88 /* we need at least a = since the last & */
91 val
, (size_t)(qrystr
->ptr
+ i
- val
));
94 key
= qrystr
->ptr
+ i
+ 1;
104 int cache_parse_lua(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
) {
110 /* push the lua file to the interpreter and see what happends */
114 /* register functions */
115 lua_register(L
, "md5", f_crypto_md5
);
116 lua_register(L
, "file_mtime", f_file_mtime
);
117 lua_register(L
, "file_isreg", f_file_isreg
);
118 lua_register(L
, "file_isdir", f_file_isreg
);
119 lua_register(L
, "dir_files", f_dir_files
);
122 lua_pushlightuserdata(L
, p
->conf
.memc
);
123 lua_pushcclosure(L
, f_memcache_get_long
, 1);
124 lua_setglobal(L
, "memcache_get_long");
126 lua_pushlightuserdata(L
, p
->conf
.memc
);
127 lua_pushcclosure(L
, f_memcache_get_string
, 1);
128 lua_setglobal(L
, "memcache_get_string");
130 lua_pushlightuserdata(L
, p
->conf
.memc
);
131 lua_pushcclosure(L
, f_memcache_exists
, 1);
132 lua_setglobal(L
, "memcache_exists");
135 /* register CGI environment */
138 int header_tbl
= lua_gettop(L
);
140 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con
->request
.orig_uri
));
141 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con
->uri
.path
));
142 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con
->physical
.path
));
143 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con
->physical
.basedir
));
144 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
145 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con
->request
.pathinfo
));
148 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p
->basedir
));
149 c_to_lua_push(L
, header_tbl
, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p
->baseurl
));
151 lua_setglobal(L
, "request");
153 /* register GET parameter */
155 cache_export_get_params(L
, lua_gettop(L
), con
->uri
.query
);
156 lua_setglobal(L
, "get");
158 /* 2 default constants */
159 lua_pushinteger(L
, 0);
160 lua_setglobal(L
, "CACHE_HIT");
162 lua_pushinteger(L
, 1);
163 lua_setglobal(L
, "CACHE_MISS");
165 /* load lua program */
166 ret
= luaL_loadfile(L
, fn
->ptr
);
168 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
169 "failed loading cml_lua script",
172 lua_tostring(L
, -1));
176 if (lua_pcall(L
, 0, 1, 0)) {
177 log_error_write(srv
, __FILE__
, __LINE__
, "sbsS",
178 "failed running cml_lua script",
181 lua_tostring(L
, -1));
185 /* get return value */
186 ret
= (int)lua_tointeger(L
, -1);
189 /* fetch the data from lua */
190 lua_to_c_get_string(L
, "trigger_handler", p
->trigger_handler
);
192 if (0 == lua_to_c_get_string(L
, "output_contenttype", b
)) {
193 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b
));
197 /* up to now it is a cache-hit, check if all files exist */
202 if (!lua_to_c_is_table(L
, "output_include")) {
203 log_error_write(srv
, __FILE__
, __LINE__
, "s",
204 "output_include is missing or not a table");
210 lua_getglobal(L
, "output_include");
211 curelem
= lua_gettop(L
);
213 /* HOW-TO build a etag ?
214 * as we don't just have one file we have to take the stat()
215 * from all base files, merge them and build the etag from
218 * The mtime of the content is the mtime of the freshest base file
222 lua_pushnil(L
); /* first key */
223 while (lua_next(L
, curelem
) != 0) {
224 /* key' is at index -2 and value' at index -1 */
226 if (lua_isstring(L
, -1)) {
227 const char *s
= lua_tostring(L
, -1);
231 /* the file is relative, make it absolute */
233 buffer_copy_buffer(b
, p
->basedir
);
234 buffer_append_string(b
, lua_tostring(L
, -1));
236 buffer_copy_string(b
, lua_tostring(L
, -1));
239 fd
= stat_cache_open_rdonly_fstat(b
, &st
, con
->conf
.follow_symlink
);
245 /* a file is missing, call the handler to generate it */
246 if (!buffer_string_is_empty(p
->trigger_handler
)) {
247 ret
= 1; /* cache-miss */
249 log_error_write(srv
, __FILE__
, __LINE__
, "s",
250 "a file is missing, calling handler");
254 /* handler not set -> 500 */
257 log_error_write(srv
, __FILE__
, __LINE__
, "s",
258 "a file missing and no handler set");
267 chunkqueue_append_file_fd(con
->write_queue
, b
, fd
, 0, st
.st_size
);
268 if (st
.st_mtime
> mtime
) mtime
= st
.st_mtime
;
273 log_error_write(srv
, __FILE__
, __LINE__
, "s",
278 lua_pop(L
, 1); /* removes value'; keeps key' for next iteration */
281 lua_settop(L
, curelem
- 1);
284 buffer
*vb
= http_header_response_get(con
, HTTP_HEADER_LAST_MODIFIED
, CONST_STR_LEN("Last-Modified"));
285 if (NULL
== vb
) { /* no Last-Modified specified */
286 char timebuf
[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
287 if (0 == mtime
) mtime
= time(NULL
); /* default last-modified to now */
288 strftime(timebuf
, sizeof(timebuf
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime
));
289 http_header_response_set(con
, HTTP_HEADER_LAST_MODIFIED
, CONST_STR_LEN("Last-Modified"), timebuf
, sizeof(timebuf
) - 1);
290 vb
= http_header_response_get(con
, HTTP_HEADER_LAST_MODIFIED
, CONST_STR_LEN("Last-Modified"));
291 force_assert(NULL
!= vb
);
294 con
->file_finished
= 1;
296 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, vb
)) {
297 /* ok, the client already has our content,
298 * no need to send it again */
300 chunkqueue_reset(con
->write_queue
);
301 ret
= 0; /* cache-hit */
304 chunkqueue_reset(con
->write_queue
);
308 if (ret
== 1 && !buffer_string_is_empty(p
->trigger_handler
)) {
310 buffer_copy_buffer(con
->uri
.path
, p
->baseurl
);
311 buffer_append_string_buffer(con
->uri
.path
, p
->trigger_handler
);
313 buffer_copy_buffer(con
->physical
.path
, p
->basedir
);
314 buffer_append_string_buffer(con
->physical
.path
, p
->trigger_handler
);
316 chunkqueue_reset(con
->write_queue
);
324 return ret
/* cache-error */;