2
0
Fork 0

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:
Stefan Bühler 2009-01-01 16:44:42 +01:00
parent c56ade056f
commit c458377d4a
36 changed files with 954 additions and 97 deletions

View File

@ -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 *
********************/

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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;

View File

@ -77,4 +77,9 @@ typedef union {
struct sockaddr plain;
} sock_addr;
typedef struct {
socklen_t len;
sock_addr *addr;
} sockaddr;
#endif

View File

@ -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

View File

@ -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);

View File

@ -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")

View File

@ -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:

View File

@ -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;
}

View File

@ -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) {

View File

@ -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));

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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)) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

681
src/modules/mod_fastcgi.c Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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);