diff --git a/src/connection.c b/src/connection.c index c3c84ad..252eba7 100644 --- a/src/connection.c +++ b/src/connection.c @@ -268,6 +268,7 @@ void connection_state_machine(server *srv, connection *con) { connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); action_enter(con, srv->mainaction); break; + case CON_STATE_READ_REQUEST_HEADER: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "%s", "reading request header"); @@ -291,6 +292,7 @@ void connection_state_machine(server *srv, connection *con) { break; } break; + case CON_STATE_VALIDATE_REQUEST_HEADER: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "%s", "validating request header"); @@ -298,6 +300,7 @@ void connection_state_machine(server *srv, connection *con) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); request_validate_header(srv, con); break; + case CON_STATE_HANDLE_REQUEST_HEADER: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "%s", "handle request header"); @@ -318,6 +321,7 @@ void connection_state_machine(server *srv, connection *con) { break; } break; + case CON_STATE_READ_REQUEST_CONTENT: case CON_STATE_HANDLE_RESPONSE_HEADER: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { @@ -332,7 +336,10 @@ void connection_state_machine(server *srv, connection *con) { 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; @@ -346,6 +353,7 @@ void connection_state_machine(server *srv, connection *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); @@ -359,11 +367,16 @@ void connection_state_machine(server *srv, connection *con) { } response_send_headers(srv, con); } + 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) { @@ -372,11 +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: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "response end (keep_alive = %i)", con->keep_alive); } - /* TODO: call plugin callbacks */ + + plugins_handle_close(srv, con); + if (con->keep_alive) { connection_reset_keep_alive(srv, con); } else { @@ -384,19 +400,25 @@ void connection_state_machine(server *srv, connection *con) { done = TRUE; } break; + case CON_STATE_CLOSE: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "%s", "connection closed"); } - /* TODO: call plugin callbacks */ + + plugins_handle_close(srv, con); + con_put(srv, con); done = TRUE; break; + case CON_STATE_ERROR: if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) { TRACE(srv, "%s", "connection closed (error)"); } - /* TODO: call plugin callbacks */ + + plugins_handle_close(srv, con); + con_put(srv, con); done = TRUE; break; @@ -410,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); + } } diff --git a/src/plugin.c b/src/plugin.c index ce13c13..cb92433 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -270,3 +270,23 @@ gboolean call_setup(server *srv, const char *name, option *opt) { return TRUE; } + +void plugins_prepare_callbacks(server *srv) { + GHashTableIter iter; + plugin *p; + + g_hash_table_iter_init(&iter, srv->plugins); + while (g_hash_table_iter_next(&iter, NULL, (gpointer*) &p)) { + if (p->handle_close) + g_array_append_val(srv->plugins_handle_close, p); + } +} + +void plugins_handle_close(server *srv, connection *con) { + GArray *a = srv->plugins_handle_close; + guint i, len = a->len; + for (i = 0; i < len; i++) { + plugin *p = g_array_index(a, plugin*, i); + p->handle_close(srv, con, p); + } +} diff --git a/src/plugin.h b/src/plugin.h index e119d35..46f16c5 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -41,15 +41,31 @@ typedef void (*PluginFreeOption) (server *srv, plugin *p, size_t ndx, gpo typedef action* (*PluginCreateAction) (server *srv, plugin *p, option *opt); typedef gboolean (*PluginSetup) (server *srv, plugin *p, option *opt); +typedef void (*PluginHandleContent) (server *srv, connection *con, plugin *p); +typedef void (*PluginHandleClose) (server *srv, connection *con, plugin *p); + struct plugin { size_t version; const gchar *name; /**< name of the plugin */ - gpointer data; /**< private plugin data */ + gpointer data; /**< private plugin data */ size_t opt_base_index; - PluginFree free; /**< called before plugin is unloaded */ + PluginFree free; /**< called before plugin is unloaded */ + + /** called if plugin registered as indirect handler with connection_handle_indirect(srv, con, p) + * - after response headers are created: + * connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER) + * - after content is generated close output queue: + * con->out->is_closed = TRUE + */ + PluginHandleContent handle_content; + + /** called for every plugin after connection got closed (response end, reset by peer, error) + * the plugins code must not depend on any order of plugins loaded + */ + PluginHandleClose handle_close; const plugin_option *options; const plugin_action *actions; @@ -104,11 +120,15 @@ struct server_setup { /* Needed my modules to register their plugin(s) */ LI_API gboolean plugin_register(server *srv, const gchar *name, PluginInit init); +/* Internal needed functions */ LI_API void plugin_free(server *srv, plugin *p); LI_API gboolean parse_option(server *srv, const char *name, option *opt, option_set *mark); LI_API void release_option(server *srv, option_set *mark); /**< Does not free the option_set memory */ +LI_API void plugins_prepare_callbacks(server *srv); +LI_API void plugins_handle_close(server *srv, connection *con); + /* Needed for config frontends */ /** For parsing 'somemod.option = "somevalue"' */ LI_API action* option_action(server *srv, const gchar *name, option *value); diff --git a/src/server.c b/src/server.c index 046d2df..916f079 100644 --- a/src/server.c +++ b/src/server.c @@ -110,6 +110,8 @@ server* server_new() { srv->actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_action_free); srv->setups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_setup_free); + srv->plugins_handle_close = g_array_new(FALSE, TRUE, sizeof(plugin*)); + srv->mainaction = NULL; srv->exiting = FALSE; @@ -165,6 +167,8 @@ void server_free(server* srv) { g_hash_table_destroy(srv->actions); g_hash_table_destroy(srv->setups); + g_array_free(srv->plugins_handle_close, TRUE); + action_release(srv, srv->mainaction); g_string_free(srv->tmp_str, TRUE); @@ -284,6 +288,8 @@ void server_start(server *srv) { srv->option_count = g_hash_table_size(srv->options); srv->option_def_values = g_slice_alloc0(srv->option_count * sizeof(*srv->option_def_values)); + plugins_prepare_callbacks(srv); + for (i = 0; i < srv->sockets->len; i++) { server_socket *sock = g_array_index(srv->sockets, server_socket*, i); ev_io_start(srv->loop, &sock->watcher); diff --git a/src/server.h b/src/server.h index 38921c5..dda6c6f 100644 --- a/src/server.h +++ b/src/server.h @@ -39,6 +39,8 @@ struct server { GHashTable *actions; /**< const gchar* => (server_action*) */ GHashTable *setups; /**< const gchar* => (server_setup*) */ + GArray *plugins_handle_close; /** list of handle_close callbacks */ + size_t option_count; /**< set to size of option hash table */ gpointer *option_def_values; struct action *mainaction;