the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.1 KiB
307 lines
8.1 KiB
|
|
#include <lighttpd/base.h> |
|
|
|
struct action_stack_element; |
|
typedef struct action_stack_element action_stack_element; |
|
|
|
struct action_stack_element { |
|
action *act; |
|
union { |
|
gpointer context; |
|
guint pos; |
|
} data; |
|
gboolean finished, backlog_provided; |
|
}; |
|
|
|
void action_release(server *srv, action *a) { |
|
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) { |
|
case ACTION_TSETTING: |
|
release_option(srv, &a->data.setting); |
|
break; |
|
case ACTION_TFUNCTION: |
|
if (a->data.function.free) { |
|
a->data.function.free(srv, a->data.function.param); |
|
} |
|
break; |
|
case ACTION_TCONDITION: |
|
condition_release(srv, a->data.condition.cond); |
|
action_release(srv, a->data.condition.target); |
|
action_release(srv, a->data.condition.target_else); |
|
break; |
|
case ACTION_TLIST: |
|
for (i = a->data.list->len; i-- > 0; ) { |
|
action_release(srv, g_array_index(a->data.list, action*, i)); |
|
} |
|
g_array_free(a->data.list, TRUE); |
|
break; |
|
case ACTION_TBALANCER: |
|
if (a->data.balancer.free) { |
|
a->data.balancer.free(srv, a->data.balancer.param); |
|
} |
|
break; |
|
} |
|
g_slice_free(action, a); |
|
} |
|
} |
|
|
|
void action_acquire(action *a) { |
|
assert(g_atomic_int_get(&a->refcount) > 0); |
|
g_atomic_int_inc(&a->refcount); |
|
} |
|
|
|
action *action_new_setting(option_set setting) { |
|
action *a = g_slice_new(action); |
|
|
|
a->refcount = 1; |
|
a->type = ACTION_TSETTING; |
|
a->data.setting = setting; |
|
|
|
return a; |
|
} |
|
|
|
action *action_new_function(ActionFunc func, ActionCleanup fcleanup, ActionFree ffree, gpointer param) { |
|
action *a; |
|
|
|
a = g_slice_new(action); |
|
a->refcount = 1; |
|
a->type = ACTION_TFUNCTION; |
|
a->data.function.func = func; |
|
a->data.function.cleanup = fcleanup; |
|
a->data.function.free = ffree; |
|
a->data.function.param = param; |
|
|
|
return a; |
|
} |
|
|
|
action *action_new_list() { |
|
action *a; |
|
|
|
a = g_slice_new(action); |
|
a->refcount = 1; |
|
a->type = ACTION_TLIST; |
|
a->data.list = g_array_new(FALSE, TRUE, sizeof(action *)); |
|
|
|
return a; |
|
} |
|
|
|
action *action_new_condition(condition *cond, action *target, action *target_else) { |
|
action *a; |
|
|
|
a = g_slice_new(action); |
|
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; |
|
} |
|
|
|
action *action_new_balancer(BackendSelect bselect, BackendFallback bfallback, BackendFinished bfinished, BalancerFree bfree, gpointer param, gboolean provide_backlog) { |
|
action *a; |
|
|
|
a = g_slice_new(action); |
|
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(server *srv, vrequest *vr, action_stack_element *ase) { |
|
action *a = ase->act; |
|
if (!ase || !a) return; |
|
switch (a->type) { |
|
case ACTION_TSETTING: |
|
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: |
|
case ACTION_TLIST: |
|
break; |
|
case ACTION_TBALANCER: |
|
a->data.balancer.finished(vr, a->data.balancer.param, ase->data.context); |
|
break; |
|
} |
|
action_release(srv, ase->act); |
|
ase->act = NULL; |
|
ase->data.context = NULL; |
|
} |
|
|
|
void action_stack_init(action_stack *as) { |
|
as->stack = g_array_sized_new(FALSE, TRUE, sizeof(action_stack_element), 15); |
|
} |
|
|
|
void action_stack_reset(vrequest *vr, action_stack *as) { |
|
server *srv = vr->con->srv; |
|
guint i; |
|
for (i = as->stack->len; i-- > 0; ) { |
|
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, i)); |
|
} |
|
g_array_set_size(as->stack, 0); |
|
} |
|
|
|
void action_stack_clear(vrequest *vr, action_stack *as) { |
|
server *srv = vr->con->srv; |
|
guint i; |
|
for (i = as->stack->len; i-- > 0; ) { |
|
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, i)); |
|
} |
|
g_array_free(as->stack, TRUE); |
|
as->stack = NULL; |
|
} |
|
|
|
static action_stack_element *action_stack_top(action_stack* as) { |
|
return as->stack->len > 0 ? &g_array_index(as->stack, action_stack_element, as->stack->len - 1) : NULL; |
|
} |
|
|
|
/** handle sublist now, remember current position (stack) */ |
|
void action_enter(vrequest *vr, action *a) { |
|
action_stack *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) }; |
|
action_acquire(a); |
|
g_array_append_val(as->stack, ase); |
|
} |
|
|
|
static void action_stack_pop(server *srv, vrequest *vr, action_stack *as) { |
|
action_stack_element_release(srv, vr, &g_array_index(as->stack, action_stack_element, as->stack->len - 1)); |
|
g_array_set_size(as->stack, as->stack->len - 1); |
|
} |
|
|
|
handler_t action_execute(vrequest *vr) { |
|
action *a; |
|
action_stack *as = &vr->action_stack; |
|
action_stack_element *ase; |
|
handler_t res; |
|
gboolean condres; |
|
server *srv = vr->con->srv; |
|
|
|
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)) { |
|
action_stack_pop(srv, vr, as); |
|
ase = action_stack_top(as); |
|
} |
|
if (!ase) { /* no backlogging balancer found */ |
|
if (vrequest_handle_direct(vr)) |
|
vr->response.http_status = 503; |
|
return 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 HANDLER_GO_ON: |
|
ase->finished = TRUE; |
|
break; |
|
case HANDLER_ERROR: |
|
action_stack_reset(vr, as); |
|
case HANDLER_COMEBACK: |
|
case HANDLER_WAIT_FOR_EVENT: |
|
return res; |
|
} |
|
continue; |
|
} |
|
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); |
|
} |
|
action_stack_pop(srv, vr, as); |
|
continue; |
|
} |
|
|
|
vr->con->wrk->stats.actions_executed++; |
|
a = ase->act; |
|
|
|
switch (a->type) { |
|
case ACTION_TSETTING: |
|
vr->options[a->data.setting.ndx] = a->data.setting.value; |
|
action_stack_pop(srv, vr, as); |
|
break; |
|
case ACTION_TFUNCTION: |
|
res = a->data.function.func(vr, a->data.function.param, &ase->data.context); |
|
switch (res) { |
|
case HANDLER_GO_ON: |
|
ase->finished = TRUE; |
|
break; |
|
case HANDLER_ERROR: |
|
action_stack_reset(vr, as); |
|
case HANDLER_COMEBACK: |
|
case HANDLER_WAIT_FOR_EVENT: |
|
return res; |
|
} |
|
break; |
|
case ACTION_TCONDITION: |
|
condres = FALSE; |
|
res = condition_check(vr, a->data.condition.cond, &condres); |
|
switch (res) { |
|
case HANDLER_GO_ON: |
|
action_stack_pop(srv, vr, as); |
|
if (condres) { |
|
action_enter(vr, a->data.condition.target); |
|
} |
|
else if (a->data.condition.target_else) { |
|
action_enter(vr, a->data.condition.target_else); |
|
} |
|
break; |
|
case HANDLER_ERROR: |
|
action_stack_reset(vr, as); |
|
case HANDLER_COMEBACK: |
|
case HANDLER_WAIT_FOR_EVENT: |
|
return res; |
|
} |
|
break; |
|
case ACTION_TLIST: |
|
if (ase->data.pos >= a->data.list->len) { |
|
action_stack_pop(srv, vr, as); |
|
} else { |
|
action_enter(vr, g_array_index(a->data.list, action*, ase->data.pos)); |
|
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 HANDLER_GO_ON: |
|
ase->finished = TRUE; |
|
break; |
|
case HANDLER_ERROR: |
|
action_stack_reset(vr, as); |
|
case HANDLER_COMEBACK: |
|
case HANDLER_WAIT_FOR_EVENT: |
|
return res; |
|
} |
|
break; |
|
} |
|
} |
|
if (as->backend_failed) { |
|
if (vrequest_handle_direct(vr)) |
|
vr->response.http_status = 503; |
|
} |
|
return HANDLER_GO_ON; |
|
}
|
|
|