|
|
b19d6e |
--- a/server/protocol.c 2017/10/10 17:47:25 1811745
|
|
|
b19d6e |
+++ b/server/protocol.c 2017/10/10 17:51:13 1811746
|
|
|
b19d6e |
@@ -1674,62 +1674,88 @@
|
|
|
b19d6e |
ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
|
|
|
b19d6e |
}
|
|
|
b19d6e |
|
|
|
b19d6e |
- /* Loop through this set of buckets to compute their length
|
|
|
b19d6e |
- */
|
|
|
b19d6e |
+ /* Loop through the brigade to count the length. To avoid
|
|
|
b19d6e |
+ * arbitrary memory consumption with morphing bucket types, this
|
|
|
b19d6e |
+ * loop will stop and pass on the brigade when necessary. */
|
|
|
b19d6e |
e = APR_BRIGADE_FIRST(b);
|
|
|
b19d6e |
while (e != APR_BRIGADE_SENTINEL(b)) {
|
|
|
b19d6e |
+ apr_status_t rv;
|
|
|
b19d6e |
+
|
|
|
b19d6e |
if (APR_BUCKET_IS_EOS(e)) {
|
|
|
b19d6e |
eos = 1;
|
|
|
b19d6e |
break;
|
|
|
b19d6e |
}
|
|
|
b19d6e |
- if (e->length == (apr_size_t)-1) {
|
|
|
b19d6e |
+ /* For a flush bucket, fall through to pass the brigade and
|
|
|
b19d6e |
+ * flush now. */
|
|
|
b19d6e |
+ else if (APR_BUCKET_IS_FLUSH(e)) {
|
|
|
b19d6e |
+ e = APR_BUCKET_NEXT(e);
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
+ /* For metadata bucket types other than FLUSH, loop. */
|
|
|
b19d6e |
+ else if (APR_BUCKET_IS_METADATA(e)) {
|
|
|
b19d6e |
+ e = APR_BUCKET_NEXT(e);
|
|
|
b19d6e |
+ continue;
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
+ /* For determinate length data buckets, count the length and
|
|
|
b19d6e |
+ * continue. */
|
|
|
b19d6e |
+ else if (e->length != (apr_size_t)-1) {
|
|
|
b19d6e |
+ r->bytes_sent += e->length;
|
|
|
b19d6e |
+ e = APR_BUCKET_NEXT(e);
|
|
|
b19d6e |
+ continue;
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
+ /* For indeterminate length data buckets, perform one read. */
|
|
|
b19d6e |
+ else /* e->length == (apr_size_t)-1 */ {
|
|
|
b19d6e |
apr_size_t len;
|
|
|
b19d6e |
const char *ignored;
|
|
|
b19d6e |
- apr_status_t rv;
|
|
|
b19d6e |
-
|
|
|
b19d6e |
- /* This is probably a pipe bucket. Send everything
|
|
|
b19d6e |
- * prior to this, and then read the data for this bucket.
|
|
|
b19d6e |
- */
|
|
|
b19d6e |
+
|
|
|
b19d6e |
rv = apr_bucket_read(e, &ignored, &len, eblock);
|
|
|
b19d6e |
+ if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
b19d6e |
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00574)
|
|
|
b19d6e |
+ "ap_content_length_filter: "
|
|
|
b19d6e |
+ "apr_bucket_read() failed");
|
|
|
b19d6e |
+ return rv;
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
if (rv == APR_SUCCESS) {
|
|
|
b19d6e |
- /* Attempt a nonblocking read next time through */
|
|
|
b19d6e |
eblock = APR_NONBLOCK_READ;
|
|
|
b19d6e |
+ e = APR_BUCKET_NEXT(e);
|
|
|
b19d6e |
r->bytes_sent += len;
|
|
|
b19d6e |
}
|
|
|
b19d6e |
else if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
b19d6e |
- /* Output everything prior to this bucket, and then
|
|
|
b19d6e |
- * do a blocking read on the next batch.
|
|
|
b19d6e |
- */
|
|
|
b19d6e |
- if (e != APR_BRIGADE_FIRST(b)) {
|
|
|
b19d6e |
- apr_bucket *flush;
|
|
|
b19d6e |
- apr_brigade_split_ex(b, e, ctx->tmpbb);
|
|
|
b19d6e |
- flush = apr_bucket_flush_create(r->connection->bucket_alloc);
|
|
|
b19d6e |
-
|
|
|
b19d6e |
- APR_BRIGADE_INSERT_TAIL(b, flush);
|
|
|
b19d6e |
- rv = ap_pass_brigade(f->next, b);
|
|
|
b19d6e |
- if (rv != APR_SUCCESS || f->c->aborted) {
|
|
|
b19d6e |
- return rv;
|
|
|
b19d6e |
- }
|
|
|
b19d6e |
- apr_brigade_cleanup(b);
|
|
|
b19d6e |
- APR_BRIGADE_CONCAT(b, ctx->tmpbb);
|
|
|
b19d6e |
- e = APR_BRIGADE_FIRST(b);
|
|
|
b19d6e |
+ apr_bucket *flush;
|
|
|
b19d6e |
|
|
|
b19d6e |
- ctx->data_sent = 1;
|
|
|
b19d6e |
- }
|
|
|
b19d6e |
+ /* Next read must block. */
|
|
|
b19d6e |
eblock = APR_BLOCK_READ;
|
|
|
b19d6e |
- continue;
|
|
|
b19d6e |
- }
|
|
|
b19d6e |
- else {
|
|
|
b19d6e |
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00574)
|
|
|
b19d6e |
- "ap_content_length_filter: "
|
|
|
b19d6e |
- "apr_bucket_read() failed");
|
|
|
b19d6e |
- return rv;
|
|
|
b19d6e |
+
|
|
|
b19d6e |
+ /* Ensure the last bucket to pass down is a flush if
|
|
|
b19d6e |
+ * the next read will block. */
|
|
|
b19d6e |
+ flush = apr_bucket_flush_create(f->c->bucket_alloc);
|
|
|
b19d6e |
+ APR_BUCKET_INSERT_BEFORE(e, flush);
|
|
|
b19d6e |
}
|
|
|
b19d6e |
}
|
|
|
b19d6e |
- else {
|
|
|
b19d6e |
- r->bytes_sent += e->length;
|
|
|
b19d6e |
+
|
|
|
b19d6e |
+ /* Optimization: if the next bucket is EOS (directly after a
|
|
|
b19d6e |
+ * bucket morphed to the heap, or a flush), short-cut to
|
|
|
b19d6e |
+ * handle EOS straight away - allowing C-L to be determined
|
|
|
b19d6e |
+ * for content which is already entirely in memory. */
|
|
|
b19d6e |
+ if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
|
|
|
b19d6e |
+ continue;
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
+
|
|
|
b19d6e |
+ /* On reaching here, pass on everything in the brigade up to
|
|
|
b19d6e |
+ * this point. */
|
|
|
b19d6e |
+ apr_brigade_split_ex(b, e, ctx->tmpbb);
|
|
|
b19d6e |
+
|
|
|
b19d6e |
+ rv = ap_pass_brigade(f->next, b);
|
|
|
b19d6e |
+ if (rv != APR_SUCCESS) {
|
|
|
b19d6e |
+ return rv;
|
|
|
b19d6e |
+ }
|
|
|
b19d6e |
+ else if (f->c->aborted) {
|
|
|
b19d6e |
+ return APR_ECONNABORTED;
|
|
|
b19d6e |
}
|
|
|
b19d6e |
- e = APR_BUCKET_NEXT(e);
|
|
|
b19d6e |
+ apr_brigade_cleanup(b);
|
|
|
b19d6e |
+ APR_BRIGADE_CONCAT(b, ctx->tmpbb);
|
|
|
b19d6e |
+ e = APR_BRIGADE_FIRST(b);
|
|
|
b19d6e |
+
|
|
|
b19d6e |
+ ctx->data_sent = 1;
|
|
|
b19d6e |
}
|
|
|
b19d6e |
|
|
|
b19d6e |
/* If we've now seen the entire response and it's otherwise
|