6 #include "http_chunk.h"
10 #include "mod_magnet_cache.h"
12 #include "stat_cache.h"
13 #include "status_counter.h"
26 #define LUA_RIDX_LIGHTTPD_SERVER "lighty.srv"
27 #define LUA_RIDX_LIGHTTPD_CONNECTION "lighty.con"
29 #define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to"
30 #define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to"
31 #define MAGNET_RESTART_REQUEST 99
33 /* plugin config for all request/connections */
35 static jmp_buf exceptionjmp
;
49 plugin_config
**config_storage
;
54 /* init the plugin data */
55 INIT_FUNC(mod_magnet_init
) {
58 p
= calloc(1, sizeof(*p
));
60 p
->cache
= script_cache_init();
61 p
->encode_buf
= buffer_init();
66 /* detroy the plugin data */
67 FREE_FUNC(mod_magnet_free
) {
72 if (!p
) return HANDLER_GO_ON
;
74 if (p
->config_storage
) {
77 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
78 plugin_config
*s
= p
->config_storage
[i
];
80 if (NULL
== s
) continue;
82 array_free(s
->url_raw
);
83 array_free(s
->physical_path
);
87 free(p
->config_storage
);
90 script_cache_free(p
->cache
);
91 buffer_free(p
->encode_buf
);
98 /* handle plugin config and check values */
100 SETDEFAULTS_FUNC(mod_magnet_set_defaults
) {
101 plugin_data
*p
= p_d
;
104 config_values_t cv
[] = {
105 { MAGNET_CONFIG_RAW_URL
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
106 { MAGNET_CONFIG_PHYSICAL_PATH
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
107 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
110 if (!p
) return HANDLER_ERROR
;
112 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
114 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
115 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
118 s
= calloc(1, sizeof(plugin_config
));
119 s
->url_raw
= array_init();
120 s
->physical_path
= array_init();
122 cv
[0].destination
= s
->url_raw
;
123 cv
[1].destination
= s
->physical_path
;
125 p
->config_storage
[i
] = s
;
127 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
128 return HANDLER_ERROR
;
132 return HANDLER_GO_ON
;
137 static int mod_magnet_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
139 plugin_config
*s
= p
->config_storage
[0];
142 PATCH(physical_path
);
144 /* skip the first, the global context */
145 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
146 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
147 s
= p
->config_storage
[i
];
149 /* condition didn't match */
150 if (!config_check_cond(srv
, con
, dc
)) continue;
153 for (j
= 0; j
< dc
->value
->used
; j
++) {
154 data_unset
*du
= dc
->value
->data
[j
];
156 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL
))) {
158 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH
))) {
159 PATCH(physical_path
);
168 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
169 /* lua5.1 backward compat definition */
170 static void lua_pushglobaltable(lua_State
*L
) { /* (-0, +1, -) */
171 lua_pushvalue(L
, LUA_GLOBALSINDEX
);
175 static void magnet_setfenv_mainfn(lua_State
*L
, int funcIndex
) { /* (-1, 0, -) */
176 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
177 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua
178 * function if it uses any global names
181 const char* first_upvalue_name
= lua_getupvalue(L
, funcIndex
, 1);
182 if (NULL
== first_upvalue_name
) return; /* doesn't have any upvalues */
183 lua_pop(L
, 1); /* only need the name of the upvalue, not the value */
185 if (0 != strcmp(first_upvalue_name
, "_ENV")) return;
187 if (NULL
== lua_setupvalue(L
, funcIndex
, 1)) {
188 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */
192 lua_setfenv(L
, funcIndex
);
196 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
197 /* lua 5.2 already supports __pairs */
199 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details.
200 * Override the default pairs() function to allow us to use a __pairs metakey
202 static int magnet_pairs(lua_State
*L
) {
203 luaL_checkany(L
, 1); /* "self" */
205 if (luaL_getmetafield(L
, 1, "__pairs")) {
206 /* call __pairs(self) */
210 /* call <original-pairs-method>(self) */
211 lua_pushvalue(L
, lua_upvalueindex(1));
219 /* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */
220 static int magnet_array_next(lua_State
*L
) {
225 size_t pos
= lua_tointeger(L
, lua_upvalueindex(1));
226 array
*a
= lua_touserdata(L
, lua_upvalueindex(2));
230 if (pos
>= a
->used
) return 0;
231 if (NULL
!= (du
= a
->data
[pos
])) {
232 lua_pushlstring(L
, CONST_BUF_LEN(du
->key
));
235 ds
= (data_string
*)du
;
236 if (!buffer_is_empty(ds
->value
)) {
237 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
244 di
= (data_integer
*)du
;
245 lua_pushinteger(L
, di
->value
);
252 /* Update our positional upval to reflect our new current position */
254 lua_pushinteger(L
, pos
);
255 lua_replace(L
, lua_upvalueindex(1));
257 /* Returning 2 items on the stack (key, value) */
263 /* Create the closure necessary to iterate over the array *a with the above function */
264 static int magnet_array_pairs(lua_State
*L
, array
*a
) {
265 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
266 lua_pushlightuserdata(L
, a
); /* Push our array *a into upval 2 */
267 lua_pushcclosure(L
, magnet_array_next
, 2); /* Push our new closure with 2 upvals */
271 static server
* magnet_get_server(lua_State
*L
) {
274 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
275 srv
= lua_touserdata(L
, -1);
281 static connection
* magnet_get_connection(lua_State
*L
) {
284 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
285 con
= lua_touserdata(L
, -1);
296 static const_buffer
magnet_checkconstbuffer(lua_State
*L
, int index
) {
298 cb
.ptr
= luaL_checklstring(L
, index
, &cb
.len
);
302 static buffer
* magnet_checkbuffer(lua_State
*L
, int index
) {
303 const_buffer cb
= magnet_checkconstbuffer(L
, index
);
304 buffer
*b
= buffer_init();
305 buffer_copy_string_len(b
, cb
.ptr
, cb
.len
);
309 static int magnet_print(lua_State
*L
) {
310 buffer
*b
= magnet_checkbuffer(L
, 1);
312 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
321 static int magnet_stat(lua_State
*L
) {
322 buffer
*sb
= magnet_checkbuffer(L
, 1);
323 server
*srv
= magnet_get_server(L
);
324 connection
*con
= magnet_get_connection(L
);
325 stat_cache_entry
*sce
= NULL
;
328 res
= stat_cache_get_entry(srv
, con
, sb
, &sce
);
331 if (HANDLER_GO_ON
!= res
) {
336 lua_newtable(L
); // return value
338 lua_pushboolean(L
, S_ISREG(sce
->st
.st_mode
));
339 lua_setfield(L
, -2, "is_file");
341 lua_pushboolean(L
, S_ISDIR(sce
->st
.st_mode
));
342 lua_setfield(L
, -2, "is_dir");
344 lua_pushboolean(L
, S_ISCHR(sce
->st
.st_mode
));
345 lua_setfield(L
, -2, "is_char");
347 lua_pushboolean(L
, S_ISBLK(sce
->st
.st_mode
));
348 lua_setfield(L
, -2, "is_block");
350 lua_pushboolean(L
, S_ISSOCK(sce
->st
.st_mode
));
351 lua_setfield(L
, -2, "is_socket");
353 lua_pushboolean(L
, S_ISLNK(sce
->st
.st_mode
));
354 lua_setfield(L
, -2, "is_link");
356 lua_pushboolean(L
, S_ISFIFO(sce
->st
.st_mode
));
357 lua_setfield(L
, -2, "is_fifo");
359 lua_pushinteger(L
, sce
->st
.st_mtime
);
360 lua_setfield(L
, -2, "st_mtime");
362 lua_pushinteger(L
, sce
->st
.st_ctime
);
363 lua_setfield(L
, -2, "st_ctime");
365 lua_pushinteger(L
, sce
->st
.st_atime
);
366 lua_setfield(L
, -2, "st_atime");
368 lua_pushinteger(L
, sce
->st
.st_uid
);
369 lua_setfield(L
, -2, "st_uid");
371 lua_pushinteger(L
, sce
->st
.st_gid
);
372 lua_setfield(L
, -2, "st_gid");
374 lua_pushinteger(L
, sce
->st
.st_size
);
375 lua_setfield(L
, -2, "st_size");
377 lua_pushinteger(L
, sce
->st
.st_ino
);
378 lua_setfield(L
, -2, "st_ino");
380 if (!buffer_string_is_empty(sce
->etag
)) {
381 /* we have to mutate the etag */
382 buffer
*b
= buffer_init();
383 etag_mutate(b
, sce
->etag
);
385 lua_pushlstring(L
, CONST_BUF_LEN(b
));
390 lua_setfield(L
, -2, "etag");
392 if (!buffer_string_is_empty(sce
->content_type
)) {
393 lua_pushlstring(L
, CONST_BUF_LEN(sce
->content_type
));
397 lua_setfield(L
, -2, "content-type");
403 static int magnet_atpanic(lua_State
*L
) {
404 buffer
*b
= magnet_checkbuffer(L
, 1);
406 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
412 longjmp(exceptionjmp
, 1);
415 static int magnet_reqhdr_get(lua_State
*L
) {
416 connection
*con
= magnet_get_connection(L
);
419 /* __index: param 1 is the (empty) table the value was not found in */
420 const char *key
= luaL_checkstring(L
, 2);
422 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, key
))) {
423 if (!buffer_is_empty(ds
->value
)) {
424 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
434 static int magnet_reqhdr_pairs(lua_State
*L
) {
435 connection
*con
= magnet_get_connection(L
);
437 return magnet_array_pairs(L
, con
->request
.headers
);
440 static int magnet_status_get(lua_State
*L
) {
442 server
*srv
= magnet_get_server(L
);
444 /* __index: param 1 is the (empty) table the value was not found in */
445 const_buffer key
= magnet_checkconstbuffer(L
, 2);
447 di
= status_counter_get_counter(srv
, key
.ptr
, key
.len
);
449 lua_pushinteger(L
, (lua_Integer
)di
->value
);
454 static int magnet_status_set(lua_State
*L
) {
455 server
*srv
= magnet_get_server(L
);
457 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
458 const_buffer key
= magnet_checkconstbuffer(L
, 2);
459 int counter
= (int) luaL_checkinteger(L
, 3);
461 status_counter_set(srv
, key
.ptr
, key
.len
, counter
);
466 static int magnet_status_pairs(lua_State
*L
) {
467 server
*srv
= magnet_get_server(L
);
469 return magnet_array_pairs(L
, srv
->status
);
477 MAGNET_ENV_PHYICAL_PATH
,
478 MAGNET_ENV_PHYICAL_REL_PATH
,
479 MAGNET_ENV_PHYICAL_DOC_ROOT
,
480 MAGNET_ENV_PHYICAL_BASEDIR
,
483 MAGNET_ENV_URI_PATH_RAW
,
484 MAGNET_ENV_URI_SCHEME
,
485 MAGNET_ENV_URI_AUTHORITY
,
486 MAGNET_ENV_URI_QUERY
,
488 MAGNET_ENV_REQUEST_METHOD
,
489 MAGNET_ENV_REQUEST_URI
,
490 MAGNET_ENV_REQUEST_ORIG_URI
,
491 MAGNET_ENV_REQUEST_PATH_INFO
,
492 MAGNET_ENV_REQUEST_REMOTE_IP
,
493 MAGNET_ENV_REQUEST_PROTOCOL
497 static const magnet_env_t magnet_env
[] = {
498 { "physical.path", MAGNET_ENV_PHYICAL_PATH
},
499 { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH
},
500 { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT
},
501 { "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR
},
503 { "uri.path", MAGNET_ENV_URI_PATH
},
504 { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW
},
505 { "uri.scheme", MAGNET_ENV_URI_SCHEME
},
506 { "uri.authority", MAGNET_ENV_URI_AUTHORITY
},
507 { "uri.query", MAGNET_ENV_URI_QUERY
},
509 { "request.method", MAGNET_ENV_REQUEST_METHOD
},
510 { "request.uri", MAGNET_ENV_REQUEST_URI
},
511 { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI
},
512 { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO
},
513 { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP
},
514 { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL
},
516 { NULL
, MAGNET_ENV_UNSET
}
519 static buffer
*magnet_env_get_buffer_by_id(server
*srv
, connection
*con
, int id
) {
525 * map all internal variables to lua
530 case MAGNET_ENV_PHYICAL_PATH
: dest
= con
->physical
.path
; break;
531 case MAGNET_ENV_PHYICAL_REL_PATH
: dest
= con
->physical
.rel_path
; break;
532 case MAGNET_ENV_PHYICAL_DOC_ROOT
: dest
= con
->physical
.doc_root
; break;
533 case MAGNET_ENV_PHYICAL_BASEDIR
: dest
= con
->physical
.basedir
; break;
535 case MAGNET_ENV_URI_PATH
: dest
= con
->uri
.path
; break;
536 case MAGNET_ENV_URI_PATH_RAW
: dest
= con
->uri
.path_raw
; break;
537 case MAGNET_ENV_URI_SCHEME
: dest
= con
->uri
.scheme
; break;
538 case MAGNET_ENV_URI_AUTHORITY
: dest
= con
->uri
.authority
; break;
539 case MAGNET_ENV_URI_QUERY
: dest
= con
->uri
.query
; break;
541 case MAGNET_ENV_REQUEST_METHOD
:
542 buffer_copy_string(srv
->tmp_buf
, get_http_method_name(con
->request
.http_method
));
545 case MAGNET_ENV_REQUEST_URI
: dest
= con
->request
.uri
; break;
546 case MAGNET_ENV_REQUEST_ORIG_URI
: dest
= con
->request
.orig_uri
; break;
547 case MAGNET_ENV_REQUEST_PATH_INFO
: dest
= con
->request
.pathinfo
; break;
548 case MAGNET_ENV_REQUEST_REMOTE_IP
: dest
= con
->dst_addr_buf
; break;
549 case MAGNET_ENV_REQUEST_PROTOCOL
:
550 buffer_copy_string(srv
->tmp_buf
, get_http_version_name(con
->request
.http_version
));
554 case MAGNET_ENV_UNSET
: break;
560 static buffer
*magnet_env_get_buffer(server
*srv
, connection
*con
, const char *key
) {
563 for (i
= 0; magnet_env
[i
].name
; i
++) {
564 if (0 == strcmp(key
, magnet_env
[i
].name
)) break;
567 return magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[i
].type
);
570 static int magnet_env_get(lua_State
*L
) {
571 server
*srv
= magnet_get_server(L
);
572 connection
*con
= magnet_get_connection(L
);
574 /* __index: param 1 is the (empty) table the value was not found in */
575 const char *key
= luaL_checkstring(L
, 2);
578 dest
= magnet_env_get_buffer(srv
, con
, key
);
580 if (!buffer_is_empty(dest
)) {
581 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
589 static int magnet_env_set(lua_State
*L
) {
590 server
*srv
= magnet_get_server(L
);
591 connection
*con
= magnet_get_connection(L
);
593 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
594 const char *key
= luaL_checkstring(L
, 2);
597 luaL_checkany(L
, 3); /* nil or a string */
599 if (NULL
!= (dest
= magnet_env_get_buffer(srv
, con
, key
))) {
600 if (lua_isnil(L
, 3)) {
603 const_buffer val
= magnet_checkconstbuffer(L
, 3);
604 buffer_copy_string_len(dest
, val
.ptr
, val
.len
);
609 return luaL_error(L
, "couldn't store '%s' in lighty.env[]", key
);
615 static int magnet_env_next(lua_State
*L
) {
616 server
*srv
= magnet_get_server(L
);
617 connection
*con
= magnet_get_connection(L
);
618 const int pos
= lua_tointeger(L
, lua_upvalueindex(1));
622 /* ignore previous key: use upvalue for current pos */
625 if (NULL
== magnet_env
[pos
].name
) return 0; /* end of list */
626 /* Update our positional upval to reflect our new current position */
627 lua_pushinteger(L
, pos
+ 1);
628 lua_replace(L
, lua_upvalueindex(1));
631 lua_pushstring(L
, magnet_env
[pos
].name
);
634 dest
= magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[pos
].type
);
635 if (!buffer_is_empty(dest
)) {
636 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
641 /* return 2 items on the stack (key, value) */
645 static int magnet_env_pairs(lua_State
*L
) {
646 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
647 lua_pushcclosure(L
, magnet_env_next
, 1); /* Push our new closure with 1 upvals */
651 static int magnet_cgi_get(lua_State
*L
) {
652 connection
*con
= magnet_get_connection(L
);
655 /* __index: param 1 is the (empty) table the value was not found in */
656 const char *key
= luaL_checkstring(L
, 2);
658 ds
= (data_string
*)array_get_element(con
->environment
, key
);
659 if (NULL
!= ds
&& !buffer_is_empty(ds
->value
))
660 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
667 static int magnet_cgi_set(lua_State
*L
) {
668 connection
*con
= magnet_get_connection(L
);
670 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
671 const_buffer key
= magnet_checkconstbuffer(L
, 2);
672 const_buffer val
= magnet_checkconstbuffer(L
, 2);
674 array_set_key_value(con
->environment
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
679 static int magnet_cgi_pairs(lua_State
*L
) {
680 connection
*con
= magnet_get_connection(L
);
682 return magnet_array_pairs(L
, con
->environment
);
686 static int magnet_copy_response_header(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
687 force_assert(lua_istable(L
, lighty_table_ndx
));
689 lua_getfield(L
, lighty_table_ndx
, "header"); /* lighty.header */
690 if (lua_istable(L
, -1)) {
691 /* header is found, and is a table */
694 while (lua_next(L
, -2) != 0) {
695 if (lua_isstring(L
, -1) && lua_isstring(L
, -2)) {
696 const_buffer key
= magnet_checkconstbuffer(L
, -2);
697 const_buffer val
= magnet_checkconstbuffer(L
, -1);
699 response_header_overwrite(srv
, con
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
705 lua_pop(L
, 1); /* pop lighty.header */
711 * walk through the content array
713 * content = { "<pre>", { file = "/content" } , "</pre>" }
715 * header["Content-Type"] = "text/html"
719 static int magnet_attach_content(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
720 force_assert(lua_istable(L
, lighty_table_ndx
));
722 lua_getfield(L
, lighty_table_ndx
, "content"); /* lighty.content */
723 if (lua_istable(L
, -1)) {
725 /* content is found, and is a table */
728 lua_rawgeti(L
, -1, i
);
730 /* -1 is the value and should be the value ... aka a table */
731 if (lua_isstring(L
, -1)) {
732 const_buffer data
= magnet_checkconstbuffer(L
, -1);
734 chunkqueue_append_mem(con
->write_queue
, data
.ptr
, data
.len
);
735 } else if (lua_istable(L
, -1)) {
736 lua_getfield(L
, -1, "filename");
737 lua_getfield(L
, -2, "length"); /* (0-based) end of range (not actually "length") */
738 lua_getfield(L
, -3, "offset"); /* (0-based) start of range */
740 if (lua_isstring(L
, -3)) { /* filename has to be a string */
741 buffer
*fn
= magnet_checkbuffer(L
, -3);
742 off_t off
= (off_t
) luaL_optinteger(L
, -1, 0);
743 off_t len
= (off_t
) luaL_optinteger(L
, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/
746 return luaL_error(L
, "offset for '%s' is negative", lua_tostring(L
, -3));
751 } else if (-1 != len
) {
753 return luaL_error(L
, "offset > length for '%s'", lua_tostring(L
, -3));
756 if (0 != len
&& 0 != http_chunk_append_file_range(srv
, con
, fn
, off
, len
)) {
758 return luaL_error(L
, "error opening file content '%s' at offset %lld", lua_tostring(L
, -3), (long long)off
);
763 return luaL_error(L
, "content[%d] is a table and requires the field \"filename\"", i
);
767 } else if (lua_isnil(L
, -1)) {
774 return luaL_error(L
, "content[%d] is neither a string nor a table: ", i
);
777 lua_pop(L
, 1); /* pop the content[...] entry value */
780 return luaL_error(L
, "lighty.content has to be a table");
782 lua_pop(L
, 1); /* pop lighty.content */
787 static int traceback(lua_State
*L
) {
788 if (!lua_isstring(L
, 1)) /* 'message' not a string? */
789 return 1; /* keep it intact */
790 lua_getglobal(L
, "debug");
791 if (!lua_istable(L
, -1)) {
795 lua_getfield(L
, -1, "traceback");
796 if (!lua_isfunction(L
, -1)) {
800 lua_pushvalue(L
, 1); /* pass error message */
801 lua_pushinteger(L
, 2); /* skip this function and traceback */
802 lua_call(L
, 2, 1); /* call debug.traceback */
806 /* push traceback function before calling lua_pcall after narg arguments
807 * have been pushed (inserts it before the arguments). returns index for
808 * traceback function ("msgh" in lua_pcall)
810 static int push_traceback(lua_State
*L
, int narg
) {
811 int base
= lua_gettop(L
) - narg
; /* function index */
812 lua_pushcfunction(L
, traceback
);
817 static handler_t
magnet_attract(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*name
) {
819 int lua_return_value
;
820 const int func_ndx
= 1;
821 const int lighty_table_ndx
= 2;
823 /* get the script-context */
824 L
= script_cache_get_script(srv
, con
, p
->cache
, name
);
826 if (lua_isstring(L
, -1)) {
827 log_error_write(srv
, __FILE__
, __LINE__
,
832 lua_tostring(L
, -1));
836 force_assert(lua_gettop(L
) == 0); /* only the error should have been on the stack */
838 con
->http_status
= 500;
841 return HANDLER_FINISHED
;
844 force_assert(lua_gettop(L
) == 1);
845 force_assert(lua_isfunction(L
, func_ndx
));
847 lua_pushlightuserdata(L
, srv
);
848 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
850 lua_pushlightuserdata(L
, con
);
851 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
853 lua_atpanic(L
, magnet_atpanic
);
856 * we want to create empty environment for our script
858 * setmetatable({}, {__index = _G})
860 * if a function symbol is not defined in our env, __index will lookup
863 * all variables created in the script-env will be thrown
864 * away at the end of the script run.
866 lua_newtable(L
); /* my empty environment aka {} (sp += 1) */
868 /* we have to overwrite the print function */
869 lua_pushcfunction(L
, magnet_print
); /* (sp += 1) */
870 lua_setfield(L
, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
873 * lighty.request[] (ro) has the HTTP-request headers
874 * lighty.env[] (rw) has various url/physical file paths and
875 * request meta data; might contain nil values
876 * lighty.req_env[] (ro) has the cgi environment
877 * lighty.status[] (ro) has the status counters
878 * lighty.content[] (rw) is a table of string/file
879 * lighty.header[] (rw) is a array to set response headers
882 lua_newtable(L
); /* lighty.* (sp += 1) */
884 lua_newtable(L
); /* {} (sp += 1) */
885 lua_newtable(L
); /* the meta-table for the request-table (sp += 1) */
886 lua_pushcfunction(L
, magnet_reqhdr_get
); /* (sp += 1) */
887 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
888 lua_pushcfunction(L
, magnet_reqhdr_pairs
); /* (sp += 1) */
889 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
890 lua_setmetatable(L
, -2); /* tie the metatable to request (sp -= 1) */
891 lua_setfield(L
, -2, "request"); /* content = {} (sp -= 1) */
893 lua_newtable(L
); /* {} (sp += 1) */
894 lua_newtable(L
); /* the meta-table for the env-table (sp += 1) */
895 lua_pushcfunction(L
, magnet_env_get
); /* (sp += 1) */
896 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
897 lua_pushcfunction(L
, magnet_env_set
); /* (sp += 1) */
898 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
899 lua_pushcfunction(L
, magnet_env_pairs
); /* (sp += 1) */
900 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
901 lua_setmetatable(L
, -2); /* tie the metatable to env (sp -= 1) */
902 lua_setfield(L
, -2, "env"); /* content = {} (sp -= 1) */
904 lua_newtable(L
); /* {} (sp += 1) */
905 lua_newtable(L
); /* the meta-table for the req_env-table (sp += 1) */
906 lua_pushcfunction(L
, magnet_cgi_get
); /* (sp += 1) */
907 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
908 lua_pushcfunction(L
, magnet_cgi_set
); /* (sp += 1) */
909 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
910 lua_pushcfunction(L
, magnet_cgi_pairs
); /* (sp += 1) */
911 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
912 lua_setmetatable(L
, -2); /* tie the metatable to req_env (sp -= 1) */
913 lua_setfield(L
, -2, "req_env"); /* content = {} (sp -= 1) */
915 lua_newtable(L
); /* {} (sp += 1) */
916 lua_newtable(L
); /* the meta-table for the status-table (sp += 1) */
917 lua_pushcfunction(L
, magnet_status_get
); /* (sp += 1) */
918 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
919 lua_pushcfunction(L
, magnet_status_set
); /* (sp += 1) */
920 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
921 lua_pushcfunction(L
, magnet_status_pairs
); /* (sp += 1) */
922 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
923 lua_setmetatable(L
, -2); /* tie the metatable to statzs (sp -= 1) */
924 lua_setfield(L
, -2, "status"); /* content = {} (sp -= 1) */
926 /* add empty 'content' and 'header' tables */
927 lua_newtable(L
); /* {} (sp += 1) */
928 lua_setfield(L
, -2, "content"); /* content = {} (sp -= 1) */
930 lua_newtable(L
); /* {} (sp += 1) */
931 lua_setfield(L
, -2, "header"); /* header = {} (sp -= 1) */
933 lua_pushinteger(L
, MAGNET_RESTART_REQUEST
);
934 lua_setfield(L
, -2, "RESTART_REQUEST");
936 lua_pushcfunction(L
, magnet_stat
); /* (sp += 1) */
937 lua_setfield(L
, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
939 /* insert lighty table at index 2 */
940 lua_pushvalue(L
, -1);
941 lua_insert(L
, lighty_table_ndx
);
943 lua_setfield(L
, -2, "lighty"); /* lighty.* (sp -= 1) */
945 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
946 /* override the default pairs() function to our __pairs capable version;
947 * not needed for lua 5.2+
949 lua_getglobal(L
, "pairs"); /* push original pairs() (sp += 1) */
950 lua_pushcclosure(L
, magnet_pairs
, 1);
951 lua_setfield(L
, -2, "pairs"); /* (sp -= 1) */
954 lua_newtable(L
); /* the meta-table for the new env (sp += 1) */
955 lua_pushglobaltable(L
); /* (sp += 1) */
956 lua_setfield(L
, -2, "__index"); /* { __index = _G } (sp -= 1) */
957 lua_setmetatable(L
, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
959 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
961 /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */
962 lua_pushvalue(L
, func_ndx
);
964 int errfunc
= push_traceback(L
, 0);
965 int ret
= lua_pcall(L
, 0, 1, errfunc
);
966 lua_remove(L
, errfunc
);
968 /* reset environment */
969 lua_pushglobaltable(L
); /* (sp += 1) */
970 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
973 log_error_write(srv
, __FILE__
, __LINE__
,
976 lua_tostring(L
, -1));
977 lua_pop(L
, 2); /* remove the error-msg and the lighty table at index 2 */
979 force_assert(lua_gettop(L
) == 1); /* only the function should be on the stack */
981 con
->http_status
= 500;
984 return HANDLER_FINISHED
;
988 /* we should have the function, the lighty table and the return value on the stack */
989 force_assert(lua_gettop(L
) == 3);
991 lua_return_value
= (int) luaL_optinteger(L
, -1, -1);
992 lua_pop(L
, 1); /* pop return value */
994 magnet_copy_response_header(srv
, con
, L
, lighty_table_ndx
);
997 handler_t result
= HANDLER_GO_ON
;
999 if (lua_return_value
> 99) {
1000 con
->http_status
= lua_return_value
;
1001 con
->file_finished
= 1;
1004 if (0 == setjmp(exceptionjmp
)) {
1005 magnet_attach_content(srv
, con
, L
, lighty_table_ndx
);
1006 if (!chunkqueue_is_empty(con
->write_queue
)) {
1010 lua_settop(L
, 2); /* remove all but function and lighty table */
1012 con
->http_status
= 500;
1016 result
= HANDLER_FINISHED
;
1017 } else if (MAGNET_RESTART_REQUEST
== lua_return_value
) {
1018 if (!buffer_is_equal(con
->request
.uri
, con
->request
.orig_uri
)
1019 && !array_get_element(con
->environment
, "REDIRECT_URI")) {
1020 array_set_key_value(con
->environment
,
1021 CONST_STR_LEN("REDIRECT_URI"),
1022 CONST_BUF_LEN(con
->request
.orig_uri
));
1024 result
= HANDLER_COMEBACK
;
1027 lua_pop(L
, 1); /* pop the lighty table */
1028 force_assert(lua_gettop(L
) == 1); /* only the function should remain on the stack */
1034 static handler_t
magnet_attract_array(server
*srv
, connection
*con
, plugin_data
*p
, array
*files
) {
1036 handler_t ret
= HANDLER_GO_ON
;
1038 /* no filename set */
1039 if (files
->used
== 0) return HANDLER_GO_ON
;
1042 * execute all files and jump out on the first !HANDLER_GO_ON
1044 for (i
= 0; i
< files
->used
&& ret
== HANDLER_GO_ON
; i
++) {
1045 data_string
*ds
= (data_string
*)files
->data
[i
];
1047 if (buffer_string_is_empty(ds
->value
)) continue;
1049 ret
= magnet_attract(srv
, con
, p
, ds
->value
);
1052 if (con
->error_handler_saved_status
) {
1053 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */
1055 data_string
* const ds
= (data_string
*)array_get_element(con
->environment
, "REDIRECT_STATUS");
1056 if (ds
&& (x
= strtoul(ds
->value
->ptr
, NULL
, 10)) < 1000)
1057 /*(simplified validity check x < 1000)*/
1058 con
->error_handler_saved_status
=
1059 con
->error_handler_saved_status
> 0 ? (int)x
: -(int)x
;
1065 URIHANDLER_FUNC(mod_magnet_uri_handler
) {
1066 plugin_data
*p
= p_d
;
1068 mod_magnet_patch_connection(srv
, con
, p
);
1070 return magnet_attract_array(srv
, con
, p
, p
->conf
.url_raw
);
1073 URIHANDLER_FUNC(mod_magnet_physical
) {
1074 plugin_data
*p
= p_d
;
1076 mod_magnet_patch_connection(srv
, con
, p
);
1078 return magnet_attract_array(srv
, con
, p
, p
->conf
.physical_path
);
1082 /* this function is called at dlopen() time and inits the callbacks */
1084 int mod_magnet_plugin_init(plugin
*p
);
1085 int mod_magnet_plugin_init(plugin
*p
) {
1086 p
->version
= LIGHTTPD_VERSION_ID
;
1087 p
->name
= buffer_init_string("magnet");
1089 p
->init
= mod_magnet_init
;
1090 p
->handle_uri_clean
= mod_magnet_uri_handler
;
1091 p
->handle_physical
= mod_magnet_physical
;
1092 p
->set_defaults
= mod_magnet_set_defaults
;
1093 p
->cleanup
= mod_magnet_free
;
1102 #pragma message("lua is required, but was not found")
1104 int mod_magnet_plugin_init(plugin
*p
);
1105 int mod_magnet_plugin_init(plugin
*p
) {