summaryrefslogtreecommitdiffstats
path: root/httpd-2.4.27-r1808230.patch
blob: e4062eaab6f4f8f32ea932f2c289fdbcd4894719 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# ./pullrev.sh 1808230
http://svn.apache.org/viewvc?view=revision&revision=1808230

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