Added mod_fastcgi, some api functions for it, and some new optional compiler warnings (which led to many small changes)
This commit is contained in:
parent
c56ade056f
commit
c458377d4a
|
@ -99,7 +99,7 @@ LI_API void chunkqueue_free(chunkqueue *cq);
|
|||
LI_API void chunkqueue_append_string(chunkqueue *cq, GString *str);
|
||||
|
||||
/* memory gets copied */
|
||||
LI_API void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len);
|
||||
LI_API void chunkqueue_append_mem(chunkqueue *cq, const void *mem, gssize len);
|
||||
|
||||
/* pass ownership of filename, do not free it */
|
||||
LI_API void chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length);
|
||||
|
@ -135,6 +135,8 @@ INLINE chunkiter chunkqueue_iter(chunkqueue *cq);
|
|||
|
||||
INLINE chunk* chunkqueue_first_chunk(chunkqueue *cq);
|
||||
|
||||
LI_API gboolean chunkqueue_extract_to(vrequest *vr, chunkqueue *cq, goffset len, GString *dest);
|
||||
|
||||
/********************
|
||||
* Inline functions *
|
||||
********************/
|
||||
|
|
|
@ -82,6 +82,7 @@ LI_API void connection_reset_keep_alive(connection *con);
|
|||
LI_API void connection_free(connection *con);
|
||||
|
||||
LI_API void connection_error(connection *con);
|
||||
LI_API void connection_internal_error(connection *con);
|
||||
|
||||
LI_API void connection_handle_direct(connection *con);
|
||||
LI_API void connection_handle_indirect(connection *con, plugin *p);
|
||||
|
|
|
@ -13,7 +13,10 @@ LI_API void environment_init(environment *env);
|
|||
LI_API void environment_reset(environment *env);
|
||||
LI_API void environment_clear(environment *env);
|
||||
|
||||
/* overwrite previous value */
|
||||
LI_API void environment_set(environment *env, const gchar *key, size_t keylen, const gchar *val, size_t valuelen);
|
||||
/* do not overwrite */
|
||||
LI_API void environment_insert(environment *env, const gchar *key, size_t keylen, const gchar *val, size_t valuelen);
|
||||
LI_API void environment_remove(environment *env, const gchar *key, size_t keylen);
|
||||
LI_API GString* environment_get(environment *env, const gchar *key, size_t keylen);
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@ typedef void (*PluginFreeOption) (server *srv, plugin *p, size_t ndx, opt
|
|||
typedef action* (*PluginCreateAction) (server *srv, plugin *p, value *val);
|
||||
typedef gboolean (*PluginSetup) (server *srv, plugin *p, value *val);
|
||||
|
||||
typedef void (*PluginHandleContent) (connection *con, plugin *p);
|
||||
typedef void (*PluginHandleClose) (connection *con, plugin *p);
|
||||
typedef handler_t(*PluginHandleVRequest)(vrequest *vr, plugin *p);
|
||||
typedef void (*PluginHandleVRClose) (vrequest *vr, plugin *p);
|
||||
|
||||
struct plugin {
|
||||
size_t version;
|
||||
const gchar *name; /**< name of the plugin */
|
||||
guint id; /**< index in some plugin arrays */
|
||||
|
||||
gpointer data; /**< private plugin data */
|
||||
|
||||
|
@ -32,19 +34,16 @@ struct plugin {
|
|||
|
||||
PluginFree free; /**< called before plugin is unloaded */
|
||||
|
||||
/** called if plugin registered as indirect handler with connection_handle_indirect(srv, con, p)
|
||||
* - after response headers are created:
|
||||
* connection_set_state(con, CON_STATE_HANDLE_RESPONSE_HEADER)
|
||||
* - after content is generated close output queue:
|
||||
* con->out->is_closed = TRUE
|
||||
*/
|
||||
PluginHandleContent handle_content;
|
||||
PluginHandleVRequest handle_request_body;
|
||||
|
||||
/** called for every plugin after connection got closed (response end, reset by peer, error)
|
||||
* the plugins code must not depend on any order of plugins loaded
|
||||
*/
|
||||
PluginHandleClose handle_close;
|
||||
|
||||
/** called for every plugin after vrequest got reset */
|
||||
PluginHandleVRClose handle_vrclose;
|
||||
|
||||
const plugin_option *options;
|
||||
const plugin_action *actions;
|
||||
const plugin_setup *setups;
|
||||
|
@ -123,6 +122,7 @@ LI_API void release_option(server *srv, option_set *mark); /**< Does not free th
|
|||
|
||||
LI_API void plugins_prepare_callbacks(server *srv);
|
||||
LI_API void plugins_handle_close(connection *con);
|
||||
LI_API void plugins_handle_vrclose(vrequest *vr);
|
||||
|
||||
/* Needed for config frontends */
|
||||
/** For parsing 'somemod.option = "somevalue"', free value after call */
|
||||
|
|
|
@ -10,7 +10,7 @@ struct request_uri {
|
|||
|
||||
GString *scheme;
|
||||
GString *authority;
|
||||
GString *path;
|
||||
GString *path, *orig_path;
|
||||
GString *query;
|
||||
|
||||
GString *host; /* without userinfo and port */
|
||||
|
|
|
@ -48,6 +48,7 @@ struct server {
|
|||
GHashTable *setups; /**< const gchar* => (server_setup*) */
|
||||
|
||||
GArray *plugins_handle_close; /** list of handle_close callbacks */
|
||||
GArray *plugins_handle_vrclose; /** list of handle_vrclose callbacks */
|
||||
|
||||
GArray *option_def_values;/** array of option_value */
|
||||
struct action *mainaction;
|
||||
|
|
|
@ -77,4 +77,9 @@ typedef union {
|
|||
struct sockaddr plain;
|
||||
} sock_addr;
|
||||
|
||||
typedef struct {
|
||||
socklen_t len;
|
||||
sock_addr *addr;
|
||||
} sockaddr;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,4 +55,7 @@ LI_API GString *mimetype_get(vrequest *vr, GString *filename);
|
|||
/* converts a sock_addr to a human readable string. ipv4 and ipv6 supported. if dest is NULL, a new string will be allocated */
|
||||
LI_API GString *sockaddr_to_string(sock_addr *saddr, GString *dest);
|
||||
|
||||
LI_API sockaddr sockaddr_from_string(GString *str, guint tcp_default_port);
|
||||
LI_API void sockaddr_clear(sockaddr *saddr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef enum {
|
|||
|
||||
typedef handler_t (*filter_handler)(vrequest *vr, filter *f, plugin *p);
|
||||
typedef handler_t (*vrequest_handler)(vrequest *vr);
|
||||
typedef handler_t (*vrequest_plugin_handler)(vrequest *vr, plugin *p);
|
||||
|
||||
struct filter {
|
||||
chunkqueue *in, *out;
|
||||
|
@ -57,11 +58,12 @@ struct vrequest {
|
|||
vrequest_state state;
|
||||
|
||||
vrequest_handler
|
||||
handle_request_headers, handle_request_body,
|
||||
handle_request_headers,
|
||||
handle_response_headers, handle_response_body,
|
||||
handle_response_error; /* this is _not_ for 500 - internal error */
|
||||
|
||||
GPtrArray *plugin_ctx;
|
||||
plugin *backend;
|
||||
|
||||
request request;
|
||||
physical physical;
|
||||
|
@ -113,7 +115,7 @@ LI_API void vrequest_handle_response_body(vrequest *vr);
|
|||
/* response completely ready */
|
||||
LI_API gboolean vrequest_handle_direct(vrequest *vr);
|
||||
/* handle request over time */
|
||||
LI_API gboolean vrequest_handle_indirect(vrequest *vr, vrequest_handler handle_request_body);
|
||||
LI_API gboolean vrequest_handle_indirect(vrequest *vr, plugin *p);
|
||||
|
||||
LI_API void vrequest_state_machine(vrequest *vr);
|
||||
LI_API void vrequest_joblist_append(vrequest *vr);
|
||||
|
|
|
@ -22,6 +22,8 @@ OPTION(WITH_OPENSSL "with openssl-support [default: on]" ON)
|
|||
OPTION(WITH_PCRE "with regex support [default: on]" ON)
|
||||
OPTION(WITH_LUA "with lua 5.1 for lua-configfile [default: on]" ON)
|
||||
OPTION(BUILD_STATIC "build a static lighttpd with all modules added")
|
||||
OPTION(BUILD_EXTRA_WARNINGS "extra warnings")
|
||||
|
||||
IF(BUILD_STATIC)
|
||||
SET(LIGHTTPD_STATIC 1)
|
||||
ELSE(BUILD_STATIC)
|
||||
|
@ -332,10 +334,18 @@ ADD_EXECUTABLE(lighttpd
|
|||
)
|
||||
SET(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} lighttpd)
|
||||
|
||||
SET(COMMON_LDFLAGS "${LUA_LDFLAGS} ${EV_LDFLAGS} ${GTHREAD_LDFLAGS} ${GMODULE_LDFLAGS}")
|
||||
SET(COMMON_CFLAGS "${LUA_CFLAGS} ${EV_CFLAGS} ${GTHREAD_CFLAGS} ${GMODULE_CFLAGS}")
|
||||
IF(BUILD_EXTRA_WARNINGS)
|
||||
SET(WARN_FLAGS "-g -O2 -g2 -Wall -Wmissing-prototypes -Wmissing-declarations -Wdeclaration-after-statement -Wno-pointer-sign -Wcast-align -Winline -Wsign-compare -Wnested-externs -Wpointer-arith -Wl,--as-needed")
|
||||
# -Werror -Wbad-function-cast
|
||||
ELSE(BUILD_EXTRA_WARNINGS)
|
||||
SET(WARN_FLAGS "")
|
||||
ENDIF(BUILD_EXTRA_WARNINGS)
|
||||
|
||||
SET(COMMON_LDFLAGS "${LUA_LDFLAGS} ${EV_LDFLAGS} ${GTHREAD_LDFLAGS} ${GMODULE_LDFLAGS} ${WARN_FLAGS}")
|
||||
SET(COMMON_CFLAGS "${LUA_CFLAGS} ${EV_CFLAGS} ${GTHREAD_CFLAGS} ${GMODULE_CFLAGS} ${WARN_FLAGS}")
|
||||
|
||||
ADD_AND_INSTALL_LIBRARY(mod_balancer "modules/mod_balancer.c")
|
||||
ADD_AND_INSTALL_LIBRARY(mod_fastcgi "modules/mod_fastcgi.c")
|
||||
ADD_AND_INSTALL_LIBRARY(mod_fortune "modules/mod_fortune.c")
|
||||
ADD_AND_INSTALL_LIBRARY(mod_status "modules/mod_status.c")
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ struct action_stack_element {
|
|||
};
|
||||
|
||||
void action_release(server *srv, action *a) {
|
||||
if (!a) return;
|
||||
guint i;
|
||||
if (!a) return;
|
||||
assert(g_atomic_int_get(&a->refcount) > 0);
|
||||
if (g_atomic_int_dec_and_test(&a->refcount)) {
|
||||
switch (a->type) {
|
||||
|
@ -192,6 +192,9 @@ handler_t action_execute(vrequest *vr) {
|
|||
|
||||
while (NULL != (ase = action_stack_top(as))) {
|
||||
if (as->backend_failed) {
|
||||
vr->state = VRS_HANDLE_REQUEST_HEADERS;
|
||||
vr->backend = NULL;
|
||||
|
||||
/* pop top action in every case (if the balancer itself failed we don't want to restart it) */
|
||||
action_stack_pop(srv, vr, as);
|
||||
while (NULL != (ase = action_stack_top(as)) && (ase->act->type != ACTION_TBALANCER || !ase->act->data.balancer.provide_backlog)) {
|
||||
|
@ -206,6 +209,7 @@ handler_t action_execute(vrequest *vr) {
|
|||
as->backend_failed = FALSE;
|
||||
|
||||
ase->finished = FALSE;
|
||||
a = ase->act;
|
||||
res = a->data.balancer.fallback(vr, ase->backlog_provided, a->data.balancer.param, &ase->data.context, as->backend_error);
|
||||
switch (res) {
|
||||
case HANDLER_GO_ON:
|
||||
|
@ -232,8 +236,8 @@ handler_t action_execute(vrequest *vr) {
|
|||
continue;
|
||||
}
|
||||
|
||||
a = ase->act;
|
||||
vr->con->wrk->stats.actions_executed++;
|
||||
a = ase->act;
|
||||
|
||||
switch (a->type) {
|
||||
case ACTION_TSETTING:
|
||||
|
|
39
src/chunk.c
39
src/chunk.c
|
@ -280,7 +280,7 @@ void chunkqueue_append_string(chunkqueue *cq, GString *str) {
|
|||
}
|
||||
|
||||
/* memory gets copied */
|
||||
void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len) {
|
||||
void chunkqueue_append_mem(chunkqueue *cq, const void *mem, gssize len) {
|
||||
chunk *c;
|
||||
if (!len) return;
|
||||
c = chunk_new();
|
||||
|
@ -383,6 +383,8 @@ goffset chunkqueue_steal_len(chunkqueue *out, chunkqueue *in, goffset length) {
|
|||
|
||||
/* steal all chunks from in and put them into out, return number of bytes stolen */
|
||||
goffset chunkqueue_steal_all(chunkqueue *out, chunkqueue *in) {
|
||||
goffset len;
|
||||
|
||||
/* if in->queue is empty, do nothing */
|
||||
if (!in->length) return 0;
|
||||
/* if out->queue is empty, just swap in->queue/out->queue */
|
||||
|
@ -399,7 +401,7 @@ goffset chunkqueue_steal_all(chunkqueue *out, chunkqueue *in) {
|
|||
g_queue_init(in->queue);
|
||||
}
|
||||
/* count bytes in chunkqueues */
|
||||
goffset len = in->length;
|
||||
len = in->length;
|
||||
in->bytes_out += len;
|
||||
in->length = 0;
|
||||
out->bytes_in += len;
|
||||
|
@ -458,3 +460,36 @@ goffset chunkqueue_skip_all(chunkqueue *cq) {
|
|||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
gboolean chunkqueue_extract_to(vrequest *vr, chunkqueue *cq, goffset len, GString *dest) {
|
||||
chunkiter ci;
|
||||
goffset coff, clen;
|
||||
g_string_set_size(dest, 0);
|
||||
if (len > cq->length) return FALSE;
|
||||
|
||||
ci = chunkqueue_iter(cq);
|
||||
coff = 0;
|
||||
clen = chunkiter_length(ci);
|
||||
|
||||
while (len > 0) {
|
||||
coff = 0;
|
||||
clen = chunkiter_length(ci);
|
||||
while (coff < clen) {
|
||||
gchar *buf;
|
||||
off_t we_have;
|
||||
if (HANDLER_GO_ON != chunkiter_read(vr, ci, coff, len, &buf, &we_have)) goto error;
|
||||
g_string_append_len(dest, buf, we_have);
|
||||
coff += we_have;
|
||||
len -= we_have;
|
||||
if (len <= 0) return TRUE;
|
||||
}
|
||||
chunkiter_next(&ci);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
g_string_assign(dest, "");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,10 @@ void chunk_parser_done(chunk_parser_ctx *ctx, goffset len) {
|
|||
}
|
||||
|
||||
gboolean chunk_extract_to(vrequest *vr, chunk_parser_mark from, chunk_parser_mark to, GString *dest) {
|
||||
chunk_parser_mark i;
|
||||
|
||||
g_string_set_size(dest, 0);
|
||||
|
||||
chunk_parser_mark i;
|
||||
for ( i = from; i.ci.element != to.ci.element; chunkiter_next(&i.ci) ) {
|
||||
goffset len = chunkiter_length(i.ci);
|
||||
while (i.pos < len) {
|
||||
|
|
|
@ -67,7 +67,7 @@ condition* lua_get_condition(lua_State *L, int ndx) {
|
|||
return *(condition**) lua_touserdata(L, ndx);
|
||||
}
|
||||
|
||||
condition_lvalue* lua_get_condition_lvalue(lua_State *L, int ndx) {
|
||||
static condition_lvalue* lua_get_condition_lvalue(lua_State *L, int ndx) {
|
||||
if (!lua_isuserdata(L, ndx)) return NULL;
|
||||
if (!lua_getmetatable(L, ndx)) return NULL;
|
||||
luaL_getmetatable(L, LUA_COND_LVALUE);
|
||||
|
@ -79,7 +79,7 @@ condition_lvalue* lua_get_condition_lvalue(lua_State *L, int ndx) {
|
|||
return *(condition_lvalue**) lua_touserdata(L, ndx);
|
||||
}
|
||||
|
||||
cond_lvalue_t lua_get_cond_lvalue_t(lua_State *L, int ndx) {
|
||||
static cond_lvalue_t lua_get_cond_lvalue_t(lua_State *L, int ndx) {
|
||||
if (!lua_isuserdata(L, ndx)) return -1;
|
||||
if (!lua_getmetatable(L, ndx)) return -1;
|
||||
luaL_getmetatable(L, LUA_COND_LVALUE_T);
|
||||
|
@ -152,9 +152,12 @@ static int lua_cond_lvalue_cmp(lua_State *L) {
|
|||
server *srv;
|
||||
GString *sval;
|
||||
condition *c;
|
||||
condition_lvalue *lvalue = lua_get_condition_lvalue(L, 1);
|
||||
condition_lvalue *lvalue;
|
||||
comp_operator_t cmpop;
|
||||
|
||||
lvalue = lua_get_condition_lvalue(L, 1);
|
||||
srv = (server*) lua_touserdata(L, lua_upvalueindex(1));
|
||||
comp_operator_t cmpop = (comp_operator_t) lua_tointeger(L, lua_upvalueindex(2));
|
||||
cmpop = (comp_operator_t) lua_tointeger(L, lua_upvalueindex(2));
|
||||
|
||||
if (NULL == (sval = lua_togstring(L, 2))) return 0;
|
||||
c = condition_new_string(srv, cmpop, lvalue, sval);
|
||||
|
@ -191,7 +194,7 @@ static void lua_push_cond_lvalue_metatable(server *srv, lua_State *L) {
|
|||
}
|
||||
}
|
||||
|
||||
int lua_push_cond_lvalue(server *srv, lua_State *L, condition_lvalue *lvalue) {
|
||||
static int lua_push_cond_lvalue(server *srv, lua_State *L, condition_lvalue *lvalue) {
|
||||
condition_lvalue **pv;
|
||||
|
||||
pv = (condition_lvalue**) lua_newuserdata(L, sizeof(condition_lvalue*));
|
||||
|
@ -231,7 +234,7 @@ static void lua_push_cond_lvalue_t_metatable(server *srv, lua_State *L) {
|
|||
|
||||
/* cond_lvalue_t */
|
||||
|
||||
int lua_push_cond_lvalue_t(server *srv, lua_State *L, cond_lvalue_t t) {
|
||||
static int lua_push_cond_lvalue_t(server *srv, lua_State *L, cond_lvalue_t t) {
|
||||
cond_lvalue_t *pt;
|
||||
|
||||
pt = (cond_lvalue_t*) lua_newuserdata(L, sizeof(cond_lvalue_t));
|
||||
|
|
|
@ -1023,11 +1023,12 @@ void config_parser_finish(server *srv, GList *ctx_stack, gboolean free_all) {
|
|||
config_parser_context_t *ctx;
|
||||
GHashTableIter iter;
|
||||
gpointer key, val;
|
||||
GList *l;
|
||||
|
||||
_printf("ctx_stack size: %u\n", g_list_length(ctx_stack));
|
||||
|
||||
/* clear all contexts from the stack */
|
||||
GList *l = g_list_nth(ctx_stack, 1);
|
||||
l = g_list_nth(ctx_stack, 1);
|
||||
while (l) {
|
||||
ctx = l->data;
|
||||
config_parser_context_free(srv, ctx, FALSE);
|
||||
|
|
|
@ -99,11 +99,15 @@ void connection_internal_error(connection *con) {
|
|||
VR_ERROR(vr, "%s", "Couldn't send '500 Internal Error': headers already sent");
|
||||
connection_error(con);
|
||||
} else {
|
||||
vrequest_reset(con->mainvr);
|
||||
http_headers_reset(con->mainvr->response.headers);
|
||||
VR_ERROR(vr, "%s", "internal error");
|
||||
con->mainvr->response.http_status = 500;
|
||||
con->state = CON_STATE_WRITE;
|
||||
con->mainvr->state = VRS_WRITE_CONTENT;
|
||||
chunkqueue_reset(con->mainvr->out);
|
||||
chunkqueue_reset(con->out);
|
||||
con->mainvr->out->is_closed = TRUE;
|
||||
con->out->is_closed = TRUE;
|
||||
forward_response_body(con);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,15 @@ void environment_set(environment *env, const gchar *key, size_t keylen, const gc
|
|||
g_hash_table_insert(env->table, skey, sval);
|
||||
}
|
||||
|
||||
void environment_insert(environment *env, const gchar *key, size_t keylen, const gchar *val, size_t valuelen) {
|
||||
GString *sval = environment_get(env, key, keylen), *skey;
|
||||
if (!sval) {
|
||||
skey = g_string_new_len(key, keylen);
|
||||
sval = g_string_new_len(val, valuelen);
|
||||
g_hash_table_insert(env->table, skey, sval);
|
||||
}
|
||||
}
|
||||
|
||||
void environment_remove(environment *env, const gchar *key, size_t keylen) {
|
||||
const GString skey = { (gchar*) key, keylen, 0 }; /* fake a constant GString */
|
||||
g_hash_table_remove(env->table, &skey);
|
||||
|
|
|
@ -116,7 +116,7 @@ void http_header_remove_link(http_headers *headers, GList *l) {
|
|||
}
|
||||
|
||||
gboolean http_header_remove(http_headers *headers, const gchar *key, size_t keylen) {
|
||||
GList *l, *lp = NULL;;
|
||||
GList *l, *lp = NULL;
|
||||
gboolean res = FALSE;
|
||||
|
||||
for (l = http_header_find_first(headers, key, keylen); l; l = http_header_find_next(l, key, keylen)) {
|
||||
|
|
|
@ -87,13 +87,15 @@ int main(int argc, char *argv[]) {
|
|||
if (!luaconfig) {
|
||||
GTimeVal start, end;
|
||||
gulong s, millis, micros;
|
||||
g_get_current_time(&start);
|
||||
guint64 d;
|
||||
action *a;
|
||||
config_parser_context_t *ctx;
|
||||
|
||||
g_get_current_time(&start);
|
||||
|
||||
/* standard config frontend */
|
||||
ctx_stack = config_parser_init(srv);
|
||||
config_parser_context_t *ctx = (config_parser_context_t*) ctx_stack->data;
|
||||
ctx = (config_parser_context_t*) ctx_stack->data;
|
||||
if (!config_parser_file(srv, ctx_stack, config_path)) {
|
||||
config_parser_finish(srv, ctx_stack, TRUE);
|
||||
log_thread_start(srv);
|
||||
|
|
|
@ -415,6 +415,8 @@ void log_init(server *srv) {
|
|||
|
||||
void log_cleanup(server *srv) {
|
||||
guint i;
|
||||
log_timestamp_t *ts;
|
||||
|
||||
/* wait for logging thread to exit */
|
||||
if (g_atomic_int_get(&srv->logs.thread_alive) == TRUE)
|
||||
{
|
||||
|
@ -422,14 +424,12 @@ void log_cleanup(server *srv) {
|
|||
g_thread_join(srv->logs.thread);
|
||||
}
|
||||
|
||||
|
||||
log_free(srv, srv->logs.stderr);
|
||||
|
||||
g_hash_table_destroy(srv->logs.targets);
|
||||
g_mutex_free(srv->logs.mutex);
|
||||
g_async_queue_unref(srv->logs.queue);
|
||||
|
||||
log_timestamp_t *ts;
|
||||
for (i = 0; i < srv->logs.timestamps->len; i++) {
|
||||
ts = g_array_index(srv->logs.timestamps, log_timestamp_t*, i);
|
||||
g_print("ts #%d refcount: %d\n", i, ts->refcount);
|
||||
|
@ -475,9 +475,10 @@ void log_thread_finish(server *srv) {
|
|||
}
|
||||
|
||||
void log_thread_wakeup(server *srv) {
|
||||
log_entry_t *e;
|
||||
|
||||
if (!g_atomic_int_get(&srv->logs.thread_alive))
|
||||
log_thread_start(srv);
|
||||
log_entry_t *e;
|
||||
|
||||
e = g_slice_new0(log_entry_t);
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
LI_API gboolean mod_balancer_init(modules *mods, module *mod);
|
||||
LI_API gboolean mod_balancer_free(modules *mods, module *mod);
|
||||
|
||||
typedef enum {
|
||||
BE_ALIVE,
|
||||
BE_OVERLOADED,
|
||||
|
@ -65,9 +68,11 @@ static gboolean balancer_fill_backends(balancer *b, server *srv, value *val) {
|
|||
return FALSE;
|
||||
}
|
||||
assert(srv == oa->data.val_action.srv);
|
||||
backend be = { oa->data.val_action.action, 0, BE_ALIVE };
|
||||
action_acquire(be.act);
|
||||
g_array_append_val(b->backends, be);
|
||||
{
|
||||
backend be = { oa->data.val_action.action, 0, BE_ALIVE };
|
||||
action_acquire(be.act);
|
||||
g_array_append_val(b->backends, be);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} else {
|
||||
|
@ -173,7 +178,7 @@ static void plugin_init(server *srv, plugin *p) {
|
|||
}
|
||||
|
||||
|
||||
LI_API gboolean mod_balancer_init(modules *mods, module *mod) {
|
||||
gboolean mod_balancer_init(modules *mods, module *mod) {
|
||||
MODULE_VERSION_CHECK(mods);
|
||||
|
||||
mod->config = plugin_register(mods->main, "mod_balancer", plugin_init);
|
||||
|
@ -181,7 +186,7 @@ LI_API gboolean mod_balancer_init(modules *mods, module *mod) {
|
|||
return mod->config != NULL;
|
||||
}
|
||||
|
||||
LI_API gboolean mod_balancer_free(modules *mods, module *mod) {
|
||||
gboolean mod_balancer_free(modules *mods, module *mod) {
|
||||
if (mod->config)
|
||||
plugin_free(mods->main, mod->config);
|
||||
|
||||
|
|
|
@ -0,0 +1,681 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
#include <lighttpd/plugin_core.h>
|
||||
|
||||
LI_API gboolean mod_fastcgi_init(modules *mods, module *mod);
|
||||
LI_API gboolean mod_fastcgi_free(modules *mods, module *mod);
|
||||
|
||||
|
||||
struct fastcgi_connection;
|
||||
typedef struct fastcgi_connection fastcgi_connection;
|
||||
struct fastcgi_context;
|
||||
typedef struct fastcgi_context fastcgi_context;
|
||||
struct FCGI_Record;
|
||||
typedef struct FCGI_Record FCGI_Record;
|
||||
|
||||
|
||||
typedef enum {
|
||||
FS_WAIT_FOR_REQUEST,
|
||||
FS_CONNECT,
|
||||
FS_CONNECTING,
|
||||
FS_CONNECTED,
|
||||
FS_DONE
|
||||
} fastcgi_state;
|
||||
|
||||
|
||||
struct FCGI_Record {
|
||||
guint8 version;
|
||||
guint8 type;
|
||||
guint16 requestID;
|
||||
guint16 contentLength;
|
||||
guint8 paddingLength;
|
||||
};
|
||||
|
||||
|
||||
struct fastcgi_connection {
|
||||
fastcgi_context *ctx;
|
||||
vrequest *vr;
|
||||
fastcgi_state state;
|
||||
int fd;
|
||||
ev_io fd_watcher;
|
||||
chunkqueue *fcgi_in, *fcgi_out, *stdout;
|
||||
|
||||
GString *buf_in_record;
|
||||
FCGI_Record fcgi_in_record;
|
||||
guint16 requestid;
|
||||
};
|
||||
|
||||
struct fastcgi_context {
|
||||
gint refcount;
|
||||
sockaddr socket;
|
||||
guint timeout;
|
||||
plugin *plugin;
|
||||
};
|
||||
|
||||
/* fastcgi types */
|
||||
|
||||
#define FCGI_VERSION_1 1
|
||||
#define FCGI_HEADER_LEN 8
|
||||
|
||||
enum FCGI_Type {
|
||||
FCGI_BEGIN_REQUEST = 1,
|
||||
FCGI_ABORT_REQUEST = 2,
|
||||
FCGI_END_REQUEST = 3,
|
||||
FCGI_PARAMS = 4,
|
||||
FCGI_STDIN = 5,
|
||||
FCGI_STDOUT = 6,
|
||||
FCGI_STDERR = 7,
|
||||
FCGI_DATA = 8,
|
||||
FCGI_GET_VALUES = 9,
|
||||
FCGI_GET_VALUES_RESULT = 10,
|
||||
FCGI_UNKNOWN_TYPE = 11
|
||||
};
|
||||
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
|
||||
|
||||
enum FCGI_Flags {
|
||||
FCGI_KEEP_CONN = 1
|
||||
};
|
||||
|
||||
enum FCGI_Role {
|
||||
FCGI_RESPONDER = 1,
|
||||
FCGI_AUTHORIZER = 2,
|
||||
FCGI_FILTER = 3
|
||||
};
|
||||
|
||||
enum FCGI_ProtocolStatus {
|
||||
FCGI_REQUEST_COMPLETE = 0,
|
||||
FCGI_CANT_MPX_CONN = 1,
|
||||
FCGI_OVERLOADED = 2,
|
||||
FCGI_UNKNOWN_ROLE = 3
|
||||
};
|
||||
|
||||
/**********************************************************************************/
|
||||
|
||||
static fastcgi_context* fastcgi_context_new(server *srv, plugin *p, GString *dest_socket) {
|
||||
sockaddr saddr;
|
||||
fastcgi_context* ctx;
|
||||
saddr = sockaddr_from_string(dest_socket, 0);
|
||||
if (NULL == saddr.addr) {
|
||||
ERROR(srv, "Invalid socket address '%s'", dest_socket->str);
|
||||
return NULL;
|
||||
}
|
||||
ctx = g_slice_new0(fastcgi_context);
|
||||
ctx->refcount = 1;
|
||||
ctx->socket = saddr;
|
||||
ctx->timeout = 5;
|
||||
ctx->plugin = p;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void fastcgi_context_release(fastcgi_context *ctx) {
|
||||
if (!ctx) return;
|
||||
assert(g_atomic_int_get(&ctx->refcount) > 0);
|
||||
if (g_atomic_int_dec_and_test(&ctx->refcount)) {
|
||||
sockaddr_clear(&ctx->socket);
|
||||
g_slice_free(fastcgi_context, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void fastcgi_context_acquire(fastcgi_context *ctx) {
|
||||
assert(g_atomic_int_get(&ctx->refcount) > 0);
|
||||
g_atomic_int_inc(&ctx->refcount);
|
||||
}
|
||||
|
||||
static void fastcgi_fd_cb(struct ev_loop *loop, ev_io *w, int revents);
|
||||
|
||||
static fastcgi_connection* fastcgi_connection_new(vrequest *vr, fastcgi_context *ctx) {
|
||||
fastcgi_connection* fcon = g_slice_new(fastcgi_connection);
|
||||
|
||||
fastcgi_context_acquire(ctx);
|
||||
fcon->ctx = ctx;
|
||||
fcon->vr = vr;
|
||||
fcon->fd = -1;
|
||||
ev_init(&fcon->fd_watcher, fastcgi_fd_cb);
|
||||
ev_io_set(&fcon->fd_watcher, -1, 0);
|
||||
fcon->fd_watcher.data = fcon;
|
||||
fcon->fcgi_in = chunkqueue_new();
|
||||
fcon->fcgi_out = chunkqueue_new();
|
||||
fcon->stdout = chunkqueue_new();
|
||||
fcon->buf_in_record = g_string_sized_new(FCGI_HEADER_LEN);
|
||||
fcon->requestid = 1;
|
||||
fcon->state = FS_WAIT_FOR_REQUEST;
|
||||
return fcon;
|
||||
}
|
||||
|
||||
static void fastcgi_connection_free(fastcgi_connection *fcon) {
|
||||
vrequest *vr;
|
||||
if (!fcon) return;
|
||||
|
||||
vr = fcon->vr;
|
||||
ev_io_stop(vr->con->wrk->loop, &fcon->fd_watcher);
|
||||
fastcgi_context_release(fcon->ctx);
|
||||
if (fcon->fd != -1) close(fcon->fd);
|
||||
|
||||
chunkqueue_free(fcon->fcgi_in);
|
||||
chunkqueue_free(fcon->fcgi_out);
|
||||
chunkqueue_free(fcon->stdout);
|
||||
g_string_free(fcon->buf_in_record, TRUE);
|
||||
|
||||
g_slice_free(fastcgi_connection, fcon);
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/* fastcgi stream helper */
|
||||
|
||||
static const gchar __padding[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
static void append_padding(GString *s, guint8 padlen) {
|
||||
g_string_append_len(s, __padding, padlen);
|
||||
}
|
||||
|
||||
/* returns padding length */
|
||||
static guint8 stream_build_fcgi_record(GString *buf, guint8 type, guint16 requestid, guint16 datalen) {
|
||||
guint16 w;
|
||||
guint8 padlen = (8 - (datalen & 0x7)) % 8; /* padding must be < 8 */
|
||||
|
||||
g_string_set_size(buf, FCGI_HEADER_LEN);
|
||||
g_string_truncate(buf, 0);
|
||||
|
||||
g_string_append_c(buf, FCGI_VERSION_1);
|
||||
g_string_append_c(buf, type);
|
||||
w = htons(requestid);
|
||||
g_string_append_len(buf, (const gchar*) &w, sizeof(w));
|
||||
w = htons(datalen);
|
||||
g_string_append_len(buf, (const gchar*) &w, sizeof(w));
|
||||
g_string_append_c(buf, padlen);
|
||||
g_string_append_c(buf, 0);
|
||||
return padlen;
|
||||
}
|
||||
|
||||
/* returns padding length */
|
||||
static guint8 stream_send_fcgi_record(chunkqueue *out, guint8 type, guint16 requestid, guint16 datalen) {
|
||||
GString *record = g_string_sized_new(FCGI_HEADER_LEN);
|
||||
guint8 padlen = stream_build_fcgi_record(record, type, requestid, datalen);
|
||||
chunkqueue_append_string(out, record);
|
||||
return padlen;
|
||||
}
|
||||
|
||||
static void stream_send_data(chunkqueue *out, guint8 type, guint16 requestid, const gchar *data, size_t datalen) {
|
||||
while (datalen > 0) {
|
||||
guint16 tosend = (datalen > G_MAXUINT16) ? G_MAXUINT16 : datalen;
|
||||
guint8 padlen = stream_send_fcgi_record(out, type, requestid, tosend);
|
||||
GString *tmps = g_string_sized_new(tosend + padlen);
|
||||
g_string_append_len(tmps, data, tosend);
|
||||
append_padding(tmps, padlen);
|
||||
chunkqueue_append_string(out, tmps);
|
||||
data += tosend;
|
||||
datalen -= tosend;
|
||||
}
|
||||
}
|
||||
|
||||
/* kills string */
|
||||
static void stream_send_string(chunkqueue *out, guint8 type, guint16 requestid, GString *data) {
|
||||
if (data->len > G_MAXUINT16) {
|
||||
stream_send_data(out, type, requestid, GSTR_LEN(data));
|
||||
g_string_free(data, TRUE);
|
||||
} else {
|
||||
guint8 padlen = stream_send_fcgi_record(out, type, requestid, data->len);
|
||||
append_padding(data, padlen);
|
||||
chunkqueue_append_string(out, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_send_chunks(chunkqueue *out, guint8 type, guint16 requestid, chunkqueue *in) {
|
||||
while (in->length > 0) {
|
||||
guint16 tosend = (in->length > G_MAXUINT16) ? G_MAXUINT16 : in->length;
|
||||
guint8 padlen = stream_send_fcgi_record(out, type, requestid, tosend);
|
||||
chunkqueue_steal_len(out, in, tosend);
|
||||
chunkqueue_append_mem(out, __padding, padlen);
|
||||
}
|
||||
|
||||
if (in->is_closed) {
|
||||
stream_send_fcgi_record(out, type, requestid, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean _append_str_len(GString *s, size_t len) {
|
||||
if (len > G_MAXINT32) return FALSE;
|
||||
if (len > 127) {
|
||||
guint32 i = htonl(len | (1 << 31));
|
||||
g_string_append_len(s, (const gchar*) &i, sizeof(i));
|
||||
} else {
|
||||
g_string_append_c(s, (char) len);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean append_key_value_pair(GString *s, const gchar *key, size_t keylen, const gchar *val, size_t valuelen) {
|
||||
if (!_append_str_len(s, keylen) || !_append_str_len(s, valuelen)) return FALSE;
|
||||
g_string_append_len(s, key, keylen);
|
||||
g_string_append_len(s, val, valuelen);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
|
||||
static void fastcgi_send_begin(fastcgi_connection *fcon) {
|
||||
GString *buf = g_string_sized_new(16);
|
||||
guint16 w;
|
||||
|
||||
stream_build_fcgi_record(buf, FCGI_BEGIN_REQUEST, fcon->requestid, 8);
|
||||
w = htons(FCGI_RESPONDER);
|
||||
g_string_append_len(buf, (const char*) &w, sizeof(w));
|
||||
g_string_append_c(buf, 0); /* TODO: FCGI_KEEP_CONN */
|
||||
append_padding(buf, 5);
|
||||
chunkqueue_append_string(fcon->fcgi_out, buf);
|
||||
}
|
||||
|
||||
static void fastcgi_env_setup(vrequest *vr) {
|
||||
connection *con = vr->con;
|
||||
GString *tmp = con->wrk->tmp_str;
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_SOFTWARE"), GSTR_LEN(CORE_OPTION(CORE_OPTION_SERVER_TAG).string));
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_NAME"), GSTR_LEN(vr->request.uri.host));
|
||||
environment_insert(&vr->env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
|
||||
{
|
||||
guint port = 0;
|
||||
switch (con->local_addr.plain.sa_family) {
|
||||
case AF_INET: port = con->local_addr.ipv4.sin_port; break;
|
||||
#ifdef HAVE_IPV6
|
||||
case AF_INET6: port = con->local_addr.ipv6.sin6_port; break;
|
||||
#endif
|
||||
}
|
||||
if (port) {
|
||||
g_string_printf(tmp, "%u", port);
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_PORT"), GSTR_LEN(tmp));
|
||||
}
|
||||
}
|
||||
{
|
||||
sockaddr_to_string(&con->local_addr, tmp);
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_ADDR"), GSTR_LEN(tmp));
|
||||
}
|
||||
|
||||
{
|
||||
guint port = 0;
|
||||
switch (con->remote_addr.plain.sa_family) {
|
||||
case AF_INET: port = con->remote_addr.ipv4.sin_port; break;
|
||||
#ifdef HAVE_IPV6
|
||||
case AF_INET6: port = con->remote_addr.ipv6.sin6_port; break;
|
||||
#endif
|
||||
}
|
||||
if (port) {
|
||||
g_string_printf(tmp, "%u", port);
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REMOTE_PORT"), GSTR_LEN(tmp));
|
||||
}
|
||||
}
|
||||
{
|
||||
sockaddr_to_string(&con->remote_addr, tmp);
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REMOTE_ADDR"), GSTR_LEN(tmp));
|
||||
}
|
||||
|
||||
/* TODO? auth vars; i think it would be easier if the auth mod sets them:
|
||||
* REMOTE_USER, AUTH_TYPE
|
||||
*/
|
||||
{
|
||||
g_string_printf(tmp, "%" L_GOFFSET_MODIFIER "i", vr->request.content_length);
|
||||
environment_insert(&vr->env, CONST_STR_LEN("CONTENT_LENGTH"), GSTR_LEN(tmp));
|
||||
}
|
||||
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SCRIPT_NAME"), GSTR_LEN(vr->request.uri.path));
|
||||
|
||||
environment_insert(&vr->env, CONST_STR_LEN("PATH_INFO"), GSTR_LEN(vr->physical.pathinfo));
|
||||
if (vr->physical.pathinfo->len) {
|
||||
g_string_truncate(tmp, 0);
|
||||
g_string_append_len(tmp, GSTR_LEN(vr->physical.doc_root)); /* TODO: perhaps an option for alternative doc-root? */
|
||||
g_string_append_len(tmp, GSTR_LEN(vr->physical.pathinfo));
|
||||
environment_insert(&vr->env, CONST_STR_LEN("PATH_TRANSLATED"), GSTR_LEN(tmp));
|
||||
}
|
||||
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SCRIPT_FILENAME"), GSTR_LEN(vr->physical.path));
|
||||
environment_insert(&vr->env, CONST_STR_LEN("DOCUMENT_ROOT"), GSTR_LEN(vr->physical.doc_root));
|
||||
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REQUEST_URI"), GSTR_LEN(vr->request.uri.orig_path));
|
||||
if (!g_string_equal(vr->request.uri.orig_path, vr->request.uri.path)) {
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REDIRECT_URI"), GSTR_LEN(vr->request.uri.path));
|
||||
}
|
||||
environment_insert(&vr->env, CONST_STR_LEN("QUERY_STRING"), GSTR_LEN(vr->request.uri.query));
|
||||
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REQUEST_METHOD"), GSTR_LEN(vr->request.http_method_str));
|
||||
environment_insert(&vr->env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */
|
||||
switch (vr->request.http_version) {
|
||||
case HTTP_VERSION_1_1:
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_PROTOCOL"), CONST_STR_LEN("HTTP/1.1"));
|
||||
break;
|
||||
case HTTP_VERSION_1_0:
|
||||
default:
|
||||
environment_insert(&vr->env, CONST_STR_LEN("SERVER_PROTOCOL"), CONST_STR_LEN("HTTP/1.0"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (con->is_ssl) {
|
||||
environment_insert(&vr->env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
|
||||
}
|
||||
}
|
||||
|
||||
static void fastcgi_send_env(vrequest *vr, fastcgi_connection *fcon) {
|
||||
GHashTableIter i;
|
||||
gpointer key, val;
|
||||
GString *buf = g_string_sized_new(0);
|
||||
|
||||
g_hash_table_iter_init(&i, vr->env.table);
|
||||
while (g_hash_table_iter_next(&i, &key, &val)) {
|
||||
append_key_value_pair(buf, GSTR_LEN((GString*) key), GSTR_LEN((GString*) val));
|
||||
}
|
||||
|
||||
/* TODO: send headers */
|
||||
|
||||
stream_send_string(fcon->fcgi_out, FCGI_PARAMS, fcon->requestid, buf);
|
||||
stream_send_fcgi_record(fcon->fcgi_out, FCGI_PARAMS, fcon->requestid, 0);
|
||||
}
|
||||
|
||||
static void fastcgi_forward_request(vrequest *vr, fastcgi_connection *fcon) {
|
||||
stream_send_chunks(fcon->fcgi_out, FCGI_STDIN, fcon->requestid, vr->in);
|
||||
if (fcon->fcgi_out->length > 0)
|
||||
ev_io_add_events(vr->con->wrk->loop, &fcon->fd_watcher, EV_WRITE);
|
||||
}
|
||||
|
||||
static gboolean fastcgi_get_packet(fastcgi_connection *fcon) {
|
||||
const gchar *data;
|
||||
gint len;
|
||||
if (!chunkqueue_extract_to(fcon->vr, fcon->fcgi_in, FCGI_HEADER_LEN, fcon->buf_in_record)) return FALSE; /* need more data */
|
||||
|
||||
data = fcon->buf_in_record->str;
|
||||
fcon->fcgi_in_record.version = data[0];
|
||||
fcon->fcgi_in_record.type = data[1];
|
||||
fcon->fcgi_in_record.requestID = (data[2] << 8) | (data[3]);
|
||||
fcon->fcgi_in_record.contentLength = (data[4] << 8) | (data[5]);
|
||||
fcon->fcgi_in_record.paddingLength = data[6];
|
||||
|
||||
len = ((gint) fcon->fcgi_in_record.contentLength) + fcon->fcgi_in_record.paddingLength + FCGI_HEADER_LEN;
|
||||
|
||||
if (len > fcon->fcgi_in->length) return FALSE; /* need more data */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void fastcgi_parse_response(fastcgi_connection *fcon) {
|
||||
while (fastcgi_get_packet(fcon)) {
|
||||
if (fcon->fcgi_in_record.version != FCGI_VERSION_1) {
|
||||
VR_ERROR(fcon->vr, "Unknown fastcgi protocol version %i", (gint) fcon->fcgi_in_record.version);
|
||||
close(fcon->fd);
|
||||
fcon->fd = -1;
|
||||
vrequest_error(fcon->vr);
|
||||
return;
|
||||
}
|
||||
chunkqueue_skip(fcon->fcgi_in, FCGI_HEADER_LEN);
|
||||
switch (fcon->fcgi_in_record.type) {
|
||||
case FCGI_END_REQUEST:
|
||||
chunkqueue_skip(fcon->fcgi_in, fcon->fcgi_in_record.contentLength);
|
||||
fcon->stdout->is_closed = TRUE;
|
||||
break;
|
||||
case FCGI_STDOUT:
|
||||
if (0 == fcon->fcgi_in_record.contentLength) {
|
||||
fcon->stdout->is_closed = TRUE;
|
||||
} else {
|
||||
chunkqueue_steal_len(fcon->stdout, fcon->fcgi_in, fcon->fcgi_in_record.contentLength);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VR_WARNING(fcon->vr, "Unhandled fastcgi record type %i", (gint) fcon->fcgi_in_record.type);
|
||||
chunkqueue_skip(fcon->fcgi_in, fcon->fcgi_in_record.contentLength);
|
||||
break;
|
||||
}
|
||||
chunkqueue_skip(fcon->fcgi_in, fcon->fcgi_in_record.paddingLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
|
||||
static handler_t fastcgi_statemachine(vrequest *vr, fastcgi_connection *fcon);
|
||||
|
||||
static void fastcgi_fd_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
fastcgi_connection *fcon = (fastcgi_connection*) w->data;
|
||||
|
||||
if (fcon->state == FS_CONNECTING) {
|
||||
if (HANDLER_GO_ON != fastcgi_statemachine(fcon->vr, fcon)) {
|
||||
vrequest_error(fcon->vr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (revents & EV_READ) {
|
||||
if (fcon->fcgi_in->is_closed) {
|
||||
ev_io_rem_events(loop, w, EV_READ);
|
||||
} else {
|
||||
switch (network_read(fcon->vr, w->fd, fcon->fcgi_in)) {
|
||||
case NETWORK_STATUS_SUCCESS:
|
||||
break;
|
||||
case NETWORK_STATUS_FATAL_ERROR:
|
||||
VR_ERROR(fcon->vr, "%s", "network read fatal error");
|
||||
vrequest_error(fcon->vr);
|
||||
return;
|
||||
case NETWORK_STATUS_CONNECTION_CLOSE:
|
||||
fcon->fcgi_in->is_closed = TRUE;
|
||||
ev_io_stop(loop, w);
|
||||
close(fcon->fd);
|
||||
fcon->fd = -1;
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_EVENT:
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
|
||||
/* TODO: aio */
|
||||
ev_io_rem_events(loop, w, EV_READ);
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_FD:
|
||||
/* TODO: wait for fd */
|
||||
ev_io_rem_events(loop, w, EV_READ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fcon->fd != -1 && (revents & EV_WRITE)) {
|
||||
if (fcon->fcgi_out->length > 0) {
|
||||
switch (network_write(fcon->vr, w->fd, fcon->fcgi_out)) {
|
||||
case NETWORK_STATUS_SUCCESS:
|
||||
break;
|
||||
case NETWORK_STATUS_FATAL_ERROR:
|
||||
VR_ERROR(fcon->vr, "%s", "network write fatal error");
|
||||
vrequest_error(fcon->vr);
|
||||
return;
|
||||
case NETWORK_STATUS_CONNECTION_CLOSE:
|
||||
fcon->fcgi_in->is_closed = TRUE;
|
||||
ev_io_stop(loop, w);
|
||||
close(fcon->fd);
|
||||
fcon->fd = -1;
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_EVENT:
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
|
||||
ev_io_rem_events(loop, w, EV_WRITE);
|
||||
/* TODO: aio */
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_FD:
|
||||
ev_io_rem_events(loop, w, EV_WRITE);
|
||||
/* TODO: wait for fd */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fcon->fcgi_out->length == 0) {
|
||||
ev_io_rem_events(loop, w, EV_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
fastcgi_parse_response(fcon);
|
||||
|
||||
/* TODO: parse stdout response */
|
||||
if (fcon->vr->out->bytes_in == 0 && fcon->stdout->length > 0) {
|
||||
fcon->vr->response.http_status = 200;
|
||||
vrequest_handle_response_headers(fcon->vr);
|
||||
}
|
||||
chunkqueue_steal_all(fcon->vr->out, fcon->stdout);
|
||||
fcon->vr->out->is_closed = fcon->stdout->is_closed;
|
||||
vrequest_handle_response_body(fcon->vr);
|
||||
|
||||
if (fcon->fcgi_in->is_closed && !fcon->vr->out->is_closed) {
|
||||
VR_ERROR(fcon->vr, "%s", "unexpected end-of-file (perhaps the fastcgi process died)");
|
||||
vrequest_error(fcon->vr);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/* state machine */
|
||||
|
||||
static void fastcgi_close(vrequest *vr, plugin *p);
|
||||
|
||||
static handler_t fastcgi_statemachine(vrequest *vr, fastcgi_connection *fcon) {
|
||||
plugin *p = fcon->ctx->plugin;
|
||||
|
||||
switch (fcon->state) {
|
||||
case FS_WAIT_FOR_REQUEST:
|
||||
if (-1 == vr->request.content_length || vr->request.content_length != vr->in->length) return HANDLER_GO_ON;
|
||||
fcon->state = FS_CONNECT;
|
||||
|
||||
/* fall through */
|
||||
case FS_CONNECT:
|
||||
fcon->fd = socket(fcon->ctx->socket.addr->plain.sa_family, SOCK_STREAM, 0);
|
||||
if (-1 == fcon->fd) {
|
||||
if (errno == EMFILE || errno == EINTR) {
|
||||
return HANDLER_WAIT_FOR_FD;
|
||||
}
|
||||
VR_ERROR(vr, "Couldn't open socket: %s", g_strerror(errno));
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
fd_init(fcon->fd);
|
||||
ev_io_set(&fcon->fd_watcher, fcon->fd, EV_READ | EV_WRITE);
|
||||
ev_io_start(vr->con->wrk->loop, &fcon->fd_watcher);
|
||||
|
||||
/* fall through */
|
||||
case FS_CONNECTING:
|
||||
if (-1 == connect(fcon->fd, &fcon->ctx->socket.addr->plain, fcon->ctx->socket.len)) {
|
||||
switch (errno) {
|
||||
case EINPROGRESS:
|
||||
case EALREADY:
|
||||
case EINTR:
|
||||
fcon->state = FS_CONNECTING;
|
||||
return HANDLER_GO_ON;
|
||||
case EAGAIN: /* backend overloaded */
|
||||
fastcgi_close(vr, p);
|
||||
vrequest_backend_overloaded(vr);
|
||||
return HANDLER_GO_ON;
|
||||
default:
|
||||
VR_ERROR(vr, "Couldn't connect: %s", g_strerror(errno));
|
||||
fastcgi_close(vr, p);
|
||||
vrequest_backend_dead(vr);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
}
|
||||
|
||||
fcon->state = FS_CONNECTED;
|
||||
|
||||
/* prepare stream */
|
||||
fastcgi_send_begin(fcon);
|
||||
fastcgi_env_setup(vr);
|
||||
fastcgi_send_env(vr, fcon);
|
||||
|
||||
/* fall through */
|
||||
case FS_CONNECTED:
|
||||
fastcgi_forward_request(vr, fcon);
|
||||
break;
|
||||
|
||||
case FS_DONE:
|
||||
break;
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************************/
|
||||
|
||||
static handler_t fastcgi_handle(vrequest *vr, gpointer param, gpointer *context) {
|
||||
fastcgi_context *ctx = (fastcgi_context*) param;
|
||||
fastcgi_connection *fcon;
|
||||
UNUSED(context);
|
||||
if (!vrequest_handle_indirect(vr, ctx->plugin)) return HANDLER_GO_ON;
|
||||
|
||||
fcon = fastcgi_connection_new(vr, ctx);
|
||||
if (!fcon) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
g_ptr_array_index(vr->plugin_ctx, ctx->plugin->id) = fcon;
|
||||
|
||||
return fastcgi_statemachine(vr, fcon);
|
||||
}
|
||||
|
||||
|
||||
static handler_t fastcgi_handle_request_body(vrequest *vr, plugin *p) {
|
||||
fastcgi_connection *fcon = (fastcgi_connection*) g_ptr_array_index(vr->plugin_ctx, p->id);
|
||||
if (!fcon) return HANDLER_ERROR;
|
||||
|
||||
return fastcgi_statemachine(vr, fcon);
|
||||
}
|
||||
|
||||
static void fastcgi_close(vrequest *vr, plugin *p) {
|
||||
fastcgi_connection *fcon = (fastcgi_connection*) g_ptr_array_index(vr->plugin_ctx, p->id);
|
||||
g_ptr_array_index(vr->plugin_ctx, p->id) = NULL;
|
||||
|
||||
fastcgi_connection_free(fcon);
|
||||
}
|
||||
|
||||
|
||||
static void fastcgi_free(server *srv, gpointer param) {
|
||||
fastcgi_context *ctx = (fastcgi_context*) param;
|
||||
UNUSED(srv);
|
||||
|
||||
fastcgi_context_release(ctx);
|
||||
}
|
||||
|
||||
static action* fastcgi_create(server *srv, plugin* p, value *val) {
|
||||
fastcgi_context *ctx;
|
||||
|
||||
if (val->type != VALUE_STRING) {
|
||||
ERROR(srv, "%s", "fastcgi expects a string as parameter");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ctx = fastcgi_context_new(srv, p, val->data.string);
|
||||
if (!ctx) return NULL;
|
||||
|
||||
return action_new_function(fastcgi_handle, NULL, fastcgi_free, ctx);
|
||||
}
|
||||
|
||||
static const plugin_option options[] = {
|
||||
{ NULL, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static const plugin_action actions[] = {
|
||||
{ "fastcgi", fastcgi_create },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const plugin_setup setups[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
static void plugin_init(server *srv, plugin *p) {
|
||||
UNUSED(srv);
|
||||
|
||||
p->options = options;
|
||||
p->actions = actions;
|
||||
p->setups = setups;
|
||||
|
||||
p->handle_request_body = fastcgi_handle_request_body;
|
||||
p->handle_vrclose = fastcgi_close;
|
||||
}
|
||||
|
||||
|
||||
gboolean mod_fastcgi_init(modules *mods, module *mod) {
|
||||
MODULE_VERSION_CHECK(mods);
|
||||
|
||||
mod->config = plugin_register(mods->main, "mod_fastcgi", plugin_init);
|
||||
|
||||
return mod->config != NULL;
|
||||
}
|
||||
|
||||
gboolean mod_fastcgi_free(modules *mods, module *mod) {
|
||||
if (mod->config)
|
||||
plugin_free(mods->main, mod->config);
|
||||
|
||||
return TRUE;
|
||||
}
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
LI_API gboolean mod_fortune_init(modules *mods, module *mod);
|
||||
LI_API gboolean mod_fortune_free(modules *mods, module *mod);
|
||||
|
||||
/* globals */
|
||||
struct fortune_data;
|
||||
typedef struct fortune_data fortune_data;
|
||||
|
@ -153,8 +156,8 @@ static const plugin_setup setups[] = {
|
|||
|
||||
|
||||
static void plugin_fortune_free(server *srv, plugin *p) {
|
||||
UNUSED(srv);
|
||||
fortune_data *fd = p->data;
|
||||
UNUSED(srv);
|
||||
|
||||
/* free the cookies! */
|
||||
for (guint i = 0; i < fd->cookies->len; i++)
|
||||
|
@ -167,8 +170,8 @@ static void plugin_fortune_free(server *srv, plugin *p) {
|
|||
}
|
||||
|
||||
static void plugin_fortune_init(server *srv, plugin *p) {
|
||||
UNUSED(srv);
|
||||
fortune_data *fd;
|
||||
UNUSED(srv);
|
||||
|
||||
p->options = options;
|
||||
p->actions = actions;
|
||||
|
@ -182,7 +185,7 @@ static void plugin_fortune_init(server *srv, plugin *p) {
|
|||
}
|
||||
|
||||
|
||||
LI_API gboolean mod_fortune_init(modules *mods, module *mod) {
|
||||
gboolean mod_fortune_init(modules *mods, module *mod) {
|
||||
server *srv = mods->main;
|
||||
|
||||
MODULE_VERSION_CHECK(mods);
|
||||
|
@ -195,7 +198,7 @@ LI_API gboolean mod_fortune_init(modules *mods, module *mod) {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
LI_API gboolean mod_fortune_free(modules *mods, module *mod) {
|
||||
gboolean mod_fortune_free(modules *mods, module *mod) {
|
||||
if (mod->config)
|
||||
plugin_free(mods->main, mod->config);
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <lighttpd/base.h>
|
||||
#include <lighttpd/collect.h>
|
||||
|
||||
LI_API gboolean mod_status_init(modules *mods, module *mod);
|
||||
LI_API gboolean mod_status_free(modules *mods, module *mod);
|
||||
|
||||
/* html snippet constants */
|
||||
static const gchar header[] =
|
||||
|
@ -162,8 +164,9 @@ struct mod_status_wrk_data {
|
|||
|
||||
/* the CollectFunc */
|
||||
static gpointer status_collect_func(worker *wrk, gpointer fdata) {
|
||||
UNUSED(fdata);
|
||||
mod_status_wrk_data *sd = g_slice_new(mod_status_wrk_data);
|
||||