+++ /dev/null
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file fcgi_protocol.h
- * @brief FastCGI protocol defines
- *
- * @defgroup FCGI_defines FastCGI protocol definition
- * @ingroup APACHE_INTERNAL
- * @{
- */
-
-#ifndef FCGI_PROTOCOL_H
-#define FCGI_PROTOCOL_H
-
-
-#define FCGI_VERSION 1
-
-#define FCGI_BEGIN_REQUEST 1
-#define FCGI_ABORT_REQUEST 2
-#define FCGI_END_REQUEST 3
-#define FCGI_PARAMS 4
-#define FCGI_STDIN 5
-#define FCGI_STDOUT 6
-#define FCGI_STDERR 7
-#define FCGI_DATA 8
-#define FCGI_GET_VALUES 9
-#define FCGI_GET_VALUES_RESULT 10
-#define FCGI_UNKNOWN_TYPE 11
-#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
-
-typedef struct {
- unsigned char version;
- unsigned char type;
- unsigned char requestIdB1;
- unsigned char requestIdB0;
- unsigned char contentLengthB1;
- unsigned char contentLengthB0;
- unsigned char paddingLength;
- unsigned char reserved;
-} fcgi_header;
-
-#define FCGI_HDR_VERSION_OFFSET 0
-#define FCGI_HDR_TYPE_OFFSET 1
-#define FCGI_HDR_REQUEST_ID_B1_OFFSET 2
-#define FCGI_HDR_REQUEST_ID_B0_OFFSET 3
-#define FCGI_HDR_CONTENT_LEN_B1_OFFSET 4
-#define FCGI_HDR_CONTENT_LEN_B0_OFFSET 5
-#define FCGI_HDR_PADDING_LEN_OFFSET 6
-#define FCGI_HDR_RESERVED_OFFSET 7
-
-#define FCGI_BRB_ROLEB1_OFFSET 0
-#define FCGI_BRB_ROLEB0_OFFSET 1
-#define FCGI_BRB_FLAGS_OFFSET 2
-#define FCGI_BRB_RESERVED0_OFFSET 3
-#define FCGI_BRB_RESERVED1_OFFSET 4
-#define FCGI_BRB_RESERVED2_OFFSET 5
-#define FCGI_BRB_RESERVED3_OFFSET 6
-#define FCGI_BRB_RESERVED4_OFFSET 7
-
-/*
- * Number of bytes in a fcgi_header. Future versions of the protocol
- * will not reduce this number.
- */
-#define FCGI_HEADER_LEN 8
-
-/*
- * Mask for flags component of FCGI_BeginRequestBody
- */
-#define FCGI_KEEP_CONN 1
-
-/*
- * Values for role component of FCGI_BeginRequestBody
- */
-#define FCGI_RESPONDER 1
-#define FCGI_AUTHORIZER 2
-#define FCGI_FILTER 3
-
-typedef struct {
- unsigned char roleB1;
- unsigned char roleB0;
- unsigned char flags;
- unsigned char reserved[5];
-} fcgi_begin_request_body;
-
-/*
- * Maximum size of the allowed environment.
- */
-#define FCGI_MAX_ENV_SIZE 65535
-
-/* #define FCGI_DUMP_ENV_VARS */
-
-
-#endif /* FCGI_PROTOCOL_H */
-/** @} */
*/
#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
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,
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);
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];
+ ap_fcgi_header header;
+ unsigned char farray[AP_FCGI_HEADER_LEN];
apr_size_t bodylen, envlen;
- char *body, *itr;
+ char *body;
apr_status_t rv;
- apr_size_t len;
- int i, numenv;
+ apr_size_t avail_len, len;
+ int next_elem, starting_elem;
ap_add_common_vars(r);
ap_add_cgi_vars(r);
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;
- }
- else {
- envlen += 4;
- }
-
- envlen += vallen;
-
- /* 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);
- break;
+ {
+ const apr_table_entry_t *elts;
+ int i;
+
+ elts = (const apr_table_entry_t *) envarr->elts;
+ 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);
}
-
- bodylen = envlen;
}
+#endif
- numenv = i;
+ next_elem = 0; /* start encoding with first element */
+ starting_elem = next_elem;
+ avail_len = AP_FCGI_MAX_CONTENT_LEN;
+ bodylen = ap_fcgi_encoded_env_len(r->subprocess_env,
+ avail_len,
+ &next_elem);
+
+ if (next_elem < envarr->nelts) {
+ /* not everything encodable within limit specified in avail_len */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01063)
+ "truncating environment to %d bytes and %d elements",
+ (int)bodylen, next_elem);
+ }
body = apr_pcalloc(r->pool, bodylen);
+ rv = ap_fcgi_encode_env(r, r->subprocess_env, body, bodylen,
+ &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);
- itr = body;
-
- 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;
- }
- 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, (apr_uint16_t)bodylen, 0);
- fcgi_header_to_array(&header, farray);
+ ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id,
+ (apr_uint16_t)bodylen, 0);
+ ap_fcgi_header_to_array(&header, farray);
vec[0].iov_base = (void *)farray;
vec[0].iov_len = sizeof(farray);
return rv;
}
- fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
- fcgi_header_to_array(&header, farray);
+ 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);
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;
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);
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);
* 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;
+ readbuflen = AP_FCGI_HEADER_LEN;
rv = get_data(conn, (char *) farray, &readbuflen);
if (rv != APR_SUCCESS) {
dump_header_to_log(r, farray, readbuflen);
- if (readbuflen != FCGI_HEADER_LEN) {
+ if (readbuflen != AP_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);
+ readbuflen, AP_FCGI_HEADER_LEN);
rv = APR_EINVAL;
break;
}
- fcgi_header_from_array(&header, farray);
+ 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",
break;
}
- clen = header.contentLengthB1 << 8;
- clen |= header.contentLengthB0;
-
- plen = header.paddingLength;
-
recv_again:
if (clen > sizeof(readbuf) - 1) {
readbuflen = sizeof(readbuf) - 1;
}
switch (type) {
- case FCGI_STDOUT:
+ case AP_FCGI_STDOUT:
if (clen != 0) {
b = apr_bucket_transient_create(readbuf,
readbuflen,
}
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)
}
break;
- case FCGI_END_REQUEST:
+ case AP_FCGI_END_REQUEST:
done = 1;
break;
apr_uint16_t request_id = 1;
apr_status_t rv;
- /* 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)