6 #include "http_chunk.h"
10 #include "mod_magnet_cache.h"
12 #include "stat_cache.h"
13 #include "status_counter.h"
25 #define LUA_RIDX_LIGHTTPD_SERVER "lighty.srv"
26 #define LUA_RIDX_LIGHTTPD_CONNECTION "lighty.con"
28 #define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to"
29 #define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to"
30 #define MAGNET_RESTART_REQUEST 99
32 /* plugin config for all request/connections */
34 static jmp_buf exceptionjmp
;
48 plugin_config
**config_storage
;
53 /* init the plugin data */
54 INIT_FUNC(mod_magnet_init
) {
57 p
= calloc(1, sizeof(*p
));
59 p
->cache
= script_cache_init();
60 p
->encode_buf
= buffer_init();
65 /* detroy the plugin data */
66 FREE_FUNC(mod_magnet_free
) {
71 if (!p
) return HANDLER_GO_ON
;
73 if (p
->config_storage
) {
76 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
77 plugin_config
*s
= p
->config_storage
[i
];
79 if (NULL
== s
) continue;
81 array_free(s
->url_raw
);
82 array_free(s
->physical_path
);
86 free(p
->config_storage
);
89 script_cache_free(p
->cache
);
90 buffer_free(p
->encode_buf
);
97 /* handle plugin config and check values */
99 SETDEFAULTS_FUNC(mod_magnet_set_defaults
) {
100 plugin_data
*p
= p_d
;
103 config_values_t cv
[] = {
104 { MAGNET_CONFIG_RAW_URL
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
105 { MAGNET_CONFIG_PHYSICAL_PATH
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
106 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
109 if (!p
) return HANDLER_ERROR
;
111 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
113 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
114 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
117 s
= calloc(1, sizeof(plugin_config
));
118 s
->url_raw
= array_init();
119 s
->physical_path
= array_init();
121 cv
[0].destination
= s
->url_raw
;
122 cv
[1].destination
= s
->physical_path
;
124 p
->config_storage
[i
] = s
;
126 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
127 return HANDLER_ERROR
;
131 return HANDLER_GO_ON
;
136 static int mod_magnet_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
138 plugin_config
*s
= p
->config_storage
[0];
141 PATCH(physical_path
);
143 /* skip the first, the global context */
144 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
145 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
146 s
= p
->config_storage
[i
];
148 /* condition didn't match */
149 if (!config_check_cond(srv
, con
, dc
)) continue;
152 for (j
= 0; j
< dc
->value
->used
; j
++) {
153 data_unset
*du
= dc
->value
->data
[j
];
155 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL
))) {
157 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH
))) {
158 PATCH(physical_path
);
167 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
168 /* lua5.1 backward compat definition */
169 static void lua_pushglobaltable(lua_State
*L
) { /* (-0, +1, -) */
170 lua_pushvalue(L
, LUA_GLOBALSINDEX
);
174 static void magnet_setfenv_mainfn(lua_State
*L
, int funcIndex
) { /* (-1, 0, -) */
175 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
176 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua
177 * function if it uses any global names
180 const char* first_upvalue_name
= lua_getupvalue(L
, funcIndex
, 1);
181 if (NULL
== first_upvalue_name
) return; /* doesn't have any upvalues */
182 lua_pop(L
, 1); /* only need the name of the upvalue, not the value */
184 if (0 != strcmp(first_upvalue_name
, "_ENV")) return;
186 if (NULL
== lua_setupvalue(L
, funcIndex
, 1)) {
187 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */
191 lua_setfenv(L
, funcIndex
);
195 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
196 /* lua 5.2 already supports __pairs */
198 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details.
199 * Override the default pairs() function to allow us to use a __pairs metakey
201 static int magnet_pairs(lua_State
*L
) {
202 luaL_checkany(L
, 1); /* "self" */
204 if (luaL_getmetafield(L
, 1, "__pairs")) {
205 /* call __pairs(self) */
209 /* call <original-pairs-method>(self) */
210 lua_pushvalue(L
, lua_upvalueindex(1));
218 /* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */
219 static int magnet_array_next(lua_State
*L
) {
224 size_t pos
= lua_tointeger(L
, lua_upvalueindex(1));
225 array
*a
= lua_touserdata(L
, lua_upvalueindex(2));
229 if (pos
>= a
->used
) return 0;
230 if (NULL
!= (du
= a
->data
[pos
])) {
231 lua_pushlstring(L
, CONST_BUF_LEN(du
->key
));
234 ds
= (data_string
*)du
;
235 if (!buffer_is_empty(ds
->value
)) {
236 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
242 di
= (data_integer
*)du
;
243 lua_pushinteger(L
, di
->value
);
250 /* Update our positional upval to reflect our new current position */
252 lua_pushinteger(L
, pos
);
253 lua_replace(L
, lua_upvalueindex(1));
255 /* Returning 2 items on the stack (key, value) */
261 /* Create the closure necessary to iterate over the array *a with the above function */
262 static int magnet_array_pairs(lua_State
*L
, array
*a
) {
263 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
264 lua_pushlightuserdata(L
, a
); /* Push our array *a into upval 2 */
265 lua_pushcclosure(L
, magnet_array_next
, 2); /* Push our new closure with 2 upvals */
269 static server
* magnet_get_server(lua_State
*L
) {
272 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
273 srv
= lua_touserdata(L
, -1);
279 static connection
* magnet_get_connection(lua_State
*L
) {
282 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
283 con
= lua_touserdata(L
, -1);
294 static const_buffer
magnet_checkconstbuffer(lua_State
*L
, int index
) {
296 cb
.ptr
= luaL_checklstring(L
, index
, &cb
.len
);
300 static buffer
* magnet_checkbuffer(lua_State
*L
, int index
) {
301 const_buffer cb
= magnet_checkconstbuffer(L
, index
);
302 buffer
*b
= buffer_init();
303 buffer_copy_string_len(b
, cb
.ptr
, cb
.len
);
307 static int magnet_print(lua_State
*L
) {
308 buffer
*b
= magnet_checkbuffer(L
, 1);
310 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
319 static int magnet_stat(lua_State
*L
) {
320 buffer
*sb
= magnet_checkbuffer(L
, 1);
321 server
*srv
= magnet_get_server(L
);
322 connection
*con
= magnet_get_connection(L
);
323 stat_cache_entry
*sce
= NULL
;
326 res
= stat_cache_get_entry(srv
, con
, sb
, &sce
);
329 if (HANDLER_GO_ON
!= res
) {
334 lua_newtable(L
); // return value
336 lua_pushboolean(L
, S_ISREG(sce
->st
.st_mode
));
337 lua_setfield(L
, -2, "is_file");
339 lua_pushboolean(L
, S_ISDIR(sce
->st
.st_mode
));
340 lua_setfield(L
, -2, "is_dir");
342 lua_pushboolean(L
, S_ISCHR(sce
->st
.st_mode
));
343 lua_setfield(L
, -2, "is_char");
345 lua_pushboolean(L
, S_ISBLK(sce
->st
.st_mode
));
346 lua_setfield(L
, -2, "is_block");
348 lua_pushboolean(L
, S_ISSOCK(sce
->st
.st_mode
));
349 lua_setfield(L
, -2, "is_socket");
351 lua_pushboolean(L
, S_ISLNK(sce
->st
.st_mode
));
352 lua_setfield(L
, -2, "is_link");
354 lua_pushboolean(L
, S_ISFIFO(sce
->st
.st_mode
));
355 lua_setfield(L
, -2, "is_fifo");
357 lua_pushinteger(L
, sce
->st
.st_mtime
);
358 lua_setfield(L
, -2, "st_mtime");
360 lua_pushinteger(L
, sce
->st
.st_ctime
);
361 lua_setfield(L
, -2, "st_ctime");
363 lua_pushinteger(L
, sce
->st
.st_atime
);
364 lua_setfield(L
, -2, "st_atime");
366 lua_pushinteger(L
, sce
->st
.st_uid
);
367 lua_setfield(L
, -2, "st_uid");
369 lua_pushinteger(L
, sce
->st
.st_gid
);
370 lua_setfield(L
, -2, "st_gid");
372 lua_pushinteger(L
, sce
->st
.st_size
);
373 lua_setfield(L
, -2, "st_size");
375 lua_pushinteger(L
, sce
->st
.st_ino
);
376 lua_setfield(L
, -2, "st_ino");
378 if (!buffer_string_is_empty(sce
->etag
)) {
379 /* we have to mutate the etag */
380 buffer
*b
= buffer_init();
381 etag_mutate(b
, sce
->etag
);
383 lua_pushlstring(L
, CONST_BUF_LEN(b
));
388 lua_setfield(L
, -2, "etag");
390 if (!buffer_string_is_empty(sce
->content_type
)) {
391 lua_pushlstring(L
, CONST_BUF_LEN(sce
->content_type
));
395 lua_setfield(L
, -2, "content-type");
401 static int magnet_atpanic(lua_State
*L
) {
402 buffer
*b
= magnet_checkbuffer(L
, 1);
404 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
410 longjmp(exceptionjmp
, 1);
413 static int magnet_reqhdr_get(lua_State
*L
) {
414 connection
*con
= magnet_get_connection(L
);
417 /* __index: param 1 is the (empty) table the value was not found in */
418 const char *key
= luaL_checkstring(L
, 2);
420 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, key
))) {
421 if (!buffer_is_empty(ds
->value
)) {
422 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
432 static int magnet_reqhdr_pairs(lua_State
*L
) {
433 connection
*con
= magnet_get_connection(L
);
435 return magnet_array_pairs(L
, con
->request
.headers
);
438 static int magnet_status_get(lua_State
*L
) {
440 server
*srv
= magnet_get_server(L
);
442 /* __index: param 1 is the (empty) table the value was not found in */
443 const_buffer key
= magnet_checkconstbuffer(L
, 2);
445 di
= status_counter_get_counter(srv
, key
.ptr
, key
.len
);
447 lua_pushinteger(L
, (lua_Integer
)di
->value
);
452 static int magnet_status_set(lua_State
*L
) {
453 server
*srv
= magnet_get_server(L
);
455 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
456 const_buffer key
= magnet_checkconstbuffer(L
, 2);
457 int counter
= (int) luaL_checkinteger(L
, 3);
459 status_counter_set(srv
, key
.ptr
, key
.len
, counter
);
464 static int magnet_status_pairs(lua_State
*L
) {
465 server
*srv
= magnet_get_server(L
);
467 return magnet_array_pairs(L
, srv
->status
);
475 MAGNET_ENV_PHYICAL_PATH
,
476 MAGNET_ENV_PHYICAL_REL_PATH
,
477 MAGNET_ENV_PHYICAL_DOC_ROOT
,
478 MAGNET_ENV_PHYICAL_BASEDIR
,
481 MAGNET_ENV_URI_PATH_RAW
,
482 MAGNET_ENV_URI_SCHEME
,
483 MAGNET_ENV_URI_AUTHORITY
,
484 MAGNET_ENV_URI_QUERY
,
486 MAGNET_ENV_REQUEST_METHOD
,
487 MAGNET_ENV_REQUEST_URI
,
488 MAGNET_ENV_REQUEST_ORIG_URI
,
489 MAGNET_ENV_REQUEST_PATH_INFO
,
490 MAGNET_ENV_REQUEST_REMOTE_IP
,
491 MAGNET_ENV_REQUEST_PROTOCOL
495 static const magnet_env_t magnet_env
[] = {
496 { "physical.path", MAGNET_ENV_PHYICAL_PATH
},
497 { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH
},
498 { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT
},
499 { "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR
},
501 { "uri.path", MAGNET_ENV_URI_PATH
},
502 { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW
},
503 { "uri.scheme", MAGNET_ENV_URI_SCHEME
},
504 { "uri.authority", MAGNET_ENV_URI_AUTHORITY
},
505 { "uri.query", MAGNET_ENV_URI_QUERY
},
507 { "request.method", MAGNET_ENV_REQUEST_METHOD
},
508 { "request.uri", MAGNET_ENV_REQUEST_URI
},
509 { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI
},
510 { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO
},
511 { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP
},
512 { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL
},
514 { NULL
, MAGNET_ENV_UNSET
}
517 static buffer
*magnet_env_get_buffer_by_id(server
*srv
, connection
*con
, int id
) {
523 * map all internal variables to lua
528 case MAGNET_ENV_PHYICAL_PATH
: dest
= con
->physical
.path
; break;
529 case MAGNET_ENV_PHYICAL_REL_PATH
: dest
= con
->physical
.rel_path
; break;
530 case MAGNET_ENV_PHYICAL_DOC_ROOT
: dest
= con
->physical
.doc_root
; break;
531 case MAGNET_ENV_PHYICAL_BASEDIR
: dest
= con
->physical
.basedir
; break;
533 case MAGNET_ENV_URI_PATH
: dest
= con
->uri
.path
; break;
534 case MAGNET_ENV_URI_PATH_RAW
: dest
= con
->uri
.path_raw
; break;
535 case MAGNET_ENV_URI_SCHEME
: dest
= con
->uri
.scheme
; break;
536 case MAGNET_ENV_URI_AUTHORITY
: dest
= con
->uri
.authority
; break;
537 case MAGNET_ENV_URI_QUERY
: dest
= con
->uri
.query
; break;
539 case MAGNET_ENV_REQUEST_METHOD
:
540 buffer_copy_string(srv
->tmp_buf
, get_http_method_name(con
->request
.http_method
));
543 case MAGNET_ENV_REQUEST_URI
: dest
= con
->request
.uri
; break;
544 case MAGNET_ENV_REQUEST_ORIG_URI
: dest
= con
->request
.orig_uri
; break;
545 case MAGNET_ENV_REQUEST_PATH_INFO
: dest
= con
->request
.pathinfo
; break;
546 case MAGNET_ENV_REQUEST_REMOTE_IP
: dest
= con
->dst_addr_buf
; break;
547 case MAGNET_ENV_REQUEST_PROTOCOL
:
548 buffer_copy_string(srv
->tmp_buf
, get_http_version_name(con
->request
.http_version
));
552 case MAGNET_ENV_UNSET
: break;
558 static buffer
*magnet_env_get_buffer(server
*srv
, connection
*con
, const char *key
) {
561 for (i
= 0; magnet_env
[i
].name
; i
++) {
562 if (0 == strcmp(key
, magnet_env
[i
].name
)) break;
565 return magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[i
].type
);
568 static int magnet_env_get(lua_State
*L
) {
569 server
*srv
= magnet_get_server(L
);
570 connection
*con
= magnet_get_connection(L
);
572 /* __index: param 1 is the (empty) table the value was not found in */
573 const char *key
= luaL_checkstring(L
, 2);
576 dest
= magnet_env_get_buffer(srv
, con
, key
);
578 if (!buffer_is_empty(dest
)) {
579 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
587 static int magnet_env_set(lua_State
*L
) {
588 server
*srv
= magnet_get_server(L
);
589 connection
*con
= magnet_get_connection(L
);
591 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
592 const char *key
= luaL_checkstring(L
, 2);
595 luaL_checkany(L
, 3); /* nil or a string */
597 if (NULL
!= (dest
= magnet_env_get_buffer(srv
, con
, key
))) {
598 if (lua_isnil(L
, 3)) {
601 const_buffer val
= magnet_checkconstbuffer(L
, 3);
602 buffer_copy_string_len(dest
, val
.ptr
, val
.len
);
607 return luaL_error(L
, "couldn't store '%s' in lighty.env[]", key
);
613 static int magnet_env_next(lua_State
*L
) {
614 server
*srv
= magnet_get_server(L
);
615 connection
*con
= magnet_get_connection(L
);
616 const int pos
= lua_tointeger(L
, lua_upvalueindex(1));
620 /* ignore previous key: use upvalue for current pos */
623 if (NULL
== magnet_env
[pos
].name
) return 0; /* end of list */
624 /* Update our positional upval to reflect our new current position */
625 lua_pushinteger(L
, pos
+ 1);
626 lua_replace(L
, lua_upvalueindex(1));
629 lua_pushstring(L
, magnet_env
[pos
].name
);
632 dest
= magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[pos
].type
);
633 if (!buffer_is_empty(dest
)) {
634 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
639 /* return 2 items on the stack (key, value) */
643 static int magnet_env_pairs(lua_State
*L
) {
644 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
645 lua_pushcclosure(L
, magnet_env_next
, 1); /* Push our new closure with 1 upvals */
649 static int magnet_cgi_get(lua_State
*L
) {
650 connection
*con
= magnet_get_connection(L
);
653 /* __index: param 1 is the (empty) table the value was not found in */
654 const char *key
= luaL_checkstring(L
, 2);
656 ds
= (data_string
*)array_get_element(con
->environment
, key
);
657 if (NULL
!= ds
&& !buffer_is_empty(ds
->value
))
658 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
665 static int magnet_cgi_set(lua_State
*L
) {
666 connection
*con
= magnet_get_connection(L
);
668 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
669 const_buffer key
= magnet_checkconstbuffer(L
, 2);
670 const_buffer val
= magnet_checkconstbuffer(L
, 3);
672 array_set_key_value(con
->environment
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
677 static int magnet_cgi_pairs(lua_State
*L
) {
678 connection
*con
= magnet_get_connection(L
);
680 return magnet_array_pairs(L
, con
->environment
);
684 static int magnet_copy_response_header(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
685 force_assert(lua_istable(L
, lighty_table_ndx
));
687 lua_getfield(L
, lighty_table_ndx
, "header"); /* lighty.header */
688 if (lua_istable(L
, -1)) {
689 /* header is found, and is a table */
692 while (lua_next(L
, -2) != 0) {
693 if (lua_isstring(L
, -1) && lua_isstring(L
, -2)) {
694 const_buffer key
= magnet_checkconstbuffer(L
, -2);
695 const_buffer val
= magnet_checkconstbuffer(L
, -1);
697 response_header_overwrite(srv
, con
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
703 lua_pop(L
, 1); /* pop lighty.header */
709 * walk through the content array
711 * content = { "<pre>", { file = "/content" } , "</pre>" }
713 * header["Content-Type"] = "text/html"
717 static int magnet_attach_content(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
718 force_assert(lua_istable(L
, lighty_table_ndx
));
720 lua_getfield(L
, lighty_table_ndx
, "content"); /* lighty.content */
721 if (lua_istable(L
, -1)) {
723 /* content is found, and is a table */
726 lua_rawgeti(L
, -1, i
);
728 /* -1 is the value and should be the value ... aka a table */
729 if (lua_isstring(L
, -1)) {
730 const_buffer data
= magnet_checkconstbuffer(L
, -1);
732 chunkqueue_append_mem(con
->write_queue
, data
.ptr
, data
.len
);
733 } else if (lua_istable(L
, -1)) {
734 lua_getfield(L
, -1, "filename");
735 lua_getfield(L
, -2, "length"); /* (0-based) end of range (not actually "length") */
736 lua_getfield(L
, -3, "offset"); /* (0-based) start of range */
738 if (lua_isstring(L
, -3)) { /* filename has to be a string */
739 buffer
*fn
= magnet_checkbuffer(L
, -3);
740 off_t off
= (off_t
) luaL_optinteger(L
, -1, 0);
741 off_t len
= (off_t
) luaL_optinteger(L
, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/
744 return luaL_error(L
, "offset for '%s' is negative", lua_tostring(L
, -3));
749 } else if (-1 != len
) {
751 return luaL_error(L
, "offset > length for '%s'", lua_tostring(L
, -3));
754 if (0 != len
&& 0 != http_chunk_append_file_range(srv
, con
, fn
, off
, len
)) {
756 return luaL_error(L
, "error opening file content '%s' at offset %lld", lua_tostring(L
, -3), (long long)off
);
761 return luaL_error(L
, "content[%d] is a table and requires the field \"filename\"", i
);
765 } else if (lua_isnil(L
, -1)) {
772 return luaL_error(L
, "content[%d] is neither a string nor a table: ", i
);
775 lua_pop(L
, 1); /* pop the content[...] entry value */
778 return luaL_error(L
, "lighty.content has to be a table");
780 lua_pop(L
, 1); /* pop lighty.content */
785 static int traceback(lua_State
*L
) {
786 if (!lua_isstring(L
, 1)) /* 'message' not a string? */
787 return 1; /* keep it intact */
788 lua_getglobal(L
, "debug");
789 if (!lua_istable(L
, -1)) {
793 lua_getfield(L
, -1, "traceback");
794 if (!lua_isfunction(L
, -1)) {
798 lua_pushvalue(L
, 1); /* pass error message */
799 lua_pushinteger(L
, 2); /* skip this function and traceback */
800 lua_call(L
, 2, 1); /* call debug.traceback */
804 /* push traceback function before calling lua_pcall after narg arguments
805 * have been pushed (inserts it before the arguments). returns index for
806 * traceback function ("msgh" in lua_pcall)
808 static int push_traceback(lua_State
*L
, int narg
) {
809 int base
= lua_gettop(L
) - narg
; /* function index */
810 lua_pushcfunction(L
, traceback
);
815 static handler_t
magnet_attract(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*name
) {
817 int lua_return_value
;
818 const int func_ndx
= 1;
819 const int lighty_table_ndx
= 2;
821 /* get the script-context */
822 L
= script_cache_get_script(srv
, con
, p
->cache
, name
);
824 if (lua_isstring(L
, -1)) {
825 log_error_write(srv
, __FILE__
, __LINE__
,
830 lua_tostring(L
, -1));
834 force_assert(lua_gettop(L
) == 0); /* only the error should have been on the stack */
836 con
->http_status
= 500;
839 return HANDLER_FINISHED
;
842 force_assert(lua_gettop(L
) == 1);
843 force_assert(lua_isfunction(L
, func_ndx
));
845 lua_pushlightuserdata(L
, srv
);
846 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
848 lua_pushlightuserdata(L
, con
);
849 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
851 lua_atpanic(L
, magnet_atpanic
);
854 * we want to create empty environment for our script
856 * setmetatable({}, {__index = _G})
858 * if a function symbol is not defined in our env, __index will lookup
861 * all variables created in the script-env will be thrown
862 * away at the end of the script run.
864 lua_newtable(L
); /* my empty environment aka {} (sp += 1) */
866 /* we have to overwrite the print function */
867 lua_pushcfunction(L
, magnet_print
); /* (sp += 1) */
868 lua_setfield(L
, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
871 * lighty.request[] (ro) has the HTTP-request headers
872 * lighty.env[] (rw) has various url/physical file paths and
873 * request meta data; might contain nil values
874 * lighty.req_env[] (ro) has the cgi environment
875 * lighty.status[] (ro) has the status counters
876 * lighty.content[] (rw) is a table of string/file
877 * lighty.header[] (rw) is a array to set response headers
880 lua_newtable(L
); /* lighty.* (sp += 1) */
882 lua_newtable(L
); /* {} (sp += 1) */
883 lua_newtable(L
); /* the meta-table for the request-table (sp += 1) */
884 lua_pushcfunction(L
, magnet_reqhdr_get
); /* (sp += 1) */
885 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
886 lua_pushcfunction(L
, magnet_reqhdr_pairs
); /* (sp += 1) */
887 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
888 lua_setmetatable(L
, -2); /* tie the metatable to request (sp -= 1) */
889 lua_setfield(L
, -2, "request"); /* content = {} (sp -= 1) */
891 lua_newtable(L
); /* {} (sp += 1) */
892 lua_newtable(L
); /* the meta-table for the env-table (sp += 1) */
893 lua_pushcfunction(L
, magnet_env_get
); /* (sp += 1) */
894 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
895 lua_pushcfunction(L
, magnet_env_set
); /* (sp += 1) */
896 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
897 lua_pushcfunction(L
, magnet_env_pairs
); /* (sp += 1) */
898 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
899 lua_setmetatable(L
, -2); /* tie the metatable to env (sp -= 1) */
900 lua_setfield(L
, -2, "env"); /* content = {} (sp -= 1) */
902 lua_newtable(L
); /* {} (sp += 1) */
903 lua_newtable(L
); /* the meta-table for the req_env-table (sp += 1) */
904 lua_pushcfunction(L
, magnet_cgi_get
); /* (sp += 1) */
905 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
906 lua_pushcfunction(L
, magnet_cgi_set
); /* (sp += 1) */
907 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
908 lua_pushcfunction(L
, magnet_cgi_pairs
); /* (sp += 1) */
909 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
910 lua_setmetatable(L
, -2); /* tie the metatable to req_env (sp -= 1) */
911 lua_setfield(L
, -2, "req_env"); /* content = {} (sp -= 1) */
913 lua_newtable(L
); /* {} (sp += 1) */
914 lua_newtable(L
); /* the meta-table for the status-table (sp += 1) */
915 lua_pushcfunction(L
, magnet_status_get
); /* (sp += 1) */
916 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
917 lua_pushcfunction(L
, magnet_status_set
); /* (sp += 1) */
918 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
919 lua_pushcfunction(L
, magnet_status_pairs
); /* (sp += 1) */
920 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
921 lua_setmetatable(L
, -2); /* tie the metatable to statzs (sp -= 1) */
922 lua_setfield(L
, -2, "status"); /* content = {} (sp -= 1) */
924 /* add empty 'content' and 'header' tables */
925 lua_newtable(L
); /* {} (sp += 1) */
926 lua_setfield(L
, -2, "content"); /* content = {} (sp -= 1) */
928 lua_newtable(L
); /* {} (sp += 1) */
929 lua_setfield(L
, -2, "header"); /* header = {} (sp -= 1) */
931 lua_pushinteger(L
, MAGNET_RESTART_REQUEST
);
932 lua_setfield(L
, -2, "RESTART_REQUEST");
934 lua_pushcfunction(L
, magnet_stat
); /* (sp += 1) */
935 lua_setfield(L
, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
937 /* insert lighty table at index 2 */
938 lua_pushvalue(L
, -1);
939 lua_insert(L
, lighty_table_ndx
);
941 lua_setfield(L
, -2, "lighty"); /* lighty.* (sp -= 1) */
943 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
944 /* override the default pairs() function to our __pairs capable version;
945 * not needed for lua 5.2+
947 lua_getglobal(L
, "pairs"); /* push original pairs() (sp += 1) */
948 lua_pushcclosure(L
, magnet_pairs
, 1);
949 lua_setfield(L
, -2, "pairs"); /* (sp -= 1) */
952 lua_newtable(L
); /* the meta-table for the new env (sp += 1) */
953 lua_pushglobaltable(L
); /* (sp += 1) */
954 lua_setfield(L
, -2, "__index"); /* { __index = _G } (sp -= 1) */
955 lua_setmetatable(L
, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
957 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
959 /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */
960 lua_pushvalue(L
, func_ndx
);
962 int errfunc
= push_traceback(L
, 0);
963 int ret
= lua_pcall(L
, 0, 1, errfunc
);
964 lua_remove(L
, errfunc
);
966 /* reset environment */
967 lua_pushglobaltable(L
); /* (sp += 1) */
968 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
971 log_error_write(srv
, __FILE__
, __LINE__
,
974 lua_tostring(L
, -1));
975 lua_pop(L
, 2); /* remove the error-msg and the lighty table at index 2 */
977 force_assert(lua_gettop(L
) == 1); /* only the function should be on the stack */
979 con
->http_status
= 500;
982 return HANDLER_FINISHED
;
986 /* we should have the function, the lighty table and the return value on the stack */
987 force_assert(lua_gettop(L
) == 3);
989 lua_return_value
= (int) luaL_optinteger(L
, -1, -1);
990 lua_pop(L
, 1); /* pop return value */
992 magnet_copy_response_header(srv
, con
, L
, lighty_table_ndx
);
995 handler_t result
= HANDLER_GO_ON
;
997 if (lua_return_value
> 99) {
998 con
->http_status
= lua_return_value
;
999 con
->file_finished
= 1;
1002 if (0 == setjmp(exceptionjmp
)) {
1003 magnet_attach_content(srv
, con
, L
, lighty_table_ndx
);
1004 if (!chunkqueue_is_empty(con
->write_queue
)) {
1008 lua_settop(L
, 2); /* remove all but function and lighty table */
1010 con
->http_status
= 500;
1014 result
= HANDLER_FINISHED
;
1015 } else if (MAGNET_RESTART_REQUEST
== lua_return_value
) {
1016 result
= HANDLER_COMEBACK
;
1019 lua_pop(L
, 1); /* pop the lighty table */
1020 force_assert(lua_gettop(L
) == 1); /* only the function should remain on the stack */
1026 static handler_t
magnet_attract_array(server
*srv
, connection
*con
, plugin_data
*p
, array
*files
) {
1028 handler_t ret
= HANDLER_GO_ON
;
1030 /* no filename set */
1031 if (files
->used
== 0) return HANDLER_GO_ON
;
1034 if (con
->ssl
) http_cgi_ssl_env(srv
, con
);
1038 * execute all files and jump out on the first !HANDLER_GO_ON
1040 for (i
= 0; i
< files
->used
&& ret
== HANDLER_GO_ON
; i
++) {
1041 data_string
*ds
= (data_string
*)files
->data
[i
];
1043 if (buffer_string_is_empty(ds
->value
)) continue;
1045 ret
= magnet_attract(srv
, con
, p
, ds
->value
);
1048 if (con
->error_handler_saved_status
) {
1049 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */
1051 data_string
* const ds
= (data_string
*)array_get_element(con
->environment
, "REDIRECT_STATUS");
1052 if (ds
&& (x
= strtoul(ds
->value
->ptr
, NULL
, 10)) < 1000)
1053 /*(simplified validity check x < 1000)*/
1054 con
->error_handler_saved_status
=
1055 con
->error_handler_saved_status
> 0 ? (int)x
: -(int)x
;
1061 URIHANDLER_FUNC(mod_magnet_uri_handler
) {
1062 plugin_data
*p
= p_d
;
1064 mod_magnet_patch_connection(srv
, con
, p
);
1066 return magnet_attract_array(srv
, con
, p
, p
->conf
.url_raw
);
1069 URIHANDLER_FUNC(mod_magnet_physical
) {
1070 plugin_data
*p
= p_d
;
1072 mod_magnet_patch_connection(srv
, con
, p
);
1074 return magnet_attract_array(srv
, con
, p
, p
->conf
.physical_path
);
1078 /* this function is called at dlopen() time and inits the callbacks */
1080 int mod_magnet_plugin_init(plugin
*p
);
1081 int mod_magnet_plugin_init(plugin
*p
) {
1082 p
->version
= LIGHTTPD_VERSION_ID
;
1083 p
->name
= buffer_init_string("magnet");
1085 p
->init
= mod_magnet_init
;
1086 p
->handle_uri_clean
= mod_magnet_uri_handler
;
1087 p
->handle_physical
= mod_magnet_physical
;
1088 p
->set_defaults
= mod_magnet_set_defaults
;
1089 p
->cleanup
= mod_magnet_free
;