Merge virtual requests
commit
73fff068c3
|
@ -294,6 +294,7 @@ SET(COMMON_SRC
|
|||
url_parser.c
|
||||
utils.c
|
||||
value.c
|
||||
virtualrequest.c
|
||||
worker.c
|
||||
|
||||
plugin_core.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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
22
src/chunk.c
22
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));
|
||||
}
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
140
src/condition.c
140
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;
|
||||
}
|
||||
|
|
|
@ -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) */
|
||||
|
|
504
src/connection.c
504
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||