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/07/02 01:28:57 UTC

[PATCH] revised, final filtering patch. (ready for review)

This is a revision of my patch to incorporate feedback from Roy, Ryan, and
Dirk.

Specifically, Dirk has an outstanding veto caused by the "char* handler"
from my previous posted patch. This has been removed in this patch, so I
would hope that Dirk likewise removes his veto.

Other changes in this patch:

*) full-on commenting of the filtering types and interfaces in ap_filter.h

*) simplifications in http_protocol.c to demo how BUFF can be a filter;
   it is /not/ actually installed as a filter because the changes for that
   are a bit wider and would make this patch harder to verify
   [ but the interaction with BUFF is placed into an ap_filter_bucket_cb, so
     it should be straightforward to see this insertion is possible ]

*) renamed ap_l*() to ap_fc_*(). the former name was based on the term
   "layer" which has been replaced. ap_fc_*() are the "filter chain"
   functions.

*) buckets now have an AP_BUCKET_EOS type to represent the end-of-stream.
   ap_lend() has been removed in favor of sending this new bucket.
   [ this is sent from http_protocol.c::flush_filters() ]

*) buckets now have a doubly-linked list to enable whole sets of buckets to
   be passed around. This enables Roy's posted scenario with a header and
   footer.
   [ note: Roy's scenario presupposes that Apache sends a two-item bucket
     list containing a file and an EOS; while this filter framework supports
     that, Apache itself is not yet able to do this ]

*) char* callbacks have been removed


The patch is attached below. There are two +1 votes for it (assuming Jim
reaffirms his +1 for this updatd patch), and Dirk's veto. I believe this
address Dirk's veto; assuming he removes that, then we would need one more
+1 vote to get filtering into Apache right away.

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/

Index: include/httpd.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/httpd.h,v
retrieving revision 1.64
diff -u -r1.64 httpd.h
--- include/httpd.h	2000/06/30 21:18:13	1.64
+++ include/httpd.h	2000/07/01 23:12:26
@@ -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.89
diff -u -r1.89 http_protocol.c
--- main/http_protocol.c	2000/06/28 14:33:32	1.89
+++ main/http_protocol.c	2000/07/01 23:12:44
@@ -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
@@ -99,6 +101,9 @@
           ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
   } while (0)
 
+#define DECL_FILTER_HEAD(r, f) \
+    ap_filter_t f = { NULL, (r), NULL, 0, (r)->filters }
+
 
 /* if this is the first error, then log an INFO message and shut down the
  * connection.
@@ -406,6 +411,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;
@@ -1277,8 +1285,17 @@
     rnew->main = (request_rec *) r;
 }
 
+static void flush_filters(request_rec *r)
+{
+    DECL_FILTER_HEAD(r, filter);
+    ap_bucket_t bucket = { AP_BUCKET_EOS };
+
+    ap_fc_putbucket(&filter, &bucket);
+}
+
 void ap_finalize_sub_req_protocol(request_rec *sub)
 {
+    flush_filters(sub);
     SET_BYTES_SENT(sub->main);
 }
 
@@ -1827,11 +1844,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
@@ -2434,107 +2446,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_fc_putc(&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_fc_puts(&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_fc_write(&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_fc_vprintf(&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_fc_vprintf(&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_fc_vputstrs(&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)
@@ -2548,6 +2541,210 @@
     return 0;
 }
 
+static void BUFF_filter_callback(ap_filter_t *filter, ap_bucket_t *bucket)
+{
+    ap_bucket_t *bscan = bucket;
+
+    for (bscan = bucket; bscan != NULL; bscan = bscan->next) {
+        int n = 0;
+
+        switch (bscan->type) {
+        case AP_BUCKET_PTRLEN:
+            if (bscan->len == 1) {
+                n = ap_bputc(*bscan->buf, filter->r->connection->client);
+            }
+            else {
+                ap_status_t rv;
+                ap_ssize_t written;
+
+                /* ### should loop to ensure everything is written */
+                rv = ap_bwrite(filter->r->connection->client, bscan->buf,
+                               bscan->len, &written);
+                if (written < 0) {
+                    check_first_conn_error(filter->r, "BUFF_filter_callback",
+                                           rv);
+                }
+                /* fallthru; n == 0 */
+            }
+            break;
+
+        case AP_BUCKET_STRINGS:
+            n = ap_vbputstrs(filter->r->connection->client, bscan->va);
+            break;
+
+        case AP_BUCKET_PRINTF:
+            n = ap_vbprintf(filter->r->connection->client, bscan->fmt,
+                            bscan->va);
+            break;
+
+        case AP_BUCKET_FILE:
+            /* ### fill in file case */
+            /* ### fallthru; n == 0 */
+            break;
+
+        case AP_BUCKET_EOS:
+            /* there is nothing to do here */
+            /* fallthru; n == 0 */
+            break;
+
+        default:
+            /* ### set some kind of error */
+            break;
+        }
+
+        if (n < 0)
+            check_first_conn_error(filter->r, "BUFF_filter_callback", 0);
+    }
+}
+
+API_EXPORT(void) ap_fc_write(ap_filter_t *filter, const char *buf,
+                             ap_size_t len)
+{
+    ap_filter_t *next;
+    ap_bucket_t bucket = { AP_BUCKET_PTRLEN, buf, len };
+
+    if (filter->r->connection->aborted || len == 0)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT(void) ap_fc_putc(ap_filter_t *filter, int c)
+{
+    ap_filter_t *next;
+    char c2 = (char)c;
+    ap_bucket_t bucket = { AP_BUCKET_PTRLEN, &c2, 1 };
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT(void) ap_fc_puts(ap_filter_t *filter, const char *str)
+{
+    ap_filter_t *next;
+    ap_bucket_t bucket = { AP_BUCKET_PTRLEN, str, strlen(str) };
+
+    if (filter->r->connection->aborted || *str == '\0')
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT_NONSTD(void) ap_fc_putstrs(ap_filter_t *filter, ...)
+{
+    va_list va;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    va_start(va, filter);
+    ap_fc_vputstrs(filter, va);
+    va_end(va);
+}
+
+API_EXPORT(void) ap_fc_vputstrs(ap_filter_t *filter, va_list va)
+{
+    ap_filter_t *next;
+    ap_bucket_t bucket = { AP_BUCKET_STRINGS, NULL, 0, NULL, va };
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT_NONSTD(void) ap_fc_printf(ap_filter_t *filter, const char *fmt, ...)
+{
+    va_list va;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    va_start(va, fmt);
+    ap_fc_vprintf(filter, fmt, va);
+    va_end(va);
+}
+
+API_EXPORT(void) ap_fc_vprintf(ap_filter_t *filter,
+                               const char *fmt, va_list va)
+{
+    ap_filter_t *next;
+    ap_bucket_t bucket = { AP_BUCKET_PRINTF, NULL, 0, fmt, va };
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT(void) ap_fc_sendfile(ap_filter_t *filter, ap_file_t *file,
+                                ap_ssize_t flen)
+{
+    ap_filter_t *next;
+    ap_bucket_t bucket = {
+        AP_BUCKET_FILE, NULL, 0, NULL, NULL, file, flen
+    };
+
+    if (filter->r->connection->aborted || flen == 0)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, &bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, &bucket);
+    }
+}
+
+API_EXPORT(void) ap_fc_putbucket(ap_filter_t *filter, ap_bucket_t *bucket)
+{
+    ap_filter_t *next;
+
+    if (filter->r->connection->aborted)
+        return;
+
+    if ((next = filter->next) == NULL) {
+        /* ### until we really put it into place */
+        BUFF_filter_callback(filter, bucket);
+    }
+    else {
+        (*next->bucket_cb)(next, bucket);
+    }
+}
+
 /* 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),
@@ -2936,6 +3133,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/07/01 23:12:59
@@ -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 */
@@ -857,6 +860,9 @@
     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);
 
@@ -960,16 +966,22 @@
 
 API_EXPORT(int) ap_run_sub_req(request_rec *r)
 {
-#ifndef APACHE_XLATE
-    int 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;
 
-    ap_bgetopt(r->connection->client, BO_WXLATE, &saved_xlate);
-    retval  = ap_invoke_handler(r);
-    ap_bsetopt(r->connection->client, BO_WXLATE, &saved_xlate);
+    /* see comments in process_request_internal() */
+    ap_run_insert_filter(r);
+
+#ifndef APACHE_XLATE
+    retval = ap_invoke_handler(r);
+#else /*APACHE_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);
+    }
 #endif /*APACHE_XLATE*/
     ap_finalize_sub_req_protocol(r);
     return retval;
Index: main/Makefile.in
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/Makefile.in,v
retrieving revision 1.16
diff -u -r1.16 Makefile.in
--- main/Makefile.in	2000/07/01 14:14:15	1.16
+++ main/Makefile.in	2000/07/01 23:13:08
@@ -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 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 filters.c
 
 include $(top_srcdir)/build/ltlib.mk
 
----------------------------------------------------------------------
include/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"

/*
 * 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).
 */

/*
 * BUCKETS
 *
 * Filtering uses a "bucket" metaphor for holding content to be processed.
 * These buckets may contain arbitrary types of data. The selection of the
 * type is dependent upon how the "upstream" filter/generator places content
 * into the filter chain stream.
 *
 * For example, if a content generator uses ap_rwrite(), then the data will
 * be placed into an AP_BUCKET_PTRLEN bucket. This bucket type contains a
 * single pointer/length pair which will refer to the data.
 *
 * Buckets types are defined around the need to avoid copying the data if
 * at all possible. Whatever the "natural" input form is for a piece of
 * content, this is modelled within the bucket types. For example, when a
 * content generator uses ap_rprintf() or a filter uses ap_fc_printf(),
 * the format string and arguments are fed into/down the filter chain as
 * just theat: a format string and its arguments. The filter mechanism avoids
 * reducing the format/args to a final string for as long as possible. At
 * some point, a filter or the output of the chain will combine these to
 * produce actual bytes, but it is most optimal to defer this until it is
 * truly needed.
 *
 * See the ap_bucket_type enumeration for the different bucket types which
 * are currently defined.
 *
 * Buckets may also be linked into a list so that they may be passed as
 * entire groups of content. The filter may insert/remove/replace the buckets
 * within this list before passing the list to the next filter.
 */

/* forward declare some types */
typedef struct ap_filter_t ap_filter_t;
typedef struct ap_bucket_t ap_bucket_t;

/*
 * ap_filter_bucket_cb:
 *
 * 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 void (*ap_filter_bucket_cb)(ap_filter_t *filter,
                                    ap_bucket_t *bucket);

/*
 * 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_bucket_cb bucket_cb;
    request_rec *r;

    void *ctx;

    ap_filter_type ftype;
    ap_filter_t *next;
};

/*
 * ap_bucket_type:
 *
 * This enumeration is used to specify what type of bucket is present when
 * an ap_bucket_t is provided.
 *
 * AP_BUCKET_PTRLEN:
 *     This bucket type defines a simple pointer/length pair for the content.
 *     The content is NOT necessarily null-terminated.
 *
 *     This type occurs when ap_rwrite(), ap_fc_write(), ap_rputs(),
 *     ap_fc_puts(), ap_rputc(), or ap_fc_putc() is used by the upstream
 *     filter/generator.
 *
 * AP_BUCKET_STRINGS:
 *     This bucket type defines a set of null-terminated strings. The actual
 *     representation is through varargs' va_list type. A filter can sequence
 *     through the arguments using the va_arg() macro (and the "const char *"
 *     type). The filter should NOT use va_start() or va_end(). When va_arg()
 *     returns a NULL pointer, the list of strings is complete.
 *
 *     Note that you can only sequence through the strings once, due to the
 *     definition of va_list. Thus, the first filter to do this sequencing
 *     must pass the resulting content to the next filter in a new form (the
 *     bucket cannot simply be passed because ->va is useless).
 *
 *     This type occurs when ap_rvputs(), ap_fc_putstrs, or ap_fc_vputstrs()
 *     is used by the upstream filter/generator.
 *
 * AP_BUCKET_PRINTF:
 *     This bucket type defines a printf-style format and arguments. Similar
 *     to AP_BUCKET_STRINGS, this type also uses the ->va field to refer to
 *     the arguments. The format for the printf is stored in ->fmt.
 *
 *     Also similar to AP_BUCKET_STRINGS, the va_start/va_end macros should
 *     not be used, and ->va should be processed only once. The bucket may
 *     not be passed after this processing.
 *
 *     This type occurs when ap_rprintf(), ap_vrprintf(), ap_fc_printf(), or
 *     ap_fc_vprintf() is used by the upstream filter/generator.
 *
 * AP_BUCKET_FILE:
 *     This bucket type refers to an open file, from the current position
 *     and extending for ->flen bytes. Since there are some ap_file_t
 *     implementations/types that are not seekable, it may be impossible to
 *     "rewind" the file's current position after reading the contenxt.
 *     Therefore, it is important to note that once the content has been
 *     read, it must be passed to the next filter in a different form.
 *
 *     Note: if there is a way to determine whether a file is seekable, then
 *     it would be legal to fetch the current position, read the contents,
 *     rewind to the original position, and then pass this bucket/file down
 *     to the next filter in the output chain.
 *
 *     This type occurs when ap_send_fd(), ap_send_fd_length(), or
 *     ap_fc_sendfile() are used by the upstream filter/generator.
 *
 * AP_BUCKET_EOS:
 *     This bucket signals the end of the content stream. The filter should
 *     flush any internal state and issue errors if the state specifies that
 *     and end of stream cannot occur now (e.g. a command directive is
 *     incomplete).
 *
 *     This type occurs when Apache finalizes a (sub)request, or when an
 *     upstream filter passes this bucket along.
 */
typedef enum {
    AP_BUCKET_PTRLEN,
    AP_BUCKET_STRINGS,
    AP_BUCKET_PRINTF,
    AP_BUCKET_FILE,
    AP_BUCKET_EOS
} ap_bucket_type;

/*
 * ap_bucket_t:
 *
 * The actual bucket definition. The type is determined by the ->type field.
 * Which fields are valid/useful in the bucket is determined by the type,
 * as noted below and in the comments above for each type.
 *
 * Buckets are arranged in a doubly-linked list so that a filter may insert,
 * remove, or replace elements in a list of buckets. Generally, a filter
 * should not change any bucket values other than these link pointers.
 */
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 */

    ap_bucket_t *next;          /* next bucket in list */
    ap_bucket_t *prev;          /* previous bucket in list */
};

/*
 * FILTER CHAIN OUTPUT FUNCTIONS
 *
 * These functions are used to deliver output/content down to the next
 * filter in the chain.
 *
 * ap_fc_write(): write a block of bytes
 * ap_fc_putc(): write a single character
 * ap_fc_puts(): write a null-terminated string
 * ap_fc_putstrs(): write a set of null-termianted strings; the end is
 *                  signaled by a NULL parameter
 * ap_fc_vputstrs(): same as ap_fc_putstrs(), but where the set of strings
 *                   is defined by a va_list
 * ap_fc_printf(): use printf-like semantics for writing a string
 * ap_fc_vprintf(): use printf-like semantics, but with a va_list for the args
 * ap_fc_sendfile(): send the file contents, from the current file position,
 *                   and extending for "len" bytes; AP_FC_SENDFILE_ALL is
 *                   used to send from current-position to the end-of-file.
 * ap_fc_putbucket(): write a bucket into the filter chain
 */
API_EXPORT(void) ap_fc_write(ap_filter_t *filter, const char *buf,
                             ap_size_t len);
API_EXPORT(void) ap_fc_putc(ap_filter_t *filter, int c);
API_EXPORT(void) ap_fc_puts(ap_filter_t *filter, const char *str);

API_EXPORT_NONSTD(void) ap_fc_putstrs(ap_filter_t *filter, ...);
API_EXPORT(void) ap_fc_vputstrs(ap_filter_t *filter, va_list va);

API_EXPORT_NONSTD(void) ap_fc_printf(ap_filter_t *filter,
                                     const char *fmt, ...);
API_EXPORT(void) ap_fc_vprintf(ap_filter_t *filter,
                               const char *fmt, va_list va);

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

/* note: bucket->next and ->prev may be changed upon return from this */
API_EXPORT(void) ap_fc_putbucket(ap_filter_t *filter, ap_bucket_t *bucket);


/*
 * 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_bucket_cb bucket_cb,
                                    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);

#ifdef __cplusplus
}
#endif

#endif	/* !AP_FILTER_H */
 
----------------------------------------------------------------------
main/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"



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


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_bucket_cb bucket_cb,
                                    ap_filter_type ftype)
{
    ap_filter_rec_t *frec = ap_palloc(FILTER_POOL, sizeof(*frec));

    frec->name = name;
    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));

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

updated mod_gargle? not really... (was: [PATCH] revised, final filtering patch. (ready for review))

Posted by Greg Stein <gs...@lyra.org>.
On Mon, Jul 03, 2000 at 08:48:24AM -0400, Jeff Trawick wrote:
> Greg:
> 
> Do you have a revised mod_gargle.c to go with this latest patch?

Only for the header/footer part. The other two depended on the char*
handler. With the removal of that functionality, I must now go and duplicate
the whole char* functionality inside the functions. (well, develop some
utility functions)

Until I get those utilities done, which allows my handlers to look at the
bytes of the content (given arbitrary buckets), then the two old examples
are hopelessly broken/impossible.

Here is the header/footer function now:

static void gargle_bucket(ap_filter_t *filter, ap_bucket_t *bucket)
{
    gargle_ctx *ctx = filter->ctx;
    
    if (bucket->type == AP_BUCKET_EOS) {
        ap_fc_puts(filter, "Here is the gargle footer!");
    }
    else if (ctx->position == 0) {
        ap_fc_puts(filter, "Here is the gargle header!");
        ctx->position = 1;
    }

    ap_fc_putbucket(filter, bucket);
}

Note that it would also be possible to create new buckets for the strings
and hook them into a chain for delivery down the pipe. I chose to use the
simpler route of ac_fc_puts().

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/

Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by Jeff Trawick <tr...@ibm.net>.
Greg:

Do you have a revised mod_gargle.c to go with this latest patch?

-- 
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
     http://www.geocities.com/SiliconValley/Park/9289/
          Born in Roswell... married an alien...

Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by David Reid <dr...@jetnet.co.uk>.
I don't really understand it either.  Does anyone?

Off to Genoa...

david

> Why this incredible rush ? If we are in a rush to get 2.0 out; then we can
> always do it without filtering ? Doing filtering according to your design
> in 2.0 would lead to a 2.1 or 3.0 rather soon I fear ? Or is this
> something the group wants ?



Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by Dirk-Willem van Gulik <di...@covalent.net>.
On Wed, 5 Jul 2000, Greg Stein wrote:

> Okay... you posted this on Saturday and it is fast becoming Wednesday. Are
> you ready to give a slow answer yet? :-)

No, I am at this moment packing my stuff into a sea container to have it
ready to ship on Friday. Real Live Things take priority for the next 48
hours.
 
> If you have a veto in place, then I'd think it would be obvious to you what
> the issue was. Or is it simply difficult to articulate? 

Yes. Both yours and Ryans designs are lacking in area's IMHO and I simply
do not want salami tacktics; i.e. 'fixing' each little nit and little
issue than rather to go for the beef. There are lots of postings from both
Dean, Roy, Cliff, Ryan and various others about the core design and I am
going through them slowly, summarizing their points and trying to come
with a coherent approach.

So this is the simple reason I am not going to fall for a simple tick-off
list of small nits. I want to address the whole or at least convince
myself that we are not wielding the hood shut. And end up with a 3.0
strand in no time.

> Please let me know, as I'd like to see your veto removed.

Why this incredible rush ? If we are in a rush to get 2.0 out; then we can
always do it without filtering ? Doing filtering according to your design
in 2.0 would lead to a 2.1 or 3.0 rather soon I fear ? Or is this
something the group wants ?

Dw


Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by Greg Stein <gs...@lyra.org>.
On Sat, Jul 01, 2000 at 07:18:36PM -0700, Dirk-Willem van Gulik wrote:
> On Sat, 1 Jul 2000, Greg Stein wrote:
> 
> > Specifically, Dirk has an outstanding veto caused by the "char* handler"
> > from my previous posted patch. This has been removed in this patch, so I
> > would hope that Dirk likewise removes his veto.
> 
> Aye. I saw this and am going through it to see if I should make my -1 a
> -0.
> 
> > The patch is attached below. There are two +1 votes for it (assuming Jim
> > reaffirms his +1 for this updatd patch), and Dirk's veto. I believe this
> > address Dirk's veto; assuming he removes that, then we would need one more
> > +1 vote to get filtering into Apache right away.
> 
> At this point this does not quite address all concern's, in that your
> patch does not quite go far enough and does not quite set out to do all
> the things I thoughd we went into 2.0 for; including chaching and room for
> furure efficiency improvements. But in all fairness I've not accurately
> pinpointed my concern's, nor do I think that Roys/RBB's concern's where
> that narrowly targeted. Working on that as we speak. But I dare not give a
> quick answer as this is complex stuff and a grey line.

Okay... you posted this on Saturday and it is fast becoming Wednesday. Are
you ready to give a slow answer yet? :-)

If you have a veto in place, then I'd think it would be obvious to you what
the issue was. Or is it simply difficult to articulate? Please let me know,
as I'd like to see your veto removed.

Thx,
-g

-- 
Greg Stein, http://www.lyra.org/

Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by rb...@covalent.net.
> Could you explain this point, please? The only allocations anywhere in the
> patch are in ap_register_filter() and ap_add_filter(). Neither are
> associated with the content data.

Sorry, I didn't review the patch closely enough.  I said I would do a much
closer review later.  :-)

> I'll happily address the concern, but I don't see the allocation that you
> are referring to.

I do have other concerns based on another five second review.  I'll do a
real review now and post my comments.

Ryan

_______________________________________________________________________________
Ryan Bloom                        	rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------


Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by Greg Stein <gs...@lyra.org>.
On Sat, Jul 01, 2000 at 06:02:09PM -0700, rbb@covalent.net wrote:
>...
> I have made only the briefest of scans over this patch, and I will do a
> more thorough review either tomorrow or Monday.  On my very first review,
> one point still bothers me.  This patch still uses pools to allocate the
> memory for the data.  This means that all of the memory is allocated for
> the lifetime of the request.

Could you explain this point, please? The only allocations anywhere in the
patch are in ap_register_filter() and ap_add_filter(). Neither are
associated with the content data.

I'll happily address the concern, but I don't see the allocation that you
are referring to.

Thanx,
-g

-- 
Greg Stein, http://www.lyra.org/

Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by rb...@covalent.net.
On Sat, 1 Jul 2000, Greg Stein wrote:

> This is a revision of my patch to incorporate feedback from Roy, Ryan, and
> Dirk.
> 
> Specifically, Dirk has an outstanding veto caused by the "char* handler"
> from my previous posted patch. This has been removed in this patch, so I
> would hope that Dirk likewise removes his veto.
> 
> Other changes in this patch:

I have made only the briefest of scans over this patch, and I will do a
more thorough review either tomorrow or Monday.  On my very first review,
one point still bothers me.  This patch still uses pools to allocate the
memory for the data.  This means that all of the memory is allocated for
the lifetime of the request.

I realize that this patch only implements the string-like buckets, but
if a module uses these buckets at all, then that memory will stick around
through the life of the request.  This means even if we flush out to the
disk, the memory is around.

I really think we need to avoid pools when we do filtering.  See the
beginnings of the bucket implementation that I posted earlier today.

Ryan



Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by rb...@covalent.net.
On Sat, 1 Jul 2000, Greg Stein wrote:

> This is a revision of my patch to incorporate feedback from Roy, Ryan, and
> Dirk.
> 
> Specifically, Dirk has an outstanding veto caused by the "char* handler"
> from my previous posted patch. This has been removed in this patch, so I
> would hope that Dirk likewise removes his veto.
> 
> Other changes in this patch:

My concerns:

The buckets aren't complete enough.  This doesn't mean that we need to
implement more buckets.  It means that the bucket infrastructure is not
complete enough.  The buckets as currently implemented don't keep any
information about how they were created.  This means that filters later
down the chain don't know anything about the data.

How does a filter do anything?  All this implements is const char
*'s.  I also don't see anyway to actually chain buckets together.  The
buckets have a next and prev pointer, but nothing ever sets it.  Does this
mean its up to the filter to set these?

In general, I don't think this is it.  This is the previous patch except
it doesn't have the char * functions.  buckets may as well be char *'s in
this patch.  How are bucket's freed once a filter is done with them?  I
can't see how the data can be flushed to the disk and then removed from
memory.

At the very least, the buckets need to be allocated with
malloc, and they need a free pointer.  This design is just not complete.

The buckets are also likely to get VERY complex.  Having all of the bucket
types in one structure is going to get complex as soon as we start to
implement more of these things.

I really think we need Roy's bucket brigades.  This design just doesn't do
everything.  I'm not veto'ing, I'm just giving my opinion that this patch
isn't enough.

Ryan

_______________________________________________________________________________
Ryan Bloom                        	rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------



MAC Adress Resolving.

Posted by terdudio <te...@worldonline.nl>.
Hi,

I'm getting a little bit tired of kids switching from ip to prevent a site
flood Block

They Switch very fast from one ip to another (thanks to a
not-so-well-configured cable company)

As They Seem to be not using a Proxy, I'm now coding a Etag-Block penalty,
which at least means those losers have to clean their cache each time they
return.


But it seems to be also, that the Ip-Switcher (Running on Win
'95/98/ME/2000 ) Does not change there MAC-Adress.
I know it's easely to change on those platforms either. But i want to make
their lives as hard as possible.

Is there any known module available which handles MAC-Adresses ?

Regards,
    Reinder.



Re: [PATCH] revised, final filtering patch. (ready for review)

Posted by Dirk-Willem van Gulik <di...@covalent.net>.

On Sat, 1 Jul 2000, Greg Stein wrote:

> Specifically, Dirk has an outstanding veto caused by the "char* handler"
> from my previous posted patch. This has been removed in this patch, so I
> would hope that Dirk likewise removes his veto.

Aye. I saw this and am going through it to see if I should make my -1 a
-0.

> The patch is attached below. There are two +1 votes for it (assuming Jim
> reaffirms his +1 for this updatd patch), and Dirk's veto. I believe this
> address Dirk's veto; assuming he removes that, then we would need one more
> +1 vote to get filtering into Apache right away.

At this point this does not quite address all concern's, in that your
patch does not quite go far enough and does not quite set out to do all
the things I thoughd we went into 2.0 for; including chaching and room for
furure efficiency improvements. But in all fairness I've not accurately
pinpointed my concern's, nor do I think that Roys/RBB's concern's where
that narrowly targeted. Working on that as we speak. But I dare not give a
quick answer as this is complex stuff and a grey line.

Dw