From: Ryan Bloom Date: Mon, 2 Oct 2000 00:47:28 +0000 (+0000) Subject: Implement input filtering. This is definately not completely correct, but X-Git-Tag: APACHE_2_0_ALPHA_7~82 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7ae2f294fd1ec0dccee54639ef95ef4766a5f81a;p=thirdparty%2Fapache%2Fhttpd.git 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. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86359 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/include/httpd.h b/include/httpd.h index c422b01da68..f222e0d4baa 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -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... */ diff --git a/include/util_filter.h b/include/util_filter.h index 2f0567203ae..41b4cd914cc 100644 --- a/include/util_filter.h +++ b/include/util_filter.h @@ -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. diff --git a/modules/http/http_core.c b/modules/http/http_core.c index 19870480dae..2709fd6ee96 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -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); } diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 664bb0d1a11..b0fd3979ad7 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -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)) { diff --git a/server/connection.c b/server/connection.c index cd9c1819636..d36ff7064d5 100644 --- a/server/connection.c +++ b/server/connection.c @@ -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 @@ -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; } diff --git a/server/util_filter.c b/server/util_filter.c index 1e5d6eb636b..db3582596b2 100644 --- a/server/util_filter.c +++ b/server/util_filter.c @@ -118,6 +118,36 @@ API_EXPORT(void) ap_register_output_filter(const char *name, ®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 @@ 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