From f89738e6b66e45d7f8a396e8b1652bf6ca529a60 Mon Sep 17 00:00:00 2001 From: Ryan Bloom Date: Fri, 28 Jul 2000 20:31:02 +0000 Subject: [PATCH] Add the ability to register filters. This commit introduces a warning into the build. This warning will be removed automatically, as soon as we decide on a prototype for the function causing the warning. That decision is tied to which filtering mechanism we decide on. Submitted by: Ryan Bloom and Greg Stein git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85926 13f79535-47bb-0310-9956-ffa450edef68 --- include/httpd.h | 4 +- include/util_filter.h | 220 +++++++++++++++++++++++++++++++++++ modules/http/http_protocol.c | 19 ++- modules/http/http_request.c | 26 +++-- server/Makefile.in | 3 +- server/util_filter.c | 146 +++++++++++++++++++++++ 6 files changed, 403 insertions(+), 15 deletions(-) create mode 100644 include/util_filter.h create mode 100644 server/util_filter.c diff --git a/include/httpd.h b/include/httpd.h index ae7843a035..2c2f5ed20b 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -731,7 +731,9 @@ struct request_rec { #ifdef APACHE_XLATE struct ap_rr_xlate *rrx; #endif /*APACHE_XLATE*/ - + + struct ap_filter_t *filters; + /* Things placed at the end of the record to avoid breaking binary * compatibility. It would be nice to remember to reorder the entire * record to improve 64bit alignment the next time we need to break diff --git a/include/util_filter.h b/include/util_filter.h new file mode 100644 index 0000000000..f2f564a5a2 --- /dev/null +++ b/include/util_filter.h @@ -0,0 +1,220 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +#ifndef AP_FILTER_H +#define AP_FILTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef APR_HAVE_STDARG_H +#include +#endif + +#include "httpd.h" +#include "apr.h" + +/* + * FILTER CHAIN + * + * Filters operate using a "chaining" mechanism. The filters are chained + * together into a sequence. When output is generated, it is passed through + * each of the filters on this chain, until it reaches the end (or "bottom") + * and is placed onto the network. + * + * The top of the chain, the code generating the output, is typically called + * a "content generator." The content generator's output is fed into the + * filter chain using the standard Apache output mechanisms: ap_rputs(), + * ap_rprintf(), ap_rwrite(), etc. + * + * Each filter is defined by a callback. This callback takes the output from + * the previous filter (or the content generator if there is no previous + * filter), operates on it, and passes the result to the next filter in the + * chain. This pass-off is performed using the ap_fc_* functions, such as + * ap_fc_puts(), ap_fc_printf(), ap_fc_write(), etc. + * + * When content generation is complete, the system will pass an "end of + * stream" marker into the filter chain. The filters will use this to flush + * out any internal state and to detect incomplete syntax (for example, an + * unterminated SSI directive). + */ + +/* forward declare the filter type */ +typedef struct ap_filter_t ap_filter_t; + +/* + * ap_filter_func: + * + * This function type is used for filter callbacks. It will be passed a + * pointer to "this" filter, and a "bucket" containing the content to be + * filtered. + * + * In filter->ctx, the callback will find its context. This context is + * provided here, so that a filter may be installed multiple times, each + * receiving its own per-install context pointer. + * + * Callbacks are associated with a filter definition, which is specified + * by name. See ap_register_filter() for setting the association between + * a name for a filter and its associated callback (and other information). + * + * The *bucket structure (and all those referenced by ->next and ->prev) + * should be considered "const". The filter is allowed to modify the + * 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 ap_status_t (*ap_filter_func)(); + +/* + * ap_filter_type: + * + * Filters have different types/classifications. These are used to group + * and sort the filters to properly sequence their operation. + * + * AP_FTYPE_CONTENT: + * These filters are used to alter the content that is passed through + * them. Examples are SSI or PHP. + * + * AP_FTYPE_CONNECTION: + * These filters will alter the content, but in ways that are more + * strongly associated with the output connection. Examples are + * compression, character recoding, or chunked transfer coding. + * + * It is important to note that these types of filters are not allowed + * in a sub-request. A sub-requests output can certainly be filtered + * by AP_FTYPE_CONTENT filters, but all of the "final processing" is + * determined by the main request. + * + * The types have a particular sort order, which allows us to insert them + * into the filter chain in a determistic order. Within a particular grouping, + * the ordering is equivalent to the order of calls to ap_add_filter(). + */ +typedef enum { + AP_FTYPE_CONTENT, + AP_FTYPE_CONNECTION +} ap_filter_type; + +/* + * ap_filter_t: + * + * This is the request-time context structure for an installed filter (in + * the output filter chain). It provides the callback to use for filtering, + * the request this filter is associated with (which is important when + * an output chain also includes sub-request filters), the context for this + * installed filter, and the filter ordering/chaining fields. + * + * Filter callbacks are free to use ->ctx as they please, to store context + * during the filter process. Generally, this is superior over associating + * the state directly with the request. A callback should not change any of + * the other fields. + */ +struct ap_filter_t { + ap_filter_func filter_func; + + void *ctx; + + ap_filter_type ftype; + ap_filter_t *next; +}; + +/* + * ap_register_filter(): + * + * This function is used to register a filter with the system. After this + * registration is performed, then a filter may be added into the filter + * chain by using ap_add_filter() and simply specifying the name. + * + * The filter's callback and type should be passed. + */ +API_EXPORT(void) ap_register_filter(const char *name, + ap_filter_func filter_func, + ap_filter_type ftype); + +/* + * ap_add_filter(): + * + * Adds a named filter into the filter chain on the specified request record. + * The filter will be installed with the specified context pointer. + * + * Filters added in this way will always be placed at the end of the filters + * that have the same type (thus, the filters have the same order as the + * 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. + */ +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. + */ +#ifdef __cplusplus +} +#endif + +#endif /* !AP_FILTER_H */ diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 87ae898513..5b8c3c22b7 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -1278,8 +1278,19 @@ void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r) rnew->main = (request_rec *) r; } +static void end_output_stream(request_rec *r) +{ + /* + ** ### place holder to tell filters that no more content will be + ** ### arriving. typically, they will flush any pending content + */ +} + void ap_finalize_sub_req_protocol(request_rec *sub) { + /* tell the filter chain there is no more content coming */ + end_output_stream(sub); + SET_BYTES_SENT(sub->main); } @@ -1833,11 +1844,6 @@ API_EXPORT(void) ap_send_http_header(request_rec *r) #endif /*APACHE_XLATE*/ } -static void flush_filters(request_rec *r) -{ - /* ### place holder to flush pending content through the filters */ -} - /* finalize_request_protocol is called at completion of sending the * response. It's sole purpose is to send the terminating protocol * information for any wrappers around the response message body @@ -1845,7 +1851,8 @@ static void flush_filters(request_rec *r) */ API_EXPORT(void) ap_finalize_request_protocol(request_rec *r) { - flush_filters(r); + /* tell the filter chain there is no more content coming */ + end_output_stream(r); if (r->chunked && !r->connection->aborted) { #ifdef APACHE_XLATE diff --git a/modules/http/http_request.c b/modules/http/http_request.c index 4c3e9730a3..9bde8654f1 100644 --- a/modules/http/http_request.c +++ b/modules/http/http_request.c @@ -770,6 +770,9 @@ API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method, rnew->htaccess = r->htaccess; rnew->per_dir_config = r->server->lookup_defaults; + /* start with the same set of output filters */ + rnew->filters = r->filters; + ap_set_sub_req_protocol(rnew, r); /* would be nicer to pass "method" to ap_set_sub_req_protocol */ @@ -858,6 +861,9 @@ API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file, rnew->htaccess = r->htaccess; rnew->chunked = r->chunked; + /* start with the same set of output filters */ + rnew->filters = r->filters; + ap_set_sub_req_protocol(rnew, r); fdir = ap_make_dirstr_parent(rnew->pool, r->filename); @@ -961,16 +967,22 @@ API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file, API_EXPORT(int) ap_run_sub_req(request_rec *r) { + int retval; + + /* see comments in process_request_internal() */ + ap_run_insert_filter(r); + #ifndef APACHE_XLATE - int retval = ap_invoke_handler(r); + retval = ap_invoke_handler(r); #else /*APACHE_XLATE*/ - /* Save the output conversion setting of the caller across subrequests */ - int retval; - ap_xlate_t *saved_xlate; + { + /* Save the output conversion setting across subrequests */ + ap_xlate_t *saved_xlate; - ap_bgetopt(r->connection->client, BO_WXLATE, &saved_xlate); - retval = ap_invoke_handler(r); - ap_bsetopt(r->connection->client, BO_WXLATE, &saved_xlate); + ap_bgetopt(r->connection->client, BO_WXLATE, &saved_xlate); + retval = ap_invoke_handler(r); + ap_bsetopt(r->connection->client, BO_WXLATE, &saved_xlate); + } #endif /*APACHE_XLATE*/ ap_finalize_sub_req_protocol(r); return retval; diff --git a/server/Makefile.in b/server/Makefile.in index 497079cf13..a5e5432ee7 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -8,7 +8,8 @@ LTLIBRARY_SOURCES = \ http_protocol.c http_request.c http_vhost.c util.c util_date.c \ util_script.c util_uri.c util_md5.c util_cfgtree.c util_ebcdic.c \ rfc1413.c http_connection.c iol_file.c iol_socket.c listen.c \ - mpm_common.c util_charset.c util_debug.c util_xml.c + mpm_common.c util_charset.c util_debug.c util_xml.c \ + util_filter.c include $(top_srcdir)/build/ltlib.mk diff --git a/server/util_filter.c b/server/util_filter.c new file mode 100644 index 0000000000..77bdc98728 --- /dev/null +++ b/server/util_filter.c @@ -0,0 +1,146 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +#include "util_filter.h" + +/* + * ap_filter_rec_t: + * + * This (internal) structure is used for recording information about the + * registered filters. It associates a name with the filter's callback + * and filter type. + * + * At the moment, these are simply linked in a chain, so a ->next pointer + * is available. + */ +typedef struct ap_filter_rec_t { + const char *name; + ap_filter_func filter_func; + ap_filter_type ftype; + + struct ap_filter_rec_t *next; +} ap_filter_rec_t; + +/* ### make this visible for direct manipulation? + ### use a hash table +*/ +static ap_filter_rec_t *registered_filters = NULL; + +/* NOTE: Apache's current design doesn't allow a pool to be passed thu, + so we depend on a global to hold the correct pool +*/ +#define FILTER_POOL ap_global_hook_pool +#include "ap_hooks.h" /* for ap_global_hook_pool */ + +/* +** This macro returns true/false if a given filter should be inserted BEFORE +** another filter. This will happen when one of: 1) there isn't another +** filter; 2) that filter has a higher filter type (class); 3) that filter +** corresponds to a different request. +*/ +#define INSERT_BEFORE(f, before_this) ((before_this) == NULL \ + || (before_this)->ftype > (f)->ftype) + + +static ap_status_t filter_cleanup(void *ctx) +{ + registered_filters = NULL; + return APR_SUCCESS; +} + +API_EXPORT(void) ap_register_filter(const char *name, + ap_filter_func filter_func, + ap_filter_type ftype) +{ + ap_filter_rec_t *frec = ap_palloc(FILTER_POOL, sizeof(*frec)); + + frec->name = name; + frec->filter_func = filter_func; + frec->ftype = ftype; + + frec->next = registered_filters; + registered_filters = frec; + + ap_register_cleanup(FILTER_POOL, NULL, filter_cleanup, NULL); +} + +API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r) +{ + ap_filter_rec_t *frec = registered_filters; + + for (; frec != NULL; frec = frec->next) { + if (!strcasecmp(name, frec->name)) { + ap_filter_t *f = ap_pcalloc(r->pool, sizeof(*f)); + + f->filter_func = frec->filter_func; + f->ctx = ctx; + f->ftype = frec->ftype; + + if (INSERT_BEFORE(f, r->filters)) { + f->next = r->filters; + r->filters = f; + } + else { + ap_filter_t *fscan = r->filters; + while (!INSERT_BEFORE(f, fscan->next)) + fscan = fscan->next; + f->next = fscan->next; + fscan->next = f; + } + + break; + } + } +} + -- 2.50.1