diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bc692e..dc02373 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -294,6 +294,7 @@ SET(COMMON_SRC url_parser.c utils.c value.c + virtualrequest.c worker.c plugin_core.c diff --git a/src/actions.c b/src/actions.c index 71a82cb..d2dde8a 100644 --- a/src/actions.c +++ b/src/actions.c @@ -121,10 +121,10 @@ void action_stack_clear(server *srv, action_stack *as) { } /** handle sublist now, remember current position (stack) */ -void action_enter(connection *con, action *a) { +void action_enter(vrequest *vr, action *a) { action_acquire(a); action_stack_element ase = { a, 0 }; - g_array_append_val(con->action_stack.stack, ase); + g_array_append_val(vr->action_stack.stack, ase); } static action_stack_element *action_stack_top(action_stack* as) { @@ -145,51 +145,66 @@ static action* action_stack_element_action(action_stack_element *ase) { } } -action_result action_execute(connection *con) { +handler_t action_execute(vrequest *vr) { action *a; - action_stack *as = &con->action_stack; + action_stack *as = &vr->action_stack; action_stack_element *ase; - action_result res; + handler_t res; + gboolean condres; while (NULL != (ase = action_stack_top(as))) { a = action_stack_element_action(ase); if (!a) { - action_stack_pop(con->srv, as); + action_stack_pop(vr->con->srv, as); continue; } - con->wrk->stats.actions_executed++; + vr->con->wrk->stats.actions_executed++; switch (a->type) { case ACTION_TSETTING: - con->options[a->data.setting.ndx] = a->data.setting.value; + vr->con->options[a->data.setting.ndx] = a->data.setting.value; break; case ACTION_TFUNCTION: - res = a->data.function.func(con, a->data.function.param); + res = a->data.function.func(vr, a->data.function.param); switch (res) { - case ACTION_GO_ON: - case ACTION_FINISHED: + case HANDLER_GO_ON: + case HANDLER_FINISHED: break; - case ACTION_ERROR: - action_stack_reset(con->srv, as); + case HANDLER_ERROR: + action_stack_reset(vr->con->srv, as); + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_WAIT_FOR_FD: return res; - case ACTION_WAIT_FOR_EVENT: - return ACTION_WAIT_FOR_EVENT; } break; case ACTION_TCONDITION: - if (condition_check(con, a->data.condition.cond)) { - action_enter(con, a->data.condition.target); - } - else if (a->data.condition.target_else) { - action_enter(con, a->data.condition.target_else); + condres = FALSE; + res = condition_check(vr, a->data.condition.cond, &condres); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + 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->con->srv, as); + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_WAIT_FOR_FD: + return res; } break; case ACTION_TLIST: - action_enter(con, a); + action_enter(vr, a); break; } ase->pos++; } - return ACTION_FINISHED; + return HANDLER_FINISHED; } diff --git a/src/actions.h b/src/actions.h index d821dfa..9526156 100644 --- a/src/actions.h +++ b/src/actions.h @@ -3,12 +3,12 @@ #include "settings.h" -typedef enum { - ACTION_GO_ON, - ACTION_FINISHED, - ACTION_ERROR, - ACTION_WAIT_FOR_EVENT -} action_result; +// typedef enum { +// ACTION_GO_ON, +// ACTION_FINISHED, +// ACTION_ERROR, +// ACTION_WAIT_FOR_EVENT +// } action_result; // action type typedef enum { @@ -28,8 +28,8 @@ struct action_stack { GArray* stack; }; -struct server; struct connection; -typedef action_result (*ActionFunc)(struct connection *con, gpointer param); +struct server; struct connection; struct vrequest; +typedef handler_t (*ActionFunc)(struct vrequest *vr, gpointer param); typedef void (*ActionFree)(struct server *srv, gpointer param); struct action_func { @@ -68,8 +68,8 @@ LI_API void action_stack_reset(server *srv, action_stack *as); LI_API void action_stack_clear(server *srv, action_stack *as); /** handle sublist now, remember current position (stack) */ -LI_API void action_enter(connection *con, action *a); -LI_API action_result action_execute(connection *con); +LI_API void action_enter(struct vrequest *vr, action *a); +LI_API handler_t action_execute(struct vrequest *vr); LI_API void action_release(server *srv, action *a); diff --git a/src/base.h b/src/base.h index 8349d9e..fc4a70b 100644 --- a/src/base.h +++ b/src/base.h @@ -44,6 +44,7 @@ typedef struct connection connection; #include "plugin.h" #include "request.h" #include "response.h" +#include "virtualrequest.h" #include "log.h" #include "connection.h" diff --git a/src/chunk.c b/src/chunk.c index 7af54d4..1334e37 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -45,16 +45,16 @@ static void chunkfile_release(chunkfile *cf) { /* open the file cf->name if it is not already opened for reading * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -handler_t chunkfile_open(connection *con, chunkfile *cf) { +handler_t chunkfile_open(vrequest *vr, chunkfile *cf) { if (!cf) return HANDLER_ERROR; if (-1 != cf->fd) return HANDLER_GO_ON; if (!cf->name) { - CON_ERROR(con, "%s", "Missing filename for FILE_CHUNK"); + VR_ERROR(vr, "%s", "Missing filename for FILE_CHUNK"); return HANDLER_ERROR; } if (-1 == (cf->fd = open(cf->name->str, O_RDONLY))) { if (EMFILE == errno) return HANDLER_WAIT_FOR_FD; - CON_ERROR(con, "Couldn't open file '%s': %s", GSTR_SAFE_STR(cf->name), g_strerror(errno)); + VR_ERROR(vr, "Couldn't open file '%s': %s", GSTR_SAFE_STR(cf->name), g_strerror(errno)); return HANDLER_ERROR; } #ifdef FD_CLOEXEC @@ -64,7 +64,7 @@ handler_t chunkfile_open(connection *con, chunkfile *cf) { /* tell the kernel that we want to stream the file */ if (-1 == posix_fadvise(cf->fd, 0, 0, POSIX_FADV_SEQUENTIAL)) { if (ENOSYS != errno) { - CON_ERROR(con, "posix_fadvise failed for '%s': %s (%i)", GSTR_SAFE_STR(cf->name), g_strerror(errno), cf->fd); + VR_ERROR(vr, "posix_fadvise failed for '%s': %s (%i)", GSTR_SAFE_STR(cf->name), g_strerror(errno), cf->fd); } } #endif @@ -85,7 +85,7 @@ handler_t chunkfile_open(connection *con, chunkfile *cf) { * the data is _not_ marked as "done" * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -handler_t chunkiter_read(connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len) { +handler_t chunkiter_read(vrequest *vr, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len) { chunk *c = chunkiter_chunk(iter); off_t we_want, we_have, our_start, our_offset; handler_t res = HANDLER_GO_ON; @@ -105,7 +105,7 @@ handler_t chunkiter_read(connection *con, chunkiter iter, off_t start, off_t len *data_len = length; break; case FILE_CHUNK: - if (HANDLER_GO_ON != (res = chunkfile_open(con, c->file.file))) return res; + if (HANDLER_GO_ON != (res = chunkfile_open(vr, c->file.file))) return res; if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK; @@ -142,11 +142,11 @@ handler_t chunkiter_read(connection *con, chunkiter iter, off_t start, off_t len if (-1 == lseek(c->file.file->fd, our_start, SEEK_SET)) { /* prefer the error of the first syscall */ if (0 != mmap_errno) { - CON_ERROR(con, "mmap failed for '%s' (fd = %i): %s", + VR_ERROR(vr, "mmap failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->file.file->name), c->file.file->fd, g_strerror(mmap_errno)); } else { - CON_ERROR(con, "lseek failed for '%s' (fd = %i): %s", + VR_ERROR(vr, "lseek failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->file.file->name), c->file.file->fd, g_strerror(errno)); } @@ -159,11 +159,11 @@ read_chunk: if (EINTR == errno) goto read_chunk; /* prefer the error of the first syscall */ if (0 != mmap_errno) { - CON_ERROR(con, "mmap failed for '%s' (fd = %i): %s", + VR_ERROR(vr, "mmap failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->file.file->name), c->file.file->fd, g_strerror(mmap_errno)); } else { - CON_ERROR(con, "read failed for '%s' (fd = %i): %s", + VR_ERROR(vr, "read failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->file.file->name), c->file.file->fd, g_strerror(errno)); } @@ -183,7 +183,7 @@ read_chunk: /* don't advise files < 64Kb */ if (c->file.mmap.length > (64*1024) && 0 != madvise(c->file.mmap.data, c->file.mmap.length, MADV_WILLNEED)) { - CON_ERROR(con, "madvise failed for '%s' (fd = %i): %s", + VR_ERROR(vr, "madvise failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->file.file->name), c->file.file->fd, g_strerror(errno)); } diff --git a/src/chunk.h b/src/chunk.h index 18e9743..7b707b5 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -16,7 +16,7 @@ struct chunkiter; typedef struct chunkiter chunkiter; struct server; -struct connection; +struct vrequest; #include "settings.h" @@ -76,7 +76,7 @@ struct chunkiter { /* open the file cf->name if it is not already opened for reading * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -LI_API handler_t chunkfile_open(struct connection *con, chunkfile *cf); +LI_API handler_t chunkfile_open(struct vrequest *vr, chunkfile *cf); /****************** * chunk iterator * @@ -92,7 +92,7 @@ INLINE goffset chunkiter_length(chunkiter iter); * the data is _not_ marked as "done" * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -LI_API handler_t chunkiter_read(struct connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len); +LI_API handler_t chunkiter_read(struct vrequest *vr, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len); /****************** * chunk * diff --git a/src/chunk_parser.c b/src/chunk_parser.c index 5058db5..1fbb783 100644 --- a/src/chunk_parser.c +++ b/src/chunk_parser.c @@ -23,7 +23,7 @@ handler_t chunk_parser_prepare(chunk_parser_ctx *ctx) { return HANDLER_GO_ON; } -handler_t chunk_parser_next(connection *con, chunk_parser_ctx *ctx, char **p, char **pe) { +handler_t chunk_parser_next(vrequest *vr, chunk_parser_ctx *ctx, char **p, char **pe) { off_t l; handler_t res; @@ -39,7 +39,7 @@ handler_t chunk_parser_next(connection *con, chunk_parser_ctx *ctx, char **p, ch if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT; - if (HANDLER_GO_ON != (res = chunkiter_read(con, ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length))) { + if (HANDLER_GO_ON != (res = chunkiter_read(vr, ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length))) { return res; } @@ -53,7 +53,7 @@ void chunk_parser_done(chunk_parser_ctx *ctx, goffset len) { ctx->start += len; } -gboolean chunk_extract_to(connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest) { +gboolean chunk_extract_to(vrequest *vr, chunk_parser_mark from, chunk_parser_mark to, GString *dest) { g_string_set_size(dest, 0); chunk_parser_mark i; @@ -62,7 +62,7 @@ gboolean chunk_extract_to(connection *con, chunk_parser_mark from, chunk_parser_ while (i.pos < len) { char *buf; off_t we_have; - if (HANDLER_GO_ON != chunkiter_read(con, i.ci, i.pos, len - i.pos, &buf, &we_have)) goto error; + if (HANDLER_GO_ON != chunkiter_read(vr, i.ci, i.pos, len - i.pos, &buf, &we_have)) goto error; g_string_append_len(dest, buf, we_have); i.pos += we_have; } @@ -71,7 +71,7 @@ gboolean chunk_extract_to(connection *con, chunk_parser_mark from, chunk_parser_ while (i.pos < to.pos) { char *buf; off_t we_have; - if (HANDLER_GO_ON != chunkiter_read(con, i.ci, i.pos, to.pos - i.pos, &buf, &we_have)) goto error; + if (HANDLER_GO_ON != chunkiter_read(vr, i.ci, i.pos, to.pos - i.pos, &buf, &we_have)) goto error; g_string_append_len(dest, buf, we_have); i.pos += we_have; } @@ -83,9 +83,9 @@ error: return FALSE; } -GString* chunk_extract(connection *con, chunk_parser_mark from, chunk_parser_mark to) { +GString* chunk_extract(vrequest *vr, chunk_parser_mark from, chunk_parser_mark to) { GString *str = g_string_sized_new(0); - if (chunk_extract_to(con, from, to, str)) return str; + if (chunk_extract_to(vr, from, to, str)) return str; g_string_free(str, TRUE); return NULL; } diff --git a/src/chunk_parser.h b/src/chunk_parser.h index c736e96..be19af5 100644 --- a/src/chunk_parser.h +++ b/src/chunk_parser.h @@ -32,11 +32,11 @@ struct chunk_parser_mark { LI_API void chunk_parser_init(chunk_parser_ctx *ctx, chunkqueue *cq); LI_API void chunk_parser_reset(chunk_parser_ctx *ctx); LI_API handler_t chunk_parser_prepare(chunk_parser_ctx *ctx); -LI_API handler_t chunk_parser_next(struct connection *con, chunk_parser_ctx *ctx, char **p, char **pe); +LI_API handler_t chunk_parser_next(struct vrequest *vr, chunk_parser_ctx *ctx, char **p, char **pe); LI_API void chunk_parser_done(chunk_parser_ctx *ctx, goffset len); -LI_API gboolean chunk_extract_to(struct connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest); -LI_API GString* chunk_extract(struct connection *con, chunk_parser_mark from, chunk_parser_mark to); +LI_API gboolean chunk_extract_to(struct vrequest *vr, chunk_parser_mark from, chunk_parser_mark to, GString *dest); +LI_API GString* chunk_extract(struct vrequest *vr, chunk_parser_mark from, chunk_parser_mark to); INLINE chunk_parser_mark chunk_parser_getmark(chunk_parser_ctx *ctx, const char *fpc); diff --git a/src/condition.c b/src/condition.c index bcd83a4..3ef7b2f 100644 --- a/src/condition.c +++ b/src/condition.c @@ -249,9 +249,10 @@ const char* cond_lvalue_to_string(cond_lvalue_t t) { } /* COND_VALUE_STRING and COND_VALUE_REGEXP only */ -static gboolean condition_check_eval_string(connection *con, condition *cond) { +static handler_t condition_check_eval_string(vrequest *vr, condition *cond, gboolean *res) { + connection *con = vr->con; const char *val = ""; - gboolean result = FALSE; + *res = FALSE; switch (cond->lvalue->type) { case COMP_REQUEST_LOCALIP: @@ -261,28 +262,28 @@ static gboolean condition_check_eval_string(connection *con, condition *cond) { val = con->remote_addr_str->str; break; case COMP_REQUEST_PATH: - val = con->request.uri.path->str; + val = vr->request.uri.path->str; break; case COMP_REQUEST_HOST: - val = con->request.uri.host->str; + val = vr->request.uri.host->str; break; case COMP_REQUEST_SCHEME: val = con->is_ssl ? "https" : "http"; break; case COMP_REQUEST_QUERY_STRING: - val = con->request.uri.query->str; + val = vr->request.uri.query->str; break; case COMP_REQUEST_METHOD: - val = con->request.http_method_str->str; + val = vr->request.http_method_str->str; break; case COMP_PHYSICAL_PATH: - val = con->physical.path->str; + val = vr->physical.path->str; break; case COMP_PHYSICAL_PATH_EXISTS: /* TODO: physical path exists */ break; case COMP_REQUEST_HEADER: - http_header_get_fast(con->wrk->tmp_str, con->request.headers, GSTR_LEN(cond->lvalue->key)); + http_header_get_fast(con->wrk->tmp_str, vr->request.headers, GSTR_LEN(cond->lvalue->key)); val = con->wrk->tmp_str->str; break; case COMP_PHYSICAL_SIZE: @@ -291,37 +292,39 @@ static gboolean condition_check_eval_string(connection *con, condition *cond) { val = con->wrk->tmp_str->str; break; case COMP_REQUEST_CONTENT_LENGTH: - g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, con->request.content_length); + g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, vr->request.content_length); val = con->wrk->tmp_str->str; break; } switch (cond->op) { case CONFIG_COND_EQ: - result = g_str_equal(val, cond->rvalue.string->str); + *res = g_str_equal(val, cond->rvalue.string->str); break; case CONFIG_COND_NE: - result = g_str_equal(val, cond->rvalue.string->str); + *res = g_str_equal(val, cond->rvalue.string->str); break; case CONFIG_COND_PREFIX: - result = g_str_has_prefix(val, cond->rvalue.string->str); + *res = g_str_has_prefix(val, cond->rvalue.string->str); break; case CONFIG_COND_NOPREFIX: - result = !g_str_has_prefix(val, cond->rvalue.string->str); + *res = !g_str_has_prefix(val, cond->rvalue.string->str); break; case CONFIG_COND_SUFFIX: - result = g_str_has_suffix(val, cond->rvalue.string->str); + *res = g_str_has_suffix(val, cond->rvalue.string->str); break; case CONFIG_COND_NOSUFFIX: - result = !g_str_has_suffix(val, cond->rvalue.string->str); + *res = !g_str_has_suffix(val, cond->rvalue.string->str); break; case CONFIG_COND_MATCH: case CONFIG_COND_NOMATCH: #ifdef HAVE_PCRE_H /* TODO: pcre */ - CON_ERROR(con, "%s", "regexp match not supported yet"); + VR_ERROR(vr, "%s", "regexp match not supported yet"); + return HANDLER_ERROR; #else - CON_ERROR(con, "compiled without pcre, cannot use '%s'", comp_op_to_string(cond->op)); + VR_ERROR(vr, "compiled without pcre, cannot use '%s'", comp_op_to_string(cond->op)); + return HANDLER_ERROR; #endif break; case CONFIG_COND_IP: @@ -330,41 +333,49 @@ static gboolean condition_check_eval_string(connection *con, condition *cond) { case CONFIG_COND_GT: case CONFIG_COND_LE: case CONFIG_COND_LT: - CON_ERROR(con, "cannot compare string/regexp with '%s'", comp_op_to_string(cond->op)); - break; + VR_ERROR(vr, "cannot compare string/regexp with '%s'", comp_op_to_string(cond->op)); + return HANDLER_ERROR; } - return result; + return HANDLER_GO_ON; } -static gboolean condition_check_eval_int(connection *con, condition *cond) { +static handler_t condition_check_eval_int(vrequest *vr, condition *cond, gboolean *res) { gint64 val; + *res = FALSE; switch (cond->lvalue->type) { case COMP_REQUEST_CONTENT_LENGTH: - val = con->request.content_length; + val = vr->request.content_length; case COMP_PHYSICAL_SIZE: - val = con->physical.size; + /* TODO: stat file */ + val = vr->physical.size; break; default: - CON_ERROR(con, "couldn't get int value for '%s', using -1", cond_lvalue_to_string(cond->lvalue->type)); - val = -1; + VR_ERROR(vr, "couldn't get int value for '%s'", cond_lvalue_to_string(cond->lvalue->type)); + return HANDLER_ERROR; } - if (val > 0) switch (cond->op) { + switch (cond->op) { case CONFIG_COND_EQ: /** == */ - return (val == cond->rvalue.i); + *res = (val == cond->rvalue.i); + break; case CONFIG_COND_NE: /** != */ - return (val != cond->rvalue.i); + *res = (val != cond->rvalue.i); + break; case CONFIG_COND_LT: /** < */ - return (val < cond->rvalue.i); + *res = (val < cond->rvalue.i); + break; case CONFIG_COND_LE: /** <= */ - return (val <= cond->rvalue.i); + *res = (val <= cond->rvalue.i); + break; case CONFIG_COND_GT: /** > */ - return (val > cond->rvalue.i); + *res = (val > cond->rvalue.i); + break; case CONFIG_COND_GE: /** >= */ - return (val >= cond->rvalue.i); + *res = (val >= cond->rvalue.i); + break; case CONFIG_COND_PREFIX: case CONFIG_COND_NOPREFIX: case CONFIG_COND_SUFFIX: @@ -373,11 +384,11 @@ static gboolean condition_check_eval_int(connection *con, condition *cond) { case CONFIG_COND_NOMATCH: case CONFIG_COND_IP: case CONFIG_COND_NOTIP: - CON_ERROR(con, "cannot compare int with '%s'", comp_op_to_string(cond->op)); - return FALSE; + VR_ERROR(vr, "cannot compare int with '%s'", comp_op_to_string(cond->op)); + return HANDLER_ERROR; } - return FALSE; + return HANDLER_GO_ON; } static gboolean ipv4_in_ipv4_net(guint32 target, guint32 match, guint32 networkmask) { @@ -422,65 +433,65 @@ static gboolean ip_in_net(condition_rvalue *target, condition_rvalue *network) { } /* CONFIG_COND_IP and CONFIG_COND_NOTIP only */ -static gboolean condition_check_eval_ip(connection *con, condition *cond) { +static handler_t condition_check_eval_ip(vrequest *vr, condition *cond, gboolean *res) { + connection *con = vr->con; condition_rvalue ipval; const char *val = NULL; - gboolean result = FALSE; + *res = (cond->op == CONFIG_COND_NOTIP); ipval.type = COND_VALUE_NUMBER; switch (cond->lvalue->type) { case COMP_REQUEST_LOCALIP: if (!condition_ip_from_socket(&ipval, &con->local_addr)) - return (cond->op == CONFIG_COND_NOTIP); + return HANDLER_GO_ON; break; case COMP_REQUEST_REMOTEIP: if (!condition_ip_from_socket(&ipval, &con->remote_addr)) - return (cond->op == CONFIG_COND_NOTIP); + return HANDLER_GO_ON; break; case COMP_REQUEST_PATH: - CON_ERROR(con, "%s", "Cannot parse request.path as ip"); - return (cond->op == CONFIG_COND_NOTIP); - break; + VR_ERROR(vr, "%s", "Cannot parse request.path as ip"); + return HANDLER_ERROR; case COMP_REQUEST_HOST: - val = con->request.uri.host->str; + val = vr->request.uri.host->str; break; case COMP_REQUEST_SCHEME: - CON_ERROR(con, "%s", "Cannot parse request.scheme as ip"); - return (cond->op == CONFIG_COND_NOTIP); + VR_ERROR(vr, "%s", "Cannot parse request.scheme as ip"); + return HANDLER_ERROR; case COMP_REQUEST_QUERY_STRING: - val = con->request.uri.query->str; + val = vr->request.uri.query->str; break; case COMP_REQUEST_METHOD: - CON_ERROR(con, "%s", "Cannot request.method as ip"); - return (cond->op == CONFIG_COND_NOTIP); + VR_ERROR(vr, "%s", "Cannot parse request.method as ip"); + return HANDLER_ERROR; break; case COMP_PHYSICAL_PATH: case COMP_PHYSICAL_PATH_EXISTS: - CON_ERROR(con, "%s", "Cannot physical.path(-exists) as ip"); - return (cond->op == CONFIG_COND_NOTIP); + VR_ERROR(vr, "%s", "Cannot parse physical.path(-exists) as ip"); + return HANDLER_ERROR; break; case COMP_REQUEST_HEADER: - http_header_get_fast(con->wrk->tmp_str, con->request.headers, GSTR_LEN(cond->lvalue->key)); + http_header_get_fast(con->wrk->tmp_str, vr->request.headers, GSTR_LEN(cond->lvalue->key)); val = con->wrk->tmp_str->str; break; case COMP_PHYSICAL_SIZE: case COMP_REQUEST_CONTENT_LENGTH: - CON_ERROR(con, "%s", "Cannot parse integers as ip"); - return (cond->op == CONFIG_COND_NOTIP); + VR_ERROR(vr, "%s", "Cannot parse integers as ip"); + return HANDLER_ERROR; break; } if (ipval.type == COND_VALUE_NUMBER) { if (!val || !condition_parse_ip(&ipval, val)) - return (cond->op == CONFIG_COND_NOTIP); + return HANDLER_GO_ON; } switch (cond->op) { case CONFIG_COND_IP: - return ip_in_net(&ipval, &cond->rvalue); + *res = ip_in_net(&ipval, &cond->rvalue); case CONFIG_COND_NOTIP: - return !ip_in_net(&ipval, &cond->rvalue); + *res = !ip_in_net(&ipval, &cond->rvalue); case CONFIG_COND_PREFIX: case CONFIG_COND_NOPREFIX: case CONFIG_COND_SUFFIX: @@ -493,25 +504,26 @@ static gboolean condition_check_eval_ip(connection *con, condition *cond) { case CONFIG_COND_GT: case CONFIG_COND_LE: case CONFIG_COND_LT: - CON_ERROR(con, "cannot match ips with '%s'", comp_op_to_string(cond->op)); - break; + VR_ERROR(vr, "cannot match ips with '%s'", comp_op_to_string(cond->op)); + return HANDLER_ERROR; } - return result; + return HANDLER_GO_ON; } -gboolean condition_check(connection *con, condition *cond) { +handler_t condition_check(vrequest *vr, condition *cond, gboolean *res) { switch (cond->rvalue.type) { case COND_VALUE_STRING: #ifdef HAVE_PCRE_H case COND_VALUE_REGEXP: #endif - return condition_check_eval_string(con, cond); + return condition_check_eval_string(vr, cond, res); case COND_VALUE_NUMBER: - return condition_check_eval_int(con, cond); + return condition_check_eval_int(vr, cond, res); case COND_VALUE_SOCKET_IPV4: case COND_VALUE_SOCKET_IPV6: - return condition_check_eval_ip(con, cond); + return condition_check_eval_ip(vr, cond, res); } - return FALSE; + VR_ERROR(vr, "Unsupported conditional type: %i", cond->rvalue.type); + return HANDLER_ERROR; } diff --git a/src/condition.h b/src/condition.h index 7987f25..352cfea 100644 --- a/src/condition.h +++ b/src/condition.h @@ -125,7 +125,8 @@ LI_API void condition_release(server *srv, condition* c); LI_API const char* comp_op_to_string(comp_operator_t op); LI_API const char* cond_lvalue_to_string(cond_lvalue_t t); -LI_API gboolean condition_check(connection *con, condition *cond); +struct vrequest; +LI_API handler_t condition_check(struct vrequest *vr, condition *cond, gboolean *result); /* parser */ /** parse an IPv4 (if netmask is not NULL with cidr netmask) */ diff --git a/src/connection.c b/src/connection.c index a6f7581..be99013 100644 --- a/src/connection.c +++ b/src/connection.c @@ -7,75 +7,188 @@ /* only call it from the worker context the con belongs to */ void worker_con_put(connection *con); /* worker.c */ -void internal_error(connection *con) { - if (con->response_headers_sent) { - CON_ERROR(con, "%s", "Couldn't send '500 Internal Error': headers already sent"); - connection_set_state(con, CON_STATE_ERROR); - } else { - http_headers_reset(con->response.headers); - con->response.http_status = 500; - con->content_handler = NULL; - connection_set_state(con, CON_STATE_WRITE_RESPONSE); - } -} - static void parse_request_body(connection *con) { - if ( con->state >= CON_STATE_READ_REQUEST_CONTENT - && con->state <= CON_STATE_WRITE_RESPONSE - && !con->in->is_closed) { - if (con->request.content_length == -1) { + if ((con->state > CON_STATE_HANDLE_MAINVR || con->mainvr->state >= VRS_READ_CONTENT) && !con->in->is_closed) { + ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_READ); + if (con->mainvr->request.content_length == -1) { /* TODO: parse chunked encoded request body, filters */ chunkqueue_steal_all(con->in, con->raw_in); } else { - if (con->in->bytes_in < con->request.content_length) { - chunkqueue_steal_len(con->in, con->raw_in, con->request.content_length - con->in->bytes_in); + if (con->in->bytes_in < con->mainvr->request.content_length) { + chunkqueue_steal_len(con->in, con->raw_in, con->mainvr->request.content_length - con->in->bytes_in); + } + if (con->in->bytes_in == con->mainvr->request.content_length) { + con->in->is_closed = TRUE; + ev_io_rem_events(con->wrk->loop, &con->sock_watcher, EV_READ); } - if (con->in->bytes_in == con->request.content_length) con->in->is_closed = TRUE; } + } else { + ev_io_rem_events(con->wrk->loop, &con->sock_watcher, EV_READ); } } static void forward_response_body(connection *con) { - if (con->state == CON_STATE_WRITE_RESPONSE && !con->raw_out->is_closed) { - if (con->out->length > 0) { - /* TODO: chunked encoding, filters */ - chunkqueue_steal_all(con->raw_out, con->out); + vrequest *vr = con->mainvr; + if (con->state >= CON_STATE_HANDLE_MAINVR) { + if (!con->response_headers_sent) { + con->response_headers_sent = TRUE; + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "write response headers"); + } + response_send_headers(con); } - if (con->in->is_closed && 0 == con->raw_out->length) - con->raw_out->is_closed = TRUE; + + chunkqueue_steal_all(con->raw_out, con->out); + if (con->out->is_closed) con->raw_out->is_closed = TRUE; if (con->raw_out->length > 0) { ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE); } else { ev_io_rem_events(con->wrk->loop, &con->sock_watcher, EV_WRITE); } + } else { + ev_io_rem_events(con->wrk->loop, &con->sock_watcher, EV_WRITE); } } +static void connection_request_done(connection *con) { + vrequest *vr = con->mainvr; + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "response end (keep_alive = %i)", con->keep_alive); + } + + plugins_handle_close(con); + + if (con->keep_alive) { + connection_reset_keep_alive(con); + } else { + worker_con_put(con); + } +} + +static gboolean check_response_done(connection *con) { + if (con->in->is_closed && con->raw_out->is_closed && 0 == con->raw_out->length) { + connection_request_done(con); + return TRUE; + } + return FALSE; +} + +static void connection_close(connection *con) { + vrequest *vr = con->mainvr; + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "connection closed"); + } + + plugins_handle_close(con); + + worker_con_put(con); +} + +void connection_error(connection *con) { + vrequest *vr = con->mainvr; + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "connection closed (error)"); + } + + plugins_handle_close(con); + + worker_con_put(con); +} + +void connection_internal_error(connection *con) { + vrequest *vr = con->mainvr; + if (con->response_headers_sent) { + 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; + forward_response_body(con); + } +} + +static gboolean connection_handle_read(connection *con) { + vrequest *vr = con->mainvr; + if (con->raw_in->length == 0) return TRUE; + + if (con->state == CON_STATE_KEEP_ALIVE) { + /* stop keep alive timeout watchers */ + if (con->keep_alive_data.link) { + g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link); + con->keep_alive_data.link = NULL; + } + con->keep_alive_data.timeout = 0; + ev_timer_stop(con->wrk->loop, &con->keep_alive_data.watcher); + + con->state = CON_STATE_READ_REQUEST_HEADER; + } else if (con->state == CON_STATE_REQUEST_START) { + con->state = CON_STATE_READ_REQUEST_HEADER; + } + + if (con->state == CON_STATE_READ_REQUEST_HEADER && con->mainvr->state == VRS_CLEAN) { + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "reading request header"); + } + switch(http_request_parse(con->mainvr, &con->req_parser_ctx)) { + case HANDLER_FINISHED: + case HANDLER_GO_ON: + break; /* go on */ + case HANDLER_WAIT_FOR_EVENT: + return TRUE; + case HANDLER_ERROR: + case HANDLER_COMEBACK: /* unexpected */ + case HANDLER_WAIT_FOR_FD: /* unexpected */ + /* unparsable header */ + connection_error(con); + return FALSE; + } + + con->wrk->stats.requests++; + + /* headers ready */ + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "validating request header"); + } + if (!request_validate_header(con)) { + /* skip mainvr handling */ + con->state = CON_STATE_WRITE; + con->keep_alive = FALSE; + con->in->is_closed = TRUE; + forward_response_body(con); + } else { + con->state = CON_STATE_HANDLE_MAINVR; + action_enter(con->mainvr, con->srv->mainaction); + vrequest_handle_request_headers(con->mainvr); + } + } + + return TRUE; +} + static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) { connection *con = (connection*) w->data; - gboolean dojoblist = FALSE; - UNUSED(loop); if (revents & EV_READ) { if (con->in->is_closed) { /* don't read the next request before current one is done */ ev_io_rem_events(loop, w, EV_READ); } else { - switch (network_read(con, w->fd, con->raw_in)) { + switch (network_read(con->mainvr, w->fd, con->raw_in)) { case NETWORK_STATUS_SUCCESS: - dojoblist = TRUE; + if (!connection_handle_read(con)) return; break; case NETWORK_STATUS_FATAL_ERROR: CON_ERROR(con, "%s", "network read fatal error"); - connection_set_state(con, CON_STATE_ERROR); - dojoblist = TRUE; - break; + connection_error(con); + return; case NETWORK_STATUS_CONNECTION_CLOSE: con->raw_in->is_closed = TRUE; shutdown(w->fd, SHUT_RD); - connection_set_state(con, CON_STATE_CLOSE); - dojoblist = TRUE; - break; + connection_close(con); + return; case NETWORK_STATUS_WAIT_FOR_EVENT: break; case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: @@ -91,40 +204,32 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) { } if (revents & EV_WRITE) { + ev_io_rem_events(loop, w, EV_WRITE); if (con->raw_out->length > 0) { - switch (network_write(con, w->fd, con->raw_out)) { + switch (network_write(con->mainvr, w->fd, con->raw_out)) { case NETWORK_STATUS_SUCCESS: - dojoblist = TRUE; + vrequest_joblist_append(con->mainvr); break; case NETWORK_STATUS_FATAL_ERROR: CON_ERROR(con, "%s", "network write fatal error"); - connection_set_state(con, CON_STATE_ERROR); - dojoblist = TRUE; + connection_error(con); break; case NETWORK_STATUS_CONNECTION_CLOSE: - connection_set_state(con, CON_STATE_CLOSE); - dojoblist = TRUE; - break; + connection_close(con); + return; case NETWORK_STATUS_WAIT_FOR_EVENT: break; case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: /* TODO: aio */ - ev_io_rem_events(loop, w, EV_WRITE); break; case NETWORK_STATUS_WAIT_FOR_FD: /* TODO: wait for fd */ - ev_io_rem_events(loop, w, EV_WRITE); break; } } - if (con->raw_out->length == 0) { - ev_io_rem_events(loop, w, EV_WRITE); - dojoblist = TRUE; - } } - if (dojoblist) - joblist_append(con); + check_response_done(con); } static void connection_keepalive_cb(struct ev_loop *loop, ev_timer *w, int revents) { @@ -133,6 +238,51 @@ static void connection_keepalive_cb(struct ev_loop *loop, ev_timer *w, int reven worker_con_put(con); } +static handler_t mainvr_handle_response_headers(vrequest *vr) { + connection *con = vr->con; + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + VR_TRACE(vr, "%s", "read request/handle response header"); + } + if (con->expect_100_cont) { + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + VR_TRACE(vr, "%s", "send 100 Continue"); + } + chunkqueue_append_mem(con->raw_out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n")); + con->expect_100_cont = FALSE; + ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE); + } + parse_request_body(con); + + return HANDLER_GO_ON; +} + +static handler_t mainvr_handle_response_body(vrequest *vr) { + connection *con = vr->con; + if (check_response_done(con)) return HANDLER_FINISHED; + + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + CON_TRACE(con, "%s", "write response"); + } + + parse_request_body(con); + forward_response_body(con); + + if (check_response_done(con)) return HANDLER_FINISHED; + + return HANDLER_GO_ON; +} + +static handler_t mainvr_handle_response_error(vrequest *vr) { + connection_internal_error(vr->con); + return HANDLER_FINISHED; +} + +static handler_t mainvr_handle_request_headers(vrequest *vr) { + /* start reading input */ + parse_request_body(vr->con); + return HANDLER_GO_ON; +} + connection* connection_new(worker *wrk) { server *srv = wrk->srv; connection *con = g_slice_new0(connection); @@ -152,17 +302,18 @@ connection* connection_new(worker *wrk) { con->raw_in = chunkqueue_new(); con->raw_out = chunkqueue_new(); - con->in = chunkqueue_new(); - con->out = chunkqueue_new(); - - action_stack_init(&con->action_stack); con->options = g_slice_copy(srv->option_def_values->len * sizeof(option_value), srv->option_def_values->data); - request_init(&con->request); - physical_init(&con->physical); - response_init(&con->response); - http_request_parser_init(&con->req_parser_ctx, &con->request, con->raw_in); + con->mainvr = vrequest_new(con, + mainvr_handle_response_headers, + mainvr_handle_response_body, + mainvr_handle_response_error, + mainvr_handle_request_headers); + http_request_parser_init(&con->req_parser_ctx, &con->mainvr->request, con->raw_in); + + con->in = con->mainvr->vr_in; + con->out = con->mainvr->vr_out; con->keep_alive_data.link = NULL; con->keep_alive_data.timeout = 0; @@ -194,16 +345,10 @@ void connection_reset(connection *con) { chunkqueue_reset(con->raw_in); chunkqueue_reset(con->raw_out); - chunkqueue_reset(con->in); - chunkqueue_reset(con->out); - - action_stack_reset(con->srv, &con->action_stack); memcpy(con->options, con->srv->option_def_values->data, con->srv->option_def_values->len * sizeof(option_value)); - request_reset(&con->request); - physical_reset(&con->physical); - response_reset(&con->response); + vrequest_reset(con->mainvr); http_request_parser_reset(&con->req_parser_ctx); if (con->keep_alive_data.link) { @@ -217,6 +362,7 @@ void connection_reset(connection *con) { void server_check_keepalive(server *srv); void connection_reset_keep_alive(connection *con) { + vrequest *vr = con->mainvr; ev_timer_stop(con->wrk->loop, &con->keep_alive_data.watcher); { con->keep_alive_data.max_idle = CORE_OPTION(CORE_OPTION_MAX_KEEP_ALIVE_IDLE).number; @@ -248,16 +394,10 @@ void connection_reset_keep_alive(connection *con) { con->keep_alive = TRUE; con->raw_out->is_closed = FALSE; - chunkqueue_reset(con->in); - chunkqueue_reset(con->out); - - action_stack_reset(con->srv, &con->action_stack); memcpy(con->options, con->srv->option_def_values->data, con->srv->option_def_values->len * sizeof(option_value)); - request_reset(&con->request); - physical_reset(&con->physical); - response_reset(&con->response); + vrequest_reset(con->mainvr); http_request_parser_reset(&con->req_parser_ctx); } @@ -280,16 +420,10 @@ void connection_free(connection *con) { chunkqueue_free(con->raw_in); chunkqueue_free(con->raw_out); - chunkqueue_free(con->in); - chunkqueue_free(con->out); - - action_stack_clear(con->srv, &con->action_stack); g_slice_free1(con->srv->option_def_values->len * sizeof(option_value), con->options); - request_clear(&con->request); - physical_clear(&con->physical); - response_clear(&con->response); + vrequest_free(con->mainvr); http_request_parser_clear(&con->req_parser_ctx); if (con->keep_alive_data.link && con->wrk) { @@ -303,215 +437,3 @@ void connection_free(connection *con) { g_slice_free(connection, con); } - -void connection_set_state(connection *con, connection_state_t state) { - if (state < con->state) { - CON_ERROR(con, "Cannot move into requested state: %i => %i, move to error state", con->state, state); - state = CON_STATE_ERROR; - } - con->state = state; -} - -void connection_state_machine(connection *con) { - gboolean done = FALSE; - do { - switch (con->state) { - case CON_STATE_DEAD: - done = TRUE; - break; - - case CON_STATE_KEEP_ALIVE: - if (con->raw_in->length > 0) { - /* stop keep alive timeout watchers */ - if (con->keep_alive_data.link) { - g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link); - con->keep_alive_data.link = NULL; - } - con->keep_alive_data.timeout = 0; - ev_timer_stop(con->wrk->loop, &con->keep_alive_data.watcher); - - connection_set_state(con, CON_STATE_REQUEST_START); - } else - done = TRUE; - break; - - - case CON_STATE_REQUEST_START: - connection_set_state(con, CON_STATE_READ_REQUEST_HEADER); - action_enter(con, con->srv->mainaction); - break; - - case CON_STATE_READ_REQUEST_HEADER: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "reading request header"); - } - switch(http_request_parse(con, &con->req_parser_ctx)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - connection_set_state(con, CON_STATE_VALIDATE_REQUEST_HEADER); - break; - case HANDLER_WAIT_FOR_FD: - /* TODO: wait for fd */ - done = TRUE; - break; - case HANDLER_WAIT_FOR_EVENT: - done = TRUE; - break; - case HANDLER_ERROR: - case HANDLER_COMEBACK: /* unexpected */ - /* unparsable header */ - connection_set_state(con, CON_STATE_ERROR); - break; - } - break; - - case CON_STATE_VALIDATE_REQUEST_HEADER: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "validating request header"); - } - connection_set_state(con, CON_STATE_HANDLE_REQUEST_HEADER); - request_validate_header(con); - con->wrk->stats.requests++; - break; - - case CON_STATE_HANDLE_REQUEST_HEADER: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "handle request header"); - } - switch (action_execute(con)) { - case ACTION_WAIT_FOR_EVENT: - done = TRUE; - break; - case ACTION_GO_ON: - case ACTION_FINISHED: - if (con->state == CON_STATE_HANDLE_REQUEST_HEADER) { - internal_error(con); - } - connection_set_state(con, CON_STATE_WRITE_RESPONSE); - break; - case ACTION_ERROR: - internal_error(con); - break; - } - break; - - case CON_STATE_READ_REQUEST_CONTENT: - case CON_STATE_HANDLE_RESPONSE_HEADER: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "read request/handle response header"); - } - if (con->expect_100_cont) { - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "send 100 Continue"); - } - chunkqueue_append_mem(con->raw_out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n")); - con->expect_100_cont = FALSE; - ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE); - } - parse_request_body(con); - - if (con->content_handler) - con->content_handler->handle_content(con, con->content_handler); - - switch (action_execute(con)) { - case ACTION_WAIT_FOR_EVENT: - done = TRUE; - break; - case ACTION_GO_ON: - case ACTION_FINISHED: - connection_set_state(con, CON_STATE_WRITE_RESPONSE); - break; - case ACTION_ERROR: - internal_error(con); - break; - } - break; - - case CON_STATE_WRITE_RESPONSE: - if (con->in->is_closed && con->raw_out->is_closed) { - connection_set_state(con, CON_STATE_RESPONSE_END); - break; - } - - if (!con->response_headers_sent) { - con->response_headers_sent = TRUE; - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "write response headers"); - } - response_send_headers(con); - } - - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "write response"); - } - - parse_request_body(con); - - if (con->content_handler) - con->content_handler->handle_content(con, con->content_handler); - - forward_response_body(con); - - if (con->in->is_closed && con->raw_out->is_closed) { - connection_set_state(con, CON_STATE_RESPONSE_END); - break; - } - if (con->state == CON_STATE_WRITE_RESPONSE) done = TRUE; - break; - - case CON_STATE_RESPONSE_END: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "response end (keep_alive = %i)", con->keep_alive); - } - - plugins_handle_close(con); - - if (con->keep_alive) { - connection_reset_keep_alive(con); - } else { - worker_con_put(con); - done = TRUE; - } - break; - - case CON_STATE_CLOSE: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "connection closed"); - } - - plugins_handle_close(con); - - worker_con_put(con); - done = TRUE; - break; - - case CON_STATE_ERROR: - if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { - CON_TRACE(con, "%s", "connection closed (error)"); - } - - plugins_handle_close(con); - - worker_con_put(con); - done = TRUE; - break; - } - } while (!done); -} - -void connection_handle_direct(connection *con) { - connection_set_state(con, CON_STATE_WRITE_RESPONSE); - con->out->is_closed = TRUE; -} - -void connection_handle_indirect(connection *con, plugin *p) { - if (!p) { - connection_handle_direct(con); - } else if (p->handle_content) { - connection_set_state(con, CON_STATE_READ_REQUEST_CONTENT); - con->content_handler = p; - } else { - CON_ERROR(con, "Indirect plugin '%s' handler has no handle_content callback", p->name); - internal_error(con); - } -} diff --git a/src/connection.h b/src/connection.h index 1919c9f..cdb5754 100644 --- a/src/connection.h +++ b/src/connection.h @@ -10,54 +10,17 @@ typedef enum { /** waiting for new input after first request */ CON_STATE_KEEP_ALIVE, - /** after the connect, the request is initialized, keep-alive starts here again */ + /** after the connect, the request is initialized */ CON_STATE_REQUEST_START, /** loop in the read-request-header until the full header is received */ CON_STATE_READ_REQUEST_HEADER, - /** validate the request-header */ - CON_STATE_VALIDATE_REQUEST_HEADER, - /** find a handler for the request; there are two ways to produce responses: - * - direct response: for things like errors/auth/redirect - * just set the status code, perhaps fill in some headers, - * append your content (if any) to the queue and do: - * connection_handle_direct(con); - * this moves into the CON_STATE_HANDLE_RESPONSE_HEADER - * request body gets dropped - * - indirect response: you register your plugin as the content handler: - * connection_handle_indirect(con, plugin); - * this moves into the CON_STATE_READ_REQUEST_CONTENT state automatically - * as soon as you build the response headers (e.g. from a backend), - * change to the CON_STATE_HANDLE_RESPONSE_HEADER state: - * connection_set_state(con, CON_STATE_HANDLE_RESPONSE_HEADER); - */ - CON_STATE_HANDLE_REQUEST_HEADER, + /** handle in main virtual request */ + CON_STATE_HANDLE_MAINVR, - /** start forwarding the request content to the handler; - * any filter for the request content must register before a handler - * for the response content registers - */ - CON_STATE_READ_REQUEST_CONTENT, - - /** response headers available; this is were compress/deflate should register - * their response content filters - * if all actions are done (or one returns HANDLER_FINISHED) we start - * writing the response content - */ - CON_STATE_HANDLE_RESPONSE_HEADER, - - /** start sending content */ - CON_STATE_WRITE_RESPONSE, - - /** successful request, connection closed - final state */ - CON_STATE_RESPONSE_END, - - /** connection reset by peer - final state */ - CON_STATE_CLOSE, - - /** fatal error, connection closed - final state */ - CON_STATE_ERROR + /** write remaining bytes from raw_out, mainvr finished (or not started) */ + CON_STATE_WRITE, } connection_state_t; struct connection { @@ -69,24 +32,18 @@ struct connection { gboolean response_headers_sent, expect_100_cont; chunkqueue *raw_in, *raw_out; - chunkqueue *in, *out; + chunkqueue *in, *out; /* link to mainvr->in/out */ ev_io sock_watcher; sock_addr remote_addr, local_addr; GString *remote_addr_str, *local_addr_str; gboolean is_ssl, keep_alive; - action_stack action_stack; - option_value *options; - request request; - physical physical; - response response; + vrequest *mainvr; http_request_ctx req_parser_ctx; - plugin *content_handler; - struct log_t *log; gint log_level; @@ -101,10 +58,10 @@ struct connection { LI_API connection* connection_new(worker *wrk); LI_API void connection_reset(connection *con); +LI_API void connection_reset_keep_alive(connection *con); LI_API void connection_free(connection *con); -LI_API void connection_set_state(connection *con, connection_state_t state); -LI_API void connection_state_machine(connection *con); +LI_API void connection_error(connection *con); LI_API void connection_handle_direct(connection *con); LI_API void connection_handle_indirect(connection *con, plugin *p); diff --git a/src/http_request_parser.h b/src/http_request_parser.h index 1fa0f92..251cf04 100644 --- a/src/http_request_parser.h +++ b/src/http_request_parser.h @@ -22,7 +22,7 @@ LI_API void http_request_parser_init(http_request_ctx* ctx, request *req, chunkq LI_API void http_request_parser_reset(http_request_ctx* ctx); LI_API void http_request_parser_clear(http_request_ctx *ctx); -LI_API handler_t http_request_parse(struct connection *con, http_request_ctx *ctx); +LI_API handler_t http_request_parse(struct vrequest *vr, http_request_ctx *ctx); #endif diff --git a/src/http_request_parser.rl b/src/http_request_parser.rl index 6ccd1e7..547be9d 100644 --- a/src/http_request_parser.rl +++ b/src/http_request_parser.rl @@ -4,10 +4,10 @@ /** Machine **/ -#define _getString(M, FPC) (chunk_extract(con, ctx->M, GETMARK(FPC))) +#define _getString(M, FPC) (chunk_extract(vr, ctx->M, GETMARK(FPC))) #define getString(FPC) _getString(mark, FPC) -#define _getStringTo(M, FPC, s) (chunk_extract_to(con, ctx->M, GETMARK(FPC), s)) +#define _getStringTo(M, FPC, s) (chunk_extract_to(vr, ctx->M, GETMARK(FPC), s)) #define getStringTo(FPC, s) _getStringTo(mark, FPC, s) @@ -148,7 +148,7 @@ void http_request_parser_clear(http_request_ctx *ctx) { g_string_free(ctx->h_value, TRUE); } -handler_t http_request_parse(connection *con, http_request_ctx *ctx) { +handler_t http_request_parse(vrequest *vr, http_request_ctx *ctx) { handler_t res; if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON; @@ -158,7 +158,7 @@ handler_t http_request_parse(connection *con, http_request_ctx *ctx) { while (!http_request_parser_has_error(ctx) && !http_request_parser_is_finished(ctx)) { char *p, *pe; - if (HANDLER_GO_ON != (res = chunk_parser_next(con, &ctx->chunk_ctx, &p, &pe))) return res; + if (HANDLER_GO_ON != (res = chunk_parser_next(vr, &ctx->chunk_ctx, &p, &pe))) return res; %% write exec; diff --git a/src/log.c b/src/log.c index 110544c..00da8ff 100644 --- a/src/log.c +++ b/src/log.c @@ -41,6 +41,7 @@ gboolean log_write_(server *srv, connection *con, log_level_t log_level, guint f log_t *log = NULL; log_entry_t *log_entry; log_timestamp_t *ts = NULL; + vrequest *vr = con ? con->mainvr : NULL; if (con != NULL) { diff --git a/src/log.h b/src/log.h index 7879b06..4ac5220 100644 --- a/src/log.h +++ b/src/log.h @@ -42,6 +42,9 @@ LI_API const char *remove_path(const char *path); abort();\ } while(0) +#define VR_ERROR(vr, fmt, ...) CON_ERROR(vr->con, fmt, __VA_ARGS__) +#define VR_TRACE(vr, fmt, ...) CON_TRACE(vr->con, fmt, __VA_ARGS__) +#define VR_SEGFAULT(vr, fmt, ...) CON_SEGFAULT(vr->con, fmt, __VA_ARGS__) #undef ERROR #define ERROR(srv, fmt, ...) \ diff --git a/src/network.c b/src/network.c index e0c8bdc..77f59ce 100644 --- a/src/network.c +++ b/src/network.c @@ -35,7 +35,7 @@ ssize_t net_read(int fd, void *buf, ssize_t nbyte) { return r; } -network_status_t network_write(connection *con, int fd, chunkqueue *cq) { +network_status_t network_write(vrequest *vr, int fd, chunkqueue *cq) { network_status_t res; #ifdef TCP_CORK int corked = 0; @@ -52,7 +52,7 @@ network_status_t network_write(connection *con, int fd, chunkqueue *cq) { #endif /* res = network_write_writev(con, fd, cq); */ - res = network_write_sendfile(con, fd, cq); + res = network_write_sendfile(vr, fd, cq); #ifdef TCP_CORK if (corked) { @@ -64,7 +64,7 @@ network_status_t network_write(connection *con, int fd, chunkqueue *cq) { return res; } -network_status_t network_read(connection *con, int fd, chunkqueue *cq) { +network_status_t network_read(vrequest *vr, int fd, chunkqueue *cq) { const ssize_t blocksize = 16*1024; /* 16k */ const off_t max_read = 16 * blocksize; /* 256k */ ssize_t r; @@ -84,7 +84,7 @@ network_status_t network_read(connection *con, int fd, chunkqueue *cq) { case ECONNRESET: return NETWORK_STATUS_CONNECTION_CLOSE; default: - CON_ERROR(con, "oops, read from fd=%d failed: %s", fd, g_strerror(errno) ); + VR_ERROR(vr, "oops, read from fd=%d failed: %s", fd, g_strerror(errno) ); return NETWORK_STATUS_FATAL_ERROR; } } else if (0 == r) { diff --git a/src/network.h b/src/network.h index 65680ba..6bb5e17 100644 --- a/src/network.h +++ b/src/network.h @@ -18,23 +18,23 @@ LI_API ssize_t net_write(int fd, void *buf, ssize_t nbyte); /** repeats read after EINTR */ LI_API ssize_t net_read(int fd, void *buf, ssize_t nbyte); -LI_API network_status_t network_write(connection *con, int fd, chunkqueue *cq); -LI_API network_status_t network_read(connection *con, int fd, chunkqueue *cq); +LI_API network_status_t network_write(vrequest *vr, int fd, chunkqueue *cq); +LI_API network_status_t network_read(vrequest *vr, int fd, chunkqueue *cq); /* use writev for mem chunks, buffered read/write for files */ -LI_API network_status_t network_write_writev(connection *con, int fd, chunkqueue *cq); +LI_API network_status_t network_write_writev(vrequest *vr, int fd, chunkqueue *cq); /* use sendfile for files, writev for mem chunks */ -LI_API network_status_t network_write_sendfile(connection *con, int fd, chunkqueue *cq); +LI_API network_status_t network_write_sendfile(vrequest *vr, int fd, chunkqueue *cq); /* write backends */ -LI_API network_status_t network_backend_write(connection *con, int fd, chunkqueue *cq, goffset *write_max); -LI_API network_status_t network_backend_writev(connection *con, int fd, chunkqueue *cq, goffset *write_max); -LI_API network_status_t network_backend_writev(connection *con, int fd, chunkqueue *cq, goffset *write_max); +LI_API network_status_t network_backend_write(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max); +LI_API network_status_t network_backend_writev(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max); +LI_API network_status_t network_backend_writev(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max); #define NETWORK_FALLBACK(f, write_max) do { \ network_status_t res; \ - switch(res = f(con, fd, cq, write_max)) { \ + switch(res = f(vr, fd, cq, write_max)) { \ case NETWORK_STATUS_SUCCESS: \ break; \ default: \ diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c index 7c193bb..9eb6e5c 100644 --- a/src/network_linux_sendfile.c +++ b/src/network_linux_sendfile.c @@ -2,7 +2,7 @@ #include "network.h" /* first chunk must be a FILE_CHUNK ! */ -network_status_t network_backend_sendfile(connection *con, int fd, chunkqueue *cq, goffset *write_max) { +network_status_t network_backend_sendfile(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max) { off_t file_offset, toSend; ssize_t r; gboolean did_write_something = FALSE; @@ -18,7 +18,7 @@ network_status_t network_backend_sendfile(connection *con, int fd, chunkqueue *c return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_FATAL_ERROR; } - switch (chunkfile_open(con, c->file.file)) { + switch (chunkfile_open(vr, c->file.file)) { case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: @@ -49,7 +49,7 @@ network_status_t network_backend_sendfile(connection *con, int fd, chunkqueue *c NETWORK_FALLBACK(network_backend_write, write_max); return NETWORK_STATUS_SUCCESS; default: - CON_ERROR(con, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); + VR_ERROR(vr, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); return NETWORK_STATUS_FATAL_ERROR; } } @@ -57,13 +57,13 @@ network_status_t network_backend_sendfile(connection *con, int fd, chunkqueue *c /* don't care about cached stat - file is open */ struct stat st; if (-1 == fstat(fd, &st)) { - CON_ERROR(con, "Couldn't fstat file: %s", g_strerror(errno)); + VR_ERROR(vr, "Couldn't fstat file: %s", g_strerror(errno)); return NETWORK_STATUS_FATAL_ERROR; } if (file_offset > st.st_size) { /* file shrinked, close the connection */ - CON_ERROR(con, "%s", "File shrinked, aborting"); + VR_ERROR(vr, "%s", "File shrinked, aborting"); return NETWORK_STATUS_FATAL_ERROR; } return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; @@ -77,7 +77,7 @@ network_status_t network_backend_sendfile(connection *con, int fd, chunkqueue *c return NETWORK_STATUS_SUCCESS; } -network_status_t network_write_sendfile(connection *con, int fd, chunkqueue *cq) { +network_status_t network_write_sendfile(vrequest *vr, int fd, chunkqueue *cq) { goffset write_max = 256*1024; // 256kB //; if (cq->length == 0) return NETWORK_STATUS_FATAL_ERROR; do { diff --git a/src/network_write.c b/src/network_write.c index 6461807..183f94f 100644 --- a/src/network_write.c +++ b/src/network_write.c @@ -1,7 +1,7 @@ #include "network.h" -network_status_t network_backend_write(connection *con, int fd, chunkqueue *cq, goffset *write_max) { +network_status_t network_backend_write(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max) { const ssize_t blocksize = 16*1024; /* 16k */ char *block_data; off_t block_len; @@ -14,7 +14,7 @@ network_status_t network_backend_write(connection *con, int fd, chunkqueue *cq, return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_FATAL_ERROR; ci = chunkqueue_iter(cq); - switch (chunkiter_read(con, ci, 0, blocksize, &block_data, &block_len)) { + switch (chunkiter_read(vr, ci, 0, blocksize, &block_data, &block_len)) { case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: @@ -35,7 +35,7 @@ network_status_t network_backend_write(connection *con, int fd, chunkqueue *cq, case EPIPE: return NETWORK_STATUS_CONNECTION_CLOSE; default: - CON_ERROR(con, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); + VR_ERROR(vr, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); return NETWORK_STATUS_FATAL_ERROR; } } else if (0 == r) { diff --git a/src/network_writev.c b/src/network_writev.c index 6349d98..dc2b072 100644 --- a/src/network_writev.c +++ b/src/network_writev.c @@ -25,7 +25,7 @@ #endif /* first chunk must be a MEM_CHUNK ! */ -network_status_t network_backend_writev(connection *con, int fd, chunkqueue *cq, goffset *write_max) { +network_status_t network_backend_writev(vrequest *vr, int fd, chunkqueue *cq, goffset *write_max) { off_t we_have; ssize_t r; gboolean did_write_something = FALSE; @@ -76,7 +76,7 @@ network_status_t network_backend_writev(connection *con, int fd, chunkqueue *cq, case EINTR: break; /* try again */ default: - CON_ERROR(con, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); + VR_ERROR(vr, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); goto cleanup; } } @@ -108,7 +108,7 @@ cleanup: return res; } -network_status_t network_write_writev(connection *con, int fd, chunkqueue *cq) { +network_status_t network_write_writev(vrequest *vr, int fd, chunkqueue *cq) { goffset write_max = 256*1024; // 256k //; if (cq->length == 0) return NETWORK_STATUS_FATAL_ERROR; do { diff --git a/src/plugin.h b/src/plugin.h index e6c3ed7..39d9d8b 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -160,8 +160,8 @@ LI_API gboolean call_setup(server *srv, const char *name, value *val); LI_API gboolean plugin_set_default_option(server *srv, const gchar* name, value *val); /* needs connection *con and plugin *p */ -#define OPTION(idx) _OPTION(con, p, idx) -#define _OPTION(con, p, idx) (con->options[p->opt_base_index + idx]) -#define _OPTION_ABS(con, idx) (con->options[idx]) +#define OPTION(idx) _OPTION(vr, p, idx) +#define _OPTION(vr, p, idx) (vr->con->options[p->opt_base_index + idx]) +#define _OPTION_ABS(vr, idx) (vr->con->options[idx]) #endif diff --git a/src/plugin_core.c b/src/plugin_core.c index 336bf62..00afcfd 100644 --- a/src/plugin_core.c +++ b/src/plugin_core.c @@ -119,33 +119,59 @@ static action* core_set(server *srv, plugin* p, value *val) { return a; } -static action_result core_handle_static(connection *con, gpointer param) { +static gboolean core_setup_set(server *srv, plugin* p, value *val) { + value *val_val, *val_name; + UNUSED(p); + + if (!val) { + ERROR(srv, "%s", "need parameter"); + return FALSE; + } + if (val->type != VALUE_LIST) { + ERROR(srv, "expected list, got %s", value_type_string(val->type)); + return FALSE; + } + if (val->data.list->len != 2) { + ERROR(srv, "expected list with length 2, has length %u", val->data.list->len); + return FALSE; + } + val_name = g_array_index(val->data.list, value*, 0); + val_val = g_array_index(val->data.list, value*, 1); + if (val_name->type != VALUE_STRING) { + ERROR(srv, "expected string as first parameter, got %s", value_type_string(val_name->type)); + return FALSE; + } + return plugin_set_default_option(srv, val_name->data.string->str, val_val); +} + +static handler_t core_handle_static(vrequest *vr, gpointer param) { UNUSED(param); int fd; - if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON; - /* build physical path: docroot + uri.path */ - g_string_truncate(con->physical.path, 0); - g_string_append_len(con->physical.path, GSTR_LEN(CORE_OPTION(CORE_OPTION_DOCROOT).string)); - g_string_append_len(con->physical.path, GSTR_LEN(con->request.uri.path)); + g_string_truncate(vr->physical.path, 0); + g_string_append_len(vr->physical.path, GSTR_LEN(CORE_OPTION(CORE_OPTION_DOCROOT).string)); + g_string_append_len(vr->physical.path, GSTR_LEN(vr->request.uri.path)); - CON_TRACE(con, "physical path: %s", con->physical.path->str); + VR_TRACE(vr, "physical path: %s", vr->physical.path->str); if (con->physical.path->len == 0) return ACTION_GO_ON; - fd = open(con->physical.path->str, O_RDONLY); + if (!vrequest_handle_direct(vr)) return HANDLER_GO_ON; + + fd = open(vr->physical.path->str, O_RDONLY); if (fd == -1) { - CON_TRACE(con, "open() failed: %s (%d)\n", g_strerror(errno), errno); + vr->response.http_status = 404; + VR_TRACE(con, "open() failed: %s (%d)\n", g_strerror(errno), errno); switch (errno) { case ENOENT: - con->response.http_status = 404; break; + vr->response.http_status = 404; break; case EACCES: case EFAULT: - con->response.http_status = 403; break; + vr->response.http_status = 403; break; default: - con->response.http_status = 500; + vr->response.http_status = 500; } } else { struct stat st; @@ -155,21 +181,20 @@ static action_result core_handle_static(connection *con, gpointer param) { fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if (!S_ISREG(st.st_mode)) { - con->response.http_status = 404; + vr->response.http_status = 404; close(fd); } else { - GString *mime_str = mimetype_get(con, con->request.uri.path); - con->response.http_status = 200; + GString *mime_str = mimetype_get(con, vr->request.uri.path); + vr->response.http_status = 200; if (mime_str) - http_header_overwrite(con->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str)); + http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str)); else - http_header_overwrite(con->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); - chunkqueue_append_file_fd(con->out, NULL, 0, st.st_size, fd); + http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); + chunkqueue_append_file_fd(vr->out, NULL, 0, st.st_size, fd); } } - connection_handle_direct(con); - return ACTION_GO_ON; + return HANDLER_GO_ON; } static action* core_static(server *srv, plugin* p, value *val) { @@ -182,7 +207,8 @@ static action* core_static(server *srv, plugin* p, value *val) { return action_new_function(core_handle_static, NULL, NULL); } -static action_result core_handle_test(connection *con, gpointer param) { +static handler_t core_handle_test(vrequest *vr, gpointer param) { + connection *con = vr->con; server *srv = con->srv; worker *wrk = con->wrk; /*GHashTableIter iter; @@ -195,17 +221,17 @@ static action_result core_handle_test(connection *con, gpointer param) { gchar suffix1[2] = {0,0}, suffix2[2] = {0,0}, suffix3[2] = {0,0}; UNUSED(param); - if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON; + if (!vrequest_handle_direct(vr)) return HANDLER_GO_ON; - con->response.http_status = 200; - chunkqueue_append_mem(con->out, CONST_STR_LEN("host: ")); - chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.host)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\npath: ")); - chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.path)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\nquery: ")); - chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.query)); + vr->response.http_status = 200; + chunkqueue_append_mem(vr->out, CONST_STR_LEN("host: ")); + chunkqueue_append_mem(vr->out, GSTR_LEN(vr->request.uri.host)); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\npath: ")); + chunkqueue_append_mem(vr->out, GSTR_LEN(vr->request.uri.path)); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\nquery: ")); + chunkqueue_append_mem(vr->out, GSTR_LEN(vr->request.uri.query)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n\r\nactions executed: ")); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\n\r\nactions executed: ")); uptime = (guint64)(ev_now(con->wrk->loop) - srv->started); if (uptime == 0) uptime = 1; @@ -220,36 +246,35 @@ static action_result core_handle_test(connection *con, gpointer param) { "%"G_GUINT64_FORMAT"%s (%"G_GUINT64_FORMAT"%s/s, %"G_GUINT64_FORMAT"%s/req)", avg1, suffix1, avg2, suffix2, avg3, suffix3 ); - chunkqueue_append_string(con->out, str); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\nrequests: ")); + chunkqueue_append_string(vr->out, str); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\nrequests: ")); avg1 = wrk->stats.requests; suffix1[0] = counter_format(&avg1, 1000); avg2 = wrk->stats.requests / uptime; suffix2[0] = counter_format(&avg2, 1000); str = g_string_sized_new(0); g_string_printf(str, "%"G_GUINT64_FORMAT"%s (%"G_GUINT64_FORMAT"%s/s)", avg1, suffix1, avg2, suffix2); - chunkqueue_append_string(con->out, str); + chunkqueue_append_string(vr->out, str); backend = ev_backend_string(ev_backend(con->wrk->loop)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\nevent handler: ")); - chunkqueue_append_mem(con->out, backend, strlen(backend)); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\nevent handler: ")); + chunkqueue_append_mem(vr->out, backend, strlen(backend)); -/* chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n\r\n--- headers ---\r\n")); +/* chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\n\r\n--- headers ---\r\n")); g_hash_table_iter_init(&iter, con->request.headers->table); while (g_hash_table_iter_next(&iter, &k, &v)) { hv = g_queue_peek_head_link(&((http_header*)v)->values); while (hv != NULL) { - chunkqueue_append_mem(con->out, GSTR_LEN(((http_header*)v)->key)); - chunkqueue_append_mem(con->out, CONST_STR_LEN(": ")); - chunkqueue_append_mem(con->out, GSTR_LEN((GString*)hv->data)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n")); + chunkqueue_append_mem(vr->out, GSTR_LEN(((http_header*)v)->key)); + chunkqueue_append_mem(vr->out, CONST_STR_LEN(": ")); + chunkqueue_append_mem(vr->out, GSTR_LEN((GString*)hv->data)); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\n")); hv = hv->next; } }*/ - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n")); - connection_handle_direct(con); + chunkqueue_append_mem(vr->out, CONST_STR_LEN("\r\n")); - return ACTION_GO_ON; + return HANDLER_GO_ON; } static action* core_test(server *srv, plugin* p, value *val) { @@ -263,15 +288,14 @@ static action* core_test(server *srv, plugin* p, value *val) { return action_new_function(core_handle_test, NULL, NULL); } -static action_result core_handle_blank(connection *con, gpointer param) { +static handler_t core_handle_blank(vrequest *vr, gpointer param) { UNUSED(param); - if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON; + if (!vrequest_handle_direct(vr)) return HANDLER_GO_ON; - con->response.http_status = 200; - connection_handle_direct(con); + vr->response.http_status = 200; - return ACTION_GO_ON; + return HANDLER_GO_ON; } static action* core_blank(server *srv, plugin* p, value *val) { diff --git a/src/plugin_core.h b/src/plugin_core.h index b2ded4f..424d188 100644 --- a/src/plugin_core.h +++ b/src/plugin_core.h @@ -18,7 +18,7 @@ enum core_options_t { }; /* the core plugin always has base index 0, as it is the first plugin loaded */ -#define CORE_OPTION(idx) _CORE_OPTION(con, idx) -#define _CORE_OPTION(con, idx) _OPTION_ABS(con, idx) +#define CORE_OPTION(idx) _CORE_OPTION(vr, idx) +#define _CORE_OPTION(vr, idx) _OPTION_ABS(vr, idx) #endif diff --git a/src/request.c b/src/request.c index 8830901..27b5bb2 100644 --- a/src/request.c +++ b/src/request.c @@ -57,12 +57,12 @@ void request_clear(request *req) { /* closes connection after response */ static void bad_request(connection *con, int status) { con->keep_alive = FALSE; - con->response.http_status = status; - connection_handle_direct(con); + con->mainvr->response.http_status = status; + vrequest_handle_direct(con->mainvr); } -gboolean request_parse_url(connection *con) { - request *req = &con->request; +gboolean request_parse_url(vrequest *vr) { + request *req = &vr->request; g_string_truncate(req->uri.query, 0); g_string_truncate(req->uri.path, 0); @@ -80,8 +80,8 @@ gboolean request_parse_url(connection *con) { return TRUE; } -void request_validate_header(connection *con) { - request *req = &con->request; +gboolean request_validate_header(connection *con) { + request *req = &con->mainvr->request; http_header *hh; GList *l; @@ -96,12 +96,12 @@ void request_validate_header(connection *con) { break; case HTTP_VERSION_UNSET: bad_request(con, 505); /* Version not Supported */ - return; + return FALSE; } if (req->uri.raw->len == 0) { bad_request(con, 400); /* bad request */ - return; + return FALSE; } /* get hostname */ @@ -109,26 +109,26 @@ void request_validate_header(connection *con) { if (NULL != l && NULL != http_header_find_next(l, CONST_STR_LEN("host"))) { /* more than one "host" header */ bad_request(con, 400); /* bad request */ - return; + return FALSE; } else { hh = (http_header*) l->data; g_string_append_len(req->uri.authority, HEADER_VALUE_LEN(hh)); if (!parse_hostname(&req->uri)) { bad_request(con, 400); /* bad request */ - return; + return FALSE; } } /* Need hostname in HTTP/1.1 */ if (req->uri.host->len == 0 && req->http_version == HTTP_VERSION_1_1) { bad_request(con, 400); /* bad request */ - return; + return FALSE; } /* may override hostname */ - if (!request_parse_url(con)) { + if (!request_parse_url(con->mainvr)) { bad_request(con, 400); /* bad request */ - return; + return FALSE; } /* content-length */ @@ -142,7 +142,7 @@ void request_validate_header(connection *con) { if (*err != '\0') { CON_TRACE(con, "content-length is not a number: %s (Status: 400)", err); bad_request(con, 400); /* bad request */ - return; + return FALSE; } /** @@ -151,7 +151,7 @@ void request_validate_header(connection *con) { */ if (r < 0) { bad_request(con, 400); /* bad request */ - return; + return FALSE; } /** @@ -161,11 +161,11 @@ void request_validate_header(connection *con) { r == STR_OFF_T_MAX) { if (errno == ERANGE) { bad_request(con, 413); /* Request Entity Too Large */ - return; + return FALSE; } } - con->request.content_length = r; + con->mainvr->request.content_length = r; } /* Expect: 100-continue */ @@ -180,14 +180,14 @@ void request_validate_header(connection *con) { } else { /* we only support 100-continue */ bad_request(con, 417); /* Expectation Failed */ - return; + return FALSE; } } if (expect_100_cont && req->http_version == HTTP_VERSION_1_0) { /* only HTTP/1.1 clients can send us this header */ bad_request(con, 417); /* Expectation Failed */ - return; + return FALSE; } con->expect_100_cont = expect_100_cont; } @@ -198,32 +198,34 @@ void request_validate_header(connection *con) { * - Range (duplicate check) */ - switch(con->request.http_method) { + switch(con->mainvr->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_HEAD: /* content-length is forbidden for those */ - if (con->request.content_length > 0) { - CON_ERROR(con, "%s", "GET/HEAD with content-length -> 400"); + if (con->mainvr->request.content_length > 0) { + VR_ERROR(con->mainvr, "%s", "GET/HEAD with content-length -> 400"); bad_request(con, 400); /* bad request */ - return; + return FALSE; } - con->request.content_length = 0; + con->mainvr->request.content_length = 0; break; case HTTP_METHOD_POST: /* content-length is required for them */ - if (con->request.content_length == -1) { + if (con->mainvr->request.content_length == -1) { /* content-length is missing */ - CON_ERROR(con, "%s", "POST-request, but content-length missing -> 411"); + VR_ERROR(con->mainvr, "%s", "POST-request, but content-length missing -> 411"); bad_request(con, 411); /* Length Required */ - return; + return FALSE; } break; default: /* the may have a content-length */ break; } + + return TRUE; } void physical_init(physical *phys) { diff --git a/src/request.h b/src/request.h index 931b548..6fa9543 100644 --- a/src/request.h +++ b/src/request.h @@ -88,7 +88,7 @@ LI_API void request_init(request *req); LI_API void request_reset(request *req); LI_API void request_clear(request *req); -LI_API void request_validate_header(connection *con); +LI_API gboolean request_validate_header(connection *con); LI_API void physical_init(physical *phys); LI_API void physical_reset(physical *phys); diff --git a/src/response.c b/src/response.c index 5da7bab..4a73c4d 100644 --- a/src/response.c +++ b/src/response.c @@ -23,66 +23,69 @@ void response_clear(response *resp) { void response_send_headers(connection *con) { GString *head = g_string_sized_new(8*1024); + vrequest *vr = con->mainvr; - if (con->response.http_status < 100 || con->response.http_status > 999) { - con->response.http_status = 500; - con->content_handler = NULL; - chunkqueue_reset(con->out); + if (vr->response.http_status < 100 || vr->response.http_status > 999) { + VR_ERROR(vr, "wrong status: %i", vr->response.http_status); + vrequest_reset(con->mainvr); + http_headers_reset(con->mainvr->response.headers); + con->mainvr->response.http_status = 500; + con->state = CON_STATE_WRITE; } - if (0 == con->out->length && con->content_handler == NULL - && con->response.http_status >= 400 && con->response.http_status < 600) { + if (0 == con->out->length && con->mainvr->handle_response_body == NULL + && vr->response.http_status >= 400 && vr->response.http_status < 600) { /*chunkqueue_append_mem(con->out, CONST_STR_LEN("Custom error\r\n"));*/ response_send_error_page(con); } - if (con->content_handler == NULL) { + if (con->mainvr->handle_response_body == NULL) { con->out->is_closed = TRUE; } - if ((con->response.http_status >= 100 && con->response.http_status < 200) || - con->response.http_status == 204 || - con->response.http_status == 205 || - con->response.http_status == 304) { + if ((vr->response.http_status >= 100 && vr->response.http_status < 200) || + vr->response.http_status == 204 || + vr->response.http_status == 205 || + vr->response.http_status == 304) { /* They never have a content-body/length */ chunkqueue_reset(con->out); con->out->is_closed = TRUE; } else if (con->out->is_closed) { g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, con->out->length); - http_header_overwrite(con->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(con->wrk->tmp_str)); - } else if (con->keep_alive && con->request.http_version == HTTP_VERSION_1_1) { - if (!(con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED)) { - con->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED; - http_header_append(con->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); + http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(con->wrk->tmp_str)); + } else if (con->keep_alive && vr->request.http_version == HTTP_VERSION_1_1) { + if (!(vr->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED)) { + vr->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED; + http_header_append(vr->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } } else { /* Unknown content length, no chunked encoding */ con->keep_alive = FALSE; } - if (con->request.http_method == HTTP_METHOD_HEAD) { + if (vr->request.http_method == HTTP_METHOD_HEAD) { /* content-length is set, but no body */ chunkqueue_reset(con->out); con->out->is_closed = TRUE; } /* Status line */ - if (con->request.http_version == HTTP_VERSION_1_1) { + if (vr->request.http_version == HTTP_VERSION_1_1) { g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 ")); if (!con->keep_alive) - http_header_overwrite(con->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); + http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); } else { g_string_append_len(head, CONST_STR_LEN("HTTP/1.0 ")); if (con->keep_alive) - http_header_overwrite(con->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); + http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); } { guint len; gchar status_str[4]; - gchar *str = http_status_string(con->response.http_status, &len); - http_status_to_str(con->response.http_status, status_str); + gchar *str = http_status_string(vr->response.http_status, &len); + http_status_to_str(vr->response.http_status, status_str); status_str[3] = ' '; g_string_append_len(head, status_str, 4); g_string_append_len(head, str, len); @@ -95,7 +98,7 @@ void response_send_headers(connection *con) { GList *iter; gboolean have_date = FALSE, have_server = FALSE; - for (iter = g_queue_peek_head_link(&con->response.headers->entries); iter; iter = g_list_next(iter)) { + for (iter = g_queue_peek_head_link(&vr->response.headers->entries); iter; iter = g_list_next(iter)) { header = (http_header*) iter->data; g_string_append_len(head, GSTR_LEN(header->data)); g_string_append_len(head, CONST_STR_LEN("\r\n")); diff --git a/src/server.c b/src/server.c index 054b02a..5cda510 100644 --- a/src/server.c +++ b/src/server.c @@ -314,10 +314,6 @@ void server_exit(server *srv) { } } -void joblist_append(connection *con) { - connection_state_machine(con); -} - /* cache timestamp */ GString *server_current_timestamp() { static GStaticPrivate last_ts_key = G_STATIC_PRIVATE_INIT; diff --git a/src/server.h b/src/server.h index 1af94e7..dab4c88 100644 --- a/src/server.h +++ b/src/server.h @@ -92,8 +92,6 @@ LI_API void server_stop(server *srv); /* exit asap with cleanup */ LI_API void server_exit(server *srv); -LI_API void joblist_append(connection *con); - -LI_API GString *server_current_timestamp(); +GString *server_current_timestamp(); #endif diff --git a/src/virtualrequest.c b/src/virtualrequest.c new file mode 100644 index 0000000..8b34dc3 --- /dev/null +++ b/src/virtualrequest.c @@ -0,0 +1,310 @@ + +#include "base.h" +#include "plugin_core.h" + +static filter* filter_new() { + filter *f = g_slice_new0(filter); + return f; +} + +static void filter_free(filter *f) { + g_slice_free(filter, f); +} + +static void filters_init(filters *fs) { + fs->queue = g_ptr_array_new(); + fs->in = chunkqueue_new(); + fs->out = chunkqueue_new(); +} + +static void filters_clean(filters *fs) { + guint i; + for (i = 0; i < fs->queue->len; i++) { + filter_free((filter*) g_ptr_array_index(fs->queue, i)); + } + g_ptr_array_free(fs->queue, TRUE); + chunkqueue_free(fs->in); + chunkqueue_free(fs->out); +} + +static void filters_reset(filters *fs) { + guint i; + for (i = 0; i < fs->queue->len; i++) { + filter_free((filter*) g_ptr_array_index(fs->queue, i)); + } + g_ptr_array_set_size(fs->queue, 0); + chunkqueue_reset(fs->in); + chunkqueue_reset(fs->out); +} + +vrequest* vrequest_new(connection *con, vrequest_handler handle_response_headers, vrequest_handler handle_response_body, vrequest_handler handle_response_error, vrequest_handler handle_request_headers) { + vrequest *vr = g_slice_new0(vrequest); + + vr->con = con; + vr->state = VRS_CLEAN; + + vr->handle_response_headers = handle_response_headers; + vr->handle_response_body = handle_response_body; + vr->handle_response_error = handle_response_error; + vr->handle_request_headers = handle_request_headers; + + request_init(&vr->request); + physical_init(&vr->physical); + response_init(&vr->response); + + filters_init(&vr->filters_in); + filters_init(&vr->filters_out); + vr->vr_in = vr->filters_in.in; + vr->in = vr->filters_in.out; + vr->out = vr->filters_out.in; + vr->vr_out = vr->filters_out.out; + + action_stack_init(&vr->action_stack); + + return vr; +} + +void vrequest_free(vrequest* vr) { + request_clear(&vr->request); + physical_clear(&vr->physical); + response_clear(&vr->response); + + filters_clean(&vr->filters_in); + filters_clean(&vr->filters_out); + + action_stack_clear(vr->con->srv, &vr->action_stack); + + g_slice_free(vrequest, vr); +} + +void vrequest_reset(vrequest *vr) { + vr->state = VRS_CLEAN; + + vr->handle_request_body = NULL; + + request_reset(&vr->request); + physical_reset(&vr->physical); + response_reset(&vr->response); + + filters_reset(&vr->filters_in); + filters_reset(&vr->filters_out); + + action_stack_reset(vr->con->srv, &vr->action_stack); +} + +void vrequest_error(vrequest *vr) { + if (0 == vr->out->bytes_in) { + VR_ERROR(vr, "%s", "vrequest error"); + vr->response.http_status = 500; + vrequest_handle_direct(vr); + } else { + vr->state = VRS_ERROR; + vrequest_joblist_append(vr); + } +} + +/* received all request headers */ +void vrequest_handle_request_headers(vrequest *vr) { + if (VRS_CLEAN == vr->state) { + vr->state = VRS_HANDLE_REQUEST_HEADERS; + } + vrequest_joblist_append(vr); +} + +/* received (partial) request content */ +void vrequest_handle_request_body(vrequest *vr) { + if (VRS_READ_CONTENT <= vr->state) { + vrequest_joblist_append(vr); + } +} + +/* received all response headers/status code - call once from your indirect handler */ +void vrequest_handle_response_headers(vrequest *vr) { + if (VRS_HANDLE_RESPONSE_HEADERS > vr->state) { + vr->state = VRS_HANDLE_RESPONSE_HEADERS; + } + vrequest_joblist_append(vr); +} + +/* received (partial) response content - call from your indirect handler */ +void vrequest_handle_response_body(vrequest *vr) { + if (VRS_WRITE_CONTENT == vr->state) { + vrequest_joblist_append(vr); + } +} + +/* response completely ready */ +gboolean vrequest_handle_direct(vrequest *vr) { + if (vr->state < VRS_READ_CONTENT) { + vr->state = VRS_HANDLE_RESPONSE_HEADERS; + vr->out->is_closed = TRUE; + vr->handle_request_body = NULL; + return TRUE; + } else { + return FALSE; + } +} + +/* handle request over time */ +gboolean vrequest_handle_indirect(vrequest *vr, vrequest_handler handle_request_body) { + if (vr->state < VRS_READ_CONTENT) { + vr->state = VRS_READ_CONTENT; + vr->handle_request_body = handle_request_body; + return TRUE; + } else { + return FALSE; + } +} + +static gboolean vrequest_do_handle_actions(vrequest *vr) { + handler_t res = action_execute(vr); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + if (vr->state == VRS_HANDLE_REQUEST_HEADERS) { + VR_ERROR(vr, "%s", "actions didn't handle request"); + /* request not handled */ + vrequest_error(vr); + return FALSE; + } + /* otherwise state already changed */ + break; + case HANDLER_COMEBACK: + vrequest_joblist_append(vr); /* come back later */ + return FALSE; + case HANDLER_WAIT_FOR_FD: /* TODO: wait for fd */ + case HANDLER_WAIT_FOR_EVENT: + return FALSE; + case HANDLER_ERROR: + vrequest_error(vr); + return FALSE; + } + return TRUE; +} + +static gboolean vrequest_do_handle_read(vrequest *vr) { + handler_t res; + if (vr->in->is_closed && vr->in->bytes_in == vr->in->bytes_out) return TRUE; + if (vr->handle_request_body) { + chunkqueue_steal_all(vr->in, vr->vr_in); /* TODO: filters */ + if (vr->vr_in->is_closed) vr->in->is_closed = TRUE; + res = vr->handle_request_body(vr); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + case HANDLER_COMEBACK: + vrequest_joblist_append(vr); /* come back later */ + return FALSE; + case HANDLER_WAIT_FOR_FD: /* TODO: wait for fd */ + case HANDLER_WAIT_FOR_EVENT: + return FALSE; + case HANDLER_ERROR: + vrequest_error(vr); + break; + } + } else { + chunkqueue_skip_all(vr->vr_in); + if (vr->vr_in->is_closed) vr->in->is_closed = TRUE; + } + return TRUE; +} + +static gboolean vrequest_do_handle_write(vrequest *vr) { + handler_t res; + chunkqueue_steal_all(vr->vr_out, vr->out); /* TODO: filters */ + if (vr->out->is_closed) vr->vr_out->is_closed = TRUE; + res = vr->handle_response_body(vr); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + case HANDLER_COMEBACK: + vrequest_joblist_append(vr); /* come back later */ + return FALSE; + case HANDLER_WAIT_FOR_FD: /* TODO: wait for fd */ + case HANDLER_WAIT_FOR_EVENT: + return FALSE; + case HANDLER_ERROR: + vrequest_error(vr); + break; + } + return TRUE; +} + +void vrequest_state_machine(vrequest *vr) { + gboolean done = FALSE; + handler_t res; + do { + switch (vr->state) { + case VRS_CLEAN: + done = TRUE; + break; + + case VRS_HANDLE_REQUEST_HEADERS: + if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + VR_TRACE(vr, "%s", "handle request header"); + } + if (!vrequest_do_handle_actions(vr)) return; + res = vr->handle_request_headers(vr); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + case HANDLER_COMEBACK: + vrequest_joblist_append(vr); /* come back later */ + done = TRUE; + break; + case HANDLER_WAIT_FOR_FD: /* TODO: wait for fd */ + case HANDLER_WAIT_FOR_EVENT: + done = TRUE; + break; + case HANDLER_ERROR: + vrequest_error(vr); + break; + } + break; + + case VRS_READ_CONTENT: + done = !vrequest_do_handle_read(vr); + break; + + case VRS_HANDLE_RESPONSE_HEADERS: + if (!vrequest_do_handle_actions(vr)) return; + res = vr->handle_response_headers(vr); + switch (res) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + vr->state = VRS_WRITE_CONTENT; + break; + case HANDLER_COMEBACK: + vrequest_joblist_append(vr); /* come back later */ + done = TRUE; + break; + case HANDLER_WAIT_FOR_FD: /* TODO: wait for fd */ + case HANDLER_WAIT_FOR_EVENT: + done = TRUE; + break; + case HANDLER_ERROR: + vrequest_error(vr); + break; + } + break; + + case VRS_WRITE_CONTENT: + vrequest_do_handle_read(vr); + vrequest_do_handle_write(vr); + done = TRUE; + break; + + case VRS_ERROR: + vr->handle_response_error(vr); + return; + } + } while (!done); +} + +void vrequest_joblist_append(vrequest *vr) { + /* TODO */ + vrequest_state_machine(vr); +} diff --git a/src/virtualrequest.h b/src/virtualrequest.h new file mode 100644 index 0000000..7714acb --- /dev/null +++ b/src/virtualrequest.h @@ -0,0 +1,109 @@ +#ifndef _LIGHTTPD_VIRTUALREQUEST_H_ +#define _LIGHTTPD_VIRTUALREQUEST_H_ + +typedef enum { + /* waiting for request headers */ + VRS_CLEAN, + + /* all headers received, now handling them, set up input filters + * this state is set by the previous vrequest after VRS_WROTE_RESPONSE_HEADERS (or the main connection), + * and the handle_request function is called (which execute the action stack by default) + */ + VRS_HANDLE_REQUEST_HEADERS, + + /* request headers handled, input filters ready; now content is accepted + * this state is set via handle_indirect (handle_direct skips to VRS_HANDLE_RESPONSE_HEADERS + */ + VRS_READ_CONTENT, + + /* all response headers written, now set up output filters */ + VRS_HANDLE_RESPONSE_HEADERS, + + /* output filters ready, content can be written */ + VRS_WRITE_CONTENT, + + /* request done */ +// VRS_END, + + VRS_ERROR +} vrequest_state; + + +struct vrequest; +typedef struct vrequest vrequest; + +struct filter; +typedef struct filter filter; + +struct filters; +typedef struct filters filters; + +typedef handler_t (*filter_handler)(vrequest *vr, filter *f, plugin *p); +typedef handler_t (*vrequest_handler)(vrequest *vr); + +#include "base.h" + +struct filter { + chunkqueue *in, *out; + plugin *p; + filter_handler handle; +}; + +struct filters { + GPtrArray *queue; + chunkqueue *in, *out; + gboolean pending, waitforevent, waitforfd; +}; + +struct connection; +struct vrequest { + struct connection *con; + + /* TODO: move options from con */ + vrequest_state state; + + vrequest_handler + handle_request_headers, handle_request_body, + handle_response_headers, handle_response_body, + handle_response_error; /* this is _not_ for 500 - internal error */ + + GPtrArray *plugin_ctx; + + request request; + physical physical; + response response; + + /* -> vr_in -> filters_in -> in -> handle -> out -> filters_out -> vr_out -> */ + gboolean cq_memory_limit_hit; /* stop feeding chunkqueues with memory chunks */ + filters filters_in, filters_out; + chunkqueue *vr_in, *vr_out; + chunkqueue *in, *out; + + action_stack action_stack; + gboolean actions_wait_for_response; +}; + +LI_API vrequest* vrequest_new(struct connection *con, vrequest_handler handle_response_headers, vrequest_handler handle_response_body, vrequest_handler handle_response_error, vrequest_handler handle_request_headers); +LI_API void vrequest_free(vrequest *vr); +LI_API void vrequest_reset(vrequest *vr); + +LI_API void vrequest_error(vrequest *vr); + +/* received all request headers */ +LI_API void vrequest_handle_request_headers(vrequest *vr); +/* received (partial) request content */ +LI_API void vrequest_handle_request_body(vrequest *vr); +/* received all response headers/status code - call once from your indirect handler */ +LI_API void vrequest_handle_response_headers(vrequest *vr); +/* received (partial) response content - call from your indirect handler */ +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 void vrequest_state_machine(vrequest *vr); +LI_API void vrequest_joblist_append(vrequest *vr); + +#endif diff --git a/src/worker.c b/src/worker.c index 6224847..a508ede 100644 --- a/src/worker.c +++ b/src/worker.c @@ -240,8 +240,7 @@ void worker_free(worker *wrk) { ERROR(wrk->srv, "Server shutdown with unclosed connections: %u", wrk->connections_active); for (i = wrk->connections_active; i-- > 0;) { connection *con = g_array_index(wrk->connections, connection*, i); - connection_set_state(con, CON_STATE_ERROR); - connection_state_machine(con); /* cleanup plugins */ + connection_error(con); } } for (i = 0; i < wrk->connections->len; i++) {