/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* mod_buffer.c --- Buffer the input and output filter stacks, collapse
* many small buckets into fewer large buckets.
*/
#include "apr.h"
#include "apr_strings.h"
#include "apr_buckets.h"
#include "apr_lib.h"
#include "ap_config.h"
#include "util_filter.h"
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_request.h"
typedef struct buffer_conf {
} buffer_conf;
typedef struct buffer_ctx {
int seen_eos;
} buffer_ctx;
/**
* Buffer buckets being written to the output filter stack.
*/
apr_bucket *e;
request_rec *r = f->r;
int move = 0;
/* first time in? create a context */
if (!ctx) {
/* buffering won't work on subrequests, it would be nice if
* it did. Within subrequests, we have no EOS to check for,
* so we don't know when to flush the buffer to the network
*/
if (f->r->main) {
}
}
/* Do nothing if asked to filter nothing. */
if (APR_BRIGADE_EMPTY(bb)) {
}
/* Empty buffer means we can potentially optimise below */
move = 1;
}
const char *data;
e = APR_BRIGADE_FIRST(bb);
/* EOS means we are done. */
if (APR_BUCKET_IS_EOS(e)) {
/* should we add an etag? */
/* pass the EOS across */
/* pass what we have down the chain */
continue;
}
/* A flush takes precedence over buffering */
if (APR_BUCKET_IS_FLUSH(e)) {
/* pass the flush bucket across */
/* pass what we have down the chain */
continue;
}
/* metadata buckets are preserved as is */
if (APR_BUCKET_IS_METADATA(e)) {
/*
* Remove meta data bucket from old brigade and insert into the
* new.
*/
continue;
}
/* is our buffer full?
* If so, send what we have down the filter chain. If the buffer
* gets full, we can no longer compute a content length.
*/
/* pass what we have down the chain */
if (rv) {
/* should break out of the loop, since our write to the client
* failed in some way. */
continue;
}
}
/* at this point we are ready to buffer.
* Buffering takes advantage of an optimisation in the handling of
* bucket brigades. Heap buckets are always created at a fixed
* size, regardless of the size of the data placed into them.
* The apr_brigade_write() call will first try and pack the data
* into any free space in the most recent heap bucket, before
* allocating a new bucket if necessary.
*/
APR_BLOCK_READ))) {
/* further optimisation: if the buckets are already heap
* buckets, and the buckets stay exactly APR_BUCKET_BUFF_SIZE
* long (as they would be if we were reading bits of a
* large bucket), then move the buckets instead of copying
* them.
*/
if (move && APR_BUCKET_IS_HEAP(e)) {
if (APR_BUCKET_BUFF_SIZE != size) {
move = 0;
}
} else {
}
}
}
return rv;
}
/**
* Buffer buckets being read from the input filter stack.
*/
/* buffer on main requests only */
if (!ap_is_initial_req(f->r)) {
}
/* first time in? create a context */
if (!ctx) {
}
/* just get out of the way of things we don't want. */
if (mode != AP_MODE_READBYTES) {
}
/* if our buffer is empty, read off the network until the buffer is full */
int seen_flush = 0;
const char *data;
/* if an error was received, bail out now. If the error is
* EAGAIN and we have not yet seen an EOS, we will definitely
* be called again, at which point we will send our buffered
* data. Instead of sending EAGAIN, some filters return an
* empty brigade instead when data is not yet available. In
* this case, pass through the APR_SUCCESS and emulate the
* underlying filter.
*/
return rv;
}
}
do {
/* if we see an EOS, we are done */
if (APR_BUCKET_IS_EOS(e)) {
break;
}
/* flush buckets clear the buffer */
if (APR_BUCKET_IS_FLUSH(e)) {
seen_flush = 1;
break;
}
/* pass metadata buckets through */
if (APR_BUCKET_IS_METADATA(e)) {
continue;
}
/* read the bucket in, pack it into the buffer */
APR_BLOCK_READ))) {
} else {
return rv;
}
}
}
/* give the caller the data they asked for from the buffer */
while (e != after) {
if (APR_BUCKET_IS_EOS(e)) {
/* last bucket read, step out of the way */
}
}
return APR_SUCCESS;
}
return (void *) new;
}
return new;
}
<= 0) {
return "BufferSize must be a size in bytes, and greater than zero";
}
return NULL;
}
"Maximum size of the buffer used by the buffer filter"), { NULL } };
}
create_buffer_config, /* create per-directory config structure */
merge_buffer_config, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
buffer_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};