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/27 13:44:37 UTC

[PATCH] final: link-based filtering

Here is the final patch for link-based filtering. I've addressed all known
(at this time) concerns that people have had with it.


There are two suggestions for "things to be fixed", but they should occur in
a second step to retain reviewability of this step:

1) delay sending of HTTP headers (see Ryan's patch for how this is done)

2) refactor and fix up ap_send_fd() and its pal ap_send_fd_length(). see
   Bill's note about using sendfile, mmap, etc when possible. fix the
   r->finfo.size bug. shift code to appropriate spot in new filter code.
   rough consensus says to wait a bit for this work.

There is a future feature, but it needs design first:

3) some kind of SetFilter or AddFilter directives using the filters' names


I've attached the patch (weighing a mere 850 lines). This time, I haven't
included the mod_gargle -- I'll post that separately since it isn't really
an offered patch. Basic changes in this patch: the sub-pool in ap_lvprintf;
subrequest handling; filter insertion/ordering.

Please review and comment.

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/27 11:25:38
@@ -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/27 11:37:41
@@ -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;
@@ -1274,8 +1282,16 @@
     rnew->main = (request_rec *) r;
 }
 
+static void flush_filters(request_rec *r)
+{
+    DECL_FILTER_HEAD(r, filter);
+
+    ap_lend(&filter);
+}
+
 void ap_finalize_sub_req_protocol(request_rec *sub)
 {
+    flush_filters(sub);
     SET_BYTES_SENT(sub->main);
 }
 
@@ -1824,11 +1840,6 @@
 #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
@@ -2431,107 +2442,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 +2537,291 @@
     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 {
+        ap_pool_t *pool;
+        const char *str;
+        size_t len;
+
+        (void) ap_create_pool(&pool, filter->r->pool);
+
+        str = ap_pvsprintf(filter->r->pool, fmt, va);
+        len = strlen(str);
+        (*next->simple_cb)(next, str, len);
+
+        ap_destroy_pool(pool);
+    }
+}
+
+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 +3210,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/http_request.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_request.c,v
retrieving revision 1.35
diff -u -r1.35 http_request.c
--- main/http_request.c	2000/06/24 17:33:57	1.35
+++ main/http_request.c	2000/06/27 11:26:05
@@ -769,6 +769,9 @@
     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 */
@@ -856,6 +859,9 @@
     rnew->request_config = ap_create_request_config(rnew->pool);
     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);
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/27 11:26:19
@@ -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 */

/*
** 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 \
                                       || (before_this)->r != (f)->r)

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