httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r..@covalent.net
Subject PLEASE READ: Filter I/O
Date Wed, 21 Jun 2000 21:52:18 GMT

I am including in this e-mail an up to date patch of the filtered I/O
logic.  This is the same patch as was sent out last week, but it compiles
and builds against the current HEAD.

These are the current issues with the patch, and my response to them.  I
do not see any of the current outstanding issues as a reason this patch
can not be committed.

1)  complexity due to ioblock/ioqueue stuff
A)  I believe that any filtering scheme requires something like
this.  This scheme allows us to handle sendfile implementations
cleanly.  It also makes the buffering logic a part of the layer, which is
good IMHO.  Finally, having this logic lets the core maintain the state of
the filters.  As far as having a context maintained by the module, the
current scheme allows for this, because each ioqueue has a pool which by
definition has user_data.

2)  Sub requests and how they are joined
A)  Sub requests in Apache do not return a stream of data to the original
request, they write out directly to the same buff structure.  By
definition, sub requests will share the iofilters, so this works the same
way in all designs for iofilters.

3)  If I fetch 100MB from a database for insertion into the content
stream, how does that work in this scheme
A)  The 100MB is passed on to the next filter until it reaches the bottom
filter, and then it is sent to the network.  The hook scheme can allow for
a configuration directive that would throttle how much of the 100MB is
passed to subsequent filters at any one time.  Using sibling pools, this
would allow for early cleanup of some of the memory (although as I will
describe later I think this is a bad idea).  Regardless, both of the
current schemes require the same working set size, because the 100MB is
allocated on the heap and it isn't cleared until the request is
done.  There is a minimal amount of data that can be allocated on the
stack in a link based scheme, however as I will describe later, I think
this is a bad idea for optimization reasons.

4)  Flow control
A)  Flow control can be controlled with configuration directives, by not
allowing all of a chunk to be passed to subsequent filters.  Quite
honestly, this is one place where this design does flounder a bit, but
with all of the other optimizations that can be added on top of this
design, I think this is ok.

5)  Working set size
A)  See answer 3

Future Optimizations:

There are some pretty obvious optimizations that can be added to this
scheme.  I will detail but a few of them which make the most sense to me.

1)  Buffering becomes a part of the layering.  We no longer need buffering
code, because the ioqueue does the buffering for us.  If we haven't got
enough data to make it worth our while to send to the network, we just
move the ioqueue off to the side for a while until there is more data to
send.

2)  Caching.  Because this scheme really forces people to allocate out of
the heap, we can just move the whole ioqueue off to the side and use it
again easily.  For example, say we have mod_stock_price, which always
returns the same data for 20 minute blocks.  If mod_cache is installed, it
is a simple thing to just save the ioqueue off to the side in mod_cache
and skip mod_stock_price everytime it is called in the next 20
minutes.  With a scheme that allows allocation off the stack, we have to
copy everything off to the side (onto the heap) before we can cache it.

3)  Sendfile.  With the latest incarnation of this patch, it is easy to
optimize to use sendfile whenever possible.  For example, say
mod_file_cache wants to add file_foo.txt to the output data, it can just
do it with ap_sendfile_ioblock(), which mmaps the file, and throws it into
the ioqueue.  Then, mod_munge modifies the data after the 500 byte.  With
a VERY quick scan of the ioblock, we can see that it is possible to use
ap_sendfile for the first 500 bytes.  If mod_munge only modifies 10 bytes,
so that we have a 500 byte block of original data, a 10 byte block of
modified data, and then another block of original data, then we could use
ap_sendfile for the two blocks of original data.  The link-based scheme
does not maintain enough information to do this sort of thing.

I realize that this patch isn't perfect, but it is a start.  The sooner we
get real code into the server that people can play with, the sooner the
code can be made perfect.

Are there are more outstanding technical arguments against this patch?

Ryan


Index: src/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
--- src/include/ap_hooks.h	2000/05/27 22:53:46	1.20
+++ src/include/ap_hooks.h	2000/06/21 18:20:41
@@ -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: src/include/http_request.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/http_request.h,v
retrieving revision 1.11
diff -u -d -b -w -u -r1.11 http_request.h
--- src/include/http_request.h	2000/05/27 22:53:47	1.11
+++ src/include/http_request.h	2000/06/21 18:20:41
@@ -120,6 +120,7 @@
 AP_DECLARE_HOOK(int,type_checker,(request_rec *))
 AP_DECLARE_HOOK(int,access_checker,(request_rec *))
 AP_DECLARE_HOOK(int,auth_checker,(request_rec *))
+AP_DECLARE_HOOK(void,insert_filter,(request_rec *))
 
 #ifdef __cplusplus
 }
Index: src/include/httpd.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/httpd.h,v
retrieving revision 1.61
diff -u -d -b -w -u -r1.61 httpd.h
--- src/include/httpd.h	2000/06/21 14:34:41	1.61
+++ src/include/httpd.h	2000/06/21 18:20:41
@@ -81,6 +81,8 @@
 #include "apr_network_io.h"
 #include "buff.h"
 #include "ap_mmn.h"
+#include "ap_hooks.h"
+#include "ap_ioblock.h"
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -598,6 +600,8 @@
     const char *short_name;
 };
 
+AP_DECLARE_RHOOK(ap_ioqueue_t *, filter, (request_rec *r, ap_ioqueue_t *strs));
+
 struct request_rec {
 
     ap_pool_t *pool;
@@ -615,6 +619,12 @@
 				 * pointer back to the main request.
 				 */
 
+    AP_RHOOK_STRUCT (
+        AP_RHOOK_LINK(filter)   /* per-request filter I/O hook. */
+    );
+    ap_ioqueue_t **unfiltered_data;	/* array of (ap_ioqueue_t *) that has
+                                         * not been filtered for */
+
     /* Info about the request itself... we begin with stuff that only
      * protocol.c should ever touch...
      */
@@ -662,6 +672,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: src/main/Makefile.in
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/Makefile.in,v
retrieving revision 1.13
diff -u -d -b -w -u -r1.13 Makefile.in
--- src/main/Makefile.in	2000/06/17 16:29:47	1.13
+++ src/main/Makefile.in	2000/06/21 18:21:05
@@ -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: src/main/http_core.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_core.c,v
retrieving revision 1.80
diff -u -d -b -w -u -r1.80 http_core.c
--- src/main/http_core.c	2000/06/20 04:15:00	1.80
+++ src/main/http_core.c	2000/06/21 18:21:05
@@ -2765,8 +2765,8 @@
 
 	rangestatus = ap_set_byterange(r);
 
-	ap_send_http_header(r);
-	
+/*	ap_send_http_header(r);
+*/	
 	if (!r->header_only) {
 	    if (!rangestatus) {
 		ap_send_fd(fd, r);
@@ -2808,8 +2808,8 @@
 	}
 
 	rangestatus = ap_set_byterange(r);
-	ap_send_http_header(r);
-	
+	/*ap_send_http_header(r);
+	*/
 	if (!r->header_only) {
 	    if (!rangestatus) {
 		ap_send_mmap(mm, r, 0, r->finfo.size);
Index: src/main/http_protocol.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.83
diff -u -d -b -w -u -r1.83 http_protocol.c
--- src/main/http_protocol.c	2000/06/20 04:22:39	1.83
+++ src/main/http_protocol.c	2000/06/21 18:21:05
@@ -96,6 +96,86 @@
           ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
   } while (0)
 
+static int filter_io(request_rec *r, ap_ioqueue_t *iov)
+{
+    ap_ioblock_t *dptr = iov->head;
+    ap_ioqueue_t *unfiltered;
+#if 0
+    /* see below */
+    ap_size_t totlen = 0;
+#endif
+    int i, n;
+    int totsent = 0;
+    int rv;
+    int num_filters = r->_hooks.link_filter->nelts;
+
+    /* This should probably be done someplace else, but this will work for now.
+     */
+    if (r->unfiltered_data == NULL) {
+        r->unfiltered_data = ap_pcalloc(r->pool, num_filters * 
+                                           sizeof(ap_ioqueue_t *));
+    }
+    
+    for (i = 0; i < r->_hooks.link_filter->nelts; ++i) {
+        LINK_filter *hook;
+#if 0
+    /* Limit the amount of data sent to filters.  This logic needs to ensure
+     * we don't send too little or too much information.  I expect this will
+     * be added in the not too distant future.  Like days after the original
+     * filtered I/O patch.   rbb
+     */
+        while (dptr != iov->tail && (totlen < MAX_STRING_LEN)) {
+            totlen += dptr->len;
+            dptr = dptr->next;
+        }
+#endif
+
+        if (r->unfiltered_data[i] != NULL) {
+            ap_ioqueue_merge(r->unfiltered_data[i], iov);
+            iov = r->unfiltered_data[i];
+            r->unfiltered_data[i] = NULL;
+        }    
+
+        hook = (LINK_filter *)r->_hooks.link_filter->elts;
+        unfiltered = hook[i].pFunc(r, iov);
+
+        /* unfiltered is the data that the filter didn't modify for one reason
+         * or another.  This has not been tested, but the theory is sound.
+         * I'll test this after I send the patch.  I expect to have this fully
+         * tested either before the commit or just after.
+         */
+        if (unfiltered != NULL) {
+            r->unfiltered_data[i] = unfiltered;
+        }
+    }
+
+    dptr = iov->head;
+
+    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);
+    }
+
+    while (dptr != NULL) {
+        rv = ap_bwrite(r->connection->client, dptr->vec, dptr->len, &n);
+        if (n < 0) {
+            if (!r->connection->aborted) {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
+                    "client stopped connection before rwrite completed");
+                ap_bsetflag(r->connection->client, B_EOUT, 1);
+                r->connection->aborted = 1;
+            }
+            return EOF;
+        }
+        totsent += n;
+        dptr = dptr->next;
+    }
+    SET_BYTES_SENT(r);
+    return totsent;
+}
+
 /*
  * Builds the content-type that should be sent to the client from the
  * content-type specified.  The following rules are followed:
@@ -1394,7 +1474,8 @@
 API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
     const char *fieldname, const char *fieldval)
 {
-    return (0 < ap_rvputs(r, fieldname, ": ", fieldval, CRLF, NULL));
+    return (0 < ap_bvputs(r->connection->client, fieldname, ": ", fieldval, 
+                          CRLF, NULL));
 }
 
 API_EXPORT(void) ap_basic_http_header(request_rec *r)
@@ -1428,7 +1509,7 @@
 
     /* Output the HTTP/1.x Status-Line and the Date and Server fields */
 
-    ap_rvputs(r, protocol, " ", r->status_line, CRLF, NULL);
+    ap_bvputs(r->connection->client, protocol, " ", r->status_line, CRLF, NULL);
 
     date = ap_palloc(r->pool, AP_RFC822_DATE_LEN);
     ap_rfc822_date(date, r->request_time);
@@ -1465,9 +1546,9 @@
 
     ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
     if (bs >= 255 && bs <= 257)
-        ap_rputs("X-Pad: avoid browser bug" CRLF, r);
+        ap_bputs("X-Pad: avoid browser bug" CRLF, r->connection->client);
 
-    ap_rputs(CRLF, r);  /* Send the terminating empty line */
+    ap_bputs(CRLF, r->connection->client); /* Send the terminating empty line */
 }
 
 /* Build the Allow field-value from the request handler method mask.
@@ -1734,6 +1815,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);
@@ -2148,23 +2233,6 @@
     return OK;
 }
 
-/* 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,
-                                   ap_status_t status)
-{
-    if (!r->connection->aborted) {
-        if (status == 0)
-            status = ap_berror(r->connection->client);
-        ap_log_rerror(APLOG_MARK, APLOG_INFO, status, r,
-                      "client stopped connection before %s completed",
-                      operation);
-        ap_bsetflag(r->connection->client, B_EOUT, 1);
-        r->connection->aborted = 1;
-    }
-}
-
 /*
  * Send the body of a response to the client.
  */
@@ -2233,7 +2301,7 @@
         o = 0;
 
         while (n && !r->connection->aborted) {
-            rv = ap_bwrite(r->connection->client, &buf[o], n, &w);
+            w = ap_rwrite(&buf[o], n, r);
             if (w > 0) {
                 total_bytes_sent += w;
                 n -= w;
@@ -2354,13 +2422,11 @@
     size_t total_bytes_sent = 0;
     int n;
     ap_ssize_t w;
-    ap_status_t rv;
     char *addr;
     
     if (length == 0)
         return 0;
 
-
     length += offset;
     while (!r->connection->aborted && offset < length) {
         if (length - offset > MMAP_SEGMENT_SIZE) {
@@ -2372,22 +2438,12 @@
 
         while (n && !r->connection->aborted) {
             ap_mmap_offset((void**)&addr, mm, offset);
-            rv = ap_bwrite(r->connection->client, addr, n, &w);
+            w = ap_rwrite(addr, n, r);
             if (w > 0) {
                 total_bytes_sent += w;
                 n -= w;
                 offset += w;
             }
-            else if (rv != APR_SUCCESS) {
-                if (r->connection->aborted)
-                    break;
-                else if (ap_canonical_error(rv) == APR_EAGAIN)
-                    continue;
-                else {
-                    check_first_conn_error(r, "send-mmap", rv);
-                    break;
-                }
-            }
         }
     }
 
@@ -2401,6 +2457,8 @@
     if (r->connection->aborted)
         return EOF;
 
+    filter_io(r, ap_create_ioqueue(r->pool, (const char *)&c, 1));
+
     if (ap_bputc(c, r->connection->client) < 0) {
         check_first_conn_error(r, "rputc", 0);
         return EOF;
@@ -2412,10 +2470,14 @@
 API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
 {
     int rcode;
+    int len;
 
     if (r->connection->aborted)
         return EOF;
     
+    len = strlen(str);
+    filter_io(r, ap_create_ioqueue(r->pool, str, len));
+
     rcode = ap_bputs(str, r->connection->client);
     if (rcode < 0) {
         check_first_conn_error(r, "rputs", 0);
@@ -2427,77 +2489,66 @@
 
 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;
 
-    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;
+    return filter_io(r, ap_create_ioqueue(r->pool, buf, nbyte));
 }
 
 API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
 {
-    int n;
+    int len;
+    char *str;
 
     if (r->connection->aborted)
         return EOF;
 
-    n = ap_vbprintf(r->connection->client, fmt, va);
-
-    if (n < 0) {
-        check_first_conn_error(r, "vrprintf", 0);
-        return EOF;
-    }
-    SET_BYTES_SENT(r);
-    return n;
+    str = ap_pvsprintf(r->pool, fmt, va);    
+    len = strlen(str);
+    return filter_io(r, ap_create_ioqueue(r->pool, str, len));
 }
 
 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;
 
     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);
+    return filter_io(r, ap_create_ioqueue(r->pool, str, n));
 }
 
 API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r, ...)
 {
-    va_list va;
-    int n;
+    va_list args;
+    int i = 0;
+    int j, k;
+    const char *x;
 
     if (r->connection->aborted)
         return EOF;
 
-    va_start(va, r);
-    n = ap_vbputstrs(r->connection->client, va);
-    va_end(va);
+    va_start(args, r);
+    for (k = 0;;) {
+        x = va_arg(args, const char *);
+        if (x == NULL)
+            break;
+        j = strlen(x);
 
-    if (n < 0) {
-        check_first_conn_error(r, "rvputs", 0);
-        return EOF;
+        i = filter_io(r, ap_create_ioqueue(r->pool, x, j));
+
+        k += i;
     }
 
     SET_BYTES_SENT(r);
-    return n;
+    return i;
 }
 
 API_EXPORT(int) ap_rflush(request_rec *r)
@@ -2563,7 +2614,7 @@
     }
 
     if (status == HTTP_NO_CONTENT) {
-        ap_send_http_header(r);
+        /*ap_send_http_header(r);*/
         ap_finalize_request_protocol(r);
         return;
     }
@@ -2598,7 +2649,7 @@
         if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
             ap_table_setn(r->headers_out, "Allow", make_allow(r));
 
-        ap_send_http_header(r);
+        /*ap_send_http_header(r);*/
 
         if (r->header_only) {
             ap_finalize_request_protocol(r);
@@ -2906,3 +2957,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: src/main/http_request.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_request.c,v
retrieving revision 1.33
diff -u -d -b -w -u -r1.33 http_request.c
--- src/main/http_request.c	2000/06/17 16:29:48	1.33
+++ src/main/http_request.c	2000/06/21 18:21:05
@@ -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"
@@ -86,6 +87,7 @@
 	    AP_HOOK_LINK(type_checker)
 	    AP_HOOK_LINK(access_checker)
 	    AP_HOOK_LINK(auth_checker)
+	    AP_HOOK_LINK(insert_filter)
 )
 
 AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
@@ -100,6 +102,7 @@
                           (request_rec *r),(r),OK,DECLINED)
 AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
                             (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
 
 /*****************************************************************
  *
@@ -1246,6 +1249,15 @@
         ap_die(access_status, r);
         return;
     }
+
+    /* The new insert_filter stage makes sense here IMHO.  We are sure that
+     * we are going to run the request now, so we may as well insert filters
+     * if any are available.  Since the goal of this phase is to allow all
+     * modules to insert a filter if they want to, this filter returns
+     * void.  I just can't see any way that this filter can reasonably
+     * fail, either your modules inserts something or it doesn't.  rbb
+     */
+    ap_run_insert_filter(r);
 
     if ((access_status = ap_invoke_handler(r)) != 0) {
         ap_die(access_status, r);

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/>.
 *
 * This file was originally written by Ryan Bloom for Covalent Technologies
 * and gifted exclusively to the The Apache Software Foundation in June 2000.
 */

#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;
    
    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);

    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->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;
    
    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) 
{
    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/>.
 *
 * This file was originally written by Ryan Bloom for Covalent Technologies
 * and gifted exclusively to the The Apache Software Foundation in June 2000.
 */

#ifndef AP_IOBLOCK_H
#define AP_IOBLOCK_H

#include "apr_pools.h"
#include "apr_file_io.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 {
    ap_pool_t *p;
    struct ap_ioblock_t *head;
    struct ap_ioblock_t *tail;
};

typedef struct ap_ioblock_t ap_ioblock_t;
typedef struct ap_ioqueue_t ap_ioqueue_t;

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

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




Mime
View raw message