Apache HTTPD
mod_buffer.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * mod_buffer.c --- Buffer the input and output filter stacks, collapse
19 * many small buckets into fewer large buckets.
20 */
21
22#include "apr.h"
23#include "apr_strings.h"
24#include "apr_buckets.h"
25#include "apr_lib.h"
26
27#include "ap_config.h"
28#include "util_filter.h"
29#include "httpd.h"
30#include "http_config.h"
31#include "http_log.h"
32#include "http_request.h"
33
34static const char bufferFilterName[] = "BUFFER";
35module AP_MODULE_DECLARE_DATA buffer_module;
36
37#define DEFAULT_BUFFER_SIZE 128*1024
38
39typedef struct buffer_conf {
40 apr_off_t size; /* size of the buffer */
41 int size_set; /* has the size been set */
43
51
56{
58 request_rec *r = f->r;
59 buffer_ctx *ctx = f->ctx;
61 int move = 0;
62
63 /* first time in? create a context */
64 if (!ctx) {
65
66 /* buffering won't work on subrequests, it would be nice if
67 * it did. Within subrequests, we have no EOS to check for,
68 * so we don't know when to flush the buffer to the network
69 */
70 if (f->r->main) {
72 return ap_pass_brigade(f->next, bb);
73 }
74
75 ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
76 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
77 ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module);
78 }
79
80 /* Do nothing if asked to filter nothing. */
81 if (APR_BRIGADE_EMPTY(bb)) {
82 return ap_pass_brigade(f->next, bb);
83 }
84
85 /* Empty buffer means we can potentially optimise below */
86 if (APR_BRIGADE_EMPTY(ctx->bb)) {
87 move = 1;
88 }
89
90 while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
91 const char *data;
94
95 e = APR_BRIGADE_FIRST(bb);
96
97 /* EOS means we are done. */
98 if (APR_BUCKET_IS_EOS(e)) {
99
100 /* should we add an etag? */
101
102 /* pass the EOS across */
105
106 /* pass what we have down the chain */
107 rv = ap_pass_brigade(f->next, ctx->bb);
108 continue;
109 }
110
111 /* A flush takes precedence over buffering */
112 if (APR_BUCKET_IS_FLUSH(e)) {
113
114 /* pass the flush bucket across */
117
118 /* pass what we have down the chain */
119 rv = ap_pass_brigade(f->next, ctx->bb);
120 continue;
121 }
122
123 /* metadata buckets are preserved as is */
125 /*
126 * Remove meta data bucket from old brigade and insert into the
127 * new.
128 */
131 continue;
132 }
133
134 /* is our buffer full?
135 * If so, send what we have down the filter chain. If the buffer
136 * gets full, we can no longer compute a content length.
137 */
138 apr_brigade_length(ctx->bb, 1, &len);
139 if (len > ctx->conf->size) {
140
141 /* pass what we have down the chain */
142 rv = ap_pass_brigade(f->next, ctx->bb);
143 if (rv) {
144 /* should break out of the loop, since our write to the client
145 * failed in some way. */
146 continue;
147 }
148 }
149
150 /* at this point we are ready to buffer.
151 * Buffering takes advantage of an optimisation in the handling of
152 * bucket brigades. Heap buckets are always created at a fixed
153 * size, regardless of the size of the data placed into them.
154 * The apr_brigade_write() call will first try and pack the data
155 * into any free space in the most recent heap bucket, before
156 * allocating a new bucket if necessary.
157 */
158 if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
159 APR_BLOCK_READ))) {
160
161 /* further optimisation: if the buckets are already heap
162 * buckets, and the buckets stay exactly APR_BUCKET_BUFF_SIZE
163 * long (as they would be if we were reading bits of a
164 * large bucket), then move the buckets instead of copying
165 * them.
166 */
167 if (move && APR_BUCKET_IS_HEAP(e)) {
170 if (APR_BUCKET_BUFF_SIZE != size) {
171 move = 0;
172 }
173 } else {
176 }
177
178 }
179
180 }
181
182 return rv;
183
184}
185
191{
192 apr_bucket *e, *after;
193 apr_status_t rv;
194 buffer_ctx *ctx = f->ctx;
195
196 /* buffer on main requests only */
197 if (!ap_is_initial_req(f->r)) {
199 return ap_get_brigade(f->next, bb, mode, block, readbytes);
200 }
201
202 /* first time in? create a context */
203 if (!ctx) {
204 ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
205 ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
206 ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
207 ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module);
208 }
209
210 /* just get out of the way of things we don't want. */
211 if (mode != AP_MODE_READBYTES) {
212 return ap_get_brigade(f->next, bb, mode, block, readbytes);
213 }
214
215 /* if our buffer is empty, read off the network until the buffer is full */
216 if (APR_BRIGADE_EMPTY(ctx->bb)) {
217 int seen_flush = 0;
218
219 ctx->remaining = ctx->conf->size;
220
221 while (!ctx->seen_eos && !seen_flush && ctx->remaining > 0) {
222 const char *data;
223 apr_size_t size = 0;
224
225 if (APR_BRIGADE_EMPTY(ctx->tmp)) {
226 rv = ap_get_brigade(f->next, ctx->tmp, mode, block,
227 ctx->remaining);
228
229 /* if an error was received, bail out now. If the error is
230 * EAGAIN and we have not yet seen an EOS, we will definitely
231 * be called again, at which point we will send our buffered
232 * data. Instead of sending EAGAIN, some filters return an
233 * empty brigade instead when data is not yet available. In
234 * this case, pass through the APR_SUCCESS and emulate the
235 * underlying filter.
236 */
237 if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->tmp)) {
238 return rv;
239 }
240 }
241
242 do {
243 e = APR_BRIGADE_FIRST(ctx->tmp);
244
245 /* if we see an EOS, we are done */
246 if (APR_BUCKET_IS_EOS(e)) {
249 ctx->seen_eos = 1;
250 break;
251 }
252
253 /* flush buckets clear the buffer */
254 if (APR_BUCKET_IS_FLUSH(e)) {
257 seen_flush = 1;
258 break;
259 }
260
261 /* pass metadata buckets through */
265 continue;
266 }
267
268 /* read the bucket in, pack it into the buffer */
269 if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
270 APR_BLOCK_READ))) {
272 ctx->remaining -= size;
274 } else {
275 return rv;
276 }
277
278 } while (!APR_BRIGADE_EMPTY(ctx->tmp));
279 }
280 }
281
282 /* give the caller the data they asked for from the buffer */
284 e = APR_BRIGADE_FIRST(ctx->bb);
285 while (e != after) {
286 if (APR_BUCKET_IS_EOS(e)) {
287 /* last bucket read, step out of the way */
289 }
292 e = APR_BRIGADE_FIRST(ctx->bb);
293 }
294
295 return APR_SUCCESS;
296}
297
299{
300 buffer_conf *new = (buffer_conf *) apr_pcalloc(p, sizeof(buffer_conf));
301
302 new->size_set = 0; /* unset */
303 new->size = DEFAULT_BUFFER_SIZE; /* default size */
304
305 return (void *) new;
306}
307
308static void *merge_buffer_config(apr_pool_t *p, void *basev, void *addv)
309{
310 buffer_conf *new = (buffer_conf *) apr_pcalloc(p, sizeof(buffer_conf));
311 buffer_conf *add = (buffer_conf *) addv;
313
314 new->size = (add->size_set == 0) ? base->size : add->size;
315 new->size_set = add->size_set || base->size_set;
316
317 return new;
318}
319
320static const char *set_buffer_size(cmd_parms *cmd, void *dconf, const char *arg)
321{
322 buffer_conf *conf = dconf;
323
324 if (APR_SUCCESS != apr_strtoff(&(conf->size), arg, NULL, 10) || conf->size
325 <= 0) {
326 return "BufferSize must be a size in bytes, and greater than zero";
327 }
328 conf->size_set = 1;
329
330 return NULL;
331}
332
333static const command_rec buffer_cmds[] = { AP_INIT_TAKE1("BufferSize",
335 "Maximum size of the buffer used by the buffer filter"), { NULL } };
336
344
347 create_buffer_config, /* create per-directory config structure */
348 merge_buffer_config, /* merge per-directory config structures */
349 NULL, /* create per-server config structure */
350 NULL, /* merge per-server config structures */
351 buffer_cmds, /* command apr_table_t */
352 register_hooks /* register hooks */
353};
Symbol export macros and hook functions.
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL Buckets/Bucket Brigades.
APR general purpose library routines.
APR Strings library.
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
ap_conf_vector_t * base
request_rec * r
void ap_remove_input_filter(ap_filter_t *f)
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
ap_filter_rec_t * ap_register_output_filter(const char *name, ap_out_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
apr_status_t ap_filter_rec_t * ap_register_input_filter(const char *name, ap_in_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
void ap_remove_output_filter(ap_filter_t *f)
@ AP_FTYPE_CONTENT_SET
int ap_is_initial_req(request_rec *r)
Definition request.c:2567
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
apr_file_t * f
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_REMOVE(e)
#define APR_BUCKET_IS_HEAP(e)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
apr_read_type_e
Definition apr_buckets.h:57
apr_bucket * e
#define APR_BUCKET_BUFF_SIZE
Definition apr_buckets.h:54
#define APR_BRIGADE_EMPTY(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
#define ACCESS_CONF
#define STANDARD20_MODULE_STUFF
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
char * buffer
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_cmdtype_e cmd
Apache Configuration.
Apache Logging library.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static const command_rec buffer_cmds[]
Definition mod_buffer.c:333
static void * create_buffer_config(apr_pool_t *p, char *dummy)
Definition mod_buffer.c:298
#define DEFAULT_BUFFER_SIZE
Definition mod_buffer.c:37
static void register_hooks(apr_pool_t *p)
Definition mod_buffer.c:337
static const char bufferFilterName[]
Definition mod_buffer.c:34
static apr_status_t buffer_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
Definition mod_buffer.c:189
static void * merge_buffer_config(apr_pool_t *p, void *basev, void *addv)
Definition mod_buffer.c:308
static apr_status_t buffer_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
Definition mod_buffer.c:55
static const char * set_buffer_size(cmd_parms *cmd, void *dconf, const char *arg)
Definition mod_buffer.c:320
return NULL
Definition mod_so.c:359
The representation of a filter chain.
apr_pool_t * pool
apr_off_t size
Definition mod_buffer.c:40
apr_bucket_brigade * tmp
Definition mod_buffer.c:46
apr_off_t remaining
Definition mod_buffer.c:48
buffer_conf * conf
Definition mod_buffer.c:47
apr_bucket_brigade * bb
Definition mod_buffer.c:45
A structure that represents the current request.
Definition httpd.h:845
apr_pool_t * pool
Definition httpd.h:847
Apache filter library.
ap_input_mode_t
input filtering modes
Definition util_filter.h:41
@ AP_MODE_READBYTES
Definition util_filter.h:43