2
0
Fork 0
lighttpd2/src/main/actions.c

338 lines
9.6 KiB
C
Raw Normal View History

2008-06-29 14:10:00 +00:00
#include <lighttpd/base.h>
2008-06-29 14:21:50 +00:00
2008-06-30 10:25:01 +00:00
typedef struct action_stack_element action_stack_element;
2008-06-29 14:10:00 +00:00
2008-06-30 10:25:01 +00:00
struct action_stack_element {
liAction *act;
2008-12-09 23:23:27 +00:00
union {
gpointer context;
guint pos;
} data;
gboolean finished, backlog_provided;
2008-06-30 10:25:01 +00:00
};
2008-06-29 14:10:00 +00:00
2009-07-09 20:17:24 +00:00
void li_action_release(liServer *srv, liAction *a) {
2008-08-03 20:20:36 +00:00
guint i;
if (!a) return;
2008-09-09 14:41:02 +00:00
assert(g_atomic_int_get(&a->refcount) > 0);
if (g_atomic_int_dec_and_test(&a->refcount)) {
2008-08-03 20:20:36 +00:00
switch (a->type) {
case ACTION_TSETTING:
break;
case ACTION_TSETTINGPTR:
li_release_optionptr(srv, a->data.settingptr.value);
2008-08-03 20:20:36 +00:00
break;
2008-08-13 23:05:15 +00:00
case ACTION_TFUNCTION:
if (a->data.function.free) {
a->data.function.free(srv, a->data.function.param);
2008-08-03 20:20:36 +00:00
}
break;
case ACTION_TCONDITION:
2009-07-09 20:17:24 +00:00
li_condition_release(srv, a->data.condition.cond);
li_action_release(srv, a->data.condition.target);
li_action_release(srv, a->data.condition.target_else);
2008-08-03 20:20:36 +00:00
break;
case ACTION_TLIST:
for (i = a->data.list->len; i-- > 0; ) {
2009-07-09 20:17:24 +00:00
li_action_release(srv, g_array_index(a->data.list, liAction*, i));
2008-08-03 20:20:36 +00:00
}
g_array_free(a->data.list, TRUE);
2008-08-03 20:20:36 +00:00
break;
case ACTION_TBALANCER:
if (a->data.balancer.free) {
a->data.balancer.free(srv, a->data.balancer.param);
}
break;
2008-08-03 20:20:36 +00:00
}
g_slice_free(liAction, a);
2008-08-03 20:20:36 +00:00
}
}
2009-07-09 20:17:24 +00:00
void li_action_acquire(liAction *a) {
2008-09-09 14:41:02 +00:00
assert(g_atomic_int_get(&a->refcount) > 0);
g_atomic_int_inc(&a->refcount);
2008-08-03 20:20:36 +00:00
}
2009-07-09 20:17:24 +00:00
liAction *li_action_new_setting(liOptionSet setting) {
liAction *a = g_slice_new(liAction);
2008-07-19 10:23:32 +00:00
a->refcount = 1;
a->type = ACTION_TSETTING;
a->data.setting = setting;
2008-07-19 10:23:32 +00:00
return a;
}
2009-07-09 20:17:24 +00:00
liAction *li_action_new_function(liActionFuncCB func, liActionCleanupCB fcleanup, liActionFreeCB ffree, gpointer param) {
liAction *a;
a = g_slice_new(liAction);
a->refcount = 1;
a->type = ACTION_TFUNCTION;
a->data.function.func = func;
2008-12-09 23:23:27 +00:00
a->data.function.cleanup = fcleanup;
a->data.function.free = ffree;
a->data.function.param = param;
return a;
}
2009-07-09 20:17:24 +00:00
liAction *li_action_new_list() {
liAction *a;
a = g_slice_new(liAction);
a->refcount = 1;
a->type = ACTION_TLIST;
a->data.list = g_array_new(FALSE, TRUE, sizeof(liAction *));
return a;
}
2009-07-09 20:17:24 +00:00
liAction *li_action_new_condition(liCondition *cond, liAction *target, liAction *target_else) {
liAction *a;
a = g_slice_new(liAction);
a->refcount = 1;
a->type = ACTION_TCONDITION;
a->data.condition.cond = cond;
a->data.condition.target = target;
a->data.condition.target_else = target_else;
return a;
}
2008-06-29 14:10:00 +00:00
2009-07-09 20:17:24 +00:00
liAction *li_action_new_balancer(liBackendSelectCB bselect, liBackendFallbackCB bfallback, liBackendFinishedCB bfinished, liBalancerFreeCB bfree, gpointer param, gboolean provide_backlog) {
liAction *a;
a = g_slice_new(liAction);
a->refcount = 1;
a->type = ACTION_TBALANCER;
a->data.balancer.select = bselect;
a->data.balancer.fallback = bfallback;
a->data.balancer.finished = bfinished;
a->data.balancer.free = bfree;
a->data.balancer.param = param;
a->data.balancer.provide_backlog = provide_backlog;
return a;
}
static void action_stack_element_release(liServer *srv, liVRequest *vr, action_stack_element *ase) {
liAction *a = ase->act;
2008-12-09 23:23:27 +00:00
if (!ase || !a) return;
switch (a->type) {
case ACTION_TSETTING:
case ACTION_TSETTINGPTR:
break;
case ACTION_TFUNCTION:
if (ase->data.context && a->data.function.cleanup) {
a->data.function.cleanup(vr, a->data.function.param, ase->data.context);
}
break;
case ACTION_TCONDITION:
if (a->data.condition.cond->rvalue.type == LI_COND_VALUE_REGEXP) {
/* pop regex stack */
GArray *rs = vr->action_stack.regex_stack;
/* cheap check to prevent segfault if condition errored without pushing onto stack; whole stack gets cleaned anyways */
if (rs->len) {
liActionRegexStackElement *arse = &g_array_index(rs, liActionRegexStackElement, rs->len - 1);
if (arse->string)
g_string_free(arse->string, TRUE);
g_match_info_free(arse->match_info);
g_array_set_size(rs, rs->len - 1);
}
}
break;
case ACTION_TLIST:
break;
case ACTION_TBALANCER:
a->data.balancer.finished(vr, a->data.balancer.param, ase->data.context);
break;
2008-12-09 23:23:27 +00:00
}
2009-07-09 20:17:24 +00:00
li_action_release(srv, ase->act);
2008-08-03 20:20:36 +00:00
ase->act = NULL;
2008-12-09 23:23:27 +00:00
ase->data.context = NULL;
2008-06-29 14:10:00 +00:00
}
2009-07-09 20:17:24 +00:00
void li_action_stack_init(liActionStack *as) {
2008-06-30 10:25:01 +00:00
as->stack = g_array_sized_new(FALSE, TRUE, sizeof(action_stack_element), 15);
as->regex_stack = g_array_sized_new(FALSE, FALSE, sizeof(liActionRegexStackElement), 15);
g_array_set_size(as->regex_stack, 0);
2008-06-30 10:25:01 +00:00
}
2008-06-29 14:10:00 +00:00
2009-07-09 20:17:24 +00:00
void li_action_stack_reset(liVRequest *vr, liActionStack *as) {
liServer *srv = vr->wrk->srv;
2008-06-30 10:25:01 +00:00
guint i;
for (i = as->stack->len; i-- > 0; ) {
2008-12-09 23:23:27 +00:00
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, i));
2008-06-29 14:10:00 +00:00
}
2008-06-30 10:25:01 +00:00
g_array_set_size(as->stack, 0);
2009-03-01 16:16:58 +00:00
as->backend_failed = FALSE;
2008-06-29 14:10:00 +00:00
}
2009-07-09 20:17:24 +00:00
void li_action_stack_clear(liVRequest *vr, liActionStack *as) {
liServer *srv = vr->wrk->srv;
2008-06-30 10:25:01 +00:00
guint i;
for (i = as->stack->len; i-- > 0; ) {
2008-12-09 23:23:27 +00:00
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, i));
2008-06-29 14:10:00 +00:00
}
2008-06-30 10:25:01 +00:00
g_array_free(as->stack, TRUE);
g_array_free(as->regex_stack, TRUE);
2008-08-03 20:20:36 +00:00
as->stack = NULL;
2008-06-29 14:10:00 +00:00
}
static action_stack_element *action_stack_top(liActionStack* as) {
return as->stack->len > 0 ? &g_array_index(as->stack, action_stack_element, as->stack->len - 1) : NULL;
}
2008-06-30 10:25:01 +00:00
/** handle sublist now, remember current position (stack) */
2009-07-09 20:17:24 +00:00
void li_action_enter(liVRequest *vr, liAction *a) {
liActionStack *as = &vr->action_stack;
action_stack_element *top_ase = action_stack_top(as);
action_stack_element ase = { a, { 0 }, FALSE,
(top_ase ? top_ase->backlog_provided || (top_ase->act->type == ACTION_TBALANCER && top_ase->act->data.balancer.provide_backlog) : FALSE) };
2009-07-09 20:17:24 +00:00
li_action_acquire(a);
g_array_append_val(as->stack, ase);
2008-06-30 10:25:01 +00:00
}
static void action_stack_pop(liServer *srv, liVRequest *vr, liActionStack *as) {
2008-12-09 23:23:27 +00:00
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, as->stack->len - 1));
2008-06-30 10:25:01 +00:00
g_array_set_size(as->stack, as->stack->len - 1);
}
2009-07-09 20:17:24 +00:00
liHandlerResult li_action_execute(liVRequest *vr) {
liAction *a;
liActionStack *as = &vr->action_stack;
2008-06-30 10:25:01 +00:00
action_stack_element *ase;
liHandlerResult res;
gboolean condres;
liServer *srv = vr->wrk->srv;
2008-06-30 10:25:01 +00:00
while (NULL != (ase = action_stack_top(as))) {
if (as->backend_failed) {
vr->state = LI_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)) {
action_stack_pop(srv, vr, as);
}
if (!ase) { /* no backlogging balancer found */
2009-07-09 20:17:24 +00:00
if (li_vrequest_handle_direct(vr))
vr->response.http_status = 503;
return LI_HANDLER_GO_ON;
}
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 LI_HANDLER_GO_ON:
ase->finished = TRUE;
break;
case LI_HANDLER_ERROR:
2009-07-09 20:17:24 +00:00
li_action_stack_reset(vr, as);
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;
}
continue;
}
2008-12-09 23:23:27 +00:00
if (ase->finished) {
/* a TFUNCTION may enter sub actions _and_ return GO_ON, so we cannot pop the last element
* but we have to remember we already executed it
*/
if (ase->act->type == ACTION_TBALANCER) {
/* wait until we found a backend */
VREQUEST_WAIT_FOR_RESPONSE_HEADERS(vr);
}
2008-12-09 23:23:27 +00:00
action_stack_pop(srv, vr, as);
2008-06-30 10:25:01 +00:00
continue;
}
2008-08-13 23:05:15 +00:00
vr->wrk->stats.actions_executed++;
a = ase->act;
2008-08-13 23:05:15 +00:00
2008-06-30 10:25:01 +00:00
switch (a->type) {
case ACTION_TSETTING:
vr->options[a->data.setting.ndx] = a->data.setting.value;
2008-12-09 23:23:27 +00:00
action_stack_pop(srv, vr, as);
2008-06-30 10:25:01 +00:00
break;
case ACTION_TSETTINGPTR:
if (vr->optionptrs[a->data.settingptr.ndx] != a->data.settingptr.value) {
g_atomic_int_inc(&a->data.settingptr.value->refcount);
li_release_optionptr(srv, vr->optionptrs[a->data.settingptr.ndx]);
vr->optionptrs[a->data.settingptr.ndx] = a->data.settingptr.value;
}
action_stack_pop(srv, vr, as);
break;
2008-06-30 10:25:01 +00:00
case ACTION_TFUNCTION:
2008-12-09 23:23:27 +00:00
res = a->data.function.func(vr, a->data.function.param, &ase->data.context);
2008-06-30 10:25:01 +00:00
switch (res) {
case LI_HANDLER_GO_ON:
2008-12-09 23:23:27 +00:00
ase->finished = TRUE;
2008-08-06 18:46:42 +00:00
break;
case LI_HANDLER_ERROR:
2009-07-09 20:17:24 +00:00
li_action_stack_reset(vr, as);
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
if (ase != action_stack_top(as)) break; /* allow an action to push another action and rerun after it again */
2008-06-30 10:25:01 +00:00
return res;
}
break;
case ACTION_TCONDITION:
condres = FALSE;
2009-07-09 20:17:24 +00:00
res = li_condition_check(vr, a->data.condition.cond, &condres);
switch (res) {
case LI_HANDLER_GO_ON:
ase->finished = TRUE;
if (condres) {
2009-07-09 20:17:24 +00:00
if (a->data.condition.target) li_action_enter(vr, a->data.condition.target);
}
else if (a->data.condition.target_else) {
2009-07-09 20:17:24 +00:00
li_action_enter(vr, a->data.condition.target_else);
}
break;
case LI_HANDLER_ERROR:
2009-07-09 20:17:24 +00:00
li_action_stack_reset(vr, as);
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;
}
2008-06-30 10:25:01 +00:00
break;
case ACTION_TLIST:
2008-12-09 23:23:27 +00:00
if (ase->data.pos >= a->data.list->len) {
action_stack_pop(srv, vr, as);
} else {
2009-07-09 20:17:24 +00:00
li_action_enter(vr, g_array_index(a->data.list, liAction*, ase->data.pos));
2008-12-09 23:23:27 +00:00
ase->data.pos++;
}
break;
case ACTION_TBALANCER:
res = a->data.balancer.select(vr, ase->backlog_provided, a->data.balancer.param, &ase->data.context);
switch (res) {
case LI_HANDLER_GO_ON:
ase->finished = TRUE;
break;
case LI_HANDLER_ERROR:
2009-07-09 20:17:24 +00:00
li_action_stack_reset(vr, as);
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;
}
break;
2008-06-30 10:25:01 +00:00
}
2008-06-29 14:10:00 +00:00
}
if (as->backend_failed) {
2009-07-09 20:17:24 +00:00
if (li_vrequest_handle_direct(vr))
vr->response.http_status = 503;
}
return LI_HANDLER_GO_ON;
2008-06-29 14:10:00 +00:00
}