Index: include/ap_mmn.h =================================================================== --- include/ap_mmn.h (revision 1828935) +++ include/ap_mmn.h (working copy) @@ -571,14 +571,18 @@ * 20171014.7 (2.5.1-dev) Add AP_CORE_DEFAULT macro * 20171014.8 (2.5.1-dev) Add CONN_STATE_NUM to enum conn_state_e * 20171014.9 (2.5.1-dev) Add response_field_size to proxy_worker_shared + * 20180411.1 (2.5.1-dev) Add type ap_filters_ring_t, replace field 'filters' + * by the ap_filters_ring_t 'pending_filters' in struct + * conn_rec, and add ring entry 'pending' in struct + * ap_filter_t */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 20171014 +#define MODULE_MAGIC_NUMBER_MAJOR 20180411 #endif -#define MODULE_MAGIC_NUMBER_MINOR 9 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a Index: include/http_protocol.h =================================================================== --- include/http_protocol.h (revision 1828935) +++ include/http_protocol.h (working copy) @@ -631,7 +631,9 @@ AP_DECLARE(apr_status_t) ap_get_basic_auth_compone AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri); #define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */ -#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */ +#define AP_GETLINE_CRLF 2 /* Whether line ends must be in the form CR LF */ +#define AP_GETLINE_NOSPC_EOL 4 /* Whether to consume up to and including the + end of line on APR_ENOSPC */ /** * Get the next line of input for the request Index: include/httpd.h =================================================================== --- include/httpd.h (revision 1828935) +++ include/httpd.h (working copy) @@ -1224,8 +1224,8 @@ struct conn_rec { /** Empty bucket brigade */ apr_bucket_brigade *empty; - /** Hashtable of filters with setaside buckets for write completion */ - apr_hash_t *filters; + /** Ring of pending filters (with setaside buckets) */ + struct ap_filters_ring *pending_filters; /** The minimum level of filter type to allow setaside buckets */ int async_filter; Index: include/util_filter.h =================================================================== --- include/util_filter.h (revision 1828935) +++ include/util_filter.h (working copy) @@ -299,9 +299,16 @@ struct ap_filter_t { /** Dedicated pool to use for deferred writes. */ apr_pool_t *deferred_pool; + /** Entry in ring of pending filters (with setaside buckets). */ + APR_RING_ENTRY(ap_filter_t) pending; }; /** + * @brief The representation of a filters' ring. + */ +typedef APR_RING_HEAD(ap_filters_ring, ap_filter_t) ap_filters_ring_t; + +/** * Get the current bucket brigade from the next filter on the filter * stack. The filter returns an apr_status_t value. If the bottom-most * filter doesn't read from the network, then ::AP_NOBODY_READ is returned. Index: modules/filters/mod_xml2enc.c =================================================================== --- modules/filters/mod_xml2enc.c (revision 1828935) +++ modules/filters/mod_xml2enc.c (working copy) @@ -400,16 +400,34 @@ static apr_status_t xml2enc_ffunc(ap_filter_t* f, ctx->bytes = 0; if (APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->bbnext, b); + /* This resource filter is over on EOS */ if (APR_BUCKET_IS_EOS(b)) { - /* send remaining data */ - APR_BRIGADE_INSERT_TAIL(ctx->bbnext, b); - return ap_fflush(f->next, ctx->bbnext); - } else if (APR_BUCKET_IS_FLUSH(b)) { - ap_fflush(f->next, ctx->bbnext); + ap_remove_output_filter(f); + rv = ap_pass_brigade(f->next, ctx->bbnext); + apr_brigade_cleanup(ctx->bbnext); + return rv; } - apr_bucket_destroy(b); + /* Besides FLUSH, aggregate meta buckets to send + * them at once below. + */ + pending_meta = 1; + if (!APR_BUCKET_IS_FLUSH(b)) { + continue; + } } - else { /* data bucket */ + if (pending_meta) { + pending_meta = 0; + /* passing meta bucket down the chain */ + rv = ap_pass_brigade(f->next, ctx->bbnext); + apr_brigade_cleanup(ctx->bbnext); + if (rv != APR_SUCCESS) { + return rv; + } + continue; + } + /* data bucket */ + { char* buf; apr_size_t bytes = 0; char fixbuf[BUFLEN]; @@ -514,8 +532,7 @@ static apr_status_t xml2enc_ffunc(ap_filter_t* f, if (rv != APR_SUCCESS) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(01446) "ap_fflush failed"); - else - rv = ap_pass_brigade(f->next, ctx->bbnext); + apr_brigade_cleanup(ctx->bbnext); } } } else { Index: modules/proxy/mod_proxy_http.c =================================================================== --- modules/proxy/mod_proxy_http.c (revision 1828935) +++ modules/proxy/mod_proxy_http.c (working copy) @@ -32,8 +32,8 @@ static apr_status_t ap_proxygetline(apr_bucket_bri char *s, int n, request_rec *r, - int fold, - int *writen); + int flags, + int *read); /* * Canonicalise http-like URLs. @@ -1098,7 +1098,6 @@ static void ap_proxy_read_headers(request_rec *r, { int len; char *value, *end; - char field[MAX_STRING_LEN]; int saw_headers = 0; void *sconf = r->server->module_config; proxy_server_conf *psc; @@ -1122,12 +1121,24 @@ static void ap_proxy_read_headers(request_rec *r, tmp_bb = apr_brigade_create(r->pool, c->bucket_alloc); while (1) { - rc = ap_proxygetline(tmp_bb, buffer, size, rr, 1, &len); - + rc = ap_proxygetline(tmp_bb, buffer, size, rr, + AP_GETLINE_FOLD | AP_GETLINE_NOSPC_EOL, + &len); if (len <= 0) break; - ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); + if (APR_STATUS_IS_ENOSPC(rc)) { + /* The header could not fit in the provided buffer, warn. + * XXX: falls through with the truncated header, shouldn't we 502? + */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) + "header size is over the limit allowed by ResponseFieldSize (%d bytes). " + "Bad response header '%s': '%.*s'...", + size, buffer, 80, value); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); + } if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ @@ -1195,28 +1206,6 @@ static void ap_proxy_read_headers(request_rec *r, */ process_proxy_header(r, dconf, buffer, value); saw_headers = 1; - - /* The header could not fit in the provided buffer. */ - if (rc == APR_ENOSPC) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) - "header size is over the limit allowed by ResponseFieldSize (%d bytes). " - "Bad response header '%s': '%.*s'...", - size, buffer, 80, value); - - /* XXX: We overran the limit we passed to ap_rgetline_core, but if the length - * exceeded the limit by a small amount, it may have already been consumed - * by apr_brigade_split_line called from the core input filter. If that - * happens, this loop will throw away the next full line (header) instead of - * the remainder of the current long header. - */ - while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1)) - >= MAX_STRING_LEN - 1) { - /* soak up the extra data */ - } - - if (len == 0) /* time to exit the larger loop as well */ - break; - } } } @@ -1230,21 +1219,19 @@ static int addit_dammit(void *v, const char *key, static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec *r, - int fold, int *writen) + int flags, int *read) { char *tmp_s = s; apr_status_t rv; apr_size_t len; - rv = ap_rgetline(&tmp_s, n, &len, r, fold, bb); + rv = ap_rgetline_core(&tmp_s, n, &len, r, flags, bb); apr_brigade_cleanup(bb); - if (rv == APR_SUCCESS) { - *writen = (int) len; - } else if (APR_STATUS_IS_ENOSPC(rv)) { - *writen = n; + if (rv == APR_SUCCESS || APR_STATUS_IS_ENOSPC(rv)) { + *read = (int)len; } else { - *writen = -1; + *read = -1; } return rv; Index: modules/proxy/proxy_util.c =================================================================== --- modules/proxy/proxy_util.c (revision 1828935) +++ modules/proxy/proxy_util.c (working copy) @@ -1478,29 +1478,41 @@ static apr_status_t connection_cleanup(void *theco /* determine if the connection need to be closed */ if (!worker->s->is_address_reusable || worker->s->disablereuse) { - apr_pool_t *p = conn->pool; - apr_pool_clear(p); - conn = apr_pcalloc(p, sizeof(proxy_conn_rec)); - conn->pool = p; - conn->worker = worker; - apr_pool_create(&(conn->scpool), p); - apr_pool_tag(conn->scpool, "proxy_conn_scpool"); + if (worker->s->hmax && worker->cp->res) { + apr_reslist_invalidate(worker->cp->res, conn); + } + else { + apr_pool_t *p = conn->pool; + apr_pool_clear(p); + conn = apr_pcalloc(p, sizeof(proxy_conn_rec)); + conn->pool = p; + conn->worker = worker; + apr_pool_create(&(conn->scpool), p); + apr_pool_tag(conn->scpool, "proxy_conn_scpool"); + worker->cp->conn = conn; + } } else if (conn->close || (conn->connection && conn->connection->keepalive == AP_CONN_CLOSE)) { - socket_cleanup(conn); - conn->close = 0; + if (worker->s->hmax && worker->cp->res) { + apr_reslist_invalidate(worker->cp->res, conn); + } + else { + socket_cleanup(conn); + conn->close = 0; + worker->cp->conn = conn; + } } - - if (worker->s->hmax && worker->cp->res) { - conn->inreslist = 1; - apr_reslist_release(worker->cp->res, (void *)conn); + else { + if (worker->s->hmax && worker->cp->res) { + conn->inreslist = 1; + apr_reslist_release(worker->cp->res, conn); + } + else { + worker->cp->conn = conn; + } } - else - { - worker->cp->conn = conn; - } /* Always return the SUCCESS */ return APR_SUCCESS; Index: modules/ssl/ssl_engine_config.c =================================================================== --- modules/ssl/ssl_engine_config.c (revision 1828935) +++ modules/ssl/ssl_engine_config.c (working copy) @@ -40,7 +40,7 @@ ** _________________________________________________________________ */ -#define SSL_MOD_CONFIG_KEY "ssl_module" +#define MODSSL_CONFIG_KEY "ssl_module" SSLModConfigRec *ssl_config_global_create(server_rec *s) { @@ -48,7 +48,7 @@ SSLModConfigRec *ssl_config_global_create(server_r SSLModConfigRec *mc; void *vmc; - apr_pool_userdata_get(&vmc, SSL_MOD_CONFIG_KEY, pool); + apr_pool_userdata_get(&vmc, MODSSL_CONFIG_KEY, pool); if (vmc) { return vmc; /* reused for lifetime of the server */ } @@ -79,7 +79,7 @@ SSLModConfigRec *ssl_config_global_create(server_r mc->stapling_refresh_mutex = NULL; #endif - apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, + apr_pool_userdata_set(mc, MODSSL_CONFIG_KEY, apr_pool_cleanup_null, pool); @@ -542,7 +542,7 @@ static apr_hash_t *get_policies(apr_pool_t *p, int apr_hash_t *policies; void *vp; - apr_pool_userdata_get(&vp, SSL_MOD_POLICIES_KEY, p); + apr_pool_userdata_get(&vp, MODSSL_POLICIES_KEY, p); if (vp) { return vp; /* reused for lifetime of the pool */ } @@ -577,7 +577,7 @@ static apr_hash_t *get_policies(apr_pool_t *p, int SSL_POLICY_SESSION_TICKETS); #endif - apr_pool_userdata_set(policies, SSL_MOD_POLICIES_KEY, + apr_pool_userdata_set(policies, MODSSL_POLICIES_KEY, apr_pool_cleanup_null, p); return policies; } Index: modules/ssl/ssl_policies.h =================================================================== --- modules/ssl/ssl_policies.h (revision 1828935) +++ modules/ssl/ssl_policies.h (working copy) @@ -34,7 +34,7 @@ #ifndef __SSL_POLICIES_H__ #define __SSL_POLICIES_H__ -#define SSL_MOD_POLICIES_KEY "ssl_module_policies" +#define MODSSL_POLICIES_KEY "ssl_module_policies" #ifndef OPENSSL_NO_SSL3 #define SSL_PROTOCOL_CONSTANTS_SSLV3 SSL_PROTOCOL_SSLV3 Index: os/unix/unixd.c =================================================================== --- os/unix/unixd.c (revision 1828935) +++ os/unix/unixd.c (working copy) @@ -516,7 +516,7 @@ static apr_status_t unset_signals(void *unused) return APR_SUCCESS; } -static void ap_terminate(void) +static void ap_unixd_terminate(void) { ap_main_state = AP_SQ_MS_EXITING; apr_pool_destroy(ap_pglobal); @@ -532,12 +532,9 @@ AP_DECLARE(void) ap_unixd_mpm_set_signals(apr_pool if (!one_process) { ap_fatal_signal_setup(ap_server_conf, pconf); } - else { - static int once = 0; - if (!once) { - atexit(ap_terminate); - once = 1; - } + else if (!ap_retained_data_get("ap_unixd_terminate")) { + ap_retained_data_create("ap_unixd_terminate", 1); + atexit(ap_unixd_terminate); } /* Signals' handlers depend on retained data */ Index: server/core.c =================================================================== --- server/core.c (revision 1828935) +++ server/core.c (working copy) @@ -5318,7 +5318,6 @@ static conn_rec *core_create_conn(apr_pool_t *ptra c->id = id; c->bucket_alloc = alloc; c->empty = apr_brigade_create(c->pool, c->bucket_alloc); - c->filters = apr_hash_make(c->pool); c->async_filter = sconf->async_filter; c->clogging_input_filters = 0; Index: server/protocol.c =================================================================== --- server/protocol.c (revision 1828935) +++ server/protocol.c (working copy) @@ -224,6 +224,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s int do_alloc = (*s == NULL), saw_eos = 0; int fold = flags & AP_GETLINE_FOLD; int crlf = flags & AP_GETLINE_CRLF; + int nospc_eol = flags & AP_GETLINE_NOSPC_EOL; + int eol = 0; if (!n) { /* Needs room for NUL byte at least */ @@ -239,7 +241,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s if (last_char) *last_char = '\0'; - for (;;) { + do { apr_brigade_cleanup(bb); rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); @@ -283,8 +285,19 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s /* Would this overrun our buffer? If so, we'll die. */ if (n < bytes_handled + len) { + /* But before we'll fill in the buffer up to its limit (i.e. + * fall through with the remaining length if necessary), and + * optionally consume the full line until LF (nospc_eol). + */ rv = APR_ENOSPC; - goto cleanup; + eol = (str[len - 1] == APR_ASCII_LF); + len = n - bytes_handled; + if (!len) { + if (nospc_eol) { + continue; + } + goto cleanup; + } } /* Do we have to handle the allocation ourselves? */ @@ -323,8 +336,13 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s /* If we got a full line of input, stop reading */ if (last_char && (*last_char == APR_ASCII_LF)) { - break; + eol = 1; } + } while (!eol); + + if (rv != APR_SUCCESS) { + /* End of line after APR_ENOSPC above */ + goto cleanup; } /* Now terminate the string at the end of the line; Index: server/util.c =================================================================== --- server/util.c (revision 1828935) +++ server/util.c (working copy) @@ -185,55 +185,93 @@ AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_t * Based loosely on sections of wildmat.c by Rich Salz * Hmmm... shouldn't this really go component by component? */ -AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) +#if 1 +static int ap_strcmp_match_r(const char *str, const char *expected, + apr_size_t xstart, apr_size_t ystart, + int nocase) { - int x, y; + apr_size_t x, y; - for (x = 0, y = 0; expected[y]; ++y, ++x) { + do { + x = xstart++; + for (y = ystart; expected[y]; ++y, ++x) { if (expected[y] == '*') { while (expected[++y] == '*'); if (!expected[y]) return 0; - while (str[x]) { - int ret; - if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1) - return ret; + if (str[x]) { + return ap_strcmp_match_r(str, expected, x, y, nocase); } return -1; } else if (!str[x]) return -1; - else if ((expected[y] != '?') && (str[x] != expected[y])) + else if ((expected[y] != '?') && (str[x] != expected[y]) && + (!nocase || apr_tolower(str[x]) != apr_tolower(expected[y]))) return 1; } - return (str[x] != '\0'); + if (!str[x]) + return 0; + } while (ystart); + + return 1; } +AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) +{ + return ap_strcmp_match_r(str, expected, 0, 0, 0); +} + AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected) { - int x, y; + return ap_strcmp_match_r(str, expected, 0, 0, 1); +} +#else +#define MATCH_NC 0x1 +#define MATCH_REC 0x2 +static int ap_strcmp_match_r(const char *str, const char *expected, int flags) +{ + const char *x, *y; - for (x = 0, y = 0; expected[y]; ++y, ++x) { - if (!str[x] && expected[y] != '*') - return -1; - if (expected[y] == '*') { - while (expected[++y] == '*'); - if (!expected[y]) - return 0; - while (str[x]) { - int ret; - if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1) - return ret; + do { + x = str++; + for (y = expected; *y; ++y, ++x) { + if (*y == '*') { + do { + ++y; + } while (*y == '*'); + if (!*y) + return 0; + if (!*x) + return -1; + return ap_strcmp_match_r(x, y, flags | MATCH_REC); } - return -1; + else if (!*x) + return -1; + else if ((*y != '?') + && (*x != *y) + && (!(flags & MATCH_NC) + || apr_tolower(*x) != apr_tolower(*y))) + return 1; } - else if (expected[y] != '?' - && apr_tolower(str[x]) != apr_tolower(expected[y])) - return 1; - } - return (str[x] != '\0'); + if (!*x) + return 0; + } while (flags & MATCH_REC); + + return 1; } +AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) +{ + return ap_strcmp_match_r(str, expected, 0); +} + +AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected) +{ + return ap_strcmp_match_r(str, expected, MATCH_NC); +} +#endif + /* We actually compare the canonical root to this root, (but we don't * waste time checking the case), since every use of this function in * httpd-2.1 tests if the path is 'proper', meaning we've already passed Index: server/util_filter.c =================================================================== --- server/util_filter.c (revision 1828935) +++ server/util_filter.c (working copy) @@ -293,7 +293,7 @@ static ap_filter_t *add_any_filter_handle(ap_filte ap_filter_t **c_filters) { apr_pool_t *p = frec->ftype < AP_FTYPE_CONNECTION && r ? r->pool : c->pool; - ap_filter_t *f = apr_palloc(p, sizeof(*f)); + ap_filter_t *f = apr_pcalloc(p, sizeof(*f)); ap_filter_t **outf; if (frec->ftype < AP_FTYPE_PROTOCOL) { @@ -325,9 +325,7 @@ static ap_filter_t *add_any_filter_handle(ap_filte /* f->r must always be NULL for connection filters */ f->r = frec->ftype < AP_FTYPE_CONNECTION ? r : NULL; f->c = c; - f->next = NULL; - f->bb = NULL; - f->deferred_pool = NULL; + APR_RING_ELEM_INIT(f, pending); if (INSERT_BEFORE(f, *outf)) { f->next = *outf; @@ -451,6 +449,23 @@ AP_DECLARE(ap_filter_t *) ap_add_output_filter_han &c->output_filters); } +static APR_INLINE int is_pending_filter(ap_filter_t *f) +{ + return APR_RING_NEXT(f, pending) != f; +} + +static apr_status_t pending_filter_cleanup(void *arg) +{ + ap_filter_t *f = arg; + + if (is_pending_filter(f)) { + APR_RING_REMOVE(f, pending); + APR_RING_ELEM_INIT(f, pending); + } + + return APR_SUCCESS; +} + static void remove_any_filter(ap_filter_t *f, ap_filter_t **r_filt, ap_filter_t **p_filt, ap_filter_t **c_filt) { @@ -457,6 +472,8 @@ static void remove_any_filter(ap_filter_t *f, ap_f ap_filter_t **curr = r_filt ? r_filt : c_filt; ap_filter_t *fscan = *curr; + pending_filter_cleanup(f); + if (p_filt && *p_filt == f) *p_filt = (*p_filt)->next; @@ -695,39 +712,55 @@ AP_DECLARE(apr_status_t) ap_save_brigade(ap_filter return srv; } -static apr_status_t filters_cleanup(void *data) -{ - ap_filter_t **key = data; - - apr_hash_set((*key)->c->filters, key, sizeof *key, NULL); - - return APR_SUCCESS; -} - AP_DECLARE(int) ap_filter_prepare_brigade(ap_filter_t *f, apr_pool_t **p) { apr_pool_t *pool; - ap_filter_t **key; + ap_filter_t *found = NULL; + ap_filter_t *next, *e; + pool = f->r ? f->r->pool : f->c->pool; + if (p) { + *p = pool; + } if (!f->bb) { - - pool = f->r ? f->r->pool : f->c->pool; - - key = apr_pmemdup(pool, &f, sizeof f); - apr_hash_set(f->c->filters, key, sizeof *key, f); - f->bb = apr_brigade_create(pool, f->c->bucket_alloc); + apr_pool_pre_cleanup_register(pool, f, pending_filter_cleanup); + } + if (is_pending_filter(f)) { + return DECLINED; + } - apr_pool_pre_cleanup_register(pool, key, filters_cleanup); - - if (p) { - *p = pool; + /* Pending reads/writes must happen in the same order as input/output + * filters, so order this filter in both chains accordingly. Find the + * first "next" filter already in place and insert before it, if any, + * otherwise insert last. + */ + if (f->c->pending_filters) { + for (next = f->next; next && !found; next = next->next) { + for (e = APR_RING_FIRST(f->c->pending_filters); + e != APR_RING_SENTINEL(f->c->pending_filters, + ap_filter_t, pending); + e = APR_RING_NEXT(e, pending)) { + if (e == next) { + found = e; + break; + } + } } - - return OK; } - - return DECLINED; + else { + f->c->pending_filters = apr_palloc(f->c->pool, + sizeof *f->c->pending_filters); + APR_RING_INIT(f->c->pending_filters, ap_filter_t, pending); + } + if (found) { + APR_RING_INSERT_BEFORE(found, f, pending); + } + else { + APR_RING_INSERT_TAIL(f->c->pending_filters, f, ap_filter_t, pending); + } + + return OK; } AP_DECLARE(apr_status_t) ap_filter_setaside_brigade(ap_filter_t *f, @@ -967,23 +1000,24 @@ AP_DECLARE(int) ap_filter_should_yield(ap_filter_t AP_DECLARE(int) ap_filter_output_pending(conn_rec *c) { - apr_hash_index_t *rindex; int data_in_output_filters = DECLINED; + ap_filter_t *f; - rindex = apr_hash_first(NULL, c->filters); - while (rindex) { - ap_filter_t *f = apr_hash_this_val(rindex); + if (!c->pending_filters) { + return DECLINED; + } + for (f = APR_RING_FIRST(c->pending_filters); + f != APR_RING_SENTINEL(c->pending_filters, ap_filter_t, pending); + f = APR_RING_NEXT(f, pending)) { if (f->frec->direction == AP_FILTER_OUTPUT && f->bb && !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) + if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00470) "write failure in '%s' output filter", f->frec->name); return rv; } @@ -992,8 +1026,6 @@ AP_DECLARE(int) ap_filter_output_pending(conn_rec data_in_output_filters = OK; } } - - rindex = apr_hash_next(rindex); } return data_in_output_filters; @@ -1001,12 +1033,15 @@ AP_DECLARE(int) ap_filter_output_pending(conn_rec AP_DECLARE(int) ap_filter_input_pending(conn_rec *c) { - apr_hash_index_t *rindex; + ap_filter_t *f; - rindex = apr_hash_first(NULL, c->filters); - while (rindex) { - ap_filter_t *f = apr_hash_this_val(rindex); + if (!c->pending_filters) { + return DECLINED; + } + for (f = APR_RING_FIRST(c->pending_filters); + f != APR_RING_SENTINEL(c->pending_filters, ap_filter_t, pending); + f = APR_RING_NEXT(f, pending)) { if (f->frec->direction == AP_FILTER_INPUT && f->bb) { apr_bucket *e = APR_BRIGADE_FIRST(f->bb); @@ -1019,8 +1054,6 @@ AP_DECLARE(int) ap_filter_input_pending(conn_rec * } } - - rindex = apr_hash_next(rindex); } return DECLINED;