/* 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"
/** 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... */
*/
#define AP_NOBODY_WROTE -1;
+#define AP_NOBODY_READ -2;
/*
* FILTER CHAIN
/** 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
* 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
* 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.
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;
#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);
* 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);
}
* 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,
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
*/
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);
*/
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,
* 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)) {
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;
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)) {
#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>
conn->id = id;
+ ap_add_input_filter("CORE_IN", NULL, conn);
+
return conn;
}
®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;
f->frec = frec;
f->ctx = ctx;
f->r = r;
+ f->c = NULL;
if (INSERT_BEFORE(f, r->output_filters)) {
f->next = r->output_filters;
}
}
+/*
+ * 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