mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
Initial Filtering code. This uses a bucket brigade scheme to allow modules
to add and modify data while processing a request. The docs still need to be updated, and a simple html page needs to be created explaining all of this. The only filter currently in the code is the core filter. This filter takes a bucket brigade and writes it to the network through the buff structure. In time, the buff will go away completely. More filters will need to be written. Submitted by: The Apache Community Reviewed by: The Apache Community git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86059 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
19
STATUS
19
STATUS
@@ -1,5 +1,5 @@
|
||||
Apache 2.0 STATUS:
|
||||
Last modified at [$Date: 2000/08/10 06:03:31 $]
|
||||
Last modified at [$Date: 2000/08/12 18:45:22 $]
|
||||
|
||||
Release:
|
||||
|
||||
@@ -11,6 +11,13 @@ Release:
|
||||
2.0a1 : released March 10, 2000
|
||||
|
||||
RELEASE SHOWSTOPPERS:
|
||||
* All of the bucket types must be implemented. The list can be found
|
||||
in src/include/ap_buckets.h
|
||||
|
||||
* Remove Buff and IOL from the code. To do this, a chunking and
|
||||
translation filter must be written. This allows us to remove BUFF.
|
||||
IOLs can be removed as soon as somebody gets to it.
|
||||
|
||||
* apachectl not being built or installed
|
||||
|
||||
* Win32: Get mod_auth_digest working under win32
|
||||
@@ -161,16 +168,6 @@ RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP:
|
||||
|
||||
* mod_info to use the configuration tree
|
||||
|
||||
* add output filtering
|
||||
Greg's patch:
|
||||
MsgID: <20000701162857.D29590@lyra.org> (patch)
|
||||
MsgID: <20000704025038.V29590@lyra.org> (demo)
|
||||
Status: Greg +1, Jim +1(?), Dirk -0, Ryan -0
|
||||
|
||||
Ryan's patch:
|
||||
MsgID: <Pine.LNX.4.21.0007072059120.18877-100000@koj.rkbloom.net>
|
||||
Status: Dirk +1, Ryan +1, Greg -1
|
||||
|
||||
PRs that have been suspended forever waiting for someone to
|
||||
put them into 'the next release':
|
||||
|
||||
|
@@ -115,6 +115,8 @@ API_EXPORT(void) ap_send_http_header(request_rec *l);
|
||||
API_EXPORT(int) ap_send_http_trace(request_rec *r);
|
||||
int ap_send_http_options(request_rec *r);
|
||||
|
||||
/* Finish up stuff after a request */
|
||||
|
||||
/**
|
||||
* Called at completion of sending the response. It sends the terminating
|
||||
* protocol information.
|
||||
|
@@ -614,7 +614,6 @@ struct request_rec {
|
||||
/* Info about the request itself... we begin with stuff that only
|
||||
* protocol.c should ever touch...
|
||||
*/
|
||||
|
||||
/** First line of request, so we can log it */
|
||||
char *the_request;
|
||||
/** HTTP/0.9, "simple" request */
|
||||
|
@@ -65,6 +65,7 @@ extern "C" {
|
||||
|
||||
#include "httpd.h"
|
||||
#include "apr.h"
|
||||
#include "ap_buckets.h"
|
||||
|
||||
/*
|
||||
* FILTER CHAIN
|
||||
@@ -114,7 +115,7 @@ typedef struct ap_filter_t ap_filter_t;
|
||||
* next/prev to insert/remove/replace elements in the bucket list, but
|
||||
* the types and values of the individual buckets should not be altered.
|
||||
*/
|
||||
typedef apr_status_t (*ap_filter_func)();
|
||||
typedef apr_status_t (*ap_filter_func)(ap_filter_t *f, ap_bucket_brigade *b);
|
||||
|
||||
/*
|
||||
* ap_filter_type:
|
||||
@@ -162,12 +163,25 @@ typedef enum {
|
||||
struct ap_filter_t {
|
||||
ap_filter_func filter_func;
|
||||
|
||||
void *ctx;
|
||||
ap_bucket_brigade *ctx;
|
||||
|
||||
ap_filter_type ftype;
|
||||
ap_filter_t *next;
|
||||
request_rec *r;
|
||||
};
|
||||
|
||||
/* This function just passes the current bucket brigade down to the next
|
||||
* filter on the filter stack. When a filter actually writes to the network
|
||||
* (usually either core or SSL), that filter should return the number of bytes
|
||||
* actually written and it will get propogated back up to the handler. If
|
||||
* nobody writes the data to the network, then this function will most likely
|
||||
* seg fault. I haven't come up with a good way to detect that case yet, and
|
||||
* it should never happen. Regardless, it's an unrecoverable error for the
|
||||
* current request. I would just rather it didn't take out the whole child
|
||||
* process.
|
||||
*/
|
||||
API_EXPORT(int) ap_pass_brigade(ap_filter_t *filter, ap_bucket_brigade *bucket);
|
||||
|
||||
/*
|
||||
* ap_register_filter():
|
||||
*
|
||||
@@ -192,27 +206,28 @@ API_EXPORT(void) ap_register_filter(const char *name,
|
||||
* calls to ap_add_filter). If the current filter chain contains filters
|
||||
* from another request, then this filter will be added before those other
|
||||
* filters.
|
||||
*
|
||||
* To re-iterate that last comment. This function is building a FIFO
|
||||
* list of filters. Take note of that when adding your filter to the chain.
|
||||
*/
|
||||
API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r);
|
||||
|
||||
|
||||
/*
|
||||
* Things to do later:
|
||||
* Add parameters to ap_filter_func type. Those parameters will be something
|
||||
* like:
|
||||
* (request_rec *r, ap_filter_t *filter, ap_data_list *the_data)
|
||||
* obviously, the request_rec is the current request, and the filter
|
||||
* is the current filter stack. The data_list is a bucket list or
|
||||
* bucket_brigade, but I am trying to keep this patch neutral. (If this
|
||||
* comment breaks that, well sorry, but the information must be there
|
||||
* somewhere. :-)
|
||||
*
|
||||
* Add a function like ap_pass_data. This function will basically just
|
||||
* call the next filter in the chain, until the current filter is NULL. If the
|
||||
* current filter is NULL, that means that nobody wrote to the network, and
|
||||
* we have a HUGE bug, so we need to return an error and log it to the
|
||||
* log file.
|
||||
/* The next two filters are for abstraction purposes only. They could be
|
||||
* done away with, but that would require that we break modules if we ever
|
||||
* want to change our filter registration method. The basic idea, is that
|
||||
* all filters have a place to store data, the ctx pointer. These functions
|
||||
* fill out that pointer with a bucket brigade, and retrieve that data on
|
||||
* the next call. The nice thing about these functions, is that they
|
||||
* automatically concatenate the bucket brigades together for you. This means
|
||||
* that if you have already stored a brigade in the filters ctx pointer, then
|
||||
* when you add more it will be tacked onto the end of that brigade. When
|
||||
* you retrieve data, if you pass in a bucket brigade to the get function,
|
||||
* it will append the current brigade onto the one that you are retrieving.
|
||||
*/
|
||||
API_EXPORT(ap_bucket_brigade *) ap_get_saved_data(ap_filter_t *f,
|
||||
ap_bucket_brigade **b);
|
||||
API_EXPORT(void) ap_save_data_to_filter(ap_filter_t *f, ap_bucket_brigade **b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -72,6 +72,8 @@
|
||||
#include "util_md5.h"
|
||||
#include "apr_fnmatch.h"
|
||||
#include "http_connection.h"
|
||||
#include "ap_buckets.h"
|
||||
#include "util_filter.h"
|
||||
#include "util_ebcdic.h"
|
||||
#include "mpm.h"
|
||||
#ifdef HAVE_NETDB_H
|
||||
@@ -87,6 +89,10 @@
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
/* Make sure we don't write less than 4096 bytes at any one time.
|
||||
*/
|
||||
#define MIN_SIZE_TO_WRITE 4096
|
||||
|
||||
/* Allow Apache to use ap_mmap */
|
||||
#ifdef USE_MMAP_FILES
|
||||
#include "apr_mmap.h"
|
||||
@@ -2909,6 +2915,65 @@ static int default_handler(request_rec *r)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Default filter. This filter should almost always be used. It's only job
|
||||
* is to send the headers if they haven't already been sent, and then send
|
||||
* the actual data. To send the data, we create an iovec out of the bucket
|
||||
* brigade and then call the iol's writev function. On platforms that don't
|
||||
* have writev, we have the problem of creating a lot of potentially small
|
||||
* packets that we are sending to the network.
|
||||
*
|
||||
* This can be solved later by making the buckets buffer everything into a
|
||||
* single memory block that can be written using write (on those systems
|
||||
* without writev only !)
|
||||
*/
|
||||
static int core_filter(ap_filter_t *f, ap_bucket_brigade *b)
|
||||
{
|
||||
request_rec *r = f->r;
|
||||
ap_bucket *dptr = b->head;
|
||||
apr_ssize_t bytes_sent;
|
||||
int len = 0, written;
|
||||
const char *str;
|
||||
|
||||
#if 0
|
||||
/* This will all be needed once BUFF is removed from the code */
|
||||
/* At this point we need to discover if there was any data saved from
|
||||
* the last call to core_filter.
|
||||
*/
|
||||
b = ap_get_saved_data(f, &b);
|
||||
|
||||
/* It is very obvious that we need to make sure it makes sense to send data
|
||||
* out at this point.
|
||||
*/
|
||||
dptr = b->head;
|
||||
while (dptr) {
|
||||
len += ap_get_bucket_len(dptr);
|
||||
dptr = dptr->next;
|
||||
}
|
||||
if (len < MIN_SIZE_TO_WRITE && b->tail->color != AP_BUCKET_EOS) {
|
||||
ap_save_data_to_filter(f, &b);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
while (dptr->read(dptr, &str, &len, 0) != AP_END_OF_BRIGADE) {
|
||||
ap_bwrite(f->r->connection->client, str, len, &written);
|
||||
dptr = dptr->next;
|
||||
bytes_sent += written;
|
||||
if (!dptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ap_brigade_destroy(b);
|
||||
/* This line will go away as soon as the BUFFs are removed */
|
||||
if (len == AP_END_OF_BRIGADE) {
|
||||
ap_bflush(f->r->connection->client);
|
||||
}
|
||||
return bytes_sent;
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const handler_rec core_handlers[] = {
|
||||
{ "*/*", default_handler },
|
||||
{ "default-handler", default_handler },
|
||||
@@ -2931,6 +2996,11 @@ static const char *core_method(const request_rec *r)
|
||||
static unsigned short core_port(const request_rec *r)
|
||||
{ return DEFAULT_HTTP_PORT; }
|
||||
|
||||
static void core_register_filter(request_rec *r)
|
||||
{
|
||||
ap_add_filter("CORE", NULL, r);
|
||||
}
|
||||
|
||||
static void register_hooks(void)
|
||||
{
|
||||
ap_hook_post_config(core_post_config,NULL,NULL,AP_HOOK_REALLY_FIRST);
|
||||
@@ -2943,6 +3013,14 @@ static void register_hooks(void)
|
||||
/* FIXME: I suspect we can eliminate the need for these - Ben */
|
||||
ap_hook_type_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
|
||||
ap_hook_access_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
|
||||
|
||||
/* This is kind of odd, and it would be cool to clean it up a bit.
|
||||
* The first function just registers the core's register_filter hook.
|
||||
* The other associates a global name with the filter defined
|
||||
* by the core module.
|
||||
*/
|
||||
ap_hook_insert_filter(core_register_filter, NULL, NULL, AP_HOOK_MIDDLE);
|
||||
ap_register_filter("CORE", core_filter, AP_FTYPE_CONNECTION);
|
||||
}
|
||||
|
||||
API_VAR_EXPORT module core_module = {
|
||||
|
@@ -64,6 +64,8 @@
|
||||
*/
|
||||
|
||||
#define CORE_PRIVATE
|
||||
#include "ap_buckets.h"
|
||||
#include "util_filter.h"
|
||||
#include "ap_config.h"
|
||||
#include "apr_strings.h"
|
||||
#include "httpd.h"
|
||||
@@ -1869,8 +1871,6 @@ API_EXPORT(void) ap_send_http_header(request_rec *r)
|
||||
apr_table_addn(r->headers_out, "Expires", date);
|
||||
}
|
||||
|
||||
/* Send the entire apr_table_t of header fields, terminated by an empty line. */
|
||||
|
||||
apr_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
|
||||
(void *) r, r->headers_out, NULL);
|
||||
|
||||
@@ -2512,101 +2512,84 @@ API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length)
|
||||
API_EXPORT(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
|
||||
size_t length)
|
||||
{
|
||||
size_t total_bytes_sent = 0;
|
||||
int n;
|
||||
apr_ssize_t w;
|
||||
char *addr;
|
||||
size_t bytes_sent = 0;
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
/* WE probably need to do something to make sure we are respecting the
|
||||
* offset and length. I think I know how to do this, but I will wait
|
||||
* until after the commit to actually write the code.
|
||||
*/
|
||||
bb = ap_brigade_create(r->pool);
|
||||
ap_brigade_append_buckets(bb,
|
||||
ap_bucket_mmap_create(mm, mm->size, &bytes_sent));
|
||||
bytes_sent = ap_pass_brigade(r->filters, bb);
|
||||
|
||||
|
||||
length += offset;
|
||||
while (!r->connection->aborted && offset < length) {
|
||||
if (length - offset > MMAP_SEGMENT_SIZE) {
|
||||
n = MMAP_SEGMENT_SIZE;
|
||||
}
|
||||
else {
|
||||
n = length - offset;
|
||||
}
|
||||
|
||||
apr_mmap_offset((void**)&addr, mm, offset);
|
||||
w = ap_rwrite(addr, n, r);
|
||||
if (w < 0)
|
||||
break;
|
||||
total_bytes_sent += w;
|
||||
offset += w;
|
||||
}
|
||||
|
||||
SET_BYTES_SENT(r);
|
||||
return total_bytes_sent;
|
||||
return bytes_sent;
|
||||
}
|
||||
#endif /* USE_MMAP_FILES */
|
||||
|
||||
API_EXPORT(int) ap_rputc(int c, request_rec *r)
|
||||
{
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
apr_ssize_t written;
|
||||
|
||||
if (r->connection->aborted)
|
||||
return EOF;
|
||||
|
||||
if (ap_bputc(c, r->connection->client) < 0) {
|
||||
check_first_conn_error(r, "rputc", 0);
|
||||
return EOF;
|
||||
}
|
||||
SET_BYTES_SENT(r);
|
||||
bb = ap_brigade_create(r->pool);
|
||||
ap_brigade_append_buckets(bb, ap_bucket_transient_create(&c, 1, &written));
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
|
||||
{
|
||||
int rcode;
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
apr_ssize_t written;
|
||||
|
||||
if (r->connection->aborted)
|
||||
return EOF;
|
||||
|
||||
rcode = ap_bputs(str, r->connection->client);
|
||||
if (rcode < 0) {
|
||||
check_first_conn_error(r, "rputs", 0);
|
||||
return EOF;
|
||||
}
|
||||
SET_BYTES_SENT(r);
|
||||
return rcode;
|
||||
bb = ap_brigade_create(r->pool);
|
||||
ap_brigade_append_buckets(bb,
|
||||
ap_bucket_transient_create(str, strlen(str), &written));
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
|
||||
{
|
||||
apr_ssize_t n;
|
||||
apr_status_t rv;
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
apr_ssize_t written;
|
||||
|
||||
if (r->connection->aborted)
|
||||
return EOF;
|
||||
|
||||
/* ### should loop to avoid partial writes */
|
||||
rv = ap_bwrite(r->connection->client, buf, nbyte, &n);
|
||||
if (rv != APR_SUCCESS) {
|
||||
check_first_conn_error(r, "rwrite", rv);
|
||||
return EOF;
|
||||
}
|
||||
SET_BYTES_SENT(r);
|
||||
return n;
|
||||
bb = ap_brigade_create(r->pool);
|
||||
ap_brigade_append_buckets(bb, ap_bucket_transient_create(buf, nbyte, &written));
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
return written;
|
||||
}
|
||||
|
||||
API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
|
||||
{
|
||||
int n;
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
apr_ssize_t written;
|
||||
|
||||
if (r->connection->aborted)
|
||||
return EOF;
|
||||
|
||||
n = ap_vbprintf(r->connection->client, fmt, va);
|
||||
|
||||
if (n < 0) {
|
||||
check_first_conn_error(r, "vrprintf", 0);
|
||||
return EOF;
|
||||
}
|
||||
SET_BYTES_SENT(r);
|
||||
return n;
|
||||
bb = ap_brigade_create(r->pool);
|
||||
written = ap_brigade_vprintf(bb, fmt, va);
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
return written;
|
||||
}
|
||||
|
||||
/* TODO: Make ap pa_bucket_vprintf that printfs directly into a
|
||||
* bucket.
|
||||
*/
|
||||
API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
@@ -2616,46 +2599,35 @@ API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
|
||||
return EOF;
|
||||
|
||||
va_start(va, fmt);
|
||||
n = ap_vbprintf(r->connection->client, fmt, va);
|
||||
n = ap_vrprintf(r, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (n < 0) {
|
||||
check_first_conn_error(r, "rprintf", 0);
|
||||
return EOF;
|
||||
}
|
||||
SET_BYTES_SENT(r);
|
||||
return n;
|
||||
}
|
||||
|
||||
API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r, ...)
|
||||
{
|
||||
ap_bucket_brigade *bb = NULL;
|
||||
apr_ssize_t written;
|
||||
va_list va;
|
||||
int n;
|
||||
|
||||
if (r->connection->aborted)
|
||||
return EOF;
|
||||
|
||||
bb = ap_brigade_create(r->pool);
|
||||
va_start(va, r);
|
||||
n = ap_vbputstrs(r->connection->client, va);
|
||||
written = ap_brigade_vputstrs(bb, va);
|
||||
va_end(va);
|
||||
|
||||
if (n < 0) {
|
||||
check_first_conn_error(r, "rvputs", 0);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
SET_BYTES_SENT(r);
|
||||
return n;
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
return written;
|
||||
}
|
||||
|
||||
API_EXPORT(int) ap_rflush(request_rec *r)
|
||||
{
|
||||
apr_status_t rv;
|
||||
ap_bucket_brigade *bb;
|
||||
|
||||
if ((rv = ap_bflush(r->connection->client)) != APR_SUCCESS) {
|
||||
check_first_conn_error(r, "rflush", rv);
|
||||
return EOF;
|
||||
}
|
||||
bb = ap_brigade_create(r->pool);
|
||||
ap_brigade_append_buckets(bb, ap_bucket_eos_create());
|
||||
ap_pass_brigade(r->filters, bb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -1279,6 +1279,12 @@ static void process_request_internal(request_rec *r)
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need to flush the data out at this point. We probably only want to
|
||||
* do this on the main request, but this is fine for an initial patch.
|
||||
* Once we look into this more, we won't flush sub-requests.
|
||||
*/
|
||||
ap_rflush(r);
|
||||
|
||||
/* Take care of little things that need to happen when we're done */
|
||||
ap_finalize_request_protocol(r);
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@
|
||||
* <http://www.apache.org/>.
|
||||
*/
|
||||
|
||||
#include "httpd.h"
|
||||
#include "util_filter.h"
|
||||
|
||||
/*
|
||||
@@ -73,7 +74,7 @@ typedef struct ap_filter_rec_t {
|
||||
} ap_filter_rec_t;
|
||||
|
||||
/* ### make this visible for direct manipulation?
|
||||
### use a hash table
|
||||
* ### use a hash table
|
||||
*/
|
||||
static ap_filter_rec_t *registered_filters = NULL;
|
||||
|
||||
@@ -126,6 +127,7 @@ API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
|
||||
f->filter_func = frec->filter_func;
|
||||
f->ctx = ctx;
|
||||
f->ftype = frec->ftype;
|
||||
f->r = r;
|
||||
|
||||
if (INSERT_BEFORE(f, r->filters)) {
|
||||
f->next = r->filters;
|
||||
@@ -144,3 +146,63 @@ API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass the buckets to the next filter in the filter stack. If the
|
||||
* current filter is a handler, we should get NULL passed in instead of
|
||||
* the current filter. At that point, we can just call the first filter in
|
||||
* the stack, or r->filters.
|
||||
*/
|
||||
API_EXPORT(int) ap_pass_brigade(ap_filter_t *next, ap_bucket_brigade *bb)
|
||||
{
|
||||
return next->filter_func(next, bb);
|
||||
}
|
||||
|
||||
API_EXPORT(ap_bucket_brigade *) ap_get_saved_data(ap_filter_t *f,
|
||||
ap_bucket_brigade **b)
|
||||
{
|
||||
ap_bucket_brigade *bb = (ap_bucket_brigade *)f->ctx;
|
||||
|
||||
/* If we have never stored any data in the filter, then we had better
|
||||
* create an empty bucket brigade so that we can concat.
|
||||
*/
|
||||
if (!bb) {
|
||||
bb = ap_brigade_create(f->r->pool);
|
||||
}
|
||||
|
||||
/* join the two brigades together. *b is now empty so we can
|
||||
* safely destroy it.
|
||||
*/
|
||||
ap_brigade_catenate(bb, *b);
|
||||
ap_brigade_destroy(*b);
|
||||
/* clear out the filter's context pointer. If we don't do this, then
|
||||
* when we save more data to the filter, we will be appended to what is
|
||||
* currently there. This will mean repeating data.... BAD! :-)
|
||||
*/
|
||||
f->ctx = NULL;
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
API_EXPORT(void) ap_save_data_to_filter(ap_filter_t *f, ap_bucket_brigade **b)
|
||||
{
|
||||
ap_bucket_brigade *bb = (ap_bucket_brigade *)f->ctx;
|
||||
ap_bucket *dptr = bb->head;
|
||||
|
||||
/* If have never stored any data in the filter, then we had better
|
||||
* create an empty bucket brigade so that we can concat.
|
||||
*/
|
||||
if (!bb) {
|
||||
bb = ap_brigade_create(f->r->pool);
|
||||
}
|
||||
|
||||
while (dptr) {
|
||||
if (dptr->setaside) {
|
||||
dptr->setaside(dptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Apend b to bb. This means b is now empty, and we can destory it safely.
|
||||
*/
|
||||
ap_brigade_catenate(bb, *b);
|
||||
ap_brigade_destroy(*b);
|
||||
f->ctx = bb;
|
||||
}
|
||||
|
Reference in New Issue
Block a user