-*- 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]
* 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" */
#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
*/
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)
*/
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.
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
*/
}
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.
}
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.
}
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.
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)
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)
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),
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)
{