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
));
243 di
= (data_integer
*)du
;
244 lua_pushinteger(L
, di
->value
);
251 /* Update our positional upval to reflect our new current position */
253 lua_pushinteger(L
, pos
);
254 lua_replace(L
, lua_upvalueindex(1));
256 /* Returning 2 items on the stack (key, value) */
262 /* Create the closure necessary to iterate over the array *a with the above function */
263 static int magnet_array_pairs(lua_State
*L
, array
*a
) {
264 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
265 lua_pushlightuserdata(L
, a
); /* Push our array *a into upval 2 */
266 lua_pushcclosure(L
, magnet_array_next
, 2); /* Push our new closure with 2 upvals */
270 static server
* magnet_get_server(lua_State
*L
) {
273 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
274 srv
= lua_touserdata(L
, -1);
280 static connection
* magnet_get_connection(lua_State
*L
) {
283 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
284 con
= lua_touserdata(L
, -1);
295 static const_buffer
magnet_checkconstbuffer(lua_State
*L
, int index
) {
297 cb
.ptr
= luaL_checklstring(L
, index
, &cb
.len
);
301 static buffer
* magnet_checkbuffer(lua_State
*L
, int index
) {
302 const_buffer cb
= magnet_checkconstbuffer(L
, index
);
303 buffer
*b
= buffer_init();
304 buffer_copy_string_len(b
, cb
.ptr
, cb
.len
);
308 static int magnet_print(lua_State
*L
) {
309 buffer
*b
= magnet_checkbuffer(L
, 1);
311 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
320 static int magnet_stat(lua_State
*L
) {
321 buffer
*sb
= magnet_checkbuffer(L
, 1);
322 server
*srv
= magnet_get_server(L
);
323 connection
*con
= magnet_get_connection(L
);
324 stat_cache_entry
*sce
= NULL
;
327 res
= stat_cache_get_entry(srv
, con
, sb
, &sce
);
330 if (HANDLER_GO_ON
!= res
) {
335 lua_newtable(L
); // return value
337 lua_pushboolean(L
, S_ISREG(sce
->st
.st_mode
));
338 lua_setfield(L
, -2, "is_file");
340 lua_pushboolean(L
, S_ISDIR(sce
->st
.st_mode
));
341 lua_setfield(L
, -2, "is_dir");
343 lua_pushboolean(L
, S_ISCHR(sce
->st
.st_mode
));
344 lua_setfield(L
, -2, "is_char");
346 lua_pushboolean(L
, S_ISBLK(sce
->st
.st_mode
));
347 lua_setfield(L
, -2, "is_block");
349 lua_pushboolean(L
, S_ISSOCK(sce
->st
.st_mode
));
350 lua_setfield(L
, -2, "is_socket");
352 lua_pushboolean(L
, S_ISLNK(sce
->st
.st_mode
));
353 lua_setfield(L
, -2, "is_link");
355 lua_pushboolean(L
, S_ISFIFO(sce
->st
.st_mode
));
356 lua_setfield(L
, -2, "is_fifo");
358 lua_pushinteger(L
, sce
->st
.st_mtime
);
359 lua_setfield(L
, -2, "st_mtime");
361 lua_pushinteger(L
, sce
->st
.st_ctime
);
362 lua_setfield(L
, -2, "st_ctime");
364 lua_pushinteger(L
, sce
->st
.st_atime
);
365 lua_setfield(L
, -2, "st_atime");
367 lua_pushinteger(L
, sce
->st
.st_uid
);
368 lua_setfield(L
, -2, "st_uid");
370 lua_pushinteger(L
, sce
->st
.st_gid
);
371 lua_setfield(L
, -2, "st_gid");
373 lua_pushinteger(L
, sce
->st
.st_size
);
374 lua_setfield(L
, -2, "st_size");
376 lua_pushinteger(L
, sce
->st
.st_ino
);
377 lua_setfield(L
, -2, "st_ino");
379 if (!buffer_string_is_empty(sce
->etag
)) {
380 /* we have to mutate the etag */
381 buffer
*b
= buffer_init();
382 etag_mutate(b
, sce
->etag
);
384 lua_pushlstring(L
, CONST_BUF_LEN(b
));
389 lua_setfield(L
, -2, "etag");
391 if (!buffer_string_is_empty(sce
->content_type
)) {
392 lua_pushlstring(L
, CONST_BUF_LEN(sce
->content_type
));
396 lua_setfield(L
, -2, "content-type");
402 static int magnet_atpanic(lua_State
*L
) {
403 buffer
*b
= magnet_checkbuffer(L
, 1);
405 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
411 longjmp(exceptionjmp
, 1);
414 static int magnet_reqhdr_get(lua_State
*L
) {
415 connection
*con
= magnet_get_connection(L
);
418 /* __index: param 1 is the (empty) table the value was not found in */
419 const char *key
= luaL_checkstring(L
, 2);
421 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, key
))) {
422 if (!buffer_is_empty(ds
->value
)) {
423 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
433 static int magnet_reqhdr_pairs(lua_State
*L
) {
434 connection
*con
= magnet_get_connection(L
);
436 return magnet_array_pairs(L
, con
->request
.headers
);
439 static int magnet_status_get(lua_State
*L
) {
441 server
*srv
= magnet_get_server(L
);
443 /* __index: param 1 is the (empty) table the value was not found in */
444 const_buffer key
= magnet_checkconstbuffer(L
, 2);
446 di
= status_counter_get_counter(srv
, key
.ptr
, key
.len
);
448 lua_pushinteger(L
, (lua_Integer
)di
->value
);
453 static int magnet_status_set(lua_State
*L
) {
454 server
*srv
= magnet_get_server(L
);
456 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
457 const_buffer key
= magnet_checkconstbuffer(L
, 2);
458 int counter
= (int) luaL_checkinteger(L
, 3);
460 status_counter_set(srv
, key
.ptr
, key
.len
, counter
);
465 static int magnet_status_pairs(lua_State
*L
) {
466 server
*srv
= magnet_get_server(L
);
468 return magnet_array_pairs(L
, srv
->status
);
476 MAGNET_ENV_PHYICAL_PATH
,
477 MAGNET_ENV_PHYICAL_REL_PATH
,
478 MAGNET_ENV_PHYICAL_DOC_ROOT
,
479 MAGNET_ENV_PHYICAL_BASEDIR
,
482 MAGNET_ENV_URI_PATH_RAW
,
483 MAGNET_ENV_URI_SCHEME
,
484 MAGNET_ENV_URI_AUTHORITY
,
485 MAGNET_ENV_URI_QUERY
,
487 MAGNET_ENV_REQUEST_METHOD
,
488 MAGNET_ENV_REQUEST_URI
,
489 MAGNET_ENV_REQUEST_ORIG_URI
,
490 MAGNET_ENV_REQUEST_PATH_INFO
,
491 MAGNET_ENV_REQUEST_REMOTE_IP
,
492 MAGNET_ENV_REQUEST_PROTOCOL
496 static const magnet_env_t magnet_env
[] = {
497 { "physical.path", MAGNET_ENV_PHYICAL_PATH
},
498 { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH
},
499 { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT
},
500 { "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR
},
502 { "uri.path", MAGNET_ENV_URI_PATH
},
503 { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW
},
504 { "uri.scheme", MAGNET_ENV_URI_SCHEME
},
505 { "uri.authority", MAGNET_ENV_URI_AUTHORITY
},
506 { "uri.query", MAGNET_ENV_URI_QUERY
},
508 { "request.method", MAGNET_ENV_REQUEST_METHOD
},
509 { "request.uri", MAGNET_ENV_REQUEST_URI
},
510 { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI
},
511 { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO
},
512 { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP
},
513 { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL
},
515 { NULL
, MAGNET_ENV_UNSET
}
518 static buffer
*magnet_env_get_buffer_by_id(server
*srv
, connection
*con
, int id
) {
524 * map all internal variables to lua
529 case MAGNET_ENV_PHYICAL_PATH
: dest
= con
->physical
.path
; break;
530 case MAGNET_ENV_PHYICAL_REL_PATH
: dest
= con
->physical
.rel_path
; break;
531 case MAGNET_ENV_PHYICAL_DOC_ROOT
: dest
= con
->physical
.doc_root
; break;
532 case MAGNET_ENV_PHYICAL_BASEDIR
: dest
= con
->physical
.basedir
; break;
534 case MAGNET_ENV_URI_PATH
: dest
= con
->uri
.path
; break;
535 case MAGNET_ENV_URI_PATH_RAW
: dest
= con
->uri
.path_raw
; break;
536 case MAGNET_ENV_URI_SCHEME
: dest
= con
->uri
.scheme
; break;
537 case MAGNET_ENV_URI_AUTHORITY
: dest
= con
->uri
.authority
; break;
538 case MAGNET_ENV_URI_QUERY
: dest
= con
->uri
.query
; break;
540 case MAGNET_ENV_REQUEST_METHOD
:
541 buffer_copy_string(srv
->tmp_buf
, get_http_method_name(con
->request
.http_method
));
544 case MAGNET_ENV_REQUEST_URI
: dest
= con
->request
.uri
; break;
545 case MAGNET_ENV_REQUEST_ORIG_URI
: dest
= con
->request
.orig_uri
; break;
546 case MAGNET_ENV_REQUEST_PATH_INFO
: dest
= con
->request
.pathinfo
; break;
547 case MAGNET_ENV_REQUEST_REMOTE_IP
: dest
= con
->dst_addr_buf
; break;
548 case MAGNET_ENV_REQUEST_PROTOCOL
:
549 buffer_copy_string(srv
->tmp_buf
, get_http_version_name(con
->request
.http_version
));
553 case MAGNET_ENV_UNSET
: break;
559 static buffer
*magnet_env_get_buffer(server
*srv
, connection
*con
, const char *key
) {
562 for (i
= 0; magnet_env
[i
].name
; i
++) {
563 if (0 == strcmp(key
, magnet_env
[i
].name
)) break;
566 return magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[i
].type
);
569 static int magnet_env_get(lua_State
*L
) {
570 server
*srv
= magnet_get_server(L
);
571 connection
*con
= magnet_get_connection(L
);
573 /* __index: param 1 is the (empty) table the value was not found in */
574 const char *key
= luaL_checkstring(L
, 2);
577 dest
= magnet_env_get_buffer(srv
, con
, key
);
579 if (!buffer_is_empty(dest
)) {
580 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
588 static int magnet_env_set(lua_State
*L
) {
589 server
*srv
= magnet_get_server(L
);
590 connection
*con
= magnet_get_connection(L
);
592 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
593 const char *key
= luaL_checkstring(L
, 2);
596 luaL_checkany(L
, 3); /* nil or a string */
598 if (NULL
!= (dest
= magnet_env_get_buffer(srv
, con
, key
))) {
599 if (lua_isnil(L
, 3)) {
602 const_buffer val
= magnet_checkconstbuffer(L
, 3);
603 buffer_copy_string_len(dest
, val
.ptr
, val
.len
);
608 return luaL_error(L
, "couldn't store '%s' in lighty.env[]", key
);
614 static int magnet_env_next(lua_State
*L
) {
615 server
*srv
= magnet_get_server(L
);
616 connection
*con
= magnet_get_connection(L
);
617 const int pos
= lua_tointeger(L
, lua_upvalueindex(1));
621 /* ignore previous key: use upvalue for current pos */
624 if (NULL
== magnet_env
[pos
].name
) return 0; /* end of list */
625 /* Update our positional upval to reflect our new current position */
626 lua_pushinteger(L
, pos
+ 1);
627 lua_replace(L
, lua_upvalueindex(1));
630 lua_pushstring(L
, magnet_env
[pos
].name
);
633 dest
= magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[pos
].type
);
634 if (!buffer_is_empty(dest
)) {
635 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
640 /* return 2 items on the stack (key, value) */
644 static int magnet_env_pairs(lua_State
*L
) {
645 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
646 lua_pushcclosure(L
, magnet_env_next
, 1); /* Push our new closure with 1 upvals */
650 static int magnet_cgi_get(lua_State
*L
) {
651 connection
*con
= magnet_get_connection(L
);
654 /* __index: param 1 is the (empty) table the value was not found in */
655 const char *key
= luaL_checkstring(L
, 2);
657 ds
= (data_string
*)array_get_element(con
->environment
, key
);
658 if (NULL
!= ds
&& !buffer_is_empty(ds
->value
))
659 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
666 static int magnet_cgi_set(lua_State
*L
) {
667 connection
*con
= magnet_get_connection(L
);
669 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
670 const_buffer key
= magnet_checkconstbuffer(L
, 2);
671 const_buffer val
= magnet_checkconstbuffer(L
, 2);
673 array_set_key_value(con
->environment
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
678 static int magnet_cgi_pairs(lua_State
*L
) {
679 connection
*con
= magnet_get_connection(L
);
681 return magnet_array_pairs(L
, con
->environment
);
685 static int magnet_copy_response_header(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
686 force_assert(lua_istable(L
, lighty_table_ndx
));
688 lua_getfield(L
, lighty_table_ndx
, "header"); /* lighty.header */
689 if (lua_istable(L
, -1)) {
690 /* header is found, and is a table */
693 while (lua_next(L
, -2) != 0) {
694 if (lua_isstring(L
, -1) && lua_isstring(L
, -2)) {
695 const_buffer key
= magnet_checkconstbuffer(L
, -2);
696 const_buffer val
= magnet_checkconstbuffer(L
, -1);
698 response_header_overwrite(srv
, con
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
704 lua_pop(L
, 1); /* pop lighty.header */
710 * walk through the content array
712 * content = { "<pre>", { file = "/content" } , "</pre>" }
714 * header["Content-Type"] = "text/html"
718 static int magnet_attach_content(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
719 force_assert(lua_istable(L
, lighty_table_ndx
));
721 lua_getfield(L
, lighty_table_ndx
, "content"); /* lighty.content */
722 if (lua_istable(L
, -1)) {
724 /* content is found, and is a table */
727 lua_rawgeti(L
, -1, i
);
729 /* -1 is the value and should be the value ... aka a table */
730 if (lua_isstring(L
, -1)) {
731 const_buffer data
= magnet_checkconstbuffer(L
, -1);
733 chunkqueue_append_mem(con
->write_queue
, data
.ptr
, data
.len
);
734 } else if (lua_istable(L
, -1)) {
735 lua_getfield(L
, -1, "filename");
736 lua_getfield(L
, -2, "length"); /* (0-based) end of range (not actually "length") */
737 lua_getfield(L
, -3, "offset"); /* (0-based) start of range */
739 if (lua_isstring(L
, -3)) { /* filename has to be a string */
740 buffer
*fn
= magnet_checkbuffer(L
, -3);
741 off_t off
= (off_t
) luaL_optinteger(L
, -1, 0);
742 off_t len
= (off_t
) luaL_optinteger(L
, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/
745 return luaL_error(L
, "offset for '%s' is negative", lua_tostring(L
, -3));
750 } else if (-1 != len
) {
752 return luaL_error(L
, "offset > length for '%s'", lua_tostring(L
, -3));
755 if (0 != len
&& 0 != http_chunk_append_file_range(srv
, con
, fn
, off
, len
)) {
757 return luaL_error(L
, "error opening file content '%s' at offset %lld", lua_tostring(L
, -3), (long long)off
);
762 return luaL_error(L
, "content[%d] is a table and requires the field \"filename\"", i
);
766 } else if (lua_isnil(L
, -1)) {
773 return luaL_error(L
, "content[%d] is neither a string nor a table: ", i
);
776 lua_pop(L
, 1); /* pop the content[...] entry value */
779 return luaL_error(L
, "lighty.content has to be a table");
781 lua_pop(L
, 1); /* pop lighty.content */
786 static int traceback(lua_State
*L
) {
787 if (!lua_isstring(L
, 1)) /* 'message' not a string? */
788 return 1; /* keep it intact */
789 lua_getglobal(L
, "debug");
790 if (!lua_istable(L
, -1)) {
794 lua_getfield(L
, -1, "traceback");
795 if (!lua_isfunction(L
, -1)) {
799 lua_pushvalue(L
, 1); /* pass error message */
800 lua_pushinteger(L
, 2); /* skip this function and traceback */
801 lua_call(L
, 2, 1); /* call debug.traceback */
805 /* push traceback function before calling lua_pcall after narg arguments
806 * have been pushed (inserts it before the arguments). returns index for
807 * traceback function ("msgh" in lua_pcall)
809 static int push_traceback(lua_State
*L
, int narg
) {
810 int base
= lua_gettop(L
) - narg
; /* function index */
811 lua_pushcfunction(L
, traceback
);
816 static handler_t
magnet_attract(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*name
) {
818 int lua_return_value
;
819 const int func_ndx
= 1;
820 const int lighty_table_ndx
= 2;
822 /* get the script-context */
823 L
= script_cache_get_script(srv
, con
, p
->cache
, name
);
825 if (lua_isstring(L
, -1)) {
826 log_error_write(srv
, __FILE__
, __LINE__
,
831 lua_tostring(L
, -1));
835 force_assert(lua_gettop(L
) == 0); /* only the error should have been on the stack */
837 con
->http_status
= 500;
840 return HANDLER_FINISHED
;
843 force_assert(lua_gettop(L
) == 1);
844 force_assert(lua_isfunction(L
, func_ndx
));
846 lua_pushlightuserdata(L
, srv
);
847 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
849 lua_pushlightuserdata(L
, con
);
850 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
852 lua_atpanic(L
, magnet_atpanic
);
855 * we want to create empty environment for our script
857 * setmetatable({}, {__index = _G})
859 * if a function symbol is not defined in our env, __index will lookup
862 * all variables created in the script-env will be thrown
863 * away at the end of the script run.
865 lua_newtable(L
); /* my empty environment aka {} (sp += 1) */
867 /* we have to overwrite the print function */
868 lua_pushcfunction(L
, magnet_print
); /* (sp += 1) */
869 lua_setfield(L
, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
872 * lighty.request[] (ro) has the HTTP-request headers
873 * lighty.env[] (rw) has various url/physical file paths and
874 * request meta data; might contain nil values
875 * lighty.req_env[] (ro) has the cgi environment
876 * lighty.status[] (ro) has the status counters
877 * lighty.content[] (rw) is a table of string/file
878 * lighty.header[] (rw) is a array to set response headers
881 lua_newtable(L
); /* lighty.* (sp += 1) */
883 lua_newtable(L
); /* {} (sp += 1) */
884 lua_newtable(L
); /* the meta-table for the request-table (sp += 1) */
885 lua_pushcfunction(L
, magnet_reqhdr_get
); /* (sp += 1) */
886 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
887 lua_pushcfunction(L
, magnet_reqhdr_pairs
); /* (sp += 1) */
888 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
889 lua_setmetatable(L
, -2); /* tie the metatable to request (sp -= 1) */
890 lua_setfield(L
, -2, "request"); /* content = {} (sp -= 1) */
892 lua_newtable(L
); /* {} (sp += 1) */
893 lua_newtable(L
); /* the meta-table for the env-table (sp += 1) */
894 lua_pushcfunction(L
, magnet_env_get
); /* (sp += 1) */
895 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
896 lua_pushcfunction(L
, magnet_env_set
); /* (sp += 1) */
897 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
898 lua_pushcfunction(L
, magnet_env_pairs
); /* (sp += 1) */
899 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
900 lua_setmetatable(L
, -2); /* tie the metatable to env (sp -= 1) */
901 lua_setfield(L
, -2, "env"); /* content = {} (sp -= 1) */
903 lua_newtable(L
); /* {} (sp += 1) */
904 lua_newtable(L
); /* the meta-table for the req_env-table (sp += 1) */
905 lua_pushcfunction(L
, magnet_cgi_get
); /* (sp += 1) */
906 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
907 lua_pushcfunction(L
, magnet_cgi_set
); /* (sp += 1) */
908 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
909 lua_pushcfunction(L
, magnet_cgi_pairs
); /* (sp += 1) */
910 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
911 lua_setmetatable(L
, -2); /* tie the metatable to req_env (sp -= 1) */
912 lua_setfield(L
, -2, "req_env"); /* content = {} (sp -= 1) */
914 lua_newtable(L
); /* {} (sp += 1) */
915 lua_newtable(L
); /* the meta-table for the status-table (sp += 1) */
916 lua_pushcfunction(L
, magnet_status_get
); /* (sp += 1) */
917 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
918 lua_pushcfunction(L
, magnet_status_set
); /* (sp += 1) */
919 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
920 lua_pushcfunction(L
, magnet_status_pairs
); /* (sp += 1) */
921 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
922 lua_setmetatable(L
, -2); /* tie the metatable to statzs (sp -= 1) */
923 lua_setfield(L
, -2, "status"); /* content = {} (sp -= 1) */
925 /* add empty 'content' and 'header' tables */
926 lua_newtable(L
); /* {} (sp += 1) */
927 lua_setfield(L
, -2, "content"); /* content = {} (sp -= 1) */
929 lua_newtable(L
); /* {} (sp += 1) */
930 lua_setfield(L
, -2, "header"); /* header = {} (sp -= 1) */
932 lua_pushinteger(L
, MAGNET_RESTART_REQUEST
);
933 lua_setfield(L
, -2, "RESTART_REQUEST");
935 lua_pushcfunction(L
, magnet_stat
); /* (sp += 1) */
936 lua_setfield(L
, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
938 /* insert lighty table at index 2 */
939 lua_pushvalue(L
, -1);
940 lua_insert(L
, lighty_table_ndx
);
942 lua_setfield(L
, -2, "lighty"); /* lighty.* (sp -= 1) */
944 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
945 /* override the default pairs() function to our __pairs capable version;
946 * not needed for lua 5.2+
948 lua_getglobal(L
, "pairs"); /* push original pairs() (sp += 1) */
949 lua_pushcclosure(L
, magnet_pairs
, 1);
950 lua_setfield(L
, -2, "pairs"); /* (sp -= 1) */
953 lua_newtable(L
); /* the meta-table for the new env (sp += 1) */
954 lua_pushglobaltable(L
); /* (sp += 1) */
955 lua_setfield(L
, -2, "__index"); /* { __index = _G } (sp -= 1) */
956 lua_setmetatable(L
, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
958 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
960 /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */
961 lua_pushvalue(L
, func_ndx
);
963 int errfunc
= push_traceback(L
, 0);
964 int ret
= lua_pcall(L
, 0, 1, errfunc
);
965 lua_remove(L
, errfunc
);
967 /* reset environment */
968 lua_pushglobaltable(L
); /* (sp += 1) */
969 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
972 log_error_write(srv
, __FILE__
, __LINE__
,
975 lua_tostring(L
, -1));
976 lua_pop(L
, 2); /* remove the error-msg and the lighty table at index 2 */
978 force_assert(lua_gettop(L
) == 1); /* only the function should be on the stack */
980 con
->http_status
= 500;
983 return HANDLER_FINISHED
;
987 /* we should have the function, the lighty table and the return value on the stack */
988 force_assert(lua_gettop(L
) == 3);
990 lua_return_value
= (int) luaL_optinteger(L
, -1, -1);
991 lua_pop(L
, 1); /* pop return value */
993 magnet_copy_response_header(srv
, con
, L
, lighty_table_ndx
);
996 handler_t result
= HANDLER_GO_ON
;
998 if (lua_return_value
> 99) {
999 con
->http_status
= lua_return_value
;
1000 con
->file_finished
= 1;
1003 if (0 == setjmp(exceptionjmp
)) {
1004 magnet_attach_content(srv
, con
, L
, lighty_table_ndx
);
1005 if (!chunkqueue_is_empty(con
->write_queue
)) {
1009 lua_settop(L
, 2); /* remove all but function and lighty table */
1011 con
->http_status
= 500;
1015 result
= HANDLER_FINISHED
;
1016 } else if (MAGNET_RESTART_REQUEST
== lua_return_value
) {
1017 result
= HANDLER_COMEBACK
;
1020 lua_pop(L
, 1); /* pop the lighty table */
1021 force_assert(lua_gettop(L
) == 1); /* only the function should remain on the stack */
1027 static handler_t
magnet_attract_array(server
*srv
, connection
*con
, plugin_data
*p
, array
*files
) {
1029 handler_t ret
= HANDLER_GO_ON
;
1031 /* no filename set */
1032 if (files
->used
== 0) return HANDLER_GO_ON
;
1035 * execute all files and jump out on the first !HANDLER_GO_ON
1037 for (i
= 0; i
< files
->used
&& ret
== HANDLER_GO_ON
; i
++) {
1038 data_string
*ds
= (data_string
*)files
->data
[i
];
1040 if (buffer_string_is_empty(ds
->value
)) continue;
1042 ret
= magnet_attract(srv
, con
, p
, ds
->value
);
1045 if (con
->error_handler_saved_status
) {
1046 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */
1048 data_string
* const ds
= (data_string
*)array_get_element(con
->environment
, "REDIRECT_STATUS");
1049 if (ds
&& (x
= strtoul(ds
->value
->ptr
, NULL
, 10)) < 1000)
1050 /*(simplified validity check x < 1000)*/
1051 con
->error_handler_saved_status
=
1052 con
->error_handler_saved_status
> 0 ? (int)x
: -(int)x
;
1058 URIHANDLER_FUNC(mod_magnet_uri_handler
) {
1059 plugin_data
*p
= p_d
;
1061 mod_magnet_patch_connection(srv
, con
, p
);
1063 return magnet_attract_array(srv
, con
, p
, p
->conf
.url_raw
);
1066 URIHANDLER_FUNC(mod_magnet_physical
) {
1067 plugin_data
*p
= p_d
;
1069 mod_magnet_patch_connection(srv
, con
, p
);
1071 return magnet_attract_array(srv
, con
, p
, p
->conf
.physical_path
);
1075 /* this function is called at dlopen() time and inits the callbacks */
1077 int mod_magnet_plugin_init(plugin
*p
);
1078 int mod_magnet_plugin_init(plugin
*p
) {
1079 p
->version
= LIGHTTPD_VERSION_ID
;
1080 p
->name
= buffer_init_string("magnet");
1082 p
->init
= mod_magnet_init
;
1083 p
->handle_uri_clean
= mod_magnet_uri_handler
;
1084 p
->handle_physical
= mod_magnet_physical
;
1085 p
->set_defaults
= mod_magnet_set_defaults
;
1086 p
->cleanup
= mod_magnet_free
;
1095 #pragma message("lua is required, but was not found")
1097 int mod_magnet_plugin_init(plugin
*p
);
1098 int mod_magnet_plugin_init(plugin
*p
) {