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.c129
1 files changed, 104 insertions, 25 deletions
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
index e2fb59cd..f528b3df 100644
--- a/modules/proxy/mod_proxy_fcgi.c
+++ b/modules/proxy/mod_proxy_fcgi.c
@@ -20,6 +20,10 @@
module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
+typedef struct {
+ int need_dirwalk;
+} fcgi_req_config_t;
+
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
@@ -29,8 +33,11 @@ module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
static int proxy_fcgi_canon(request_rec *r, char *url)
{
char *host, sport[7];
- const char *err, *path;
+ const char *err;
+ char *path;
apr_port_t port, def_port;
+ fcgi_req_config_t *rconf = NULL;
+ const char *pathinfo_type = NULL;
if (strncasecmp(url, "fcgi:", 5) == 0) {
url += 5;
@@ -76,11 +83,51 @@ static int proxy_fcgi_canon(request_rec *r, char *url)
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060)
"set r->filename to %s", r->filename);
- if (apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo")) {
- r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
+ rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
+ if (rconf == NULL) {
+ rconf = apr_pcalloc(r->pool, sizeof(fcgi_req_config_t));
+ ap_set_module_config(r->request_config, &proxy_fcgi_module, rconf);
+ }
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
- "set r->path_info to %s", r->path_info);
+ if (NULL != (pathinfo_type = apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo"))) {
+ /* It has to be on disk for this to work */
+ if (!strcasecmp(pathinfo_type, "full")) {
+ rconf->need_dirwalk = 1;
+ ap_unescape_url_keep2f(path, 0);
+ }
+ else if (!strcasecmp(pathinfo_type, "first-dot")) {
+ char *split = ap_strchr(path, '.');
+ if (split) {
+ char *slash = ap_strchr(split, '/');
+ if (slash) {
+ r->path_info = apr_pstrdup(r->pool, slash);
+ ap_unescape_url_keep2f(r->path_info, 0);
+ *slash = '\0'; /* truncate path */
+ }
+ }
+ }
+ else if (!strcasecmp(pathinfo_type, "last-dot")) {
+ char *split = ap_strrchr(path, '.');
+ if (split) {
+ char *slash = ap_strchr(split, '/');
+ if (slash) {
+ r->path_info = apr_pstrdup(r->pool, slash);
+ ap_unescape_url_keep2f(r->path_info, 0);
+ *slash = '\0'; /* truncate path */
+ }
+ }
+ }
+ else {
+ /* before proxy-fcgi-pathinfo had multi-values. This requires the
+ * the FCGI server to fixup PATH_INFO because it's the entire path
+ */
+ r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
+ if (!strcasecmp(pathinfo_type, "unescape")) {
+ ap_unescape_url_keep2f(r->path_info, 0);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
+ "set r->path_info to %s", r->path_info);
+ }
}
return OK;
@@ -204,9 +251,26 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
apr_status_t rv;
apr_size_t avail_len, len, required_len;
int next_elem, starting_elem;
+ char *proxyfilename = r->filename;
+ fcgi_req_config_t *rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
+
+ if (rconf) {
+ if (rconf->need_dirwalk) {
+ ap_directory_walk(r);
+ }
+ }
+
+ /* Strip balancer prefix */
+ if (r->filename && !strncmp(r->filename, "proxy:balancer://", 17)) {
+ char *newfname = apr_pstrdup(r->pool, r->filename+17);
+ newfname = ap_strchr(newfname, '/');
+ r->filename = newfname;
+ }
ap_add_common_vars(r);
ap_add_cgi_vars(r);
+
+ r->filename = proxyfilename;
/* XXX are there any FastCGI specific env vars we need to send? */
@@ -308,13 +372,12 @@ enum {
*
* Returns 0 if it can't find the end of the headers, and 1 if it found the
* end of the headers. */
-static int handle_headers(request_rec *r,
- int *state,
- char *readbuf)
+static int handle_headers(request_rec *r, int *state,
+ const char *readbuf, apr_size_t readlen)
{
const char *itr = readbuf;
- while (*itr) {
+ while (readlen--) {
if (*itr == '\r') {
switch (*state) {
case HDR_STATE_GOT_CRLF:
@@ -368,7 +431,7 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
const char **err)
{
apr_bucket_brigade *ib, *ob;
- int seen_end_of_headers = 0, done = 0;
+ int seen_end_of_headers = 0, done = 0, ignore_body = 0;
apr_status_t rv = APR_SUCCESS;
int script_error_status = HTTP_OK;
conn_rec *c = r->connection;
@@ -561,7 +624,8 @@ recv_again:
APR_BRIGADE_INSERT_TAIL(ob, b);
if (! seen_end_of_headers) {
- int st = handle_headers(r, &header_state, iobuf);
+ int st = handle_headers(r, &header_state,
+ iobuf, readbuflen);
if (st == 1) {
int status;
@@ -577,9 +641,16 @@ recv_again:
APR_BRIGADE_INSERT_TAIL(ob, tmp_b);
r->status = status;
ap_pass_brigade(r->output_filters, ob);
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
- "Error parsing script headers");
- rv = APR_EINVAL;
+ if (status == HTTP_NOT_MODIFIED) {
+ /* The 304 response MUST NOT contain
+ * a message-body, ignore it. */
+ ignore_body = 1;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
+ "Error parsing script headers");
+ rv = APR_EINVAL;
+ }
break;
}
@@ -598,7 +669,7 @@ recv_again:
}
if (script_error_status == HTTP_OK
- && !APR_BRIGADE_EMPTY(ob)) {
+ && !APR_BRIGADE_EMPTY(ob) && !ignore_body) {
/* Send the part of the body that we read while
* reading the headers.
*/
@@ -626,7 +697,7 @@ recv_again:
* but that could be a huge amount of data; so we pass
* along smaller chunks
*/
- if (script_error_status == HTTP_OK) {
+ if (script_error_status == HTTP_OK && !ignore_body) {
rv = ap_pass_brigade(r->output_filters, ob);
if (rv != APR_SUCCESS) {
*err = "passing brigade to output filters";
@@ -636,7 +707,7 @@ recv_again:
apr_brigade_cleanup(ob);
}
- /* If we didn't read all the data go back and get the
+ /* If we didn't read all the data, go back and get the
* rest of it. */
if (clen > readbuflen) {
clen -= readbuflen;
@@ -663,7 +734,7 @@ recv_again:
/* TODO: Should probably clean up this logging a bit... */
if (clen) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
- "Got error '%s'", iobuf);
+ "Got error '%.*s'", (int)readbuflen, iobuf);
}
if (clen > readbuflen) {
@@ -681,12 +752,16 @@ recv_again:
"Got bogus record %d", type);
break;
}
+ /* Leave on above switch's inner error. */
+ if (rv != APR_SUCCESS) {
+ break;
+ }
if (plen) {
rv = get_data_full(conn, iobuf, plen);
if (rv != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- APLOGNO(02537) "Error occurred reading padding");
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02537)
+ "Error occurred reading padding");
break;
}
}
@@ -714,7 +789,7 @@ static int fcgi_do_request(apr_pool_t *p, request_rec *r,
char *url, char *server_portstr)
{
/* Request IDs are arbitrary numbers that we assign to a
- * single request. This would allow multiplex/pipelinig of
+ * 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. */
@@ -814,11 +889,15 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
goto cleanup;
}
- /* XXX Setting close to 0 is a great way to end up with
- * timeouts at this point, since we lack good ways to manage the
- * back end fastcgi processes. This should be revisited when we
- * have a better story on that part of things. */
+ /* This scheme handler does not reuse connections by default, to
+ * avoid tieing 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.
+ */
backend->close = 1;
+ if (worker->s->disablereuse_set && !worker->s->disablereuse) {
+ backend->close = 0;
+ }
/* Step Two: Make the Connection */
if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {