summaryrefslogtreecommitdiff
path: root/modules/proxy/mod_proxy_fcgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/proxy/mod_proxy_fcgi.c')
-rw-r--r--modules/proxy/mod_proxy_fcgi.c446
1 files changed, 133 insertions, 313 deletions
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
index 0f844163..ee702900 100644
--- a/modules/proxy/mod_proxy_fcgi.c
+++ b/modules/proxy/mod_proxy_fcgi.c
@@ -15,59 +15,12 @@
*/
#include "mod_proxy.h"
-#include "fcgi_protocol.h"
+#include "util_fcgi.h"
#include "util_script.h"
module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
/*
- * The below 3 functions serve to map the FCGI structs
- * back and forth between an 8 byte array. We do this to avoid
- * any potential padding issues when we send or read these
- * structures.
- *
- * NOTE: These have specific internal knowledge of the
- * layout of the fcgi_header and fcgi_begin_request_body
- * structs!
- */
-static void fcgi_header_to_array(fcgi_header *h, unsigned char a[])
-{
- a[FCGI_HDR_VERSION_OFFSET] = h->version;
- a[FCGI_HDR_TYPE_OFFSET] = h->type;
- a[FCGI_HDR_REQUEST_ID_B1_OFFSET] = h->requestIdB1;
- a[FCGI_HDR_REQUEST_ID_B0_OFFSET] = h->requestIdB0;
- a[FCGI_HDR_CONTENT_LEN_B1_OFFSET] = h->contentLengthB1;
- a[FCGI_HDR_CONTENT_LEN_B0_OFFSET] = h->contentLengthB0;
- a[FCGI_HDR_PADDING_LEN_OFFSET] = h->paddingLength;
- a[FCGI_HDR_RESERVED_OFFSET] = h->reserved;
-}
-
-static void fcgi_header_from_array(fcgi_header *h, unsigned char a[])
-{
- h->version = a[FCGI_HDR_VERSION_OFFSET];
- h->type = a[FCGI_HDR_TYPE_OFFSET];
- h->requestIdB1 = a[FCGI_HDR_REQUEST_ID_B1_OFFSET];
- h->requestIdB0 = a[FCGI_HDR_REQUEST_ID_B0_OFFSET];
- h->contentLengthB1 = a[FCGI_HDR_CONTENT_LEN_B1_OFFSET];
- h->contentLengthB0 = a[FCGI_HDR_CONTENT_LEN_B0_OFFSET];
- h->paddingLength = a[FCGI_HDR_PADDING_LEN_OFFSET];
- h->reserved = a[FCGI_HDR_RESERVED_OFFSET];
-}
-
-static void fcgi_begin_request_body_to_array(fcgi_begin_request_body *h,
- unsigned char a[])
-{
- a[FCGI_BRB_ROLEB1_OFFSET] = h->roleB1;
- a[FCGI_BRB_ROLEB0_OFFSET] = h->roleB0;
- a[FCGI_BRB_FLAGS_OFFSET] = h->flags;
- a[FCGI_BRB_RESERVED0_OFFSET] = h->reserved[0];
- a[FCGI_BRB_RESERVED1_OFFSET] = h->reserved[1];
- a[FCGI_BRB_RESERVED2_OFFSET] = h->reserved[2];
- a[FCGI_BRB_RESERVED3_OFFSET] = h->reserved[3];
- a[FCGI_BRB_RESERVED4_OFFSET] = h->reserved[4];
-}
-
-/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
@@ -129,33 +82,6 @@ static int proxy_fcgi_canon(request_rec *r, char *url)
return OK;
}
-/*
- * Fill in a fastcgi request header with the following type, request id,
- * content length, and padding length.
- *
- * The header array must be at least FCGI_HEADER_LEN bytes long.
- */
-static void fill_in_header(fcgi_header *header,
- unsigned char type,
- apr_uint16_t request_id,
- apr_uint16_t content_len,
- unsigned char padding_len)
-{
- header->version = FCGI_VERSION;
-
- header->type = type;
-
- header->requestIdB1 = ((request_id >> 8) & 0xff);
- header->requestIdB0 = ((request_id) & 0xff);
-
- header->contentLengthB1 = ((content_len >> 8) & 0xff);
- header->contentLengthB0 = ((content_len) & 0xff);
-
- header->paddingLength = padding_len;
-
- header->reserved = 0;
-}
-
/* Wrapper for apr_socket_sendv that handles updating the worker stats. */
static apr_status_t send_data(proxy_conn_rec *conn,
struct iovec *vec,
@@ -234,28 +160,43 @@ static apr_status_t get_data(proxy_conn_rec *conn,
return rv;
}
-static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
+static apr_status_t get_data_full(proxy_conn_rec *conn,
+ char *buffer,
+ apr_size_t buflen)
+{
+ apr_size_t readlen;
+ apr_size_t cumulative_len = 0;
+ apr_status_t rv;
+
+ do {
+ readlen = buflen - cumulative_len;
+ rv = get_data(conn, buffer + cumulative_len, &readlen);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ cumulative_len += readlen;
+ } while (cumulative_len < buflen);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t send_begin_request(proxy_conn_rec *conn,
+ apr_uint16_t request_id)
{
struct iovec vec[2];
- fcgi_header header;
- unsigned char farray[FCGI_HEADER_LEN];
- fcgi_begin_request_body brb;
- unsigned char abrb[FCGI_HEADER_LEN];
+ ap_fcgi_header header;
+ unsigned char farray[AP_FCGI_HEADER_LEN];
+ ap_fcgi_begin_request_body brb;
+ unsigned char abrb[AP_FCGI_HEADER_LEN];
apr_size_t len;
- fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
+ ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id,
+ sizeof(abrb), 0);
- brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
- brb.roleB0 = ((FCGI_RESPONDER) & 0xff);
- brb.flags = FCGI_KEEP_CONN;
- brb.reserved[0] = 0;
- brb.reserved[1] = 0;
- brb.reserved[2] = 0;
- brb.reserved[3] = 0;
- brb.reserved[4] = 0;
+ ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER, AP_FCGI_KEEP_CONN);
- fcgi_header_to_array(&header, farray);
- fcgi_begin_request_body_to_array(&brb, abrb);
+ ap_fcgi_header_to_array(&header, farray);
+ ap_fcgi_begin_request_body_to_array(&brb, abrb);
vec[0].iov_base = (void *)farray;
vec[0].iov_len = sizeof(farray);
@@ -266,26 +207,24 @@ static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
}
static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
- int request_id)
+ apr_pool_t *temp_pool,
+ apr_uint16_t request_id)
{
const apr_array_header_t *envarr;
const apr_table_entry_t *elts;
struct iovec vec[2];
- fcgi_header header;
- unsigned char farray[FCGI_HEADER_LEN];
- apr_size_t bodylen, envlen;
- char *body, *itr;
+ ap_fcgi_header header;
+ unsigned char farray[AP_FCGI_HEADER_LEN];
+ char *body;
apr_status_t rv;
- apr_size_t len;
- int i, numenv;
+ apr_size_t avail_len, len, required_len;
+ int next_elem, starting_elem;
ap_add_common_vars(r);
ap_add_cgi_vars(r);
/* XXX are there any FastCGI specific env vars we need to send? */
- bodylen = envlen = 0;
-
/* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
* the TZ value specially. We could use that, but it would mean
* parsing the key/value pairs back OUT of the allocated env array,
@@ -293,118 +232,75 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
* place, which would suck. */
envarr = apr_table_elts(r->subprocess_env);
-
elts = (const apr_table_entry_t *) envarr->elts;
- for (i = 0; i < envarr->nelts; ++i) {
- apr_size_t keylen, vallen;
-
- if (! elts[i].key) {
- continue;
- }
-
- keylen = strlen(elts[i].key);
-
- if (keylen >> 7 == 0) {
- envlen += 1;
- }
- else {
- envlen += 4;
- }
-
- envlen += keylen;
-
- vallen = strlen(elts[i].val);
-
#ifdef FCGI_DUMP_ENV_VARS
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062)
- "sending env var '%s' value '%s'",
- elts[i].key, elts[i].val);
-#endif
-
- if (vallen >> 7 == 0) {
- envlen += 1;
+ {
+ int i;
+
+ for (i = 0; i < envarr->nelts; ++i) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062)
+ "sending env var '%s' value '%s'",
+ elts[i].key, elts[i].val);
}
- else {
- envlen += 4;
- }
-
- envlen += vallen;
+ }
+#endif
- /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */
- if (envlen > FCGI_MAX_ENV_SIZE) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01063)
- "truncating environment to %d bytes and %d elements",
- (int)bodylen, i);
+ /* Send envvars over in as many FastCGI records as it takes, */
+ next_elem = 0; /* starting with the first one */
+
+ avail_len = 16 * 1024; /* our limit per record, which could have been up
+ * to AP_FCGI_MAX_CONTENT_LEN
+ */
+
+ while (next_elem < envarr->nelts) {
+ starting_elem = next_elem;
+ required_len = ap_fcgi_encoded_env_len(r->subprocess_env,
+ avail_len,
+ &next_elem);
+
+ if (!required_len) {
+ if (next_elem < envarr->nelts) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ APLOGNO(02536) "couldn't encode envvar '%s' in %"
+ APR_SIZE_T_FMT " bytes",
+ elts[next_elem].key, avail_len);
+ /* skip this envvar and continue */
+ ++next_elem;
+ continue;
+ }
+ /* only an unused element at the end of the array */
break;
}
- bodylen = envlen;
- }
+ body = apr_palloc(temp_pool, required_len);
+ rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
+ &starting_elem);
+ /* we pre-compute, so we can't run out of space */
+ ap_assert(rv == APR_SUCCESS);
+ /* compute and encode must be in sync */
+ ap_assert(starting_elem == next_elem);
- numenv = i;
+ ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id,
+ (apr_uint16_t)required_len, 0);
+ ap_fcgi_header_to_array(&header, farray);
- body = apr_pcalloc(r->pool, bodylen);
+ vec[0].iov_base = (void *)farray;
+ vec[0].iov_len = sizeof(farray);
+ vec[1].iov_base = body;
+ vec[1].iov_len = required_len;
- itr = body;
+ rv = send_data(conn, vec, 2, &len, 1);
+ apr_pool_clear(temp_pool);
- for (i = 0; i < numenv; ++i) {
- apr_size_t keylen, vallen;
-
- if (! elts[i].key) {
- continue;
- }
-
- keylen = strlen(elts[i].key);
-
- if (keylen >> 7 == 0) {
- itr[0] = keylen & 0xff;
- itr += 1;
+ if (rv) {
+ return rv;
}
- else {
- itr[0] = ((keylen >> 24) & 0xff) | 0x80;
- itr[1] = ((keylen >> 16) & 0xff);
- itr[2] = ((keylen >> 8) & 0xff);
- itr[3] = ((keylen) & 0xff);
- itr += 4;
- }
-
- vallen = strlen(elts[i].val);
-
- if (vallen >> 7 == 0) {
- itr[0] = vallen & 0xff;
- itr += 1;
- }
- else {
- itr[0] = ((vallen >> 24) & 0xff) | 0x80;
- itr[1] = ((vallen >> 16) & 0xff);
- itr[2] = ((vallen >> 8) & 0xff);
- itr[3] = ((vallen) & 0xff);
- itr += 4;
- }
-
- memcpy(itr, elts[i].key, keylen);
- itr += keylen;
-
- memcpy(itr, elts[i].val, vallen);
- itr += vallen;
}
- fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
- fcgi_header_to_array(&header, farray);
-
- vec[0].iov_base = (void *)farray;
- vec[0].iov_len = sizeof(farray);
- vec[1].iov_base = body;
- vec[1].iov_len = bodylen;
-
- rv = send_data(conn, vec, 2, &len, 1);
- if (rv) {
- return rv;
- }
-
- fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
- fcgi_header_to_array(&header, farray);
+ /* Envvars sent, so say we're done */
+ ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0);
+ ap_fcgi_header_to_array(&header, farray);
vec[0].iov_base = (void *)farray;
vec[0].iov_len = sizeof(farray);
@@ -481,69 +377,9 @@ static int handle_headers(request_rec *r,
return 0;
}
-static void dump_header_to_log(request_rec *r, unsigned char fheader[],
- apr_size_t length)
-{
-#ifdef FCGI_DUMP_HEADERS
- apr_size_t posn = 0;
- char asc_line[20];
- char hex_line[60];
- int i = 0;
-
- memset(asc_line, 0, sizeof(asc_line));
- memset(hex_line, 0, sizeof(hex_line));
-
- while (posn < length) {
- unsigned char c = fheader[posn];
-
- if (i >= 20) {
- i = 0;
-
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01064)
- "HEADER: %s %s", asc_line, hex_line);
-
- memset(asc_line, 0, sizeof(asc_line));
- memset(hex_line, 0, sizeof(hex_line));
- }
-
- if (isprint(c)) {
- asc_line[i] = c;
- }
- else {
- asc_line[i] = '.';
- }
-
- if ((c >> 4) >= 10) {
- hex_line[i * 3] = 'a' + ((c >> 4) - 10);
- }
- else {
- hex_line[i * 3] = '0' + (c >> 4);
- }
-
- if ((c & 0x0F) >= 10) {
- hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10);
- }
- else {
- hex_line[i * 3 + 1] = '0' + (c & 0xF);
- }
-
- hex_line[i * 3 + 2] = ' ';
-
- i++;
- posn++;
- }
-
- if (i != 1) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01065) "HEADER: %s %s",
- asc_line, hex_line);
- }
-
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01066) "HEADER: -EOH-");
-#endif
-}
-
static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
- request_rec *r, int request_id)
+ request_rec *r, apr_pool_t *setaside_pool,
+ apr_uint16_t request_id)
{
apr_bucket_brigade *ib, *ob;
int seen_end_of_headers = 0, done = 0;
@@ -551,14 +387,11 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
int script_error_status = HTTP_OK;
conn_rec *c = r->connection;
struct iovec vec[2];
- fcgi_header header;
- unsigned char farray[FCGI_HEADER_LEN];
+ ap_fcgi_header header;
+ unsigned char farray[AP_FCGI_HEADER_LEN];
apr_pollfd_t pfd;
int header_state = HDR_STATE_READING_HEADERS;
- apr_pool_t *setaside_pool;
-
- apr_pool_create(&setaside_pool, r->pool);
-
+
pfd.desc_type = APR_POLL_SOCKET;
pfd.desc.s = conn->sock;
pfd.p = r->pool;
@@ -568,15 +401,13 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
ob = apr_brigade_create(r->pool, c->bucket_alloc);
while (! done) {
- apr_interval_time_t timeout = conn->worker->s->timeout;
+ apr_interval_time_t timeout;
apr_size_t len;
int n;
/* We need SOME kind of timeout here, or virtually anything will
* cause timeout errors. */
- if (! conn->worker->s->timeout_set) {
- timeout = apr_time_from_sec(30);
- }
+ apr_socket_timeout_get(conn->sock, &timeout);
rv = apr_poll(&pfd, 1, &n, timeout);
if (rv != APR_SUCCESS) {
@@ -613,9 +444,9 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
break;
}
- fill_in_header(&header, FCGI_STDIN, request_id,
- (apr_uint16_t) writebuflen, 0);
- fcgi_header_to_array(&header, farray);
+ ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
+ (apr_uint16_t) writebuflen, 0);
+ ap_fcgi_header_to_array(&header, farray);
vec[nvec].iov_base = (void *)farray;
vec[nvec].iov_len = sizeof(farray);
@@ -634,9 +465,10 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
if (last_stdin) {
pfd.reqevents = APR_POLLIN; /* Done with input data */
- if (writebuflen) { /* empty FCGI_STDIN not already sent? */
- fill_in_header(&header, FCGI_STDIN, request_id, 0, 0);
- fcgi_header_to_array(&header, farray);
+ if (writebuflen) { /* empty AP_FCGI_STDIN not already sent? */
+ ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
+ 0, 0);
+ ap_fcgi_header_to_array(&header, farray);
vec[0].iov_base = (void *)farray;
vec[0].iov_len = sizeof(farray);
@@ -652,47 +484,37 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
* the headers, even if we fill the entire length in the recv. */
char readbuf[AP_IOBUFSIZE + 1];
apr_size_t readbuflen;
- apr_size_t clen;
- int rid, type;
+ apr_uint16_t clen, rid;
apr_bucket *b;
- char plen;
+ unsigned char plen;
+ unsigned char type, version;
memset(readbuf, 0, sizeof(readbuf));
memset(farray, 0, sizeof(farray));
/* First, we grab the header... */
- readbuflen = FCGI_HEADER_LEN;
-
- rv = get_data(conn, (char *) farray, &readbuflen);
+ rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN);
if (rv != APR_SUCCESS) {
- break;
- }
-
- dump_header_to_log(r, farray, readbuflen);
-
- if (readbuflen != FCGI_HEADER_LEN) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067)
- "Failed to read entire header "
- "got %" APR_SIZE_T_FMT " wanted %d",
- readbuflen, FCGI_HEADER_LEN);
- rv = APR_EINVAL;
+ "Failed to read FastCGI header");
break;
}
- fcgi_header_from_array(&header, farray);
+#ifdef FCGI_DUMP_HEADERS
+ ap_log_rdata(APLOG_MARK, APLOG_DEBUG, r, "FastCGI header",
+ farray, AP_FCGI_HEADER_LEN, 0);
+#endif
+
+ ap_fcgi_header_fields_from_array(&version, &type, &rid,
+ &clen, &plen, farray);
- if (header.version != FCGI_VERSION) {
+ if (version != AP_FCGI_VERSION_1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068)
"Got bogus version %d", (int) header.version);
rv = APR_EINVAL;
break;
}
- type = header.type;
-
- rid = header.requestIdB1 << 8;
- rid |= header.requestIdB0;
-
if (rid != request_id) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069)
"Got bogus rid %d, expected %d",
@@ -701,11 +523,6 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
break;
}
- clen = header.contentLengthB1 << 8;
- clen |= header.contentLengthB0;
-
- plen = header.paddingLength;
-
recv_again:
if (clen > sizeof(readbuf) - 1) {
readbuflen = sizeof(readbuf) - 1;
@@ -725,7 +542,7 @@ recv_again:
}
switch (type) {
- case FCGI_STDOUT:
+ case AP_FCGI_STDOUT:
if (clen != 0) {
b = apr_bucket_transient_create(readbuf,
readbuflen,
@@ -825,7 +642,7 @@ recv_again:
}
break;
- case FCGI_STDERR:
+ case AP_FCGI_STDERR:
/* TODO: Should probably clean up this logging a bit... */
if (clen) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
@@ -838,7 +655,7 @@ recv_again:
}
break;
- case FCGI_END_REQUEST:
+ case AP_FCGI_END_REQUEST:
done = 1;
break;
@@ -849,10 +666,10 @@ recv_again:
}
if (plen) {
- readbuflen = plen;
-
- rv = get_data(conn, readbuf, &readbuflen);
+ rv = get_data_full(conn, readbuf, plen);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(02537) "Error occurred reading padding");
break;
}
}
@@ -884,10 +701,11 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
* multiple requests to the same FastCGI connection, but
* we don't support that, and always use a value of '1' to
* keep things simple. */
- int request_id = 1;
+ apr_uint16_t request_id = 1;
apr_status_t rv;
+ apr_pool_t *temp_pool;
- /* Step 1: Send FCGI_BEGIN_REQUEST */
+ /* Step 1: Send AP_FCGI_BEGIN_REQUEST */
rv = send_begin_request(conn, request_id);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073)
@@ -896,8 +714,10 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
return HTTP_SERVICE_UNAVAILABLE;
}
+ apr_pool_create(&temp_pool, r->pool);
+
/* Step 2: Send Environment via FCGI_PARAMS */
- rv = send_environment(conn, r, request_id);
+ rv = send_environment(conn, r, temp_pool, request_id);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074)
"Failed writing Environment to %s:", server_portstr);
@@ -906,7 +726,7 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
}
/* Step 3: Read records from the back end server and handle them. */
- rv = dispatch(conn, conf, r, request_id);
+ rv = dispatch(conn, conf, r, temp_pool, request_id);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075)
"Error dispatching request to %s:", server_portstr);