summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ap_mmn.h2
-rw-r--r--include/util_filter.h22
-rw-r--r--modules/proxy/proxy_util.c1
-rw-r--r--server/util_filter.c45
4 files changed, 41 insertions, 29 deletions
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 0ddf227b90..4686cd3cb4 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -673,7 +673,7 @@
* ap_proxy_tunnel_conn_get_transferred() change
* ap_proxy_transfer_between_connections() sent to apr_off_t *.
* 20210531.0 (2.5.1-dev) add conn_rec->outgoing and ap_ssl_bind_outgoing()
- * 20210531.1 (2.5.1-dev) Add ap_bucket_type_wc, ap_bucket_wc_make() and
+ * 20210531.1 (2.5.1-dev) Add ap_bucket_wc_data, ap_bucket_wc_make() and
* ap_bucket_wc_create() to util_filter.h
* 20210531.2 (2.5.1-dev) Add ap_proxy_get_worker_ex() and
* ap_proxy_define_worker_ex() to mod_proxy.h
diff --git a/include/util_filter.h b/include/util_filter.h
index a0205a5f5c..a03e81c16c 100644
--- a/include/util_filter.h
+++ b/include/util_filter.h
@@ -763,15 +763,31 @@ AP_DECLARE(void) ap_filter_protocol(ap_filter_t* f, unsigned int proto_flags);
/** Filter is incompatible with "Cache-Control: no-transform" */
#define AP_FILTER_PROTO_TRANSFORM 0x20
-/** Write Completion (WC) bucket */
-AP_DECLARE_DATA extern const apr_bucket_type_t ap_bucket_type_wc;
+/**
+ * @brief Write Completion (WC) bucket
+ *
+ * A WC bucket is a FLUSH bucket with special ->data == &ap_bucket_wc_data,
+ * still both AP_BUCKET_IS_WC() and APR_BUCKET_IS_FLUSH() hold for them so
+ * they have the same semantics for most filters, namely:
+ * Everything produced before shall be passed to the next filter, including
+ * the WC/FLUSH bucket itself.
+ * The distinction between WC and FLUSH buckets is only for filters that care
+ * about write completion (calling ap_filter_reinstate_brigade() with non-NULL
+ * flush_upto), those can setaside WC buckets and the preceding data provided
+ * they have first determined that the next filter(s) have pending data
+ * already, usually by calling ap_filter_should_yield(f->next).
+ */
+
+/** Write Completion (WC) bucket data mark */
+AP_DECLARE_DATA extern const char ap_bucket_wc_data;
/**
* Determine if a bucket is a Write Completion (WC) bucket
* @param e The bucket to inspect
* @return true or false
*/
-#define AP_BUCKET_IS_WC(e) ((e)->type == &ap_bucket_type_wc)
+#define AP_BUCKET_IS_WC(e) (APR_BUCKET_IS_FLUSH(e) && \
+ (e)->data == (void *)&ap_bucket_wc_data)
/**
* Make the bucket passed in a Write Completion (WC) bucket
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 03b6d848a4..90e8e190fa 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -4551,6 +4551,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections(
APR_BRIGADE_INSERT_TAIL(bb_o, b);
}
else {
+ /* Prevent setaside/coalescing by intermediate filters. */
b = ap_bucket_wc_create(bb_o->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb_o, b);
}
diff --git a/server/util_filter.c b/server/util_filter.c
index ff33bfde21..8c278293cf 100644
--- a/server/util_filter.c
+++ b/server/util_filter.c
@@ -976,7 +976,9 @@ AP_DECLARE(apr_status_t) ap_filter_setaside_brigade(ap_filter_t *f,
e = next) {
next = APR_BUCKET_NEXT(e);
- /* Strip WC buckets added by ap_filter_output_pending(). */
+ /* WC buckets will be added back by ap_filter_output_pending()
+ * at the tail.
+ */
if (AP_BUCKET_IS_WC(e)) {
apr_bucket_delete(e);
continue;
@@ -1069,6 +1071,7 @@ AP_DECLARE(apr_status_t) ap_filter_reinstate_brigade(ap_filter_t *f,
int eor_buckets_in_brigade, opaque_buckets_in_brigade;
struct ap_filter_private *fp = f->priv;
core_server_config *conf;
+ int is_flush;
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, f->c,
"reinstate %s brigade to %s brigade in '%s' %sput filter",
@@ -1132,7 +1135,15 @@ AP_DECLARE(apr_status_t) ap_filter_reinstate_brigade(ap_filter_t *f,
bucket = next) {
next = APR_BUCKET_NEXT(bucket);
- if (AP_BUCKET_IS_EOR(bucket)) {
+ /* When called with flush_upto != NULL, we assume that the caller does
+ * the right thing to potentially setaside WC buckets (per semantics),
+ * so we don't treat them as FLUSH(_upto) here.
+ */
+ is_flush = (APR_BUCKET_IS_FLUSH(bucket) && !AP_BUCKET_IS_WC(bucket));
+ if (is_flush) {
+ /* handled below */
+ }
+ else if (AP_BUCKET_IS_EOR(bucket)) {
eor_buckets_in_brigade++;
}
else if (bucket->length == (apr_size_t)-1) {
@@ -1145,14 +1156,14 @@ AP_DECLARE(apr_status_t) ap_filter_reinstate_brigade(ap_filter_t *f,
}
}
- if (APR_BUCKET_IS_FLUSH(bucket)
+ if (is_flush
|| (memory_bytes_in_brigade > conf->flush_max_threshold)
|| (conf->flush_max_pipelined >= 0
&& eor_buckets_in_brigade > conf->flush_max_pipelined)) {
/* this segment of the brigade MUST be sent before returning. */
if (APLOGctrace6(f->c)) {
- char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
+ char *reason = is_flush ?
"FLUSH bucket" :
(memory_bytes_in_brigade > conf->flush_max_threshold) ?
"max threshold" : "max requests in pipeline";
@@ -1400,22 +1411,15 @@ AP_DECLARE(void) ap_filter_protocol(ap_filter_t *f, unsigned int flags)
f->frec->proto_flags = flags ;
}
+/* Write Completion (WC) bucket implementation */
-static apr_status_t wc_bucket_read(apr_bucket *b, const char **str,
- apr_size_t *len, apr_read_type_e block)
-{
- *str = NULL;
- *len = 0;
- return APR_SUCCESS;
-}
+AP_DECLARE_DATA const char ap_bucket_wc_data;
AP_DECLARE(apr_bucket *) ap_bucket_wc_make(apr_bucket *b)
{
- b->length = 0;
- b->start = 0;
- b->data = NULL;
- b->type = &ap_bucket_type_wc;
-
+ /* FLUSH bucket with special ->data mark (instead of NULL) */
+ b = apr_bucket_flush_make(b);
+ b->data = (void *)&ap_bucket_wc_data;
return b;
}
@@ -1428,12 +1432,3 @@ AP_DECLARE(apr_bucket *) ap_bucket_wc_create(apr_bucket_alloc_t *list)
b->list = list;
return ap_bucket_wc_make(b);
}
-
-AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_wc = {
- "WC", 5, APR_BUCKET_METADATA,
- apr_bucket_destroy_noop,
- wc_bucket_read,
- apr_bucket_setaside_noop,
- apr_bucket_split_notimpl,
- apr_bucket_simple_copy
-};