1
0
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:
Ryan Bloom
2000-08-12 18:45:35 +00:00
parent ca817f473c
commit 35d4c4a58c
8 changed files with 247 additions and 116 deletions

19
STATUS
View File

@@ -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':

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}