|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
#include "connection.h"
|
|
|
|
|
#include "network.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "plugin_core.h"
|
|
|
|
|
|
|
|
|
|
void con_put(server *srv, connection *con); /* server.c */
|
|
|
|
|
|
|
|
|
@ -68,21 +69,24 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_FATAL_ERROR:
|
|
|
|
|
CON_ERROR(srv, con, "%s", "network read fatal error");
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_ERROR);
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_CONNECTION_CLOSE:
|
|
|
|
|
con->raw_in->is_closed = TRUE;
|
|
|
|
|
shutdown(w->fd, SHUT_RD);
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_CLOSE);
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_EVENT:
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
|
|
|
|
|
/* TODO ? */
|
|
|
|
|
/* TODO: aio */
|
|
|
|
|
ev_io_rem_events(loop, w, EV_READ);
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_FD:
|
|
|
|
|
/* TODO */
|
|
|
|
|
/* TODO: wait for fd */
|
|
|
|
|
ev_io_rem_events(loop, w, EV_READ);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -96,6 +100,7 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_FATAL_ERROR:
|
|
|
|
|
CON_ERROR(srv, con, "%s", "network write fatal error");
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_ERROR);
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
break;
|
|
|
|
@ -106,18 +111,16 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_EVENT:
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
|
|
|
|
|
/* TODO ? */
|
|
|
|
|
/* TODO: aio */
|
|
|
|
|
ev_io_rem_events(loop, w, EV_WRITE);
|
|
|
|
|
break;
|
|
|
|
|
case NETWORK_STATUS_WAIT_FOR_FD:
|
|
|
|
|
/* TODO */
|
|
|
|
|
/* TODO: wait for fd */
|
|
|
|
|
ev_io_rem_events(loop, w, EV_WRITE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// CON_TRACE(srv, con, "cq->len: raw_out=%i, out=%i", (int) con->raw_out->length, (int) con->out->length);
|
|
|
|
|
}
|
|
|
|
|
if (con->raw_out->length == 0) {
|
|
|
|
|
// CON_TRACE(srv, con, "%s", "stop write");
|
|
|
|
|
ev_io_rem_events(loop, w, EV_WRITE);
|
|
|
|
|
dojoblist = TRUE;
|
|
|
|
|
}
|
|
|
|
@ -135,20 +138,24 @@ connection* connection_new(server *srv) {
|
|
|
|
|
con->response_headers_sent = FALSE;
|
|
|
|
|
con->expect_100_cont = FALSE;
|
|
|
|
|
|
|
|
|
|
con->raw_in = chunkqueue_new();
|
|
|
|
|
con->raw_out = chunkqueue_new();
|
|
|
|
|
con->in = chunkqueue_new();
|
|
|
|
|
con->out = chunkqueue_new();
|
|
|
|
|
|
|
|
|
|
ev_io_init(&con->sock.watcher, connection_cb, -1, 0);
|
|
|
|
|
my_ev_init(&con->sock.watcher, connection_cb);
|
|
|
|
|
ev_io_set(&con->sock.watcher, -1, 0);
|
|
|
|
|
con->sock.srv = srv; con->sock.con = con; con->sock.watcher.data = &con->sock;
|
|
|
|
|
con->remote_addr_str = g_string_sized_new(0);
|
|
|
|
|
con->local_addr_str = g_string_sized_new(0);
|
|
|
|
|
con->keep_alive = TRUE;
|
|
|
|
|
|
|
|
|
|
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_count * sizeof(*srv->option_def_values), srv->option_def_values);
|
|
|
|
|
|
|
|
|
|
request_init(&con->request, con->raw_in);
|
|
|
|
|
physical_init(&con->physical);
|
|
|
|
|
response_init(&con->response);
|
|
|
|
|
|
|
|
|
|
return con;
|
|
|
|
@ -159,24 +166,31 @@ void connection_reset(server *srv, connection *con) {
|
|
|
|
|
con->response_headers_sent = FALSE;
|
|
|
|
|
con->expect_100_cont = FALSE;
|
|
|
|
|
|
|
|
|
|
chunkqueue_reset(con->raw_in);
|
|
|
|
|
chunkqueue_reset(con->raw_out);
|
|
|
|
|
chunkqueue_reset(con->in);
|
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
|
|
|
|
|
|
ev_io_stop(srv->loop, &con->sock.watcher);
|
|
|
|
|
if (con->sock.watcher.fd != -1) {
|
|
|
|
|
shutdown(con->sock.watcher.fd, SHUT_RDWR);
|
|
|
|
|
close(con->sock.watcher.fd);
|
|
|
|
|
if (con->raw_in->is_closed) { /* read already shutdown */
|
|
|
|
|
shutdown(con->sock.watcher.fd, SHUT_WR);
|
|
|
|
|
close(con->sock.watcher.fd);
|
|
|
|
|
} else {
|
|
|
|
|
server_add_closing_socket(srv, con->sock.watcher.fd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ev_io_set(&con->sock.watcher, -1, 0);
|
|
|
|
|
g_string_truncate(con->remote_addr_str, 0);
|
|
|
|
|
g_string_truncate(con->local_addr_str, 0);
|
|
|
|
|
con->keep_alive = TRUE;
|
|
|
|
|
|
|
|
|
|
chunkqueue_reset(con->raw_in);
|
|
|
|
|
chunkqueue_reset(con->raw_out);
|
|
|
|
|
chunkqueue_reset(con->in);
|
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
|
|
|
|
|
|
action_stack_reset(srv, &con->action_stack);
|
|
|
|
|
|
|
|
|
|
memcpy(con->options, srv->option_def_values, srv->option_count * sizeof(*srv->option_def_values));
|
|
|
|
|
|
|
|
|
|
request_reset(&con->request);
|
|
|
|
|
physical_reset(&con->physical);
|
|
|
|
|
response_reset(&con->response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -185,18 +199,21 @@ void connection_reset_keep_alive(server *srv, connection *con) {
|
|
|
|
|
con->response_headers_sent = FALSE;
|
|
|
|
|
con->expect_100_cont = FALSE;
|
|
|
|
|
|
|
|
|
|
con->raw_out->is_closed = FALSE;
|
|
|
|
|
chunkqueue_reset(con->in);
|
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
|
|
|
|
|
|
ev_io_set_events(srv->loop, &con->sock.watcher, EV_READ);
|
|
|
|
|
g_string_truncate(con->remote_addr_str, 0);
|
|
|
|
|
g_string_truncate(con->local_addr_str, 0);
|
|
|
|
|
con->keep_alive = TRUE;
|
|
|
|
|
|
|
|
|
|
con->raw_out->is_closed = FALSE;
|
|
|
|
|
chunkqueue_reset(con->in);
|
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
|
|
|
|
|
|
action_stack_reset(srv, &con->action_stack);
|
|
|
|
|
|
|
|
|
|
memcpy(con->options, srv->option_def_values, srv->option_count * sizeof(*srv->option_def_values));
|
|
|
|
|
|
|
|
|
|
request_reset(&con->request);
|
|
|
|
|
physical_reset(&con->physical);
|
|
|
|
|
response_reset(&con->response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -205,24 +222,31 @@ void connection_free(server *srv, connection *con) {
|
|
|
|
|
con->response_headers_sent = FALSE;
|
|
|
|
|
con->expect_100_cont = FALSE;
|
|
|
|
|
|
|
|
|
|
chunkqueue_free(con->raw_in);
|
|
|
|
|
chunkqueue_free(con->raw_out);
|
|
|
|
|
chunkqueue_free(con->in);
|
|
|
|
|
chunkqueue_free(con->out);
|
|
|
|
|
|
|
|
|
|
ev_io_stop(srv->loop, &con->sock.watcher);
|
|
|
|
|
if (con->sock.watcher.fd != -1) {
|
|
|
|
|
shutdown(con->sock.watcher.fd, SHUT_RDWR);
|
|
|
|
|
close(con->sock.watcher.fd);
|
|
|
|
|
if (con->raw_in->is_closed) { /* read already shutdown */
|
|
|
|
|
shutdown(con->sock.watcher.fd, SHUT_WR);
|
|
|
|
|
close(con->sock.watcher.fd);
|
|
|
|
|
} else {
|
|
|
|
|
server_add_closing_socket(srv, con->sock.watcher.fd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ev_io_set(&con->sock.watcher, -1, 0);
|
|
|
|
|
g_string_free(con->remote_addr_str, TRUE);
|
|
|
|
|
g_string_free(con->local_addr_str, TRUE);
|
|
|
|
|
con->keep_alive = TRUE;
|
|
|
|
|
|
|
|
|
|
chunkqueue_free(con->raw_in);
|
|
|
|
|
chunkqueue_free(con->raw_out);
|
|
|
|
|
chunkqueue_free(con->in);
|
|
|
|
|
chunkqueue_free(con->out);
|
|
|
|
|
|
|
|
|
|
action_stack_clear(srv, &con->action_stack);
|
|
|
|
|
|
|
|
|
|
g_slice_free1(srv->option_count * sizeof(*srv->option_def_values), con->options);
|
|
|
|
|
|
|
|
|
|
request_clear(&con->request);
|
|
|
|
|
physical_clear(&con->physical);
|
|
|
|
|
response_clear(&con->response);
|
|
|
|
|
|
|
|
|
|
g_slice_free(connection, con);
|
|
|
|
@ -241,19 +265,21 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
do {
|
|
|
|
|
switch (con->state) {
|
|
|
|
|
case CON_STATE_REQUEST_START:
|
|
|
|
|
/* TODO: reset some values after keep alive - or do it in CON_STATE_REQUEST_END */
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER);
|
|
|
|
|
action_enter(con, srv->mainaction);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_READ_REQUEST_HEADER:
|
|
|
|
|
// TRACE(srv, "%s", "reading request header");
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "reading request header");
|
|
|
|
|
}
|
|
|
|
|
switch(http_request_parse(srv, con, &con->request.parser_ctx)) {
|
|
|
|
|
case HANDLER_FINISHED:
|
|
|
|
|
case HANDLER_GO_ON:
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER);
|
|
|
|
|
break;
|
|
|
|
|
case HANDLER_WAIT_FOR_FD:
|
|
|
|
|
/* TODO */
|
|
|
|
|
/* TODO: wait for fd */
|
|
|
|
|
done = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case HANDLER_WAIT_FOR_EVENT:
|
|
|
|
@ -266,13 +292,19 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_VALIDATE_REQUEST_HEADER:
|
|
|
|
|
// TRACE(srv, "%s", "validating request header");
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "validating request header");
|
|
|
|
|
}
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER);
|
|
|
|
|
request_validate_header(srv, con);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_HANDLE_REQUEST_HEADER:
|
|
|
|
|
// TRACE(srv, "%s", "handle request header");
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "handle request header");
|
|
|
|
|
}
|
|
|
|
|
switch (action_execute(srv, con)) {
|
|
|
|
|
case ACTION_WAIT_FOR_EVENT:
|
|
|
|
|
done = TRUE;
|
|
|
|
@ -285,17 +317,29 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE);
|
|
|
|
|
break;
|
|
|
|
|
case ACTION_ERROR:
|
|
|
|
|
/* action return error */
|
|
|
|
|
/* TODO: return 500 instead ? */
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_ERROR);
|
|
|
|
|
internal_error(srv, con);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_READ_REQUEST_CONTENT:
|
|
|
|
|
case CON_STATE_HANDLE_RESPONSE_HEADER:
|
|
|
|
|
// TRACE(srv, "%s", "read request/handle response header");
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "read request/handle response header");
|
|
|
|
|
}
|
|
|
|
|
if (con->expect_100_cont) {
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%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(srv->loop, &con->sock.watcher, EV_WRITE);
|
|
|
|
|
}
|
|
|
|
|
parse_request_body(srv, con);
|
|
|
|
|
/* TODO: call plugin content_handler */
|
|
|
|
|
|
|
|
|
|
if (con->content_handler)
|
|
|
|
|
con->content_handler->handle_content(srv, con, con->content_handler);
|
|
|
|
|
|
|
|
|
|
switch (action_execute(srv, con)) {
|
|
|
|
|
case ACTION_WAIT_FOR_EVENT:
|
|
|
|
|
done = TRUE;
|
|
|
|
@ -305,12 +349,11 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE);
|
|
|
|
|
break;
|
|
|
|
|
case ACTION_ERROR:
|
|
|
|
|
/* action return error */
|
|
|
|
|
/* TODO: return 500 instead ? */
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_ERROR);
|
|
|
|
|
internal_error(srv, con);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_WRITE_RESPONSE:
|
|
|
|
|
if (con->in->is_closed && con->raw_out->is_closed) {
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_RESPONSE_END);
|
|
|
|
@ -319,12 +362,21 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
|
|
|
|
|
if (!con->response_headers_sent) {
|
|
|
|
|
con->response_headers_sent = TRUE;
|
|
|
|
|
// TRACE(srv, "%s", "write response headers");
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "write response headers");
|
|
|
|
|
}
|
|
|
|
|
response_send_headers(srv, con);
|
|
|
|
|
}
|
|
|
|
|
// TRACE(srv, "%s", "write response");
|
|
|
|
|
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "write response");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parse_request_body(srv, con);
|
|
|
|
|
/* TODO: call plugin content_handler */
|
|
|
|
|
|
|
|
|
|
if (con->content_handler)
|
|
|
|
|
con->content_handler->handle_content(srv, con, con->content_handler);
|
|
|
|
|
|
|
|
|
|
forward_response_body(srv, con);
|
|
|
|
|
|
|
|
|
|
if (con->in->is_closed && con->raw_out->is_closed) {
|
|
|
|
@ -333,9 +385,14 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
}
|
|
|
|
|
if (con->state == CON_STATE_WRITE_RESPONSE) done = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_RESPONSE_END:
|
|
|
|
|
// TRACE(srv, "%s", "response end");
|
|
|
|
|
/* TODO: call plugin callbacks */
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "response end (keep_alive = %i)", con->keep_alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plugins_handle_close(srv, con);
|
|
|
|
|
|
|
|
|
|
if (con->keep_alive) {
|
|
|
|
|
connection_reset_keep_alive(srv, con);
|
|
|
|
|
} else {
|
|
|
|
@ -343,15 +400,25 @@ void connection_state_machine(server *srv, connection *con) {
|
|
|
|
|
done = TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_CLOSE:
|
|
|
|
|
// TRACE(srv, "%s", "connection closed");
|
|
|
|
|
/* TODO: call plugin callbacks */
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "connection closed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plugins_handle_close(srv, con);
|
|
|
|
|
|
|
|
|
|
con_put(srv, con);
|
|
|
|
|
done = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CON_STATE_ERROR:
|
|
|
|
|
// TRACE(srv, "%s", "connection closed (error)");
|
|
|
|
|
/* TODO: call plugin callbacks */
|
|
|
|
|
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
|
|
|
|
|
TRACE(srv, "%s", "connection closed (error)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plugins_handle_close(srv, con);
|
|
|
|
|
|
|
|
|
|
con_put(srv, con);
|
|
|
|
|
done = TRUE;
|
|
|
|
|
break;
|
|
|
|
@ -365,6 +432,13 @@ void connection_handle_direct(server *srv, connection *con) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void connection_handle_indirect(server *srv, connection *con, plugin *p) {
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT);
|
|
|
|
|
con->content_handler = p;
|
|
|
|
|
if (!p) {
|
|
|
|
|
connection_handle_direct(srv, con);
|
|
|
|
|
} else if (p->handle_content) {
|
|
|
|
|
connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT);
|
|
|
|
|
con->content_handler = p;
|
|
|
|
|
} else {
|
|
|
|
|
CON_ERROR(srv, con, "Indirect plugin '%s' handler has no handle_content callback", p->name);
|
|
|
|
|
internal_error(srv, con);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|