You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Greg Stein <gs...@lyra.org> on 2000/06/26 21:20:11 UTC

[PATCH] UPDATED: link-based filtering

Here is the patch from a couple days ago, plus some modifications that Ryan
requested:
*) define and use the "bucket style" of callbacks
*) code up the ap_register_filter() and ap_add_filter() functions

I had hoped to avoid the complexity of the buckets in this patch, but what
the heck. Here they are, and they actually aren't all that complicated. It
just makes the patch a bit longer.


Below, I have added four items:
1) the patch
2) new file: ap_filter.h
3) new file: filters.c
4) demo: mod_gargle.c

Missing:
*) ap_send_fd() and ap_send_fd_length() need to be refactored so that
   ap_lsendfile and ap_lputbucket can use them. The send_fd functions would
   then be recoded to use ap_lsendfile() calls.
*) filter type handling (deny connection filters in subrequests)
*) one liner: copying r->filters on subreq creation
*) AddFilter or SetFilter directives

Cheers,
-g

Index: include/httpd.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/httpd.h,v
retrieving revision 1.63
diff -u -r1.63 httpd.h
--- include/httpd.h	2000/06/24 19:31:41	1.63
+++ include/httpd.h	2000/06/26 19:05:27
@@ -731,7 +731,9 @@
 #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
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.87
diff -u -r1.87 http_protocol.c
--- main/http_protocol.c	2000/06/24 19:31:41	1.87
+++ main/http_protocol.c	2000/06/26 19:05:47
@@ -77,6 +77,8 @@
 #include "util_date.h"          /* For parseHTTPdate and BAD_DATE */
 #include "util_charset.h"
 #include "mpm_status.h"
+#include "ap_filter.h"
+
 #ifdef HAVE_STDARG_H
 #include <stdarg.h>
 #endif
@@ -96,6 +98,9 @@
           ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
   } while (0)
 
+#define DECL_FILTER_HEAD(r, f) \
+    ap_filter_t f = { NULL, NULL, (r), NULL, 0, (r)->filters }
+
 
 /* if this is the first error, then log an INFO message and shut down the
  * connection.
@@ -403,6 +408,9 @@
 
 API_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
 {
+    if (r->filters != NULL)
+        return 0;
+
     r->clength = clength;
     ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength));
     return 0;
@@ -1826,7 +1834,9 @@
 
 static void flush_filters(request_rec *r)
 {
-    /* ### place holder to flush pending content through the filters */
+    DECL_FILTER_HEAD(r, filter);
+
+    ap_lend(&filter);
 }
 
 /* finalize_request_protocol is called at completion of sending the
@@ -2431,107 +2441,88 @@
 
 API_EXPORT(int) ap_rputc(int c, request_rec *r)
 {
+    DECL_FILTER_HEAD(r, filter);
+
     if (r->connection->aborted)
         return EOF;
 
-    if (ap_bputc(c, r->connection->client) < 0) {
-        check_first_conn_error(r, "rputc", 0);
-        return EOF;
-    }
+    ap_lputc(&filter, c);
+
     SET_BYTES_SENT(r);
-    return c;
+    return 1;
 }
 
 API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
 {
-    int rcode;
+    DECL_FILTER_HEAD(r, filter);
 
     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;
-    }
+    ap_lputs(&filter, str);
+
     SET_BYTES_SENT(r);
-    return rcode;
+    return 1;
 }
 
 API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
 {
-    ap_ssize_t n;
-    ap_status_t rv;
+    DECL_FILTER_HEAD(r, filter);
 
     if (r->connection->aborted)
         return EOF;
 
-    /* ### should loop to avoid partial writes */
-    rv = ap_bwrite(r->connection->client, buf, nbyte, &n);
-    if (n < 0) {
-        check_first_conn_error(r, "rwrite", rv);
-        return EOF;
-    }
+    ap_lwrite(&filter, buf, nbyte);
+
     SET_BYTES_SENT(r);
-    return n;
+    return nbyte;
 }
 
 API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
 {
-    int n;
+    DECL_FILTER_HEAD(r, filter);
 
     if (r->connection->aborted)
         return EOF;
 
-    n = ap_vbprintf(r->connection->client, fmt, va);
+    ap_lvprintf(&filter, fmt, va);
 
-    if (n < 0) {
-        check_first_conn_error(r, "vrprintf", 0);
-        return EOF;
-    }
     SET_BYTES_SENT(r);
-    return n;
+    return 1;
 }
 
 API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
 {
     va_list va;
-    int n;
+
+    DECL_FILTER_HEAD(r, filter);
 
     if (r->connection->aborted)
         return EOF;
 
     va_start(va, fmt);
-    n = ap_vbprintf(r->connection->client, fmt, va);
+    ap_lvprintf(&filter, fmt, va);
     va_end(va);
 
-    if (n < 0) {
-        check_first_conn_error(r, "rprintf", 0);
-        return EOF;
-    }
     SET_BYTES_SENT(r);
-    return n;
+    return 1;
 }
 
 API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r, ...)
 {
     va_list va;
-    int n;
 
+    DECL_FILTER_HEAD(r, filter);
+
     if (r->connection->aborted)
         return EOF;
 
     va_start(va, r);
-    n = ap_vbputstrs(r->connection->client, va);
+    ap_lvputstrs(&filter, va);
     va_end(va);
 
-    if (n < 0) {
-        check_first_conn_error(r, "rvputs", 0);
-        return EOF;
-    }
-
     SET_BYTES_SENT(r);
-    return n;
+    return 1;
 }
 
 API_EXPORT(int) ap_rflush(request_rec *r)
@@ -2545,6 +2536,286 @@
     return 0;
 }
 
+API_EXPORT(void) ap_lwrite(ap_filter_t *filter, const char *buf, ap_size_t len)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted || len == 0)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        if (len == 1) {
+            if (ap_bputc(*buf, filter->r->connection->client) < 0) {
+                check_first_conn_error(filter->r, "ap_lwrite", 0);
+            }
+        }
+        else {
+            ap_status_t rv;
+            ap_ssize_t written;
+
+            /* ### should loop to avoid partial writes */
+            rv = ap_bwrite(filter->r->connection->client, buf, len, &written);
+            if (written < 0) {
+                check_first_conn_error(filter->r, "ap_lwrite", rv);
+            }
+        }
+    }
+    else if (next->bucket_cb != NULL) {
+        ap_bucket_t bucket = { AP_BUCKET_PTRLEN, buf, len };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        (*next->simple_cb)(next, buf, len);
+    }
+}
+
+API_EXPORT(void) ap_lputc(ap_filter_t *filter, int c)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        if (ap_bputc(c, filter->r->connection->client) < 0) {
+            check_first_conn_error(filter->r, "ap_lputc", 0);
+        }
+    }
+    else if (next->bucket_cb != NULL) {
+        char c2 = (char)c;
+        ap_bucket_t bucket = { AP_BUCKET_PTRLEN, &c2, 1 };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        char c2 = (char)c;
+
+        (*next->simple_cb)(next, &c2, 1);
+    }
+}
+
+API_EXPORT(void) ap_lputs(ap_filter_t *filter, const char *str)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted || *str == '\0')
+        return;
+
+    if ((next = filter->next) == NULL) {
+        int n;
+
+        if (str[1] == '\0') {
+            n = ap_bputc(*str, filter->r->connection->client);
+        }
+        else {
+            n = ap_bputs(str, filter->r->connection->client);
+        }
+
+        if (n < 0)
+            check_first_conn_error(filter->r, "ap_lputs", 0);
+    }
+    else if (next->bucket_cb != NULL) {
+        ap_bucket_t bucket = { AP_BUCKET_PTRLEN, str, strlen(str) };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        size_t len = strlen(str);
+
+        (*next->simple_cb)(next, str, len);
+    }
+}
+
+API_EXPORT_NONSTD(void) ap_lputstrs(ap_filter_t *filter, ...)
+{
+    va_list va;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    va_start(va, filter);
+    ap_lvputstrs(filter, va);
+    va_end(va);
+}
+
+API_EXPORT(void) ap_lvputstrs(ap_filter_t *filter, va_list va)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        if (ap_vbputstrs(filter->r->connection->client, va) < 0) {
+            check_first_conn_error(filter->r, "ap_lputstrs", 0);
+        }
+    }
+    else if (next->bucket_cb != NULL) {
+        ap_bucket_t bucket = { AP_BUCKET_STRINGS, NULL, 0, NULL, va };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        do {
+            const char *str;
+            size_t len;
+
+            str = va_arg(va, const char *);
+            if (str == NULL)
+                break;
+            len = strlen(str);
+            (*next->simple_cb)(next, str, len);
+        } while (!filter->r->connection->aborted);
+    }
+}
+
+API_EXPORT_NONSTD(void) ap_lprintf(ap_filter_t *filter, const char *fmt, ...)
+{
+    va_list va;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    va_start(va, fmt);
+    ap_lvprintf(filter, fmt, va);
+    va_end(va);
+}
+
+API_EXPORT(void) ap_lvprintf(ap_filter_t *filter, const char *fmt, va_list va)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        if (ap_vbprintf(filter->r->connection->client, fmt, va) < 0) {
+            check_first_conn_error(filter->r, "ap_lvprintf", 0);
+        }
+    }
+    else if (next->bucket_cb != NULL) {
+        ap_bucket_t bucket = { AP_BUCKET_PRINTF, NULL, 0, fmt, va };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        const char *str;
+        size_t len;
+
+        str = ap_pvsprintf(filter->r->pool, fmt, va);
+        len = strlen(str);
+        (*next->simple_cb)(next, str, len);
+    }
+}
+
+API_EXPORT(void) ap_lsendfile(ap_filter_t *filter, ap_file_t *file,
+                              ap_ssize_t len)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted || len == 0)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### fill in file code */
+    }
+    else if (next->bucket_cb != NULL) {
+        ap_bucket_t bucket = {
+            AP_BUCKET_FILE, NULL, 0, NULL, NULL, file, len
+        };
+
+        (*next->bucket_cb)(next, &bucket);
+    }
+    else {
+        /* ### fill in file code */
+    }
+}
+
+API_EXPORT(void) ap_lputbucket(ap_filter_t *filter, const ap_bucket_t *bucket)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        int n;
+
+        switch (bucket->type) {
+        case AP_BUCKET_PTRLEN:
+            if (bucket->len == 1) {
+                n = ap_bputc(*bucket->buf, filter->r->connection->client);
+            }
+            else {
+                ap_status_t rv;
+                ap_ssize_t written;
+
+                rv = ap_bwrite(filter->r->connection->client, bucket->buf,
+                               bucket->len, &written);
+                if (written < 0) {
+                    check_first_conn_error(filter->r, "ap_lputbucket", rv);
+                }
+                return;
+            }
+            break;
+        case AP_BUCKET_STRINGS:
+            n = ap_vbputstrs(filter->r->connection->client, bucket->va);
+            break;
+        case AP_BUCKET_PRINTF:
+            n = ap_vbprintf(filter->r->connection->client, bucket->fmt,
+                            bucket->va);
+            break;
+        case AP_BUCKET_FILE:
+            /* ### fill in file case */
+            break;
+        default:
+            /* ### set some kind of error */
+            break;
+        }
+
+        if (n < 0)
+            check_first_conn_error(filter->r, "ap_lputbucket", 0);
+    }
+    else if (next->bucket_cb != NULL) {
+        (*next->bucket_cb)(next, bucket);
+    }
+    else {
+        switch (bucket->type) {
+        case AP_BUCKET_PTRLEN:
+            (*next->simple_cb)(next, bucket->buf, bucket->len);
+            break;
+        case AP_BUCKET_STRINGS:
+            ap_lvputstrs(filter, bucket->va);
+            break;
+        case AP_BUCKET_PRINTF:
+            ap_lvprintf(filter, bucket->fmt, bucket->va);
+            break;
+        case AP_BUCKET_FILE:
+            ap_lsendfile(filter, bucket->file, bucket->len);
+            break;
+        default:
+            /* ### set some kind of error */
+            break;
+        }
+    }
+}
+
+API_EXPORT(void) ap_lend(ap_filter_t *filter)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) != NULL) {
+        if (next->bucket_cb != NULL)
+            (*next->bucket_cb)(next, NULL);
+        else
+            (*next->simple_cb)(next, NULL, 0);
+    }
+}
+
 /* We should have named this send_canned_response, since it is used for any
  * response that can be generated by the server from the request record.
  * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
@@ -2933,6 +3204,7 @@
     ap_finalize_request_protocol(r);
     ap_rflush(r);
 }
+
 
 AP_IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,
                           (request_rec *r),(r),OK,DECLINED)
Index: main/Makefile.in
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/Makefile.in,v
retrieving revision 1.14
diff -u -r1.14 Makefile.in
--- main/Makefile.in	2000/06/22 18:28:06	1.14
+++ main/Makefile.in	2000/06/26 19:06:25
@@ -8,7 +8,7 @@
 	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 listen.c mpm_common.c \
-	util_charset.c util_debug.c
+	util_charset.c util_debug.c filters.c
 
 include $(top_srcdir)/build/ltlib.mk
 
----------------------------------------------------------------------
ap_filter.h:

/* ====================================================================
 * 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
 * <http://www.apache.org/>.
 */

#ifndef AP_FILTER_H
#define AP_FILTER_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#include "httpd.h"
#include "apr.h"


typedef struct ap_filter_t ap_filter_t;
typedef struct ap_bucket_t ap_bucket_t;

typedef void (*ap_filter_simple_cb)(ap_filter_t *filter, const char *buf,
                                    ap_size_t len);
typedef void (*ap_filter_bucket_cb)(ap_filter_t *filter,
                                    const ap_bucket_t *bucket);

typedef enum {
    AP_FTYPE_CONTENT,
    AP_FTYPE_CONNECTION
} ap_filter_type;

struct ap_filter_t {
    ap_filter_simple_cb simple_cb;
    ap_filter_bucket_cb bucket_cb;
    request_rec *r;

    void *ctx;

    ap_filter_type ftype;
    ap_filter_t *next;
};

typedef enum {
    AP_BUCKET_PTRLEN,
    AP_BUCKET_STRINGS,
    AP_BUCKET_PRINTF,
    AP_BUCKET_FILE
} ap_bucket_type;

struct ap_bucket_t {
    ap_bucket_type type;

    const char *buf;    /* AP_BUCKET_PTRLEN */
    ap_size_t len;      /* AP_BUCKET_PTRLEN */

    const char *fmt;    /* AP_BUCKET_PRINTF */
    va_list va;         /* AP_BUCKET_STRINGS, _PRINTF */

    ap_file_t *file;    /* AP_BUCKET_FILE */
    ap_ssize_t flen;    /* AP_BUCKET_FILE */
};

API_EXPORT(void) ap_lwrite(ap_filter_t *filter, const char *buf,
                           ap_size_t len);
API_EXPORT(void) ap_lputc(ap_filter_t *filter, int c);
API_EXPORT(void) ap_lputs(ap_filter_t *filter, const char *str);
API_EXPORT_NONSTD(void) ap_lputstrs(ap_filter_t *filter, ...);
API_EXPORT(void) ap_lvputstrs(ap_filter_t *filter, va_list va);
API_EXPORT_NONSTD(void) ap_lprintf(ap_filter_t *filter, const char *fmt, ...);
API_EXPORT(void) ap_lvprintf(ap_filter_t *filter, const char *fmt, va_list va);

API_EXPORT(void) ap_lsendfile(ap_filter_t *filter, ap_file_t *file,
                              ap_ssize_t len);
#define AP_LSENDFILE_ALL ((ap_ssize_t) -1)

API_EXPORT(void) ap_lputbucket(ap_filter_t *filter, const ap_bucket_t *bucket);


API_EXPORT(void) ap_lend(ap_filter_t *filter);



typedef struct ap_filter_rec_t {
    const char *name;
    ap_filter_simple_cb simple_cb;
    ap_filter_bucket_cb bucket_cb;
    ap_filter_type ftype;

    struct ap_filter_rec_t *next;
} ap_filter_rec_t;

API_EXPORT(void) ap_register_filter(const char *name,
                                    ap_filter_simple_cb simple_cb,
                                    ap_filter_bucket_cb bucket_cb,
                                    ap_filter_type ftype);

API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r);


#ifdef __cplusplus
}
#endif

#endif	/* !AP_FILTER_H */
 
----------------------------------------------------------------------
filters.c:

/* ====================================================================
 * 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
 * <http://www.apache.org/>.
 */

#include "ap_filter.h"

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

static ap_status_t filter_cleanup(void *ctx)
{
    registered_filters = NULL;
}

API_EXPORT(void) ap_register_filter(const char *name,
                                    ap_filter_simple_cb simple_cb,
                                    ap_filter_bucket_cb bucket_cb,
                                    ap_filter_type ftype)
{
    ap_filter_rec_t *frec = ap_palloc(FILTER_POOL, sizeof(*frec));

    frec->name = name;
    frec->simple_cb = simple_cb;
    frec->bucket_cb = bucket_cb;
    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));
            ap_filter_t *fscan;

            f->simple_cb = frec->simple_cb;
            f->bucket_cb = frec->bucket_cb;
            f->r = r;
            f->ctx = ctx;
            f->ftype = frec->ftype;

            if (r->filters == NULL)
                r->filters = f;
            else {
                ap_filter_t *fscan = r->filters;
                while (fscan->next != NULL)
                    fscan = fscan->next;
                fscan->next = f;
            }

            break;
        }
    }
}
 
----------------------------------------------------------------------
mod_gargle.c:

/* ====================================================================
 * 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
 * <http://www.apache.org/>.
 */

#include "httpd.h"
#include "http_config.h"
#include "ap_filter.h"


module MODULE_VAR_EXPORT gargle_module;

typedef struct gargle_rec {
    int on;
    int head;
} gargle_rec;

typedef struct {
    int position;
    char hold;
} gargle_ctx;

#define SWAP_AT 10      /* swap chars 0/1, 10/11, 20/21, ... */

static void *create_gargle_config(ap_pool_t *p, char *dummy)
{
    gargle_rec *conf = ap_pcalloc(p, sizeof(*conf));

    return conf;
}

static void gargle_callback(ap_filter_t *filter, const char *buf,
                            ap_size_t len)
{
    gargle_ctx *ctx = filter->ctx;
    int rel = ctx->position % SWAP_AT;

    if (buf == NULL) {
        /* can't flush ctx->hold (if relevant) since we don't have the next
           character. policy is to dump the character */
        ap_lend(filter);
        return;
    }

    ctx->position += len;

    while (len > 0) {
        if (rel == 0) {
            ctx->hold = *buf;
            ++buf;
            --len;
            ++rel;
        }
        else if (rel == 1) {
            ap_lputc(filter, *buf);
            ap_lputc(filter, ctx->hold);
            ++buf;
            --len;
            ++rel;
        }
        else {
            int amt = SWAP_AT - rel;

            if (len < amt)
                amt = len;
            ap_lwrite(filter, buf, amt);
            buf += amt;
            len -= amt;
            if ((rel += amt) == SWAP_AT)
                rel = 0;
        }
    }
}

/*
** gargle_bucket:
**
** We aren't doing anything with the content, so a bucket approach is best.
** We insert our header/footer at the right moments, and just pass all the
** rest along, untouched.
*/
static void gargle_bucket(ap_filter_t *filter, const ap_bucket_t *bucket)
{
    gargle_ctx *ctx = filter->ctx;

    if (bucket == NULL) {
        ap_lputs(filter, "Here is the gargle footer!");
        ap_lend(filter);
    }
    else {
        if (ctx->position == 0) {
            ap_lputs(filter, "Here is the gargle header!");
            ctx->position = 1;
        }

        ap_lputbucket(filter, bucket);
    }
}

static const char *gargle_on(cmd_parms *cmd, void *dummy, int on)
{
    gargle_rec *cfg = (gargle_rec *)dummy;

    cfg->on = on;

    return NULL;
}

static const char *gargle_head(cmd_parms *cmd, void *dummy, int on)
{
    gargle_rec *cfg = (gargle_rec *)dummy;

    cfg->head = on;

    return NULL;
}

static const command_rec gargle_cmds[] = {
    AP_INIT_FLAG("GargleIt", gargle_on, NULL, RSRC_CONF|ACCESS_CONF,
                 "should this request be gargled"),
    AP_INIT_FLAG("GargleHead", gargle_head, NULL, RSRC_CONF|ACCESS_CONF,
                 "should this request have a gargle header/footer"),
    {NULL}                             
}; 

static int gargle_insert_filter(request_rec *r)
{
    gargle_rec *cfg = (gargle_rec *)ap_get_module_config(r->per_dir_config,
                                                         &gargle_module);

    if (cfg->on) {
        void *ctx = ap_pcalloc(r->pool, sizeof(gargle_ctx));

        ap_add_filter("gargle", ctx, r);
    }
    if (cfg->head) {
        void *ctx = ap_pcalloc(r->pool, sizeof(gargle_ctx));

        ap_add_filter("gargle-head", ctx, r);
    }
    return DECLINED;
}

static void register_hooks(void)
{
    ap_hook_insert_filter(gargle_insert_filter, NULL, NULL, AP_HOOK_MIDDLE);

    ap_register_filter("gargle", gargle_callback, NULL, AP_FTYPE_CONTENT);
    ap_register_filter("gargle-head", NULL, gargle_bucket, AP_FTYPE_CONTENT);
}

module MODULE_VAR_EXPORT gargle_module =
{
    STANDARD20_MODULE_STUFF,
    create_gargle_config,     /* dir config creater */
    NULL,                     /* dir merger --- default is to override */
    NULL,                     /* server config */                        
    NULL,                     /* merge server config */
    gargle_cmds,              /* command ap_table_t */ 
    NULL,                     /* handlers */          
    register_hooks            /* register hooks */
};