You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by rb...@covalent.net on 2000/06/27 07:31:03 UTC

[PATCH] Final filtering patch for a few days.

As I said in my response to Roy, I am posting this because there are
useful things in it.  There are also horrible things in it.  This is NOT
the correct patch.  IMHO we have not found the correct patch yet.  Greg's
has limitations with ordering, and with potential optimizations IMO.  I
have tried to outline these.  I have probably failed at outlining them
however.  In a few days I may try again if I am asked to.  I am not
looking at filtering tomorrow or Wednesday.

This patch fixes most, if not all, of the major problems outlined by Greg
in response to my last patch.  My last patch was sent far too quickly
without enough testing.  I do think all of the patches are coming closer
and closer together.  This is a good thing IMNSHO.

These sets of patches have also made me hate Greg at times.  :-)  Greg,
I hope you realize although I can hate you while reading your e-mail,
off-line I hold you in very high regards as a coder and person, and I
meant no offense at any time during this debate.

Here is my current patch.  ----NOT FOR SUBMISSION----.  This is to make
sure the patch is available the next time somebody picks up
filtering.  :-)


? include/ap_ioblock.h
? main/ap_ioblock.c
? modules/extra/mod_gargle.c
Index: include/ap_hooks.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/ap_hooks.h,v
retrieving revision 1.20
diff -u -d -b -w -u -r1.20 ap_hooks.h
--- include/ap_hooks.h	2000/05/27 22:53:46	1.20
+++ include/ap_hooks.h	2000/06/27 05:18:45
@@ -167,12 +167,153 @@
     return decline; \
     }
 
+/* Set of Hook definitions designed to put the list of functions off a
+ * pointer pre-defined in the request_rec.
+ */
+#define AP_DECLARE_RHOOK(ret,name,args) \
+typedef ret HOOK_##name args; \
+API_EXPORT(void) ap_rhook_##name(HOOK_##name *pf,request_rec *r, \
+                                 const char * const *aszPre,\
+		                 const char * const *aszSucc,int nOrder); \
+API_EXPORT(ret) ap_run_##name args; \
+typedef struct _LINK_##name \
+    { \
+    HOOK_##name *pFunc; \
+    const char *szName; \
+    const char * const *aszPredecessors; \
+    const char * const *aszSuccessors; \
+    int nOrder; \
+    } LINK_##name;
+
+#define AP_RHOOK_STRUCT(members) \
+struct { members } _hooks;
+
+#define AP_RHOOK_LINK(name) \
+    ap_array_header_t *link_##name;
+
+#define AP_IMPLEMENT_RHOOK_BASE(name) \
+API_EXPORT(void) ap_rhook_##name(HOOK_##name *pf,request_rec *r, \
+                                 const char * const *aszPre,\
+		                 const char * const *aszSucc,int nOrder) \
+    { \
+    LINK_##name *pHook; \
+    if(!r->_hooks.link_##name) \
+	{ \
+	r->_hooks.link_##name=ap_make_array(r->pool,1,sizeof(LINK_##name)); \
+	ap_hook_sort_register(#name,&r->_hooks.link_##name); \
+	} \
+    pHook=ap_push_array(r->_hooks.link_##name); \
+    pHook->pFunc=pf; \
+    pHook->aszPredecessors=aszPre; \
+    pHook->aszSuccessors=aszSucc; \
+    pHook->nOrder=nOrder; \
+    pHook->szName=ap_debug_module_name; \
+    if(ap_debug_module_hooks) \
+	ap_show_hook(#name,aszPre,aszSucc); \
+    }
+
+/* RUN_ALL runs to the first one to return other than ok or decline
+   RUN_FIRST runs to the first one to return other than decline
+   VOID runs all
+*/
+
+#define AP_IMPLEMENT_RHOOK_VOID(name,args_decl,args_use) \
+AP_IMPLEMENT_RHOOK_BASE(name) \
+API_EXPORT(void) ap_run_##name args_decl \
+    { \
+    LINK_##name *pHook; \
+    int n; \
+\
+    if(!r->_hooks.link_##name) \
+	return; \
+\
+    pHook=(LINK_##name *)r->_hooks.link_##name->elts; \
+    for(n=0 ; n < r->_hooks.link_##name->nelts ; ++n) \
+	pHook[n].pFunc args_use; \
+    }
+
+/* FIXME: note that this returns ok when nothing is run. I suspect it should
+   really return decline, but that breaks Apache currently - Ben
+*/
+#define AP_IMPLEMENT_RHOOK_RUN_ALL(ret,name,args_decl,args_use,ok,decline) \
+AP_IMPLEMENT_RHOOK_BASE(name) \
+API_EXPORT(ret) ap_run_##name args_decl \
+    { \
+    LINK_##name *pHook; \
+    int n; \
+    ret rv; \
+\
+    if(!r->_hooks.link_##name) \
+	return ok; \
+\
+    pHook=(LINK_##name *)r->_hooks.link_##name->elts; \
+    for(n=0 ; n < r->_hooks.link_##name->nelts ; ++n) \
+	{ \
+	rv=pHook[n].pFunc args_use; \
+\
+	if(rv != ok && rv != decline) \
+	    return rv; \
+	} \
+    return ok; \
+    }
+
+#define AP_IMPLEMENT_RHOOK_RUN_FIRST(ret,name,args_decl,args_use,decline) \
+AP_IMPLEMENT_RHOOK_BASE(name) \
+API_EXPORT(ret) ap_run_##name args_decl \
+    { \
+    LINK_##name *pHook; \
+    int n; \
+    ret rv; \
+\
+    if(!r->_hooks.link_##name) \
+	return decline; \
+\
+    pHook=(LINK_##name *)r->_hooks.link_##name->elts; \
+    for(n=0 ; n < r->_hooks.link_##name->nelts ; ++n) \
+	{ \
+	rv=pHook[n].pFunc args_use; \
+\
+	if(rv != decline) \
+	    return rv; \
+	} \
+    return decline; \
+    }
+
      /* Hook orderings */
 #define AP_HOOK_REALLY_FIRST	(-10)
 #define AP_HOOK_FIRST		0
 #define AP_HOOK_MIDDLE		10
 #define AP_HOOK_LAST		20
 #define AP_HOOK_REALLY_LAST	30
+
+/* I'm not sure if this is the right place for this or not, but it's a good
+ * starting point.  This is the filter hooks ordering definitions.
+ * The five classes are:
+ *
+ *    content-generators:   
+ *        These are ignored here, because all generators are implemented 
+ *        using handlers.
+ *    content-filter/munger/processor:
+ *        These are things like SSI, PHP, etc.  Anything that is likely
+ *        to change the meaning of the data being sent.
+ *    content-encoding:
+ *        Something like gzip would go here.  Anything that changes the data,
+ *        but not the meaning.
+ *    digest/message processor:
+ *        This would be something like mod_auth_digest.  Anything that
+ *        changes header fields but not the content.
+ *    transport encoding:
+ *        This is where chuncking, SSL belong.  Anything that modifies the
+ *        message for reasons related to the transport layer, but doesn't
+ *        modify the meaning of the body.
+ * 
+ * These filter types are called in the order the are listed above.
+ */
+
+#define AP_HOOK_FILTER          0      /* content-filter/munger/processor */
+#define AP_HOOK_ENCODING       10      /* content-encoding */
+#define AP_HOOK_PROCESSOR      20      /* digest/message processor */
+#define AP_HOOK_TRANSPORT      30      /* transport-encoding */
 
 extern API_VAR_EXPORT ap_pool_t *ap_global_hook_pool;
 extern API_VAR_EXPORT int ap_debug_module_hooks;
Index: include/http_main.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/http_main.h,v
retrieving revision 1.10
diff -u -d -b -w -u -r1.10 http_main.h
--- include/http_main.h	2000/05/29 04:17:41	1.10
+++ include/http_main.h	2000/06/27 05:18:45
@@ -59,6 +59,8 @@
 #ifndef APACHE_HTTP_MAIN_H
 #define APACHE_HTTP_MAIN_H
 
+#include "ap_ioblock.h"
+
 /* AP_SERVER_BASEARGS is the command argument list parsed by http_main.c
  * in ap_getopt() format.  Use this for default'ing args that the MPM
  * can safely ignore and pass on from its rewrite_args() handler.
@@ -76,6 +78,13 @@
 extern API_VAR_EXPORT ap_array_header_t *ap_server_pre_read_config;
 extern API_VAR_EXPORT ap_array_header_t *ap_server_post_read_config;
 extern API_VAR_EXPORT ap_array_header_t *ap_server_config_defines;
+
+/* This definately doesn't belong here, but the include headers are all screwed
+ * up when I put it in buff.h.  What I really want to do is to clean out
+ * buff.[ch] and put it there, but that will have to wait, because it is a
+ * big chunk of work.
+ */
+API_EXPORT(int) ap_bwrite_block(request_rec *r, BUFF *fb, ap_ioqueue_t *strs);
 
 #ifdef __cplusplus
 }
Index: include/httpd.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/httpd.h,v
retrieving revision 1.63
diff -u -d -b -w -u -r1.63 httpd.h
--- include/httpd.h	2000/06/24 19:31:41	1.63
+++ include/httpd.h	2000/06/27 05:18:45
@@ -81,6 +81,7 @@
 #include "apr_network_io.h"
 #include "buff.h"
 #include "ap_mmn.h"
+#include "ap_hooks.h"
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -596,6 +597,10 @@
 				 * pointer back to the main request.
 				 */
 
+    AP_RHOOK_STRUCT (
+        AP_RHOOK_LINK(filter)   /* per-request filter I/O hook. */
+    );
+
     /* Info about the request itself... we begin with stuff that only
      * protocol.c should ever touch...
      */
@@ -643,6 +648,7 @@
     */
     int allowed;		/* Allowed methods - for 405, OPTIONS, etc */
 
+    int headers_sent;           /* have the headers been sent yet or not */
     int sent_bodyct;		/* byte count in stream is for body */
     long bytes_sent;		/* body byte count, for easy access */
     ap_time_t mtime;		/* Time the resource was last modified */
Index: main/Makefile.in
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/Makefile.in,v
retrieving revision 1.14
diff -u -d -b -w -u -r1.14 Makefile.in
--- main/Makefile.in	2000/06/22 18:28:06	1.14
+++ main/Makefile.in	2000/06/27 05:18:54
@@ -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 ap_ioblock.c
 
 include $(top_srcdir)/build/ltlib.mk
 
Index: main/buff.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/buff.c,v
retrieving revision 1.54
diff -u -d -b -w -u -r1.54 buff.c
--- main/buff.c	2000/06/17 01:29:29	1.54
+++ main/buff.c	2000/06/27 05:18:54
@@ -57,6 +57,7 @@
  */
 
 #include "ap_config.h"
+#include "ap_ioblock.h"
 #include "httpd.h"
 #include "http_main.h"
 #include "http_log.h"
@@ -1391,3 +1392,65 @@
     ap_bpush_iol(fb, iol);
     return fb;
 }
+
+API_EXPORT(int) ap_bwrite_block(request_rec *r, BUFF *fb, ap_ioqueue_t *strs)
+{ 
+    ap_ioblock_t *dptr;
+    ap_ssize_t bytes_written = 0;
+    ap_ssize_t bytes;
+    int len = 0;
+    int rv = APR_EINIT;
+
+/* When we save unwritten things into the BUFF, we also have to take them
+ * back out when we are called here again.
+ 
+    if (fb->unwritten) {
+        strs = ap_ioqueue_merge(r->unwritten, strs);
+    }
+ */
+
+    for (dptr=strs->head; dptr != strs->tail; dptr = dptr->next) {
+        len += dptr->len;
+    }
+
+    if (len > LARGE_WRITE_THRESHOLD) {
+        for (dptr=strs->head; dptr != NULL; dptr = dptr->next) {
+
+            /* This can be made MUCH cleaner, but first we have to clean
+             * out buff.c.
+             */
+            rv = iol_write(fb->iol, dptr->vec, dptr->len, &bytes);
+            if (rv == APR_SUCCESS) {
+                bytes_written += bytes;
+                continue;
+            }
+            else {
+                fb->saved_errno = rv;
+                if (ap_canonical_error(rv) != APR_EAGAIN) {
+                    doerror(fb, B_WR);
+                }
+                check_first_conn_error(r, "bwrite_block", 0);
+            }
+        }
+    }
+    else {
+        /* obvious optimization, remove the memcpy by just saving aside the
+         * ioqueue that must be written.  For right now, use the current
+         * BUFF code.  This could probably be gotten rid of very quickly,
+         * but I want to post the patch.  I'll remove all of this immediately
+         * after posting.
+        r->unwritten = ap_ioqueue_merge(r->unwritten, strs); 
+         */
+        for (dptr=strs->head; dptr != NULL; dptr = dptr->next) {
+            rv = ap_bwrite(fb, dptr->vec, dptr->len, &bytes);
+            bytes_written += bytes;
+        }
+        return bytes_written;
+    }
+    if (rv == APR_SUCCESS) {
+        return bytes_written;
+    }
+    else return rv;
+}
+
+
Index: main/http_core.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_core.c,v
retrieving revision 1.81
diff -u -d -b -w -u -r1.81 http_core.c
--- main/http_core.c	2000/06/24 17:33:55	1.81
+++ main/http_core.c	2000/06/27 05:18:54
@@ -58,6 +58,7 @@
 
 #define CORE_PRIVATE
 #include "ap_config.h"
+#include "ap_ioblock.h"
 #include "apr_lib.h"
 #include "httpd.h"
 #include "http_config.h"
@@ -108,6 +109,11 @@
 #endif
 #endif /* USE_MMAP_FILES */
 
+#define SET_BYTES_SENT(r) \
+  do { if (r->sent_bodyct) \
+          ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
+  } while (0)
+
 /* Server core module... This module provides support for really basic
  * server operations, including options and commands which control the
  * operation of other modules.  Consider this the bureaucracy module.
@@ -2765,8 +2771,6 @@
 
 	rangestatus = ap_set_byterange(r);
 
-	ap_send_http_header(r);
-	
 	if (!r->header_only) {
 	    if (!rangestatus) {
 		ap_send_fd(fd, r);
@@ -2808,8 +2812,6 @@
 	}
 
 	rangestatus = ap_set_byterange(r);
-	ap_send_http_header(r);
-	
 	if (!r->header_only) {
 	    if (!rangestatus) {
 		ap_send_mmap(mm, r, 0, r->finfo.size);
@@ -2851,6 +2853,29 @@
 static unsigned short core_port(const request_rec *r)
     { return DEFAULT_HTTP_PORT; }
 
+static int core_write(ap_filter_t filter, request_rec *r, ap_ioqueue_t *strs)
+{
+    int n;
+
+    if (r->headers_sent != 1) {
+        /* We haven't sent any headers yet.  We had better do that before
+         * we try to send the body.
+         */
+        ap_send_http_header(r);
+        ap_bflush(r->connection->client);
+    }
+
+
+    n = ap_bwrite_block(r, r->connection->client, strs);
+    SET_BYTES_SENT(r);
+    return n;
+}
+
+static void insert_filter(request_rec *r)
+{
+    ap_rhook_filter(core_write, r, NULL, NULL, AP_HOOK_REALLY_LAST);
+}
+
 static void register_hooks(void)
 {
     ap_hook_post_config(core_post_config,NULL,NULL,AP_HOOK_REALLY_FIRST);
@@ -2863,6 +2888,10 @@
     /* FIXME: I suspect we can eliminate the need for these - Ben */
     ap_hook_type_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
     ap_hook_access_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
+      
+    /* FIXME::::  I think this allows us to remove most of the code from buff. 
+       :-) */
+    ap_hook_insert_filter(insert_filter, NULL, NULL, AP_HOOK_MIDDLE);
 }
 
 API_VAR_EXPORT module core_module = {
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.87
diff -u -d -b -w -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 05:18:55
@@ -65,6 +65,7 @@
 
 #define CORE_PRIVATE
 #include "ap_config.h"
+#include "ap_ioblock.h"
 #include "httpd.h"
 #include "http_config.h"
 #include "http_core.h"
@@ -96,11 +97,10 @@
           ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
   } while (0)
 
-
 /* if this is the first error, then log an INFO message and shut down the
  * connection.
  */
-static void check_first_conn_error(const request_rec *r, const char *operation,
+void check_first_conn_error(const request_rec *r, const char *operation,
                                    ap_status_t status)
 {
     if (!r->connection->aborted) {
@@ -1811,6 +1811,10 @@
              (void *) r, r->headers_out, NULL);
 
     terminate_header(r);
+    /* We have sent the headers once, we don't ever want to try to send them
+     * again.
+     */
+    r->headers_sent = 1;
 
 
     ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
@@ -2406,7 +2410,6 @@
     if (length == 0)
         return 0;
 
-
     length += offset;
     while (!r->connection->aborted && offset < length) {
         if (length - offset > MMAP_SEGMENT_SIZE) {
@@ -2431,107 +2434,75 @@
 
 API_EXPORT(int) ap_rputc(int c, request_rec *r)
 {
-    if (r->connection->aborted)
-        return EOF;
-
-    if (ap_bputc(c, r->connection->client) < 0) {
-        check_first_conn_error(r, "rputc", 0);
-        return EOF;
-    }
-    SET_BYTES_SENT(r);
-    return c;
+    char *ch;
+    SETUP_FILTERS
+    ch = ap_palloc(r->pool, 1);
+    ch[0] = c;
+    NEXT_FILTER(filters, r, ap_create_ioqueue(r->pool, ch, 1)); 
 }
 
 API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
 {
-    int rcode;
-
-    if (r->connection->aborted)
-        return EOF;
-    
-    rcode = ap_bputs(str, r->connection->client);
-    if (rcode < 0) {
-        check_first_conn_error(r, "rputs", 0);
-        return EOF;
-    }
-    SET_BYTES_SENT(r);
-    return rcode;
+    SETUP_FILTERS
+    NEXT_FILTER(filters, r, ap_create_ioqueue(r->pool, str, strlen(str))); 
 }
 
 API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
 {
-    ap_ssize_t n;
-    ap_status_t rv;
-
-    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;
-    }
-    SET_BYTES_SENT(r);
-    return n;
+    SETUP_FILTERS
+    NEXT_FILTER(filters, r, ap_create_ioqueue(r->pool, buf, nbyte)); 
 }
 
 API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
 {
-    int n;
-
-    if (r->connection->aborted)
-        return EOF;
+    char *str;
 
-    n = ap_vbprintf(r->connection->client, fmt, va);
+    SETUP_FILTERS
+    str = ap_pvsprintf(r->pool, fmt, va);
 
-    if (n < 0) {
-        check_first_conn_error(r, "vrprintf", 0);
-        return EOF;
-    }
-    SET_BYTES_SENT(r);
-    return n;
+    NEXT_FILTER(filters, r, ap_create_ioqueue(r->pool, str, strlen(str))); 
 }
 
 API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
 {
     va_list va;
     int n;
+    char *str;
 
-    if (r->connection->aborted)
-        return EOF;
+    SETUP_FILTERS
 
     va_start(va, fmt);
-    n = ap_vbprintf(r->connection->client, fmt, va);
-    va_end(va);
+    str = ap_pvsprintf(r->pool, fmt, va);
+    va_end(vlist);
 
-    if (n < 0) {
-        check_first_conn_error(r, "rprintf", 0);
-        return EOF;
-    }
-    SET_BYTES_SENT(r);
-    return n;
+    n = strlen(str);
+
+    NEXT_FILTER(filters, r, ap_create_ioqueue(r->pool, str, n)); 
 }
 
 API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r, ...)
 {
-    va_list va;
-    int n;
-
-    if (r->connection->aborted)
-        return EOF;
+    va_list args;
+    int j, k;
+    const char *x;
+    ap_ioqueue_t *strs = NULL;
 
-    va_start(va, r);
-    n = ap_vbputstrs(r->connection->client, va);
-    va_end(va);
+    SETUP_FILTERS
 
-    if (n < 0) {
-        check_first_conn_error(r, "rvputs", 0);
-        return EOF;
+    va_start(args, r);
+    for (k = 0;;) {
+        x = va_arg(args, const char *);
+        if (x == NULL)
+            break;
+        j = strlen(x);
+        if (k == 0) {
+            strs = ap_create_ioqueue(r->pool, x, j);
     }
-
-    SET_BYTES_SENT(r);
-    return n;
+        else {
+            strs = ap_ioblock_enqueue(strs, ap_create_ioblock(r->pool, x, j));
+        }
+    }
+    NEXT_FILTER(filters, r, strs); 
 }
 
 API_EXPORT(int) ap_rflush(request_rec *r)
@@ -2597,7 +2568,6 @@
     }
 
     if (status == HTTP_NO_CONTENT) {
-        ap_send_http_header(r);
         ap_finalize_request_protocol(r);
         return;
     }
@@ -2634,7 +2604,6 @@
             ap_table_setn(r->headers_out, "Allow", make_allow(r));
         }
 
-        ap_send_http_header(r);
 
         if (r->header_only) {
             ap_finalize_request_protocol(r);
@@ -2942,3 +2911,4 @@
                             (const request_rec *r),(r),NULL)
 AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
                             (const request_rec *r),(r),0)
+AP_IMPLEMENT_RHOOK_BASE(filter)
Index: main/http_request.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_request.c,v
retrieving revision 1.35
diff -u -d -b -w -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 05:18:56
@@ -68,6 +68,7 @@
 
 #define CORE_PRIVATE
 #include "ap_config.h"
+#include "ap_hooks.h"
 #include "httpd.h"
 #include "http_config.h"
 #include "http_request.h"


-------------
ap_ioblock.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_ioblock.h"

ap_ioblock_t *ap_create_ioblock(ap_pool_t *p, const void *v, int length)
{
    ap_ioblock_t *vector;

    vector = ap_pcalloc(p, sizeof(*vector));
    vector->vec = (void *)v;
    vector->len = length;
    vector->bitmask = AP_NOT_WRITABLE;

    return vector;
}

ap_ioqueue_t *ap_start_ioqueue(ap_ioblock_t *b)
{
    ap_ioqueue_t *q;

    q = ap_pcalloc(b->p, sizeof(*q));
    q->p = b->p;
    q->head = b; 
    b->prev = NULL;
    b->queue = q; 
    while (b->next != NULL) {
        b = b->next;
    }
    q->tail = b;

    return q;
}

ap_ioqueue_t *ap_create_ioqueue(ap_pool_t *p, const void *v, int length)
{
    ap_ioqueue_t *q;

    q = ap_pcalloc(p, sizeof(*q));
    q->p = p;
    q->head = q->tail = ap_create_ioblock(q->p, v, length);
    q->head->queue = q;

    return q;
}

ap_status_t ap_split_ioblock(ap_ioblock_t *vec, int offset)
{
    ap_ioblock_t *vector;

    vector = ap_pcalloc(vec->p, sizeof(*vector));
    vector->p = vec->p;

    vector->len = vec->len - offset; 
    vec->len = offset;
    vector->vec = vec->vec + offset;

    vector->queue = vec->queue;
    vector->queue->tail = vector;
    
    vector->next = vec->next;
    vector->prev = vec;
    vec->next = vector;
    vector->bitmask = vec->bitmask;
    return APR_SUCCESS;
}

ap_ioblock_t *ap_add_ioblock(ap_ioblock_t *vec, const void *v, int length, 
                           int front)
{
    ap_ioblock_t *vector = ap_create_ioblock(vec->p, v, length);
    ap_ioblock_t *dptr = vec;

    vector->queue = vec->queue;

    if (front) {
        vector->next = vec;
        vec->prev = vector;
        return vector; 
    }
    while (dptr->next) {
        dptr = dptr->next;
    }
    dptr->next = vector;
    vector->prev = dptr; 
    return vec;
}

ap_status_t ap_set_ioblock_writable(ap_ioblock_t *vec)
{
    if (vec->bitmask == AP_WRITABLE) {
        return APR_SUCCESS;
    }
    if (vec->bitmask == AP_NOT_WRITABLE) {
        void *v = ap_palloc(vec->p, vec->len);
        memcpy(v, vec->vec, vec->len);
        vec->vec = v;
        return APR_SUCCESS;
    }
    else return APR_ENOTIMPL;
}

ap_ioqueue_t *ap_ioblock_enqueue(ap_ioqueue_t *vec, ap_ioblock_t *toadd) 
{
    vec->tail->next = toadd;
    toadd->prev = vec->tail;
    vec->tail = toadd;
    return vec;
}

ap_ioqueue_t *ap_ioqueue_merge(ap_ioqueue_t *vec, ap_ioqueue_t *toadd) 
{
    if (!vec) {
        return toadd;
    }
    if (!toadd) {
        return vec;
    }
    vec->tail->next = toadd->head;
    toadd->head->prev = vec->tail;
    vec->tail = toadd->tail;
    return vec;
}

------------
ap_ioblock.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_IOBLOCK_H
#define AP_IOBLOCK_H

#include "apr_pools.h"
#include "apr_file_io.h"
#include "httpd.h"
#include "ap_hooks.h"
#include "ap_config.h"

#define AP_WRITABLE            1
#define AP_NOT_WRITABLE        2
#define AP_FILE_DESC           4

struct ap_ioblock_t {
    ap_pool_t *p;
    void *vec;
    int len;
    ap_file_t *fd;
    int bitmask;
    struct ap_ioblock_t *next;
    struct ap_ioblock_t *prev;
    struct ap_ioqueue_t *queue;
};

struct ap_ioqueue_t {
    ap_pool_t *p;
    void *ctx;
    struct ap_ioblock_t *head;
    struct ap_ioblock_t *tail;
};

struct ap_filter_t {
    ap_array_header_t *filter_list;
    int current_filter;
};

typedef struct ap_ioblock_t ap_ioblock_t;
typedef struct ap_ioqueue_t ap_ioqueue_t;
typedef struct ap_filter_t  ap_filter_t;

AP_DECLARE_RHOOK(int, filter, \
                (ap_filter_t filter, request_rec *r, ap_ioqueue_t *strs))

#define SETUP_FILTERS \
    ap_filter_t filters; \
    \
    if (r->connection->aborted) \
        return EOF; \
    \
    filters.filter_list = r->_hooks.link_filter; \
    filters.current_filter = 0;

#define NEXT_FILTER(filter, r, iob) \
    { \
        LINK_filter *hook; \
        int ret; \
        \
        if (r->connection->aborted) { \
            return APR_EOF; \
        } \
        hook = (LINK_filter *)r->_hooks.link_filter->elts; \
        ret = hook[filter.current_filter++].pFunc(filter, r, iob); \
        filter.current_filter--; \
        return ret; \
    }

#define SETUP_HOLDER(name, module_name) \
    holder_##name *holder = (holder_##name *) ap_palloc(r->pool, \
                             sizeof(holder_##name)); \
    holder->holding = NULL; \
    ap_set_module_config(r->request_config, &module_name, holder); 


#define FILTER_HOLD(name) \
    typedef struct holder_##name { \
        ap_ioqueue_t *holding; \
    } holder_##name

#define FETCH_FROM_HOLD(name, module_name, strs) \
    holder_##name *hold_spot = (holder_##name *) \
                     ap_get_module_config(r->request_config, &module_name); \
    if (hold_spot != NULL) { \
        strs = ap_ioqueue_merge(strs, hold_spot->holding); \
    }

#define HOLD_ONTO(name, module_name, strs) \
    if (strs->head) { \
        hold_spot->holding = strs; \
    } \
    else { \
        hold_spot->holding = NULL; \
    } \
    ap_set_module_config(r->request_config, &module_name, hold_spot);

API_EXPORT(ap_ioblock_t *) ap_create_ioblock(ap_pool_t *p, const void *v, 
                                           int length); 
API_EXPORT(ap_status_t) ap_split_ioblock(ap_ioblock_t *vec, int offset);
API_EXPORT(ap_ioblock_t *) ap_add_ioblock(ap_ioblock_t *vec, const void *v, 
                                        int length, int front);
API_EXPORT(ap_status_t) ap_set_ioblock_writable(ap_ioblock_t *vec);

API_EXPORT(ap_ioqueue_t *) ap_start_ioqueue(ap_ioblock_t *b); 
API_EXPORT(ap_ioqueue_t *) ap_create_ioqueue(ap_pool_t *p, const void *v, 
                                           int length); 
API_EXPORT(ap_ioqueue_t *) ap_ioblock_enqueue(ap_ioqueue_t *vec, ap_ioblock_t *toadd);
API_EXPORT(ap_ioqueue_t *) ap_ioqueue_merge(ap_ioqueue_t *vec, ap_ioqueue_t *toadd);

#endif

---------------
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/>.
 *
 * Portions of this software are based upon public domain software
 * originally written at the National Center for Supercomputing Applications,
 * University of Illinois, Urbana-Champaign.
 */

#include "httpd.h"
#include "ap_hooks.h"
#include "ap_ioblock.h"
#include "http_config.h"
#include "http_request.h"
#include "http_protocol.h"

module MODULE_VAR_EXPORT gargle_module;
FILTER_HOLD(gargle);


#define SWAP_THEM(dptr, temp) \
            temp->prev = dptr->prev; \
            dptr->next = temp->next; \
            temp->next = dptr; \
            dptr->prev->next = temp; \
            dptr->prev = temp;

typedef struct gargle_rec {
    int on;
} gargle_rec;

int gargle_filter(ap_filter_t filter, request_rec *r, ap_ioqueue_t *strs);

static void *create_gargle_config(ap_pool_t *p, char *dummy)
{
    gargle_rec *new = (gargle_rec *) ap_pcalloc(p, sizeof(gargle_rec));
    
    new->on = 0;
    return (void *)new;
}

int gargle_filter(ap_filter_t filter, request_rec *r, ap_ioqueue_t *strs)
{
    ap_ioblock_t *dptr = strs->head;
    ap_ioblock_t *temp = NULL;
    ap_ioqueue_t *hold = NULL;
    int curr_pos = 10;

    FETCH_FROM_HOLD(gargle, gargle_module, strs);

    while (dptr != NULL) {
        if (dptr->len > curr_pos) {

            ap_split_ioblock(dptr, curr_pos - 1);
            dptr = dptr->next;
            ap_split_ioblock(dptr, 1);
            ap_split_ioblock(dptr->next, 1);
         
            temp = dptr->next;
            SWAP_THEM(dptr, temp);
        }
        else {
            temp = dptr->next;
            dptr->next = NULL;
            if (temp) {
                temp->prev = NULL;
                hold = ap_start_ioqueue(temp);
                HOLD_ONTO(gargle, gargle_module, hold);
            }
        }    
        dptr = dptr->next;
    }
    NEXT_FILTER(filter, r, strs);
}

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

    cfg->on = atoi(arg);

    return NULL;
}

static const command_rec gargle_cmds[] = {
    AP_INIT_TAKE1("GargleIt", gargle_on, NULL, ACCESS_CONF, 
     "should this request be gargled"),
    {NULL}
}; 

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

    if (cfg->on) {
        SETUP_HOLDER(gargle, gargle_module);
        ap_rhook_filter(gargle_filter, r, NULL, NULL, AP_HOOK_FILTER);
    }
}

static void register_hooks(void)
{
    ap_hook_insert_filter(gargle_insert_filter, NULL, NULL, AP_HOOK_FILTER);
}

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


Ryan

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


Re: [PATCH] Final filtering patch for a few days.

Posted by Greg Stein <gs...@lyra.org>.
On Mon, Jun 26, 2000 at 10:31:03PM -0700, rbb@covalent.net wrote:
> As I said in my response to Roy, I am posting this because there are
> useful things in it.  There are also horrible things in it.  This is NOT
> the correct patch.

There are still issues with this patch, but since you aren't actually
submitting it for approval, I'll defer a specific review.

>...
> These sets of patches have also made me hate Greg at times.  :-)  Greg,
> I hope you realize although I can hate you while reading your e-mail,
> off-line I hold you in very high regards as a coder and person, and I
> meant no offense at any time during this debate.

No worries... no offense taken. We are discussing code -- that is a far cry
from being personal :-). At no time, have I figured that you were calling me
a dickhead :-)  [hey, maybe you were, and I am too dense to notice...]

And likewise: I intend no offense, and I truly appreciate all of your Apache
work (APR!). You've done some great work, and have really made some
outstanding contributions.

Cheers,
-g

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