/* 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
*
*
* 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.
*/
#include "mod_proxy.h"
#include "util_fcgi.h"
#include "util_script.h"
typedef struct {
int need_dirwalk;
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
* def_port is the default port for this scheme.
*/
{
const char *err;
char *path;
url += 5;
}
else {
return DECLINED;
}
"canonicalising URL %s", url);
if (err) {
return HTTP_BAD_REQUEST;
}
else
sport[0] = '\0';
/* if literal IPv6 address */
}
}
else {
r->proxyreq);
}
return HTTP_BAD_REQUEST;
"set r->filename to %s", r->filename);
}
/* It has to be on disk for this to work */
}
if (split) {
if (slash) {
ap_unescape_url_keep2f(r->path_info, 0);
}
}
}
if (split) {
if (slash) {
ap_unescape_url_keep2f(r->path_info, 0);
}
}
}
else {
/* before proxy-fcgi-pathinfo had multi-values. This requires the
* the FCGI server to fixup PATH_INFO because it's the entire path
*/
ap_unescape_url_keep2f(r->path_info, 0);
}
"set r->path_info to %s", r->path_info);
}
}
return OK;
}
/* Wrapper for apr_socket_sendv that handles updating the worker stats. */
int nvec,
{
int i, offset;
for (i = 0; i < nvec; i++) {
}
offset = 0;
while (to_write) {
apr_size_t n = 0;
if (rv != APR_SUCCESS) {
break;
}
if (n > 0) {
written += n;
break; /* short circuit out */
offset++;
} else {
break;
}
}
}
}
return rv;
}
/* Wrapper for apr_socket_recv that handles updating the worker stats. */
char *buffer,
{
if (rv == APR_SUCCESS) {
}
return rv;
}
char *buffer,
{
do {
if (rv != APR_SUCCESS) {
return rv;
}
} while (cumulative_len < buflen);
return APR_SUCCESS;
}
{
sizeof(abrb), 0);
? AP_FCGI_KEEP_CONN : 0);
}
{
char *body;
if (rconf) {
if (rconf->need_dirwalk) {
}
}
/* Strip balancer prefix */
}
ap_add_cgi_vars(r);
r->filename = proxyfilename;
/* XXX are there any FastCGI specific env vars we need to send? */
* the TZ value specially. We could use that, but it would mean
* not to mention allocating a totally useless array in the first
* place, which would suck. */
if (APLOGrtrace8(r)) {
int i;
"sending env var '%s' value '%s'",
}
}
/* Send envvars over in as many FastCGI records as it takes, */
next_elem = 0; /* starting with the first one */
* to AP_FCGI_MAX_CONTENT_LEN
*/
&next_elem);
if (!required_len) {
APR_SIZE_T_FMT " bytes",
/* skip this envvar and continue */
++next_elem;
continue;
}
/* only an unused element at the end of the array */
break;
}
/* we pre-compute, so we can't run out of space */
/* compute and encode must be in sync */
(apr_uint16_t)required_len, 0);
if (rv) {
return rv;
}
}
/* Envvars sent, so say we're done */
}
enum {
};
/* Try to find the end of the script headers in the response from the back
* end fastcgi server. STATE holds the current header parsing state for this
* request.
*
* Returns 0 if it can't find the end of the headers, and 1 if it found the
* end of the headers. */
{
while (readlen--) {
if (*itr == '\r') {
switch (*state) {
case HDR_STATE_GOT_CRLF:
break;
default:
break;
}
}
else if (*itr == '\n') {
switch (*state) {
case HDR_STATE_GOT_LF:
break;
case HDR_STATE_GOT_CR:
break;
case HDR_STATE_GOT_CRLFCR:
break;
default:
break;
}
}
else {
}
if (*state == HDR_STATE_DONE_WITH_HEADERS)
break;
++itr;
}
if (*state == HDR_STATE_DONE_WITH_HEADERS) {
return 1;
}
return 0;
}
int *bad_request, int *has_responded)
{
conn_rec *c = r->connection;
}
while (! done) {
int n;
/* We need SOME kind of timeout here, or virtually anything will
* cause timeout errors. */
if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_EINTR(rv)) {
continue;
}
*err = "polling";
break;
}
int last_stdin = 0;
char *iobuf_cursor;
if (rv != APR_SUCCESS) {
*err = "reading input brigade";
*bad_request = 1;
break;
}
last_stdin = 1;
}
if (rv != APR_SUCCESS) {
*err = "flattening brigade";
break;
}
while (to_send > 0) {
int nvec = 0;
(apr_uint16_t)write_this_time, 0);
++nvec;
if (writebuflen) {
++nvec;
}
if (rv != APR_SUCCESS) {
*err = "sending stdin";
break;
}
}
if (rv != APR_SUCCESS) {
break;
}
if (last_stdin) {
/* signal EOF (empty FCGI_STDIN) */
0, 0);
if (rv != APR_SUCCESS) {
*err = "sending empty stdin";
break;
}
}
}
apr_bucket *b;
unsigned char plen;
/* First, we grab the header... */
if (rv != APR_SUCCESS) {
"Failed to read FastCGI header");
break;
}
farray, AP_FCGI_HEADER_LEN, 0);
if (version != AP_FCGI_VERSION_1) {
"Got bogus version %d", (int)version);
rv = APR_EINVAL;
break;
}
if (rid != request_id) {
"Got bogus rid %d, expected %d",
rid, request_id);
rv = APR_EINVAL;
break;
}
if (clen > iobuf_size) {
} else {
readbuflen = clen;
}
/* Now get the actual data. Yes it sucks to do this in a second
* recv call, this will eventually change when we move to real
* nonblocking recv calls. */
if (readbuflen != 0) {
if (rv != APR_SUCCESS) {
*err = "reading response body";
break;
}
}
switch (type) {
case AP_FCGI_STDOUT:
if (clen != 0) {
c->bucket_alloc);
if (! seen_end_of_headers) {
iobuf, readbuflen);
if (st == 1) {
int status;
seen_end_of_headers = 1;
/* suck in all the rest */
*has_responded = 1;
if (rv != APR_SUCCESS) {
*err = "passing headers brigade to output filters";
}
else if (status == HTTP_NOT_MODIFIED) {
/* The 304 response MUST NOT contain
* a message-body, ignore it. */
ignore_body = 1;
}
else {
"Error parsing script headers");
rv = APR_EINVAL;
}
break;
}
if (conf->error_override &&
ap_is_HTTP_ERROR(r->status)) {
/*
* set script_error_status to discard
* everything after the headers
*/
script_error_status = r->status;
/*
* prevent ap_die() from treating this as a
* recursive error, initially:
*/
}
if (script_error_status == HTTP_OK
/* Send the part of the body that we read while
* reading the headers.
*/
*has_responded = 1;
if (rv != APR_SUCCESS) {
*err = "passing brigade to output filters";
break;
}
}
}
else {
/* We're still looking for the end of the
* headers, so this part of the data will need
* to persist. */
}
} else {
/* we've already passed along the headers, so now pass
* through the content. we could simply continue to
* setaside the content and not pass until we see the
* 0 content-length (below, where we append the EOS),
* but that could be a huge amount of data; so we pass
* along smaller chunks
*/
*has_responded = 1;
if (rv != APR_SUCCESS) {
*err = "passing brigade to output filters";
break;
}
}
}
/* If we didn't read all the data, go back and get the
* rest of it. */
if (clen > readbuflen) {
clen -= readbuflen;
goto recv_again;
}
} else {
/* XXX what if we haven't seen end of the headers yet? */
if (script_error_status == HTTP_OK) {
b = apr_bucket_eos_create(c->bucket_alloc);
*has_responded = 1;
if (rv != APR_SUCCESS) {
*err = "passing brigade to output filters";
break;
}
}
/* XXX Why don't we cleanup here? (logic from AJP) */
}
break;
case AP_FCGI_STDERR:
/* TODO: Should probably clean up this logging a bit... */
if (clen) {
}
if (clen > readbuflen) {
clen -= readbuflen;
goto recv_again;
}
break;
case AP_FCGI_END_REQUEST:
done = 1;
break;
default:
"Got bogus record %d", type);
break;
}
/* Leave on above switch's inner error. */
if (rv != APR_SUCCESS) {
break;
}
if (plen) {
if (rv != APR_SUCCESS) {
"Error occurred reading padding");
break;
}
}
}
}
if (script_error_status != HTTP_OK) {
*has_responded = 1;
}
return rv;
}
/*
* process the request and write the response.
*/
char *url, char *server_portstr)
{
/* Request IDs are arbitrary numbers that we assign to a
* single request. This would allow multiplex/pipelining of
* multiple requests to the same FastCGI connection, but
* we don't support that, and always use a value of '1' to
* keep things simple. */
const char *err;
int bad_request = 0,
has_responded = 0;
/* Step 1: Send AP_FCGI_BEGIN_REQUEST */
if (rv != APR_SUCCESS) {
"Failed Writing Request to %s:", server_portstr);
return HTTP_SERVICE_UNAVAILABLE;
}
/* Step 2: Send Environment via FCGI_PARAMS */
if (rv != APR_SUCCESS) {
"Failed writing Environment to %s:", server_portstr);
return HTTP_SERVICE_UNAVAILABLE;
}
/* Step 3: Read records from the back end server and handle them. */
if (rv != APR_SUCCESS) {
"Error dispatching request to %s: %s%s%s",
if (has_responded) {
return AP_FILTER_ERROR;
}
if (bad_request) {
}
return HTTP_SERVICE_UNAVAILABLE;
}
return OK;
}
/*
* This handles fcgi:(dest) URLs
*/
{
int status;
&proxy_module);
apr_pool_t *p = r->pool;
"url: %s proxyname: %s proxyport: %d",
return DECLINED;
}
/* Create space for state information */
r->server);
if (backend) {
}
return status;
}
/* Step One: Determine Who To Connect To */
sizeof(server_portstr));
goto cleanup;
}
/* This scheme handler does not reuse connections by default, to
* avoid tying up a fastcgi that isn't expecting to work on
* parallel requests. But if the user went out of their way to
* type the default value of disablereuse=off, we'll allow it.
*/
}
/* Step Two: Make the Connection */
"failed to make connection to backend: %s",
goto cleanup;
}
/* Step Three: Process the Request */
return status;
}
{
}
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command apr_table_t */
register_hooks /* register hooks */
};