mod_proxy_fcgi.c revision 89c7a19f9c47b03f00f622a979490c9bccb2ff03
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Licensed to the Apache Software Foundation (ASF) under one or more
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * contributor license agreements. See the NOTICE file distributed with
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * this work for additional information regarding copyright ownership.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * The ASF licenses this file to You under the Apache License, Version 2.0
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * (the "License"); you may not use this file except in compliance with
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * the License. You may obtain a copy of the License at
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Unless required by applicable law or agreed to in writing, software
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * distributed under the License is distributed on an "AS IS" BASIS,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * See the License for the specific language governing permissions and
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * limitations under the License.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * The below 3 functions serve to map the FCGI structs
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * back and forth between an 8 byte array. We do this to avoid
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * any potential padding issues when we send or read these
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * structures.
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * NOTE: These have specific internal knowledge of the
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * layout of the fcgi_header and fcgi_begin_request_body
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic void fcgi_header_to_array(fcgi_header *h, unsigned char a[])
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny a[FCGI_HDR_REQUEST_ID_B1_OFFSET] = h->requestIdB1;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny a[FCGI_HDR_REQUEST_ID_B0_OFFSET] = h->requestIdB0;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny a[FCGI_HDR_CONTENT_LEN_B1_OFFSET] = h->contentLengthB1;
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce a[FCGI_HDR_CONTENT_LEN_B0_OFFSET] = h->contentLengthB0;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny a[FCGI_HDR_PADDING_LEN_OFFSET] = h->paddingLength;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic void fcgi_header_from_array(fcgi_header *h, unsigned char a[])
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce h->requestIdB1 = a[FCGI_HDR_REQUEST_ID_B1_OFFSET];
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny h->requestIdB0 = a[FCGI_HDR_REQUEST_ID_B0_OFFSET];
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny h->contentLengthB1 = a[FCGI_HDR_CONTENT_LEN_B1_OFFSET];
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny h->contentLengthB0 = a[FCGI_HDR_CONTENT_LEN_B0_OFFSET];
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny h->paddingLength = a[FCGI_HDR_PADDING_LEN_OFFSET];
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic void fcgi_begin_request_body_to_array(fcgi_begin_request_body *h,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny unsigned char a[])
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * Canonicalise http-like URLs.
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * scheme is the scheme for the URL
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * url is the URL starting with the first '/'
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * def_port is the default port for this scheme.
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorcestatic int proxy_fcgi_canon(request_rec *r, char *url)
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, r->server,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* if literal IPv6 address */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny host = apr_pstrcat(r->pool, "[", host, "]", NULL);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "proxy: FCGI: set r->filename to %s", r->filename);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "proxy: FCGI: set r->path_info to %s", r->path_info);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * Fill in a fastcgi request header with the following type, request id,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * content length, and padding length.
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * The header array must be at least FCGI_HEADER_LEN bytes long.
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny unsigned char type,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny unsigned char padding_len)
cb388d52f49f54963379cc20a25e14d17fe6e9a3Simo Sorce header->requestIdB1 = ((request_id >> 8) & 0xff);
cb388d52f49f54963379cc20a25e14d17fe6e9a3Simo Sorce header->contentLengthB1 = ((content_len >> 8) & 0xff);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny header->contentLengthB0 = ((content_len) & 0xff);
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce/* Wrapper for apr_socket_sendv that handles updating the worker stats. */
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorcestatic apr_status_t send_data(proxy_conn_rec *conn,
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce for (i = 0; i < nvec; i++) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (n > 0) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny break; /* short circuit out */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny/* Wrapper for apr_socket_recv that handles updating the worker stats. */
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorcestatic apr_status_t get_data(proxy_conn_rec *conn,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorcestatic apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorcestatic apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* XXX are there any FastCGI specific env vars we need to send? */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce * the TZ value specially. We could use that, but it would mean
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * parsing the key/value pairs back OUT of the allocated env array,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * not to mention allocating a totally useless array in the first
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce * place, which would suck. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny "proxy: FCGI: sending env var '%s' value '%s'",
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny "proxy: FCGI: truncating environment to %d bytes and %d elements",
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce for (i = 0; i < numenv; ++i) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Try to parse the script headers in the response from the back end fastcgi
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * server. Assumes that the contents of READBUF have already been added to
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * the end of OB. STATE holds the current header parsing state for this
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Returns -1 on error, 0 if it can't find the end of the headers, and 1 if
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * it found the end of the headers and scans them successfully. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny int status = ap_scan_script_header_err_brigade(r, ob, NULL);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny "proxy: FCGI: Error parsing script headers");
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidekstatic void dump_header_to_log(request_rec *r, unsigned char fheader[],
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (i >= 20) {
if (isprint(c)) {
asc_line[i] = c;
posn++;
int request_id)
while (! done) {
int last_stdin = 0;
sizeof(writebuf));
if (last_stdin) {
apr_bucket *b;
char plen;
if (readbuflen != 0) {
switch (type) {
case FCGI_STDOUT:
if (clen != 0) {
c->bucket_alloc);
if (! seen_end_of_headers) {
goto recv_again;
case FCGI_STDERR:
if (clen) {
goto recv_again;
case FCGI_END_REQUEST:
if (plen) {
return rv;
return HTTP_SERVICE_UNAVAILABLE;
return HTTP_SERVICE_UNAVAILABLE;
return HTTP_SERVICE_UNAVAILABLE;
return OK;
int status;
&proxy_module);
return DECLINED;
if (! backend) {
r->server);
if (backend) {
return status;
sizeof(server_portstr));
goto cleanup;
goto cleanup;
return status;