6 #include "http_chunk.h"
10 #include "mod_magnet_cache.h"
12 #include "stat_cache.h"
13 #include "status_counter.h"
23 #define LUA_RIDX_LIGHTTPD_SERVER "lighty.srv"
24 #define LUA_RIDX_LIGHTTPD_CONNECTION "lighty.con"
26 #define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to"
27 #define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to"
28 #define MAGNET_RESTART_REQUEST 99
30 /* plugin config for all request/connections */
32 static jmp_buf exceptionjmp
;
46 plugin_config
**config_storage
;
51 /* init the plugin data */
52 INIT_FUNC(mod_magnet_init
) {
55 p
= calloc(1, sizeof(*p
));
57 p
->cache
= script_cache_init();
58 p
->encode_buf
= buffer_init();
63 /* detroy the plugin data */
64 FREE_FUNC(mod_magnet_free
) {
69 if (!p
) return HANDLER_GO_ON
;
71 if (p
->config_storage
) {
74 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
75 plugin_config
*s
= p
->config_storage
[i
];
77 if (NULL
== s
) continue;
79 array_free(s
->url_raw
);
80 array_free(s
->physical_path
);
84 free(p
->config_storage
);
87 script_cache_free(p
->cache
);
88 buffer_free(p
->encode_buf
);
95 /* handle plugin config and check values */
97 SETDEFAULTS_FUNC(mod_magnet_set_defaults
) {
101 config_values_t cv
[] = {
102 { MAGNET_CONFIG_RAW_URL
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
103 { MAGNET_CONFIG_PHYSICAL_PATH
, NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
104 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
107 if (!p
) return HANDLER_ERROR
;
109 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
111 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
112 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
115 s
= calloc(1, sizeof(plugin_config
));
116 s
->url_raw
= array_init();
117 s
->physical_path
= array_init();
119 cv
[0].destination
= s
->url_raw
;
120 cv
[1].destination
= s
->physical_path
;
122 p
->config_storage
[i
] = s
;
124 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
125 return HANDLER_ERROR
;
128 if (!array_is_vlist(s
->url_raw
)) {
129 log_error_write(srv
, __FILE__
, __LINE__
, "s",
130 "unexpected value for magnet.attract-raw-url-to; expected list of \"scriptpath\"");
131 return HANDLER_ERROR
;
134 if (!array_is_vlist(s
->physical_path
)) {
135 log_error_write(srv
, __FILE__
, __LINE__
, "s",
136 "unexpected value for magnet.attract-physical-path-to; expected list \"scriptpath\"");
137 return HANDLER_ERROR
;
141 return HANDLER_GO_ON
;
146 static int mod_magnet_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
148 plugin_config
*s
= p
->config_storage
[0];
151 PATCH(physical_path
);
153 /* skip the first, the global context */
154 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
155 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
156 s
= p
->config_storage
[i
];
158 /* condition didn't match */
159 if (!config_check_cond(srv
, con
, dc
)) continue;
162 for (j
= 0; j
< dc
->value
->used
; j
++) {
163 data_unset
*du
= dc
->value
->data
[j
];
165 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL
))) {
167 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH
))) {
168 PATCH(physical_path
);
177 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
178 /* lua5.1 backward compat definition */
179 static void lua_pushglobaltable(lua_State
*L
) { /* (-0, +1, -) */
180 lua_pushvalue(L
, LUA_GLOBALSINDEX
);
184 static void magnet_setfenv_mainfn(lua_State
*L
, int funcIndex
) { /* (-1, 0, -) */
185 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
186 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua
187 * function if it uses any global names
190 const char* first_upvalue_name
= lua_getupvalue(L
, funcIndex
, 1);
191 if (NULL
== first_upvalue_name
) return; /* doesn't have any upvalues */
192 lua_pop(L
, 1); /* only need the name of the upvalue, not the value */
194 if (0 != strcmp(first_upvalue_name
, "_ENV")) return;
196 if (NULL
== lua_setupvalue(L
, funcIndex
, 1)) {
197 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */
201 lua_setfenv(L
, funcIndex
);
205 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
206 /* lua 5.2 already supports __pairs */
208 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details.
209 * Override the default pairs() function to allow us to use a __pairs metakey
211 static int magnet_pairs(lua_State
*L
) {
212 luaL_checkany(L
, 1); /* "self" */
214 if (luaL_getmetafield(L
, 1, "__pairs")) {
215 /* call __pairs(self) */
219 /* call <original-pairs-method>(self) */
220 lua_pushvalue(L
, lua_upvalueindex(1));
228 /* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */
229 static int magnet_array_next(lua_State
*L
) {
234 size_t pos
= lua_tointeger(L
, lua_upvalueindex(1));
235 array
*a
= lua_touserdata(L
, lua_upvalueindex(2));
239 if (pos
>= a
->used
) return 0;
240 if (NULL
!= (du
= a
->data
[pos
])) {
241 lua_pushlstring(L
, CONST_BUF_LEN(du
->key
));
244 ds
= (data_string
*)du
;
245 if (!buffer_is_empty(ds
->value
)) {
246 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
252 di
= (data_integer
*)du
;
253 lua_pushinteger(L
, di
->value
);
260 /* Update our positional upval to reflect our new current position */
262 lua_pushinteger(L
, pos
);
263 lua_replace(L
, lua_upvalueindex(1));
265 /* Returning 2 items on the stack (key, value) */
271 /* Create the closure necessary to iterate over the array *a with the above function */
272 static int magnet_array_pairs(lua_State
*L
, array
*a
) {
273 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
274 lua_pushlightuserdata(L
, a
); /* Push our array *a into upval 2 */
275 lua_pushcclosure(L
, magnet_array_next
, 2); /* Push our new closure with 2 upvals */
279 static server
* magnet_get_server(lua_State
*L
) {
282 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
283 srv
= lua_touserdata(L
, -1);
289 static connection
* magnet_get_connection(lua_State
*L
) {
292 lua_getfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
293 con
= lua_touserdata(L
, -1);
304 static const_buffer
magnet_checkconstbuffer(lua_State
*L
, int index
) {
306 cb
.ptr
= luaL_checklstring(L
, index
, &cb
.len
);
310 static buffer
* magnet_checkbuffer(lua_State
*L
, int index
) {
311 const_buffer cb
= magnet_checkconstbuffer(L
, index
);
312 buffer
*b
= buffer_init();
313 buffer_copy_string_len(b
, cb
.ptr
, cb
.len
);
317 static int magnet_print(lua_State
*L
) {
318 buffer
*b
= magnet_checkbuffer(L
, 1);
320 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
329 static int magnet_stat(lua_State
*L
) {
330 buffer
*sb
= magnet_checkbuffer(L
, 1);
331 server
*srv
= magnet_get_server(L
);
332 connection
*con
= magnet_get_connection(L
);
333 stat_cache_entry
*sce
= NULL
;
336 res
= stat_cache_get_entry(srv
, con
, sb
, &sce
);
339 if (HANDLER_GO_ON
!= res
) {
344 lua_newtable(L
); // return value
346 lua_pushboolean(L
, S_ISREG(sce
->st
.st_mode
));
347 lua_setfield(L
, -2, "is_file");
349 lua_pushboolean(L
, S_ISDIR(sce
->st
.st_mode
));
350 lua_setfield(L
, -2, "is_dir");
352 lua_pushboolean(L
, S_ISCHR(sce
->st
.st_mode
));
353 lua_setfield(L
, -2, "is_char");
355 lua_pushboolean(L
, S_ISBLK(sce
->st
.st_mode
));
356 lua_setfield(L
, -2, "is_block");
358 lua_pushboolean(L
, S_ISSOCK(sce
->st
.st_mode
));
359 lua_setfield(L
, -2, "is_socket");
361 lua_pushboolean(L
, S_ISLNK(sce
->st
.st_mode
));
362 lua_setfield(L
, -2, "is_link");
364 lua_pushboolean(L
, S_ISFIFO(sce
->st
.st_mode
));
365 lua_setfield(L
, -2, "is_fifo");
367 lua_pushinteger(L
, sce
->st
.st_mtime
);
368 lua_setfield(L
, -2, "st_mtime");
370 lua_pushinteger(L
, sce
->st
.st_ctime
);
371 lua_setfield(L
, -2, "st_ctime");
373 lua_pushinteger(L
, sce
->st
.st_atime
);
374 lua_setfield(L
, -2, "st_atime");
376 lua_pushinteger(L
, sce
->st
.st_uid
);
377 lua_setfield(L
, -2, "st_uid");
379 lua_pushinteger(L
, sce
->st
.st_gid
);
380 lua_setfield(L
, -2, "st_gid");
382 lua_pushinteger(L
, sce
->st
.st_size
);
383 lua_setfield(L
, -2, "st_size");
385 lua_pushinteger(L
, sce
->st
.st_ino
);
386 lua_setfield(L
, -2, "st_ino");
388 if (!buffer_string_is_empty(sce
->etag
)) {
389 /* we have to mutate the etag */
390 buffer
*b
= buffer_init();
391 etag_mutate(b
, sce
->etag
);
393 lua_pushlstring(L
, CONST_BUF_LEN(b
));
398 lua_setfield(L
, -2, "etag");
400 if (!buffer_string_is_empty(sce
->content_type
)) {
401 lua_pushlstring(L
, CONST_BUF_LEN(sce
->content_type
));
405 lua_setfield(L
, -2, "content-type");
411 static int magnet_atpanic(lua_State
*L
) {
412 buffer
*b
= magnet_checkbuffer(L
, 1);
414 log_error_write(magnet_get_server(L
), __FILE__
, __LINE__
, "sB",
420 longjmp(exceptionjmp
, 1);
423 static int magnet_reqhdr_get(lua_State
*L
) {
424 connection
*con
= magnet_get_connection(L
);
427 /* __index: param 1 is the (empty) table the value was not found in */
429 const char *key
= luaL_checklstring(L
, 2, &klen
);
431 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->request
.headers
, key
, klen
))) {
432 if (!buffer_is_empty(ds
->value
)) {
433 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
443 static int magnet_reqhdr_pairs(lua_State
*L
) {
444 connection
*con
= magnet_get_connection(L
);
446 return magnet_array_pairs(L
, con
->request
.headers
);
449 static int magnet_status_get(lua_State
*L
) {
451 server
*srv
= magnet_get_server(L
);
453 /* __index: param 1 is the (empty) table the value was not found in */
454 const_buffer key
= magnet_checkconstbuffer(L
, 2);
456 di
= status_counter_get_counter(srv
, key
.ptr
, key
.len
);
458 lua_pushinteger(L
, (lua_Integer
)di
->value
);
463 static int magnet_status_set(lua_State
*L
) {
464 server
*srv
= magnet_get_server(L
);
466 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
467 const_buffer key
= magnet_checkconstbuffer(L
, 2);
468 int counter
= (int) luaL_checkinteger(L
, 3);
470 status_counter_set(srv
, key
.ptr
, key
.len
, counter
);
475 static int magnet_status_pairs(lua_State
*L
) {
476 server
*srv
= magnet_get_server(L
);
478 return magnet_array_pairs(L
, srv
->status
);
486 MAGNET_ENV_PHYICAL_PATH
,
487 MAGNET_ENV_PHYICAL_REL_PATH
,
488 MAGNET_ENV_PHYICAL_DOC_ROOT
,
489 MAGNET_ENV_PHYICAL_BASEDIR
,
492 MAGNET_ENV_URI_PATH_RAW
,
493 MAGNET_ENV_URI_SCHEME
,
494 MAGNET_ENV_URI_AUTHORITY
,
495 MAGNET_ENV_URI_QUERY
,
497 MAGNET_ENV_REQUEST_METHOD
,
498 MAGNET_ENV_REQUEST_URI
,
499 MAGNET_ENV_REQUEST_ORIG_URI
,
500 MAGNET_ENV_REQUEST_PATH_INFO
,
501 MAGNET_ENV_REQUEST_REMOTE_IP
,
502 MAGNET_ENV_REQUEST_PROTOCOL
506 static const magnet_env_t magnet_env
[] = {
507 { "physical.path", MAGNET_ENV_PHYICAL_PATH
},
508 { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH
},
509 { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT
},
510 { "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR
},
512 { "uri.path", MAGNET_ENV_URI_PATH
},
513 { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW
},
514 { "uri.scheme", MAGNET_ENV_URI_SCHEME
},
515 { "uri.authority", MAGNET_ENV_URI_AUTHORITY
},
516 { "uri.query", MAGNET_ENV_URI_QUERY
},
518 { "request.method", MAGNET_ENV_REQUEST_METHOD
},
519 { "request.uri", MAGNET_ENV_REQUEST_URI
},
520 { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI
},
521 { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO
},
522 { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP
},
523 { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL
},
525 { NULL
, MAGNET_ENV_UNSET
}
528 static buffer
*magnet_env_get_buffer_by_id(server
*srv
, connection
*con
, int id
) {
534 * map all internal variables to lua
539 case MAGNET_ENV_PHYICAL_PATH
: dest
= con
->physical
.path
; break;
540 case MAGNET_ENV_PHYICAL_REL_PATH
: dest
= con
->physical
.rel_path
; break;
541 case MAGNET_ENV_PHYICAL_DOC_ROOT
: dest
= con
->physical
.doc_root
; break;
542 case MAGNET_ENV_PHYICAL_BASEDIR
: dest
= con
->physical
.basedir
; break;
544 case MAGNET_ENV_URI_PATH
: dest
= con
->uri
.path
; break;
545 case MAGNET_ENV_URI_PATH_RAW
: dest
= con
->uri
.path_raw
; break;
546 case MAGNET_ENV_URI_SCHEME
: dest
= con
->uri
.scheme
; break;
547 case MAGNET_ENV_URI_AUTHORITY
: dest
= con
->uri
.authority
; break;
548 case MAGNET_ENV_URI_QUERY
: dest
= con
->uri
.query
; break;
550 case MAGNET_ENV_REQUEST_METHOD
:
551 buffer_copy_string(srv
->tmp_buf
, get_http_method_name(con
->request
.http_method
));
554 case MAGNET_ENV_REQUEST_URI
: dest
= con
->request
.uri
; break;
555 case MAGNET_ENV_REQUEST_ORIG_URI
: dest
= con
->request
.orig_uri
; break;
556 case MAGNET_ENV_REQUEST_PATH_INFO
: dest
= con
->request
.pathinfo
; break;
557 case MAGNET_ENV_REQUEST_REMOTE_IP
: dest
= con
->dst_addr_buf
; break;
558 case MAGNET_ENV_REQUEST_PROTOCOL
:
559 buffer_copy_string(srv
->tmp_buf
, get_http_version_name(con
->request
.http_version
));
563 case MAGNET_ENV_UNSET
: break;
569 static buffer
*magnet_env_get_buffer(server
*srv
, connection
*con
, const char *key
) {
572 for (i
= 0; magnet_env
[i
].name
; i
++) {
573 if (0 == strcmp(key
, magnet_env
[i
].name
)) break;
576 return magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[i
].type
);
579 static int magnet_env_get(lua_State
*L
) {
580 server
*srv
= magnet_get_server(L
);
581 connection
*con
= magnet_get_connection(L
);
583 /* __index: param 1 is the (empty) table the value was not found in */
584 const char *key
= luaL_checkstring(L
, 2);
587 dest
= magnet_env_get_buffer(srv
, con
, key
);
589 if (!buffer_is_empty(dest
)) {
590 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
598 static int magnet_env_set(lua_State
*L
) {
599 server
*srv
= magnet_get_server(L
);
600 connection
*con
= magnet_get_connection(L
);
602 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
603 const char *key
= luaL_checkstring(L
, 2);
606 luaL_checkany(L
, 3); /* nil or a string */
608 if (NULL
!= (dest
= magnet_env_get_buffer(srv
, con
, key
))) {
609 if (lua_isnil(L
, 3)) {
612 const_buffer val
= magnet_checkconstbuffer(L
, 3);
613 buffer_copy_string_len(dest
, val
.ptr
, val
.len
);
618 return luaL_error(L
, "couldn't store '%s' in lighty.env[]", key
);
624 static int magnet_env_next(lua_State
*L
) {
625 server
*srv
= magnet_get_server(L
);
626 connection
*con
= magnet_get_connection(L
);
627 const int pos
= lua_tointeger(L
, lua_upvalueindex(1));
631 /* ignore previous key: use upvalue for current pos */
634 if (NULL
== magnet_env
[pos
].name
) return 0; /* end of list */
635 /* Update our positional upval to reflect our new current position */
636 lua_pushinteger(L
, pos
+ 1);
637 lua_replace(L
, lua_upvalueindex(1));
640 lua_pushstring(L
, magnet_env
[pos
].name
);
643 dest
= magnet_env_get_buffer_by_id(srv
, con
, magnet_env
[pos
].type
);
644 if (!buffer_is_empty(dest
)) {
645 lua_pushlstring(L
, CONST_BUF_LEN(dest
));
650 /* return 2 items on the stack (key, value) */
654 static int magnet_env_pairs(lua_State
*L
) {
655 lua_pushinteger(L
, 0); /* Push our current pos (the start) into upval 1 */
656 lua_pushcclosure(L
, magnet_env_next
, 1); /* Push our new closure with 1 upvals */
660 static int magnet_cgi_get(lua_State
*L
) {
661 connection
*con
= magnet_get_connection(L
);
664 /* __index: param 1 is the (empty) table the value was not found in */
666 const char *key
= luaL_checklstring(L
, 2, &klen
);
668 ds
= (data_string
*)array_get_element_klen(con
->environment
, key
, klen
);
669 if (NULL
!= ds
&& !buffer_is_empty(ds
->value
))
670 lua_pushlstring(L
, CONST_BUF_LEN(ds
->value
));
677 static int magnet_cgi_set(lua_State
*L
) {
678 connection
*con
= magnet_get_connection(L
);
680 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */
681 const_buffer key
= magnet_checkconstbuffer(L
, 2);
682 const_buffer val
= magnet_checkconstbuffer(L
, 3);
684 array_set_key_value(con
->environment
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
689 static int magnet_cgi_pairs(lua_State
*L
) {
690 connection
*con
= magnet_get_connection(L
);
692 return magnet_array_pairs(L
, con
->environment
);
696 static int magnet_copy_response_header(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
697 force_assert(lua_istable(L
, lighty_table_ndx
));
699 lua_getfield(L
, lighty_table_ndx
, "header"); /* lighty.header */
700 if (lua_istable(L
, -1)) {
701 /* header is found, and is a table */
704 while (lua_next(L
, -2) != 0) {
705 if (lua_isstring(L
, -1) && lua_isstring(L
, -2)) {
706 const_buffer key
= magnet_checkconstbuffer(L
, -2);
707 const_buffer val
= magnet_checkconstbuffer(L
, -1);
709 response_header_overwrite(srv
, con
, key
.ptr
, key
.len
, val
.ptr
, val
.len
);
715 lua_pop(L
, 1); /* pop lighty.header */
721 * walk through the content array
723 * content = { "<pre>", { file = "/content" } , "</pre>" }
725 * header["Content-Type"] = "text/html"
729 static int magnet_attach_content(server
*srv
, connection
*con
, lua_State
*L
, int lighty_table_ndx
) {
730 force_assert(lua_istable(L
, lighty_table_ndx
));
732 lua_getfield(L
, lighty_table_ndx
, "content"); /* lighty.content */
733 if (lua_istable(L
, -1)) {
735 /* content is found, and is a table */
738 lua_rawgeti(L
, -1, i
);
740 /* -1 is the value and should be the value ... aka a table */
741 if (lua_isstring(L
, -1)) {
742 const_buffer data
= magnet_checkconstbuffer(L
, -1);
744 chunkqueue_append_mem(con
->write_queue
, data
.ptr
, data
.len
);
745 } else if (lua_istable(L
, -1)) {
746 lua_getfield(L
, -1, "filename");
747 lua_getfield(L
, -2, "length"); /* (0-based) end of range (not actually "length") */
748 lua_getfield(L
, -3, "offset"); /* (0-based) start of range */
750 if (lua_isstring(L
, -3)) { /* filename has to be a string */
751 buffer
*fn
= magnet_checkbuffer(L
, -3);
752 off_t off
= (off_t
) luaL_optinteger(L
, -1, 0);
753 off_t len
= (off_t
) luaL_optinteger(L
, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/
756 return luaL_error(L
, "offset for '%s' is negative", lua_tostring(L
, -3));
761 } else if (-1 != len
) {
763 return luaL_error(L
, "offset > length for '%s'", lua_tostring(L
, -3));
766 if (0 != len
&& 0 != http_chunk_append_file_range(srv
, con
, fn
, off
, len
)) {
768 return luaL_error(L
, "error opening file content '%s' at offset %lld", lua_tostring(L
, -3), (long long)off
);
773 return luaL_error(L
, "content[%d] is a table and requires the field \"filename\"", i
);
777 } else if (lua_isnil(L
, -1)) {
784 return luaL_error(L
, "content[%d] is neither a string nor a table: ", i
);
787 lua_pop(L
, 1); /* pop the content[...] entry value */
790 return luaL_error(L
, "lighty.content has to be a table");
792 lua_pop(L
, 1); /* pop lighty.content */
797 static int traceback(lua_State
*L
) {
798 if (!lua_isstring(L
, 1)) /* 'message' not a string? */
799 return 1; /* keep it intact */
800 lua_getglobal(L
, "debug");
801 if (!lua_istable(L
, -1)) {
805 lua_getfield(L
, -1, "traceback");
806 if (!lua_isfunction(L
, -1)) {
810 lua_pushvalue(L
, 1); /* pass error message */
811 lua_pushinteger(L
, 2); /* skip this function and traceback */
812 lua_call(L
, 2, 1); /* call debug.traceback */
816 /* push traceback function before calling lua_pcall after narg arguments
817 * have been pushed (inserts it before the arguments). returns index for
818 * traceback function ("msgh" in lua_pcall)
820 static int push_traceback(lua_State
*L
, int narg
) {
821 int base
= lua_gettop(L
) - narg
; /* function index */
822 lua_pushcfunction(L
, traceback
);
827 static handler_t
magnet_attract(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*name
) {
829 int lua_return_value
;
830 const int func_ndx
= 1;
831 const int lighty_table_ndx
= 2;
833 /* get the script-context */
834 L
= script_cache_get_script(srv
, con
, p
->cache
, name
);
836 if (lua_isstring(L
, -1)) {
837 log_error_write(srv
, __FILE__
, __LINE__
,
842 lua_tostring(L
, -1));
846 force_assert(lua_gettop(L
) == 0); /* only the error should have been on the stack */
848 con
->http_status
= 500;
851 return HANDLER_FINISHED
;
854 force_assert(lua_gettop(L
) == 1);
855 force_assert(lua_isfunction(L
, func_ndx
));
857 lua_pushlightuserdata(L
, srv
);
858 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_SERVER
);
860 lua_pushlightuserdata(L
, con
);
861 lua_setfield(L
, LUA_REGISTRYINDEX
, LUA_RIDX_LIGHTTPD_CONNECTION
);
863 lua_atpanic(L
, magnet_atpanic
);
866 * we want to create empty environment for our script
868 * setmetatable({}, {__index = _G})
870 * if a function symbol is not defined in our env, __index will lookup
873 * all variables created in the script-env will be thrown
874 * away at the end of the script run.
876 lua_newtable(L
); /* my empty environment aka {} (sp += 1) */
878 /* we have to overwrite the print function */
879 lua_pushcfunction(L
, magnet_print
); /* (sp += 1) */
880 lua_setfield(L
, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
883 * lighty.request[] (ro) has the HTTP-request headers
884 * lighty.env[] (rw) has various url/physical file paths and
885 * request meta data; might contain nil values
886 * lighty.req_env[] (ro) has the cgi environment
887 * lighty.status[] (ro) has the status counters
888 * lighty.content[] (rw) is a table of string/file
889 * lighty.header[] (rw) is a array to set response headers
892 lua_newtable(L
); /* lighty.* (sp += 1) */
894 lua_newtable(L
); /* {} (sp += 1) */
895 lua_newtable(L
); /* the meta-table for the request-table (sp += 1) */
896 lua_pushcfunction(L
, magnet_reqhdr_get
); /* (sp += 1) */
897 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
898 lua_pushcfunction(L
, magnet_reqhdr_pairs
); /* (sp += 1) */
899 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
900 lua_setmetatable(L
, -2); /* tie the metatable to request (sp -= 1) */
901 lua_setfield(L
, -2, "request"); /* content = {} (sp -= 1) */
903 lua_newtable(L
); /* {} (sp += 1) */
904 lua_newtable(L
); /* the meta-table for the env-table (sp += 1) */
905 lua_pushcfunction(L
, magnet_env_get
); /* (sp += 1) */
906 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
907 lua_pushcfunction(L
, magnet_env_set
); /* (sp += 1) */
908 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
909 lua_pushcfunction(L
, magnet_env_pairs
); /* (sp += 1) */
910 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
911 lua_setmetatable(L
, -2); /* tie the metatable to env (sp -= 1) */
912 lua_setfield(L
, -2, "env"); /* content = {} (sp -= 1) */
914 lua_newtable(L
); /* {} (sp += 1) */
915 lua_newtable(L
); /* the meta-table for the req_env-table (sp += 1) */
916 lua_pushcfunction(L
, magnet_cgi_get
); /* (sp += 1) */
917 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
918 lua_pushcfunction(L
, magnet_cgi_set
); /* (sp += 1) */
919 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
920 lua_pushcfunction(L
, magnet_cgi_pairs
); /* (sp += 1) */
921 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
922 lua_setmetatable(L
, -2); /* tie the metatable to req_env (sp -= 1) */
923 lua_setfield(L
, -2, "req_env"); /* content = {} (sp -= 1) */
925 lua_newtable(L
); /* {} (sp += 1) */
926 lua_newtable(L
); /* the meta-table for the status-table (sp += 1) */
927 lua_pushcfunction(L
, magnet_status_get
); /* (sp += 1) */
928 lua_setfield(L
, -2, "__index"); /* (sp -= 1) */
929 lua_pushcfunction(L
, magnet_status_set
); /* (sp += 1) */
930 lua_setfield(L
, -2, "__newindex"); /* (sp -= 1) */
931 lua_pushcfunction(L
, magnet_status_pairs
); /* (sp += 1) */
932 lua_setfield(L
, -2, "__pairs"); /* (sp -= 1) */
933 lua_setmetatable(L
, -2); /* tie the metatable to statzs (sp -= 1) */
934 lua_setfield(L
, -2, "status"); /* content = {} (sp -= 1) */
936 /* add empty 'content' and 'header' tables */
937 lua_newtable(L
); /* {} (sp += 1) */
938 lua_setfield(L
, -2, "content"); /* content = {} (sp -= 1) */
940 lua_newtable(L
); /* {} (sp += 1) */
941 lua_setfield(L
, -2, "header"); /* header = {} (sp -= 1) */
943 lua_pushinteger(L
, MAGNET_RESTART_REQUEST
);
944 lua_setfield(L
, -2, "RESTART_REQUEST");
946 lua_pushcfunction(L
, magnet_stat
); /* (sp += 1) */
947 lua_setfield(L
, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
949 /* insert lighty table at index 2 */
950 lua_pushvalue(L
, -1);
951 lua_insert(L
, lighty_table_ndx
);
953 lua_setfield(L
, -2, "lighty"); /* lighty.* (sp -= 1) */
955 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
956 /* override the default pairs() function to our __pairs capable version;
957 * not needed for lua 5.2+
959 lua_getglobal(L
, "pairs"); /* push original pairs() (sp += 1) */
960 lua_pushcclosure(L
, magnet_pairs
, 1);
961 lua_setfield(L
, -2, "pairs"); /* (sp -= 1) */
964 lua_newtable(L
); /* the meta-table for the new env (sp += 1) */
965 lua_pushglobaltable(L
); /* (sp += 1) */
966 lua_setfield(L
, -2, "__index"); /* { __index = _G } (sp -= 1) */
967 lua_setmetatable(L
, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
969 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
971 /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */
972 lua_pushvalue(L
, func_ndx
);
974 int errfunc
= push_traceback(L
, 0);
975 int ret
= lua_pcall(L
, 0, 1, errfunc
);
976 lua_remove(L
, errfunc
);
978 /* reset environment */
979 lua_pushglobaltable(L
); /* (sp += 1) */
980 magnet_setfenv_mainfn(L
, 1); /* (sp -= 1) */
983 log_error_write(srv
, __FILE__
, __LINE__
,
986 lua_tostring(L
, -1));
987 lua_pop(L
, 2); /* remove the error-msg and the lighty table at index 2 */
989 force_assert(lua_gettop(L
) == 1); /* only the function should be on the stack */
991 con
->http_status
= 500;
994 return HANDLER_FINISHED
;
998 /* we should have the function, the lighty table and the return value on the stack */
999 force_assert(lua_gettop(L
) == 3);
1001 lua_return_value
= (int) luaL_optinteger(L
, -1, -1);
1002 lua_pop(L
, 1); /* pop return value */
1004 magnet_copy_response_header(srv
, con
, L
, lighty_table_ndx
);
1007 handler_t result
= HANDLER_GO_ON
;
1009 if (lua_return_value
> 99) {
1010 con
->http_status
= lua_return_value
;
1011 con
->file_finished
= 1;
1014 if (0 == setjmp(exceptionjmp
)) {
1015 magnet_attach_content(srv
, con
, L
, lighty_table_ndx
);
1016 if (!chunkqueue_is_empty(con
->write_queue
)) {
1020 lua_settop(L
, 2); /* remove all but function and lighty table */
1022 con
->http_status
= 500;
1026 result
= HANDLER_FINISHED
;
1027 } else if (MAGNET_RESTART_REQUEST
== lua_return_value
) {
1028 result
= HANDLER_COMEBACK
;
1031 lua_pop(L
, 1); /* pop the lighty table */
1032 force_assert(lua_gettop(L
) == 1); /* only the function should remain on the stack */
1038 static handler_t
magnet_attract_array(server
*srv
, connection
*con
, plugin_data
*p
, array
*files
) {
1040 handler_t ret
= HANDLER_GO_ON
;
1042 /* no filename set */
1043 if (files
->used
== 0) return HANDLER_GO_ON
;
1045 srv
->request_env(srv
, con
);
1048 * execute all files and jump out on the first !HANDLER_GO_ON
1050 for (i
= 0; i
< files
->used
&& ret
== HANDLER_GO_ON
; i
++) {
1051 data_string
*ds
= (data_string
*)files
->data
[i
];
1053 if (buffer_string_is_empty(ds
->value
)) continue;
1055 ret
= magnet_attract(srv
, con
, p
, ds
->value
);
1058 if (con
->error_handler_saved_status
) {
1059 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */
1061 data_string
* const ds
= (data_string
*)array_get_element(con
->environment
, "REDIRECT_STATUS");
1062 if (ds
&& (x
= strtoul(ds
->value
->ptr
, NULL
, 10)) < 1000)
1063 /*(simplified validity check x < 1000)*/
1064 con
->error_handler_saved_status
=
1065 con
->error_handler_saved_status
> 0 ? (int)x
: -(int)x
;
1071 URIHANDLER_FUNC(mod_magnet_uri_handler
) {
1072 plugin_data
*p
= p_d
;
1074 mod_magnet_patch_connection(srv
, con
, p
);
1076 return magnet_attract_array(srv
, con
, p
, p
->conf
.url_raw
);
1079 URIHANDLER_FUNC(mod_magnet_physical
) {
1080 plugin_data
*p
= p_d
;
1082 mod_magnet_patch_connection(srv
, con
, p
);
1084 return magnet_attract_array(srv
, con
, p
, p
->conf
.physical_path
);
1088 /* this function is called at dlopen() time and inits the callbacks */
1090 int mod_magnet_plugin_init(plugin
*p
);
1091 int mod_magnet_plugin_init(plugin
*p
) {
1092 p
->version
= LIGHTTPD_VERSION_ID
;
1093 p
->name
= buffer_init_string("magnet");
1095 p
->init
= mod_magnet_init
;
1096 p
->handle_uri_clean
= mod_magnet_uri_handler
;
1097 p
->handle_physical
= mod_magnet_physical
;
1098 p
->set_defaults
= mod_magnet_set_defaults
;
1099 p
->cleanup
= mod_magnet_free
;