From 84051c1c9fa812575a94091b224e192118201778 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Fri, 19 Feb 2016 15:00:05 +0000 Subject: [PATCH] mpm: Add a complete_connection hook that confirms whether an MPM is allowed to leave the WRITE_COMPLETION phase. Move filter code out of the MPMs. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1731253 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 ++++ include/ap_mmn.h | 4 +++- include/mpm_common.h | 10 ++++++++++ include/util_filter.h | 12 ++++++++++++ server/core.c | 4 +++- server/mpm/event/event.c | 32 +++++--------------------------- server/mpm/motorz/motorz.c | 31 ++++--------------------------- server/mpm/simple/simple_io.c | 32 +++++--------------------------- server/mpm_common.c | 4 ++++ server/util_filter.c | 33 +++++++++++++++++++++++++++++++++ 10 files changed, 83 insertions(+), 83 deletions(-) diff --git a/CHANGES b/CHANGES index a6d5a8e54a..26504f16c7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mpm: Add a complete_connection hook that confirms whether an MPM is allowed + to leave the WRITE_COMPLETION phase. Move filter code out of the MPMs. + [Graham Leggett] + *) mod_proxy_http2: using single connection for several requests *if* master connection uses HTTP/2 itself. Not yet hardened under load. [Stefan Eissing] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index d00265edc7..9789081d6f 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -504,6 +504,8 @@ * ap_prep_lingering_close(). * 20150222.11 (2.5.0-dev) Split useragent_host from the conn_rec into * the request_rec, with ap_get_useragent_host() + * 20150222.12 (2.5.0-dev) Add complete_connection hook, + * ap_filter_complete_connection(). */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -511,7 +513,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20150222 #endif -#define MODULE_MAGIC_NUMBER_MINOR 11 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 12 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/mpm_common.h b/include/mpm_common.h index 4031b51ddf..9cc715b74b 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -457,6 +457,16 @@ AP_DECLARE_HOOK(apr_status_t, mpm_resume_suspended, (conn_rec*)) */ AP_DECLARE_HOOK(const char *,mpm_get_name,(void)) +/** + * Hook called to determine whether we should stay within the write completion + * phase. + * @param c The current connection + * @return OK if write completion should continue, DECLINED if write completion + * should end gracefully, or a positive error if we should begin to linger. + * @ingroup hooks + */ +AP_DECLARE_HOOK(int, complete_connection, (conn_rec *c)) + /** * Notification that connection handling is suspending (disassociating from the * current thread) diff --git a/include/util_filter.h b/include/util_filter.h index 0e013c68bf..cf6f09cf22 100644 --- a/include/util_filter.h +++ b/include/util_filter.h @@ -589,6 +589,18 @@ AP_DECLARE(apr_status_t) ap_filter_reinstate_brigade(ap_filter_t *f, */ AP_DECLARE(int) ap_filter_should_yield(ap_filter_t *f); +/** + * This function determines whether there is unwritten data in the output + * filters, and if so, attempts to make a single write to each filter + * with unwritten data. + * + * @param c The connection. + * @return If no unwritten data remains, this function returns DECLINED. + * If some unwritten data remains, this function returns OK. If any + * attempt to write data failed, this functions returns a positive integer. + */ +AP_DECLARE(int) ap_filter_complete_connection(conn_rec *c); + /** * Flush function for apr_brigade_* calls. This calls ap_pass_brigade * to flush the brigade if the brigade buffer overflows. diff --git a/server/core.c b/server/core.c index b440e27bd9..c7cc97538a 100644 --- a/server/core.c +++ b/server/core.c @@ -5559,7 +5559,9 @@ static void register_hooks(apr_pool_t *p) ap_hook_open_htaccess(ap_open_htaccess, NULL, NULL, APR_HOOK_REALLY_LAST); ap_hook_optional_fn_retrieve(core_optional_fn_retrieve, NULL, NULL, APR_HOOK_MIDDLE); - + ap_hook_complete_connection(ap_filter_complete_connection, NULL, NULL, + APR_HOOK_MIDDLE); + /* register the core's insert_filter hook and register core-provided * filters */ diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index fa6a8119ea..4d5e38384d 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -1147,38 +1147,16 @@ read_request: } if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) { - apr_hash_index_t *rindex; - apr_status_t rv = APR_SUCCESS; - int data_in_output_filters = 0; - ap_update_child_status_from_conn(sbh, SERVER_BUSY_WRITE, c); - - rindex = apr_hash_first(NULL, c->filters); - while (rindex) { - ap_filter_t *f = apr_hash_this_val(rindex); - - if (!APR_BRIGADE_EMPTY(f->bb)) { - - rv = ap_pass_brigade(f, c->empty); - apr_brigade_cleanup(c->empty); - if (APR_SUCCESS != rv) { - ap_log_cerror( - APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00470) - "write failure in '%s' output filter", f->frec->name); - break; - } + int not_complete_yet; - if (ap_filter_should_yield(f)) { - data_in_output_filters = 1; - } - } + ap_update_child_status_from_conn(sbh, SERVER_BUSY_WRITE, c); - rindex = apr_hash_next(rindex); - } + not_complete_yet = ap_run_complete_connection(c); - if (rv != APR_SUCCESS) { + if (not_complete_yet > OK) { cs->pub.state = CONN_STATE_LINGER; } - else if (data_in_output_filters) { + else if (not_complete_yet == OK) { /* Still in WRITE_COMPLETION_STATE: * Set a write timeout for this connection, and let the * event thread poll for writeability. diff --git a/server/mpm/motorz/motorz.c b/server/mpm/motorz/motorz.c index 8522e1b15d..682b3808fa 100644 --- a/server/mpm/motorz/motorz.c +++ b/server/mpm/motorz/motorz.c @@ -395,42 +395,19 @@ read_request: } if (scon->cs.state == CONN_STATE_WRITE_COMPLETION) { - apr_hash_index_t *rindex; - apr_status_t rv = APR_SUCCESS; - int data_in_output_filters = 0; + int not_complete_yet; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO() "motorz_io_process(): CONN_STATE_WRITE_COMPLETION"); ap_update_child_status_from_conn(scon->sbh, SERVER_BUSY_WRITE, c); - rindex = apr_hash_first(NULL, c->filters); - while (rindex) { - ap_filter_t *f = apr_hash_this_val(rindex); + not_complete_yet = ap_run_complete_connection(c); - if (!APR_BRIGADE_EMPTY(f->bb)) { - - rv = ap_pass_brigade(f, c->empty); - apr_brigade_cleanup(c->empty); - if (APR_SUCCESS != rv) { - ap_log_cerror( - APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(02848) - "write failure in '%s' output filter", f->frec->name); - break; - } - - if (ap_filter_should_yield(f)) { - data_in_output_filters = 1; - } - } - - rindex = apr_hash_next(rindex); - } - - if (rv != APR_SUCCESS) { + if (not_complete_yet > OK) { scon->cs.state = CONN_STATE_LINGER; } - else if (data_in_output_filters) { + else if (not_complete_yet == OK) { /* Still in WRITE_COMPLETION_STATE: * Set a write timeout for this connection, and let the * event thread poll for writeability. diff --git a/server/mpm/simple/simple_io.c b/server/mpm/simple/simple_io.c index 47c13d71c6..6d98935e8b 100644 --- a/server/mpm/simple/simple_io.c +++ b/server/mpm/simple/simple_io.c @@ -92,37 +92,15 @@ static apr_status_t simple_io_process(simple_conn_t * scon) } if (scon->cs.state == CONN_STATE_WRITE_COMPLETION) { - apr_hash_index_t *rindex; - apr_status_t rv = APR_SUCCESS; - int data_in_output_filters = 0; - - rindex = apr_hash_first(NULL, c->filters); - while (rindex) { - ap_filter_t *f = apr_hash_this_val(rindex); - - if (!APR_BRIGADE_EMPTY(f->bb)) { - - rv = ap_pass_brigade(f, c->empty); - apr_brigade_cleanup(c->empty); - if (APR_SUCCESS != rv) { - ap_log_cerror( - APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00249) - "write failure in '%s' output filter", f->frec->name); - break; - } - - if (ap_filter_should_yield(f)) { - data_in_output_filters = 1; - } - } + int not_complete_yet; - rindex = apr_hash_next(rindex); - } + ap_update_child_status_from_conn(sbh, SERVER_BUSY_WRITE, c); + not_complete_yet = ap_run_complete_connection(c); - if (rv != APR_SUCCESS) { + if (not_complete_yet > OK) { scon->cs.state = CONN_STATE_LINGER; } - else if (data_in_output_filters) { + else if (not_complete_yet == OK) { /* Still in WRITE_COMPLETION_STATE: * Set a write timeout for this connection, and let the * event thread poll for writeability. diff --git a/server/mpm_common.c b/server/mpm_common.c index 9a010f34af..b2133df1a1 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -75,6 +75,7 @@ APR_HOOK_LINK(mpm_resume_suspended) \ APR_HOOK_LINK(end_generation) \ APR_HOOK_LINK(child_status) \ + APR_HOOK_LINK(complete_connection) \ APR_HOOK_LINK(suspend_connection) \ APR_HOOK_LINK(resume_connection) @@ -95,6 +96,7 @@ AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor, AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges, (apr_pool_t * pchild, server_rec * s), (pchild, s), OK, DECLINED) + AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm, (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s), (pconf, plog, s), DECLINED) @@ -116,6 +118,8 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_socket_callback_timeout, AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_unregister_socket_callback, (apr_socket_t **s, apr_pool_t *p), (s, p), APR_ENOTIMPL) +AP_IMPLEMENT_HOOK_RUN_FIRST(int, complete_connection, + (conn_rec *c), (c), DECLINED) AP_IMPLEMENT_HOOK_VOID(end_generation, (server_rec *s, ap_generation_t gen), diff --git a/server/util_filter.c b/server/util_filter.c index 316a0d0dd0..aaf0173a5a 100644 --- a/server/util_filter.c +++ b/server/util_filter.c @@ -953,6 +953,39 @@ AP_DECLARE(int) ap_filter_should_yield(ap_filter_t *f) return 0; } +AP_DECLARE(int) ap_filter_complete_connection(conn_rec *c) +{ + apr_hash_index_t *rindex; + int data_in_output_filters = DECLINED; + + rindex = apr_hash_first(NULL, c->filters); + while (rindex) { + ap_filter_t *f = apr_hash_this_val(rindex); + + if (!APR_BRIGADE_EMPTY(f->bb)) { + + apr_status_t rv; + + rv = ap_pass_brigade(f, c->empty); + apr_brigade_cleanup(c->empty); + if (APR_SUCCESS != rv) { + ap_log_cerror( + APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00470) + "write failure in '%s' output filter", f->frec->name); + return rv; + } + + if (ap_filter_should_yield(f)) { + data_in_output_filters = OK; + } + } + + rindex = apr_hash_next(rindex); + } + + return data_in_output_filters; +} + AP_DECLARE_NONSTD(apr_status_t) ap_filter_flush(apr_bucket_brigade *bb, void *ctx) { -- 2.40.0