]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Implement input filtering. This is definately not completely correct, but
authorRyan Bloom <rbb@apache.org>
Mon, 2 Oct 2000 00:47:28 +0000 (00:47 +0000)
committerRyan Bloom <rbb@apache.org>
Mon, 2 Oct 2000 00:47:28 +0000 (00:47 +0000)
it is a good first step.  It is possible to add filters when reading from
the client with this change.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86359 13f79535-47bb-0310-9956-ffa450edef68

include/httpd.h
include/util_filter.h
modules/http/http_core.c
modules/http/http_protocol.c
server/connection.c
server/util_filter.c

index c422b01da68f836314be2e4a1872f2e5ee0c0394..f222e0d4baaa93140ffa9098baf1e06b22f6ac16 100644 (file)
@@ -77,6 +77,7 @@ extern "C" {
 
 /* Headers in which EVERYONE has an interest... */
 #include "ap_config.h"
+#include "ap_buckets.h"
 #include "os.h"
 #include "apr_general.h"
 #include "apr_lib.h"
@@ -881,6 +882,9 @@ struct conn_rec {
     /** A list of input filters to be used for this request 
      *  @defvar ap_filter_t *filters */
     struct ap_filter_t *input_filters;
+    /** Location to store data read from the client.
+     *  @defvar ap_bucket_brigade *input_data */
+    struct ap_bucket_brigade *input_data;
 };
 
 /* Per-vhost config... */
index 2f0567203ae530c2e7045bc05d32af4a5f5097a9..41b4cd914ccab4a1bf349f31d7dcd9f3c1314bad 100644 (file)
@@ -72,6 +72,7 @@ extern "C" {
  */
 
 #define AP_NOBODY_WROTE         -1;
+#define AP_NOBODY_READ          -2;
 
 /*
  * FILTER CHAIN
@@ -215,9 +216,14 @@ struct ap_filter_t {
 
     /** The request_rec associated with the current filter.  If a sub-request
      *  adds filters, then the sub-request is the request associated with the
-     * filter.
+     *  filter.
      */
     request_rec *r;
+
+    /** The conn_rec associated with the current filter.  This is analogous
+     *  to the request_rec, except that it is used for input filtering.
+     */
+    conn_rec *c;
 };
 
 /* This function just passes the current bucket brigade down to the next
@@ -230,6 +236,17 @@ struct ap_filter_t {
  * current request.  I would just rather it didn't take out the whole child
  * process.  
  */
+/**
+ * Get the current bucket brigade from the next filter on the filter
+ * stack.  The filter should return an apr_status_t value.  If the bottom-most 
+ * filter doesn't write to the network, then AP_NOBODY_WROTE is returned.
+ * @param filter The next filter in the chain
+ * @param bucket The current bucket brigade
+ * @return apr_status_t value
+ * @deffunc apr_status_t ap_get_brigade(ap_filter_t *filter, ap_bucket_brigade *bucket)
+ */
+API_EXPORT(apr_status_t) ap_get_brigade(ap_filter_t *filter, ap_bucket_brigade *bucket);
+
 /**
  * Pass the current bucket brigade down to the next filter on the filter
  * stack.  The filter should return an apr_status_t value.  If the bottom-most 
@@ -299,6 +316,15 @@ API_EXPORT(void) ap_register_output_filter(const char *name,
  * To re-iterate that last comment.  This function is building a FIFO
  * list of filters.  Take note of that when adding your filter to the chain.
  */
+/**
+ * Add a filter to the current connection.  Filters are added in a FIFO manner.
+ * The first filter added will be the first filter called.
+ * @param name The name of the filter to add
+ * @param c The connection to add the fillter for
+ * @deffunc void ap_add_input_filter(const char *name, void *ctx, conn_rec *r)
+ */
+API_EXPORT(void) ap_add_input_filter(const char *name, void *ctx, conn_rec *r);
+
 /**
  * Add a filter to the current request.  Filters are added in a FIFO manner.
  * The first filter added will be the first filter called.
index 19870480dae62a02febb526bbaa701ca7e126330..2709fd6ee96e81728a6b7cba8a76fa865a5d9512 100644 (file)
@@ -3246,11 +3246,33 @@ static apr_status_t chunk_filter(ap_filter_t *f, ap_bucket_brigade *b)
     return APR_SUCCESS;
 }
 
+static int core_input_filter(ap_filter_t *f, ap_bucket_brigade *b)
+{
+    char *buff;
+    apr_size_t length = HUGE_STRING_LEN;
+    apr_socket_t *csock = NULL;
+    ap_bucket *e;
+
+    /* As soon as we have pool buckets, this should become a palloc. */
+    buff = apr_palloc(f->c->pool, HUGE_STRING_LEN);
+    ap_bpop_socket(&csock, f->c->client);
+
+    if (apr_recv(csock, buff, &length) == APR_SUCCESS) {
+        /* This should probably be a pool bucket, but using a transient is 
+         * actually okay here too.  We know the pool we are using will always 
+         * be available as long as the connection is open.
+         */
+        e = ap_bucket_create_transient(buff, length);
+        AP_BRIGADE_INSERT_TAIL(b, e); 
+    }
+    return length;
+} 
+
 /* Default filter.  This filter should almost always be used.  Its only job
  * is to send the headers if they haven't already been sent, and then send
  * the actual data.
  */
-static int core_filter(ap_filter_t *f, ap_bucket_brigade *b)
+static int core_output_filter(ap_filter_t *f, ap_bucket_brigade *b)
 {
     request_rec *r = f->r;
     apr_pool_t *p = r->pool;
@@ -3262,7 +3284,7 @@ static int core_filter(ap_filter_t *f, ap_bucket_brigade *b)
 #if 0 /* XXX: bit rot! */
     /* This will all be needed once BUFF is removed from the code */
     /* At this point we need to discover if there was any data saved from
-     * the last call to core_filter.
+     * the last call to core_output_filter.
      */
     b = ap_get_saved_data(f, &b);
 
@@ -3459,7 +3481,8 @@ static void register_hooks(void)
      * request-processing time.
      */
     ap_hook_insert_filter(core_register_filter, NULL, NULL, AP_HOOK_MIDDLE);
-    ap_register_output_filter("CORE", core_filter, AP_FTYPE_CONNECTION + 1);
+    ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_CONNECTION);
+    ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_CONNECTION + 1);
     ap_register_output_filter("CHUNK", chunk_filter, AP_FTYPE_CONNECTION);
     ap_register_output_filter("BUFFER", buffer_filter, AP_FTYPE_CONNECTION);
 }
index 664bb0d1a119b6d4e7021c78ca53194e138a374a..b0fd3979ad799406b73269e49029f0aa4cc736a9 100644 (file)
@@ -880,11 +880,16 @@ API_EXPORT(const char *) ap_method_name_of(int methnum)
  *       then the actual input line exceeded the buffer length,
  *       and it would be a good idea for the caller to puke 400 or 414.
  */
-static int getline(char *s, int n, BUFF *in, int fold)
+static int getline(char *s, int n, conn_rec *c, int fold)
 {
-    char *pos, next;
+    char *pos;
+    const char *toss;
+    const char *temp;
     int retval;
     int total = 0;
+    int length;
+    ap_bucket_brigade *b;
+    ap_bucket *e;
 #ifdef APACHE_XLATE
     /* When getline() is called, the HTTP protocol is in a state
      * where we MUST be reading "plain text" protocol stuff,
@@ -899,22 +904,69 @@ static int getline(char *s, int n, BUFF *in, int fold)
 
     pos = s;
 
-    do {
-        retval = ap_bgets(pos, n, in);
-       /* retval == -1 if error, 0 if EOF */
+    if (!c->input_data) {
+        b = ap_brigade_create(c->pool);
+    }
+    else {
+        b = c->input_data;
+    }
+
+    if (AP_BRIGADE_EMPTY(b)) {
+        ap_get_brigade(c->input_filters, b);
+    }
 
-        if (retval <= 0) {
-            total = ((retval < 0) && (total == 0)) ? -1 : total;
+    if (AP_BRIGADE_EMPTY(b)) {
+        return -1;
+    }
+    e = AP_BRIGADE_FIRST(b); 
+    while (1) {
+        while (e->length == 0) {
+            AP_BUCKET_REMOVE(e);
+            e->destroy(e);
+
+            ap_get_brigade(c->input_filters, b);
+            if (!AP_BRIGADE_EMPTY(b)) {
+                e = AP_BRIGADE_FIRST(b); 
+            }
+            else {
+                return -1;
+            }
+        }
+        retval = e->read(e, &temp, &length, 0);
+        /* retval == 0 on SUCCESS */
+
+        if (retval != 0) {
+            total = ((length < 0) && (total == 0)) ? -1 : total;
             break;
         }
 
-        /* retval is the number of characters read, not including NUL      */
+        if ((toss = ap_strchr_c(temp, '\r')) != NULL) { 
+            length = toss - temp + 2;
+            e->split(e, length);
+            apr_cpystrn(pos, temp, length);
+            pos[length - 2] = '\n';
+            pos[--length] = '\0';
+            AP_BUCKET_REMOVE(e);
+            e->destroy(e);
+        }
+        c->input_data = b;
+        e = AP_BRIGADE_FIRST(b); 
+/**** XXX
+ *    Check for folding
+ * Continue appending if line folding is desired and
+ * the last line was not empty and we have room in the buffer and
+ * the next line begins with a continuation character.
+ *       if (!fold || (retval == 0) && (n > 1)
+ *          && (retval = e->read(e, ) 
+ *          && ((next == ' ') || (next == '\t')));
+ */
+        /* length is the number of characters read, not including NUL    */
 
-        n -= retval;            /* Keep track of how much of s is full     */
-        pos += (retval - 1);    /* and where s ends                        */
-        total += retval;        /* and how long s has become               */
+        n -= length;            /* Keep track of how much of s is full   */
+        pos += (length - 1);    /* and where s ends                      */
+        total += length;        /* and how long s has become             */
 
-        if (*pos == '\n') {     /* Did we get a full line of input?        */
+        if (*pos == '\n') {     /* Did we get a full line of input?      */
             /*
              * Trim any extra trailing spaces or tabs except for the first
              * space or tab at the beginning of a blank string.  This makes
@@ -923,26 +975,19 @@ static int getline(char *s, int n, BUFF *in, int fold)
              */
             while (pos > (s + 1) && (*(pos - 1) == ' '
                                     || *(pos - 1) == '\t')) {
-                --pos;          /* trim extra trailing spaces or tabs      */
-                --total;        /* but not one at the beginning of line    */
+                --pos;          /* trim extra trailing spaces or tabs    */
+                --total;        /* but not one at the beginning of line  */
                 ++n;
             }
             *pos = '\0';
             --total;
             ++n;
+            break;
         }
         else {
            break;       /* if not, input line exceeded buffer size */
        }
-        /* Continue appending if line folding is desired and
-         * the last line was not empty and we have room in the buffer and
-         * the next line begins with a continuation character.
-         */
-    } while (fold
-            && (retval != 1) && (n > 1)
-            && (next = ap_blookc(in))
-            && ((next == ' ') || (next == '\t')));
-
+    }
 #ifdef APACHE_XLATE
     /* restore translation handle */
     AP_POP_INPUTCONVERSION_STATE(in);
@@ -1029,7 +1074,7 @@ static int read_request_line(request_rec *r)
      */
     ap_bsetflag(conn->client, B_SAFEREAD, 1); 
     ap_bflush(conn->client);
-    while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) {
+    while ((len = getline(l, sizeof(l), conn, 0)) <= 0) {
         if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
            ap_bsetflag(conn->client, B_SAFEREAD, 0);
            /* this is a hack to make sure that request time is set,
@@ -1110,7 +1155,7 @@ static void get_mime_headers(request_rec *r)
      * Read header lines until we get the empty separator line, a read error,
      * the connection closes (EOF), reach the server limit, or we timeout.
      */
-    while ((len = getline(field, sizeof(field), c->client, 1)) > 0) {
+    while ((len = getline(field, sizeof(field), c, 1)) > 0) {
 
         if (r->server->limit_req_fields &&
             (++fields_read > r->server->limit_req_fields)) {
@@ -2357,7 +2402,7 @@ API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
 
     if (r->remaining == 0) {    /* Start of new chunk */
 
-        chunk_start = getline(buffer, bufsiz, r->connection->client, 0);
+        chunk_start = getline(buffer, bufsiz, r->connection, 0);
         if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
             || !apr_isxdigit(*buffer)) {
             r->connection->keepalive = -1;
@@ -2398,7 +2443,7 @@ API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
         len_read = chunk_start;
 
         while ((bufsiz > 1)
-              && ((len_read = getline(buffer, bufsiz, r->connection->client,
+              && ((len_read = getline(buffer, bufsiz, r->connection,
                                       1)) > 0)) {
 
             if (len_read != (bufsiz - 1)) {
index cd9c181963636f8b9db5ccf659a0d973ed8eec54..d36ff7064d5cb080a7b946ccfe00bd7fea52a990 100644 (file)
@@ -67,6 +67,7 @@
 #include "mpm_status.h"
 #include "http_config.h"
 #include "http_vhost.h"
+#include "util_filter.h"
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -275,6 +276,8 @@ conn_rec *ap_new_connection(apr_pool_t *p, server_rec *server, BUFF *inout,
     
     conn->id = id;
 
+    ap_add_input_filter("CORE_IN", NULL, conn);
+
     return conn;
 }
 
index 1e5d6eb636b1882f28691c10cc24bcd2d2faa829..db3582596b2b7971e985cc9dd03862fe829c27f2 100644 (file)
@@ -118,6 +118,36 @@ API_EXPORT(void) ap_register_output_filter(const char *name,
                     &registered_output_filters);
 }
 
+API_EXPORT(void) ap_add_input_filter(const char *name, void *ctx, conn_rec *c)
+{
+    ap_filter_rec_t *frec = registered_input_filters;
+
+    for (; frec != NULL; frec = frec->next) {
+        if (!strcasecmp(name, frec->name)) {
+            ap_filter_t *f = apr_pcalloc(c->pool, sizeof(*f));
+
+            f->frec = frec;
+            f->ctx = ctx;
+            f->r = NULL;
+            f->c = c;
+
+            if (INSERT_BEFORE(f, c->input_filters)) {
+                f->next = c->input_filters;
+                c->input_filters = f;
+            }
+            else {
+                ap_filter_t *fscan = c->input_filters;
+                while (!INSERT_BEFORE(f, fscan->next))
+                    fscan = fscan->next;
+                f->next = fscan->next;
+                fscan->next = f;
+            }
+
+            break;
+        }
+    }
+}
+
 API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
 {
     ap_filter_rec_t *frec = registered_output_filters;
@@ -129,6 +159,7 @@ API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
             f->frec = frec;
             f->ctx = ctx;
             f->r = r;
+            f->c = NULL;
 
             if (INSERT_BEFORE(f, r->output_filters)) {
                 f->next = r->output_filters;
@@ -147,6 +178,21 @@ API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
     }
 }
 
+/* 
+ * Read data from the next filter in the filter stack.  Data should be 
+ * modified in the bucket brigade that is passed in.  The core allocates the
+ * bucket brigade, modules that wish to replace large chunks of data or to
+ * save data off to the side should probably create their own temporary
+ * brigade especially for that use.
+ */
+API_EXPORT(apr_status_t) ap_get_brigade(ap_filter_t *next, ap_bucket_brigade *bb)
+{
+    if (next) {
+        return next->frec->filter_func(next, bb);
+    }
+    return AP_NOBODY_READ;
+}
+
 /* Pass the buckets to the next filter in the filter stack.  If the
  * current filter is a handler, we should get NULL passed in instead of
  * the current filter.  At that point, we can just call the first filter in