Return-Path: Delivered-To: apmail-apache-cvs-archive@apache.org Received: (qmail 15232 invoked by uid 500); 2 Oct 2000 00:47:33 -0000 Mailing-List: contact apache-cvs-help@apache.org; run by ezmlm Precedence: bulk Reply-To: new-httpd@apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list apache-cvs@apache.org Received: (qmail 15196 invoked by uid 500); 2 Oct 2000 00:47:31 -0000 Delivered-To: apmail-apache-2.0-cvs@apache.org Date: 2 Oct 2000 00:47:30 -0000 Message-ID: <20001002004730.15190.qmail@locus.apache.org> From: rbb@locus.apache.org To: apache-2.0-cvs@apache.org Subject: cvs commit: apache-2.0/src/main http_connection.c http_core.c http_protocol.c util_filter.c rbb 00/10/01 17:47:30 Modified: src CHANGES src/include httpd.h util_filter.h src/main http_connection.c http_core.c http_protocol.c util_filter.c Log: Implement input filtering. This is definately not completely correct, but it is a good first step. It is possible to add filters when reading from the client with this change. Revision Changes Path 1.241 +12 -0 apache-2.0/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-2.0/src/CHANGES,v retrieving revision 1.240 retrieving revision 1.241 diff -u -r1.240 -r1.241 --- CHANGES 2000/09/26 13:26:48 1.240 +++ CHANGES 2000/10/02 00:47:24 1.241 @@ -1,5 +1,17 @@ Changes with Apache 2.0a7 + *) Add input filtering to Apache. The basic idea for the input + filters is the same as the ideas for output filters. The biggest + difference is that instead of calling ap_pass_brigade, ap_get_brigade + should be called, and the order of execution for the filter itself is + different. When writing an output filter, a brigade is passed in, + and filters operate directly on that brigade, when done, they call + ap_pass_brigade. Input filters are the exact opposite. Because input + is not a push operation, filters first call ap_get_brigade. When this + function returns, the input filter will be left with a valid brigade. + The input filter should then operate on the brigade, and return. + [Ryan Bloom] + *) Fix building on BSD/OS using its native make. The build system falls back to the BSD .include directive on that host platform. [Sascha Schumann] 1.88 +4 -0 apache-2.0/src/include/httpd.h Index: httpd.h =================================================================== RCS file: /home/cvs/apache-2.0/src/include/httpd.h,v retrieving revision 1.87 retrieving revision 1.88 diff -u -r1.87 -r1.88 --- httpd.h 2000/09/19 20:56:06 1.87 +++ httpd.h 2000/10/02 00:47:25 1.88 @@ -77,6 +77,7 @@ /* 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 @@ /** 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... */ 1.19 +27 -1 apache-2.0/src/include/util_filter.h Index: util_filter.h =================================================================== RCS file: /home/cvs/apache-2.0/src/include/util_filter.h,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- util_filter.h 2000/09/19 23:28:55 1.18 +++ util_filter.h 2000/10/02 00:47:26 1.19 @@ -72,6 +72,7 @@ */ #define AP_NOBODY_WROTE -1; +#define AP_NOBODY_READ -2; /* * FILTER CHAIN @@ -215,9 +216,14 @@ /** 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 @@ -231,6 +237,17 @@ * 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 * filter doesn't write to the network, then AP_NOBODY_WROTE is returned. @@ -299,6 +316,15 @@ * 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. 1.46 +3 -0 apache-2.0/src/main/http_connection.c Index: http_connection.c =================================================================== RCS file: /home/cvs/apache-2.0/src/main/http_connection.c,v retrieving revision 1.45 retrieving revision 1.46 diff -u -r1.45 -r1.46 --- http_connection.c 2000/08/06 06:07:34 1.45 +++ http_connection.c 2000/10/02 00:47:28 1.46 @@ -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 @@ -274,6 +275,8 @@ inet_ntoa(conn->remote_addr.sin_addr)); conn->id = id; + + ap_add_input_filter("CORE_IN", NULL, conn); return conn; } 1.137 +26 -3 apache-2.0/src/main/http_core.c Index: http_core.c =================================================================== RCS file: /home/cvs/apache-2.0/src/main/http_core.c,v retrieving revision 1.136 retrieving revision 1.137 diff -u -r1.136 -r1.137 --- http_core.c 2000/09/29 18:12:13 1.136 +++ http_core.c 2000/10/02 00:47:28 1.137 @@ -3246,11 +3246,33 @@ 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 @@ #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 @@ * 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); } 1.134 +73 -28 apache-2.0/src/main/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /home/cvs/apache-2.0/src/main/http_protocol.c,v retrieving revision 1.133 retrieving revision 1.134 diff -u -r1.133 -r1.134 --- http_protocol.c 2000/09/29 18:12:14 1.133 +++ http_protocol.c 2000/10/02 00:47:28 1.134 @@ -880,11 +880,16 @@ * 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 @@ 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 (retval <= 0) { - total = ((retval < 0) && (total == 0)) ? -1 : total; - break; + if (AP_BRIGADE_EMPTY(b)) { + ap_get_brigade(c->input_filters, b); + } + + 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 */ - /* retval is the number of characters read, not including NUL */ + if (retval != 0) { + total = ((length < 0) && (total == 0)) ? -1 : total; + break; + } - 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 */ + 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 -= 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 @@ */ 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 @@ */ 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 @@ * 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 @@ 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 @@ 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)) { 1.22 +46 -0 apache-2.0/src/main/util_filter.c Index: util_filter.c =================================================================== RCS file: /home/cvs/apache-2.0/src/main/util_filter.c,v retrieving revision 1.21 retrieving revision 1.22 diff -u -r1.21 -r1.22 --- util_filter.c 2000/09/19 23:28:55 1.21 +++ util_filter.c 2000/10/02 00:47:28 1.22 @@ -118,6 +118,36 @@ ®istered_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 @@ f->frec = frec; f->ctx = ctx; f->r = r; + f->c = NULL; if (INSERT_BEFORE(f, r->output_filters)) { f->next = r->output_filters; @@ -145,6 +176,21 @@ break; } } +} + +/* + * 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