diff options
author | CoprDistGit <infra@openeuler.org> | 2024-08-05 02:42:43 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2024-08-05 02:42:43 +0000 |
commit | 03f1098961e7cd0a5a3b7d93ca59f9176b7f63ac (patch) | |
tree | 4d0a57939694fecbd2850cf3f43e9aaa1ea60017 /httpd-2.4.48-r1828172+.patch | |
parent | 707da8b4b72f9e9ec132f1a285b9ce28b6a2b667 (diff) |
automatic import of httpdopeneuler24.03_LTS
Diffstat (limited to 'httpd-2.4.48-r1828172+.patch')
-rw-r--r-- | httpd-2.4.48-r1828172+.patch | 1411 |
1 files changed, 1411 insertions, 0 deletions
diff --git a/httpd-2.4.48-r1828172+.patch b/httpd-2.4.48-r1828172+.patch new file mode 100644 index 0000000..2b99d69 --- /dev/null +++ b/httpd-2.4.48-r1828172+.patch @@ -0,0 +1,1411 @@ +--- httpd-2.4.48/modules/generators/cgi_common.h.r1828172+ ++++ httpd-2.4.48/modules/generators/cgi_common.h +@@ -0,0 +1,366 @@ ++/* 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. ++ */ ++ ++#include "apr.h" ++#include "apr_strings.h" ++#include "apr_buckets.h" ++#include "apr_lib.h" ++#include "apr_poll.h" ++ ++#define APR_WANT_STRFUNC ++#define APR_WANT_MEMFUNC ++#include "apr_want.h" ++ ++#include "httpd.h" ++#include "util_filter.h" ++ ++static void discard_script_output(apr_bucket_brigade *bb) ++{ ++ apr_bucket *e; ++ const char *buf; ++ apr_size_t len; ++ ++ for (e = APR_BRIGADE_FIRST(bb); ++ e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e); ++ e = APR_BRIGADE_FIRST(bb)) ++ { ++ if (apr_bucket_read(e, &buf, &len, APR_BLOCK_READ)) { ++ break; ++ } ++ apr_bucket_delete(e); ++ } ++} ++ ++#ifdef WANT_CGI_BUCKET ++/* A CGI bucket type is needed to catch any output to stderr from the ++ * script; see PR 22030. */ ++static const apr_bucket_type_t bucket_type_cgi; ++ ++struct cgi_bucket_data { ++ apr_pollset_t *pollset; ++ request_rec *r; ++ apr_interval_time_t timeout; ++}; ++ ++/* Create a CGI bucket using pipes from script stdout 'out' ++ * and stderr 'err', for request 'r'. */ ++static apr_bucket *cgi_bucket_create(request_rec *r, ++ apr_interval_time_t timeout, ++ apr_file_t *out, apr_file_t *err, ++ apr_bucket_alloc_t *list) ++{ ++ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); ++ apr_status_t rv; ++ apr_pollfd_t fd; ++ struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); ++ ++ /* Disable APR timeout handling since we'll use poll() entirely. */ ++ apr_file_pipe_timeout_set(out, 0); ++ apr_file_pipe_timeout_set(err, 0); ++ ++ APR_BUCKET_INIT(b); ++ b->free = apr_bucket_free; ++ b->list = list; ++ b->type = &bucket_type_cgi; ++ b->length = (apr_size_t)(-1); ++ b->start = -1; ++ ++ /* Create the pollset */ ++ rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) ++ "apr_pollset_create(); check system or user limits"); ++ return NULL; ++ } ++ ++ fd.desc_type = APR_POLL_FILE; ++ fd.reqevents = APR_POLLIN; ++ fd.p = r->pool; ++ fd.desc.f = out; /* script's stdout */ ++ fd.client_data = (void *)1; ++ rv = apr_pollset_add(data->pollset, &fd); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) ++ "apr_pollset_add(); check system or user limits"); ++ return NULL; ++ } ++ ++ fd.desc.f = err; /* script's stderr */ ++ fd.client_data = (void *)2; ++ rv = apr_pollset_add(data->pollset, &fd); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) ++ "apr_pollset_add(); check system or user limits"); ++ return NULL; ++ } ++ ++ data->r = r; ++ data->timeout = timeout; ++ b->data = data; ++ return b; ++} ++ ++/* Create a duplicate CGI bucket using given bucket data */ ++static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, ++ apr_bucket_alloc_t *list) ++{ ++ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); ++ APR_BUCKET_INIT(b); ++ b->free = apr_bucket_free; ++ b->list = list; ++ b->type = &bucket_type_cgi; ++ b->length = (apr_size_t)(-1); ++ b->start = -1; ++ b->data = data; ++ return b; ++} ++ ++/* Handle stdout from CGI child. Duplicate of logic from the _read ++ * method of the real APR pipe bucket implementation. */ ++static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, ++ const char **str, apr_size_t *len) ++{ ++ char *buf; ++ apr_status_t rv; ++ ++ *str = NULL; ++ *len = APR_BUCKET_BUFF_SIZE; ++ buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ ++ ++ rv = apr_file_read(out, buf, len); ++ ++ if (rv != APR_SUCCESS && rv != APR_EOF) { ++ apr_bucket_free(buf); ++ return rv; ++ } ++ ++ if (*len > 0) { ++ struct cgi_bucket_data *data = a->data; ++ apr_bucket_heap *h; ++ ++ /* Change the current bucket to refer to what we read */ ++ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); ++ h = a->data; ++ h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ ++ *str = buf; ++ APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); ++ } ++ else { ++ apr_bucket_free(buf); ++ a = apr_bucket_immortal_make(a, "", 0); ++ *str = a->data; ++ } ++ return rv; ++} ++ ++/* Read method of CGI bucket: polls on stderr and stdout of the child, ++ * sending any stderr output immediately away to the error log. */ ++static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, ++ apr_size_t *len, apr_read_type_e block) ++{ ++ struct cgi_bucket_data *data = b->data; ++ apr_interval_time_t timeout = 0; ++ apr_status_t rv; ++ int gotdata = 0; ++ ++ if (block != APR_NONBLOCK_READ) { ++ timeout = data->timeout > 0 ? data->timeout : data->r->server->timeout; ++ } ++ ++ do { ++ const apr_pollfd_t *results; ++ apr_int32_t num; ++ ++ rv = apr_pollset_poll(data->pollset, timeout, &num, &results); ++ if (APR_STATUS_IS_TIMEUP(rv)) { ++ if (timeout) { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) ++ "Timeout waiting for output from CGI script %s", ++ data->r->filename); ++ return rv; ++ } ++ else { ++ return APR_EAGAIN; ++ } ++ } ++ else if (APR_STATUS_IS_EINTR(rv)) { ++ continue; ++ } ++ else if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) ++ "poll failed waiting for CGI child"); ++ return rv; ++ } ++ ++ for (; num; num--, results++) { ++ if (results[0].client_data == (void *)1) { ++ /* stdout */ ++ rv = cgi_read_stdout(b, results[0].desc.f, str, len); ++ if (APR_STATUS_IS_EOF(rv)) { ++ rv = APR_SUCCESS; ++ } ++ gotdata = 1; ++ } else { ++ /* stderr */ ++ apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); ++ if (APR_STATUS_IS_EOF(rv2)) { ++ apr_pollset_remove(data->pollset, &results[0]); ++ } ++ } ++ } ++ ++ } while (!gotdata); ++ ++ return rv; ++} ++ ++static const apr_bucket_type_t bucket_type_cgi = { ++ "CGI", 5, APR_BUCKET_DATA, ++ apr_bucket_destroy_noop, ++ cgi_bucket_read, ++ apr_bucket_setaside_notimpl, ++ apr_bucket_split_notimpl, ++ apr_bucket_copy_notimpl ++}; ++ ++#endif /* WANT_CGI_BUCKET */ ++ ++/* Handle the CGI response output, having set up the brigade with the ++ * CGI or PIPE bucket as appropriate. */ ++static int cgi_handle_response(request_rec *r, int nph, apr_bucket_brigade *bb, ++ apr_interval_time_t timeout, cgi_server_conf *conf, ++ char *logdata, apr_file_t *script_err) ++{ ++ apr_status_t rv; ++ ++ /* Handle script return... */ ++ if (!nph) { ++ const char *location; ++ char sbuf[MAX_STRING_LEN]; ++ int ret; ++ ++ if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, ++ APLOG_MODULE_INDEX))) ++ { ++ /* In the case of a timeout reading script output, clear ++ * the brigade to avoid a second attempt to read the ++ * output. */ ++ if (ret == HTTP_GATEWAY_TIME_OUT) { ++ apr_brigade_cleanup(bb); ++ } ++ ++ ret = log_script(r, conf, ret, logdata, sbuf, bb, script_err); ++ ++ /* ++ * ret could be HTTP_NOT_MODIFIED in the case that the CGI script ++ * does not set an explicit status and ap_meets_conditions, which ++ * is called by ap_scan_script_header_err_brigade, detects that ++ * the conditions of the requests are met and the response is ++ * not modified. ++ * In this case set r->status and return OK in order to prevent ++ * running through the error processing stack as this would ++ * break with mod_cache, if the conditions had been set by ++ * mod_cache itself to validate a stale entity. ++ * BTW: We circumvent the error processing stack anyway if the ++ * CGI script set an explicit status code (whatever it is) and ++ * the only possible values for ret here are: ++ * ++ * HTTP_NOT_MODIFIED (set by ap_meets_conditions) ++ * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) ++ * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the ++ * processing of the response of the CGI script, e.g broken headers ++ * or a crashed CGI process). ++ */ ++ if (ret == HTTP_NOT_MODIFIED) { ++ r->status = ret; ++ return OK; ++ } ++ ++ return ret; ++ } ++ ++ location = apr_table_get(r->headers_out, "Location"); ++ ++ if (location && r->status == 200) { ++ /* For a redirect whether internal or not, discard any ++ * remaining stdout from the script, and log any remaining ++ * stderr output, as normal. */ ++ discard_script_output(bb); ++ apr_brigade_destroy(bb); ++ ++ if (script_err) { ++ apr_file_pipe_timeout_set(script_err, timeout); ++ log_script_err(r, script_err); ++ } ++ } ++ ++ if (location && location[0] == '/' && r->status == 200) { ++ /* This redirect needs to be a GET no matter what the original ++ * method was. ++ */ ++ r->method = "GET"; ++ r->method_number = M_GET; ++ ++ /* We already read the message body (if any), so don't allow ++ * the redirected request to think it has one. We can ignore ++ * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. ++ */ ++ apr_table_unset(r->headers_in, "Content-Length"); ++ ++ ap_internal_redirect_handler(location, r); ++ return OK; ++ } ++ else if (location && r->status == 200) { ++ /* XXX: Note that if a script wants to produce its own Redirect ++ * body, it now has to explicitly *say* "Status: 302" ++ */ ++ discard_script_output(bb); ++ apr_brigade_destroy(bb); ++ return HTTP_MOVED_TEMPORARILY; ++ } ++ ++ rv = ap_pass_brigade(r->output_filters, bb); ++ } ++ else /* nph */ { ++ struct ap_filter_t *cur; ++ ++ /* get rid of all filters up through protocol... since we ++ * haven't parsed off the headers, there is no way they can ++ * work ++ */ ++ ++ cur = r->proto_output_filters; ++ while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { ++ cur = cur->next; ++ } ++ r->output_filters = r->proto_output_filters = cur; ++ ++ rv = ap_pass_brigade(r->output_filters, bb); ++ } ++ ++ /* don't soak up script output if errors occurred writing it ++ * out... otherwise, we prolong the life of the script when the ++ * connection drops or we stopped sending output for some other ++ * reason */ ++ if (script_err && rv == APR_SUCCESS && !r->connection->aborted) { ++ apr_file_pipe_timeout_set(script_err, timeout); ++ log_script_err(r, script_err); ++ } ++ ++ if (script_err) apr_file_close(script_err); ++ ++ return OK; /* NOT r->status, even if it has changed. */ ++} +--- httpd-2.4.48/modules/generators/config5.m4.r1828172+ ++++ httpd-2.4.48/modules/generators/config5.m4 +@@ -78,4 +78,15 @@ + + APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) + ++AC_ARG_ENABLE(cgid-fdpassing, ++ [APACHE_HELP_STRING(--enable-cgid-fdpassing,Enable experimental mod_cgid support for fd passing)], ++ [if test "$enableval" = "yes"; then ++ AC_CHECK_DECL(CMSG_DATA, ++ [AC_DEFINE([HAVE_CGID_FDPASSING], 1, [Enable FD passing support in mod_cgid])], ++ [AC_MSG_ERROR([cannot support mod_cgid fd-passing on this system])], [ ++#include <sys/types.h> ++#include <sys/socket.h>]) ++ fi ++]) ++ + APACHE_MODPATH_FINISH +--- httpd-2.4.48/modules/generators/mod_cgi.c.r1828172+ ++++ httpd-2.4.48/modules/generators/mod_cgi.c +@@ -92,6 +92,10 @@ + apr_size_t bufbytes; + } cgi_server_conf; + ++typedef struct { ++ apr_interval_time_t timeout; ++} cgi_dirconf; ++ + static void *create_cgi_config(apr_pool_t *p, server_rec *s) + { + cgi_server_conf *c = +@@ -112,6 +116,12 @@ + return overrides->logname ? overrides : base; + } + ++static void *create_cgi_dirconf(apr_pool_t *p, char *dummy) ++{ ++ cgi_dirconf *c = (cgi_dirconf *) apr_pcalloc(p, sizeof(cgi_dirconf)); ++ return c; ++} ++ + static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) + { + server_rec *s = cmd->server; +@@ -150,6 +160,17 @@ + return NULL; + } + ++static const char *set_script_timeout(cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ cgi_dirconf *dc = dummy; ++ ++ if (ap_timeout_parameter_parse(arg, &dc->timeout, "s") != APR_SUCCESS) { ++ return "CGIScriptTimeout has wrong format"; ++ } ++ ++ return NULL; ++} ++ + static const command_rec cgi_cmds[] = + { + AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, +@@ -158,6 +179,9 @@ + "the maximum length (in bytes) of the script debug log"), + AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, + "the maximum size (in bytes) to record of a POST request"), ++AP_INIT_TAKE1("CGIScriptTimeout", set_script_timeout, NULL, RSRC_CONF | ACCESS_CONF, ++ "The amount of time to wait between successful reads from " ++ "the CGI script, in seconds."), + {NULL} + }; + +@@ -466,23 +490,26 @@ + apr_filepath_name_get(r->filename)); + } + else { ++ cgi_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgi_module); ++ apr_interval_time_t timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; ++ + apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); + + *script_in = procnew->out; + if (!*script_in) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_in, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_in, timeout); + + if (e_info->prog_type == RUN_AS_CGI) { + *script_out = procnew->in; + if (!*script_out) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_out, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_out, timeout); + + *script_err = procnew->err; + if (!*script_err) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_err, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_err, timeout); + } + } + } +@@ -536,209 +563,12 @@ + return APR_SUCCESS; + } + +-static void discard_script_output(apr_bucket_brigade *bb) +-{ +- apr_bucket *e; +- const char *buf; +- apr_size_t len; +- +- for (e = APR_BRIGADE_FIRST(bb); +- e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e); +- e = APR_BRIGADE_FIRST(bb)) +- { +- if (apr_bucket_read(e, &buf, &len, APR_BLOCK_READ)) { +- break; +- } +- apr_bucket_delete(e); +- } +-} +- + #if APR_FILES_AS_SOCKETS +- +-/* A CGI bucket type is needed to catch any output to stderr from the +- * script; see PR 22030. */ +-static const apr_bucket_type_t bucket_type_cgi; +- +-struct cgi_bucket_data { +- apr_pollset_t *pollset; +- request_rec *r; +-}; +- +-/* Create a CGI bucket using pipes from script stdout 'out' +- * and stderr 'err', for request 'r'. */ +-static apr_bucket *cgi_bucket_create(request_rec *r, +- apr_file_t *out, apr_file_t *err, +- apr_bucket_alloc_t *list) +-{ +- apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); +- apr_status_t rv; +- apr_pollfd_t fd; +- struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); +- +- APR_BUCKET_INIT(b); +- b->free = apr_bucket_free; +- b->list = list; +- b->type = &bucket_type_cgi; +- b->length = (apr_size_t)(-1); +- b->start = -1; +- +- /* Create the pollset */ +- rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) +- "apr_pollset_create(); check system or user limits"); +- return NULL; +- } +- +- fd.desc_type = APR_POLL_FILE; +- fd.reqevents = APR_POLLIN; +- fd.p = r->pool; +- fd.desc.f = out; /* script's stdout */ +- fd.client_data = (void *)1; +- rv = apr_pollset_add(data->pollset, &fd); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) +- "apr_pollset_add(); check system or user limits"); +- return NULL; +- } +- +- fd.desc.f = err; /* script's stderr */ +- fd.client_data = (void *)2; +- rv = apr_pollset_add(data->pollset, &fd); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) +- "apr_pollset_add(); check system or user limits"); +- return NULL; +- } +- +- data->r = r; +- b->data = data; +- return b; +-} +- +-/* Create a duplicate CGI bucket using given bucket data */ +-static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, +- apr_bucket_alloc_t *list) +-{ +- apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); +- APR_BUCKET_INIT(b); +- b->free = apr_bucket_free; +- b->list = list; +- b->type = &bucket_type_cgi; +- b->length = (apr_size_t)(-1); +- b->start = -1; +- b->data = data; +- return b; +-} +- +-/* Handle stdout from CGI child. Duplicate of logic from the _read +- * method of the real APR pipe bucket implementation. */ +-static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, +- const char **str, apr_size_t *len) +-{ +- char *buf; +- apr_status_t rv; +- +- *str = NULL; +- *len = APR_BUCKET_BUFF_SIZE; +- buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ +- +- rv = apr_file_read(out, buf, len); +- +- if (rv != APR_SUCCESS && rv != APR_EOF) { +- apr_bucket_free(buf); +- return rv; +- } +- +- if (*len > 0) { +- struct cgi_bucket_data *data = a->data; +- apr_bucket_heap *h; +- +- /* Change the current bucket to refer to what we read */ +- a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); +- h = a->data; +- h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ +- *str = buf; +- APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); +- } +- else { +- apr_bucket_free(buf); +- a = apr_bucket_immortal_make(a, "", 0); +- *str = a->data; +- } +- return rv; +-} +- +-/* Read method of CGI bucket: polls on stderr and stdout of the child, +- * sending any stderr output immediately away to the error log. */ +-static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, +- apr_size_t *len, apr_read_type_e block) +-{ +- struct cgi_bucket_data *data = b->data; +- apr_interval_time_t timeout; +- apr_status_t rv; +- int gotdata = 0; +- +- timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout; +- +- do { +- const apr_pollfd_t *results; +- apr_int32_t num; +- +- rv = apr_pollset_poll(data->pollset, timeout, &num, &results); +- if (APR_STATUS_IS_TIMEUP(rv)) { +- if (timeout) { +- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) +- "Timeout waiting for output from CGI script %s", +- data->r->filename); +- return rv; +- } +- else { +- return APR_EAGAIN; +- } +- } +- else if (APR_STATUS_IS_EINTR(rv)) { +- continue; +- } +- else if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) +- "poll failed waiting for CGI child"); +- return rv; +- } +- +- for (; num; num--, results++) { +- if (results[0].client_data == (void *)1) { +- /* stdout */ +- rv = cgi_read_stdout(b, results[0].desc.f, str, len); +- if (APR_STATUS_IS_EOF(rv)) { +- rv = APR_SUCCESS; +- } +- gotdata = 1; +- } else { +- /* stderr */ +- apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); +- if (APR_STATUS_IS_EOF(rv2)) { +- apr_pollset_remove(data->pollset, &results[0]); +- } +- } +- } +- +- } while (!gotdata); +- +- return rv; +-} +- +-static const apr_bucket_type_t bucket_type_cgi = { +- "CGI", 5, APR_BUCKET_DATA, +- apr_bucket_destroy_noop, +- cgi_bucket_read, +- apr_bucket_setaside_notimpl, +- apr_bucket_split_notimpl, +- apr_bucket_copy_notimpl +-}; +- ++#define WANT_CGI_BUCKET + #endif + ++#include "cgi_common.h" ++ + static int cgi_handler(request_rec *r) + { + int nph; +@@ -757,6 +587,8 @@ + apr_status_t rv; + cgi_exec_info_t e_info; + conn_rec *c; ++ cgi_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgi_module); ++ apr_interval_time_t timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; +@@ -916,10 +748,7 @@ + AP_DEBUG_ASSERT(script_in != NULL); + + #if APR_FILES_AS_SOCKETS +- apr_file_pipe_timeout_set(script_in, 0); +- apr_file_pipe_timeout_set(script_err, 0); +- +- b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc); ++ b = cgi_bucket_create(r, dc->timeout, script_in, script_err, c->bucket_alloc); + if (b == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + #else +@@ -929,111 +758,7 @@ + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + +- /* Handle script return... */ +- if (!nph) { +- const char *location; +- char sbuf[MAX_STRING_LEN]; +- int ret; +- +- if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, +- APLOG_MODULE_INDEX))) +- { +- ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err); +- +- /* +- * ret could be HTTP_NOT_MODIFIED in the case that the CGI script +- * does not set an explicit status and ap_meets_conditions, which +- * is called by ap_scan_script_header_err_brigade, detects that +- * the conditions of the requests are met and the response is +- * not modified. +- * In this case set r->status and return OK in order to prevent +- * running through the error processing stack as this would +- * break with mod_cache, if the conditions had been set by +- * mod_cache itself to validate a stale entity. +- * BTW: We circumvent the error processing stack anyway if the +- * CGI script set an explicit status code (whatever it is) and +- * the only possible values for ret here are: +- * +- * HTTP_NOT_MODIFIED (set by ap_meets_conditions) +- * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) +- * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the +- * processing of the response of the CGI script, e.g broken headers +- * or a crashed CGI process). +- */ +- if (ret == HTTP_NOT_MODIFIED) { +- r->status = ret; +- return OK; +- } +- +- return ret; +- } +- +- location = apr_table_get(r->headers_out, "Location"); +- +- if (location && r->status == 200) { +- /* For a redirect whether internal or not, discard any +- * remaining stdout from the script, and log any remaining +- * stderr output, as normal. */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- apr_file_pipe_timeout_set(script_err, r->server->timeout); +- log_script_err(r, script_err); +- } +- +- if (location && location[0] == '/' && r->status == 200) { +- /* This redirect needs to be a GET no matter what the original +- * method was. +- */ +- r->method = "GET"; +- r->method_number = M_GET; +- +- /* We already read the message body (if any), so don't allow +- * the redirected request to think it has one. We can ignore +- * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. +- */ +- apr_table_unset(r->headers_in, "Content-Length"); +- +- ap_internal_redirect_handler(location, r); +- return OK; +- } +- else if (location && r->status == 200) { +- /* XXX: Note that if a script wants to produce its own Redirect +- * body, it now has to explicitly *say* "Status: 302" +- */ +- return HTTP_MOVED_TEMPORARILY; +- } +- +- rv = ap_pass_brigade(r->output_filters, bb); +- } +- else /* nph */ { +- struct ap_filter_t *cur; +- +- /* get rid of all filters up through protocol... since we +- * haven't parsed off the headers, there is no way they can +- * work +- */ +- +- cur = r->proto_output_filters; +- while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { +- cur = cur->next; +- } +- r->output_filters = r->proto_output_filters = cur; +- +- rv = ap_pass_brigade(r->output_filters, bb); +- } +- +- /* don't soak up script output if errors occurred writing it +- * out... otherwise, we prolong the life of the script when the +- * connection drops or we stopped sending output for some other +- * reason */ +- if (rv == APR_SUCCESS && !r->connection->aborted) { +- apr_file_pipe_timeout_set(script_err, r->server->timeout); +- log_script_err(r, script_err); +- } +- +- apr_file_close(script_err); +- +- return OK; /* NOT r->status, even if it has changed. */ ++ return cgi_handle_response(r, nph, bb, timeout, conf, dbuf, script_err); + } + + /*============================================================================ +@@ -1268,7 +993,7 @@ + AP_DECLARE_MODULE(cgi) = + { + STANDARD20_MODULE_STUFF, +- NULL, /* dir config creater */ ++ create_cgi_dirconf, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cgi_config, /* server config */ + merge_cgi_config, /* merge server config */ +--- httpd-2.4.48/modules/generators/mod_cgid.c.r1828172+ ++++ httpd-2.4.48/modules/generators/mod_cgid.c +@@ -342,15 +342,19 @@ + return close(fd); + } + +-/* deal with incomplete reads and signals +- * assume you really have to read buf_size bytes +- */ +-static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size) ++/* Read from the socket dealing with incomplete messages and signals. ++ * Returns 0 on success or errno on failure. Stderr fd passed as ++ * auxiliary data from other end is written to *errfd, or else stderr ++ * fileno if not present. */ ++static apr_status_t sock_readhdr(int fd, int *errfd, void *vbuf, size_t buf_size) + { +- char *buf = vbuf; + int rc; ++#ifndef HAVE_CGID_FDPASSING ++ char *buf = vbuf; + size_t bytes_read = 0; + ++ if (errfd) *errfd = 0; ++ + do { + do { + rc = read(fd, buf + bytes_read, buf_size - bytes_read); +@@ -365,9 +369,60 @@ + } + } while (bytes_read < buf_size); + ++ ++#else /* with FD passing */ ++ struct msghdr msg = {0}; ++ struct iovec vec = {vbuf, buf_size}; ++ struct cmsghdr *cmsg; ++ union { /* union to ensure alignment */ ++ struct cmsghdr cm; ++ char buf[CMSG_SPACE(sizeof(int))]; ++ } u; ++ ++ msg.msg_iov = &vec; ++ msg.msg_iovlen = 1; ++ ++ if (errfd) { ++ msg.msg_control = u.buf; ++ msg.msg_controllen = sizeof(u.buf); ++ *errfd = 0; ++ } ++ ++ /* use MSG_WAITALL to skip loop on truncated reads */ ++ do { ++ rc = recvmsg(fd, &msg, MSG_WAITALL); ++ } while (rc < 0 && errno == EINTR); ++ ++ if (rc == 0) { ++ return ECONNRESET; ++ } ++ else if (rc < 0) { ++ return errno; ++ } ++ else if (rc != buf_size) { ++ /* MSG_WAITALL should ensure the recvmsg blocks until the ++ * entire length is read, but let's be paranoid. */ ++ return APR_INCOMPLETE; ++ } ++ ++ if (errfd ++ && (cmsg = CMSG_FIRSTHDR(&msg)) != NULL ++ && cmsg->cmsg_len == CMSG_LEN(sizeof(*errfd)) ++ && cmsg->cmsg_level == SOL_SOCKET ++ && cmsg->cmsg_type == SCM_RIGHTS) { ++ *errfd = *((int *) CMSG_DATA(cmsg)); ++ } ++#endif ++ + return APR_SUCCESS; + } + ++/* As sock_readhdr but without auxiliary fd passing. */ ++static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size) ++{ ++ return sock_readhdr(fd, NULL, vbuf, buf_size); ++} ++ + /* deal with signals + */ + static apr_status_t sock_write(int fd, const void *buf, size_t buf_size) +@@ -384,7 +439,7 @@ + return APR_SUCCESS; + } + +-static apr_status_t sock_writev(int fd, request_rec *r, int count, ...) ++static apr_status_t sock_writev(int fd, int auxfd, request_rec *r, int count, ...) + { + va_list ap; + int rc; +@@ -399,9 +454,39 @@ + } + va_end(ap); + ++#ifndef HAVE_CGID_FDPASSING + do { + rc = writev(fd, vec, count); + } while (rc < 0 && errno == EINTR); ++#else ++ { ++ struct msghdr msg = { 0 }; ++ struct cmsghdr *cmsg; ++ union { /* union for alignment */ ++ char buf[CMSG_SPACE(sizeof(int))]; ++ struct cmsghdr align; ++ } u; ++ ++ msg.msg_iov = vec; ++ msg.msg_iovlen = count; ++ ++ if (auxfd) { ++ msg.msg_control = u.buf; ++ msg.msg_controllen = sizeof(u.buf); ++ ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ cmsg->cmsg_len = CMSG_LEN(sizeof(int)); ++ *((int *) CMSG_DATA(cmsg)) = auxfd; ++ } ++ ++ do { ++ rc = sendmsg(fd, &msg, 0); ++ } while (rc < 0 && errno == EINTR); ++ } ++#endif ++ + if (rc < 0) { + return errno; + } +@@ -410,7 +495,7 @@ + } + + static apr_status_t get_req(int fd, request_rec *r, char **argv0, char ***env, +- cgid_req_t *req) ++ int *errfd, cgid_req_t *req) + { + int i; + char **environ; +@@ -421,7 +506,7 @@ + r->server = apr_pcalloc(r->pool, sizeof(server_rec)); + + /* read the request header */ +- stat = sock_read(fd, req, sizeof(*req)); ++ stat = sock_readhdr(fd, errfd, req, sizeof(*req)); + if (stat != APR_SUCCESS) { + return stat; + } +@@ -479,14 +564,15 @@ + return APR_SUCCESS; + } + +-static apr_status_t send_req(int fd, request_rec *r, char *argv0, char **env, +- int req_type) ++static apr_status_t send_req(int fd, apr_file_t *errpipe, request_rec *r, ++ char *argv0, char **env, int req_type) + { + int i; + cgid_req_t req = {0}; + apr_status_t stat; + ap_unix_identity_t * ugid = ap_run_get_suexec_identity(r); + core_dir_config *core_conf = ap_get_core_module_config(r->per_dir_config); ++ int errfd; + + + if (ugid == NULL) { +@@ -507,16 +593,21 @@ + req.args_len = r->args ? strlen(r->args) : 0; + req.loglevel = r->server->log.level; + ++ if (errpipe) ++ apr_os_file_get(&errfd, errpipe); ++ else ++ errfd = 0; ++ + /* Write the request header */ + if (req.args_len) { +- stat = sock_writev(fd, r, 5, ++ stat = sock_writev(fd, errfd, r, 5, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, + r->uri, req.uri_len, + r->args, req.args_len); + } else { +- stat = sock_writev(fd, r, 4, ++ stat = sock_writev(fd, errfd, r, 4, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, +@@ -531,7 +622,7 @@ + for (i = 0; i < req.env_count; i++) { + apr_size_t curlen = strlen(env[i]); + +- if ((stat = sock_writev(fd, r, 2, &curlen, sizeof(curlen), ++ if ((stat = sock_writev(fd, 0, r, 2, &curlen, sizeof(curlen), + env[i], curlen)) != APR_SUCCESS) { + return stat; + } +@@ -582,20 +673,34 @@ + } + } + ++/* Callback executed in the forked child process if exec of the CGI ++ * script fails. For the fd-passing case, output to stderr goes to ++ * the client (request handling thread) and is logged via ++ * ap_log_rerror there. For the non-fd-passing case, the "fake" ++ * request_rec passed via userdata is used to log. */ + static void cgid_child_errfn(apr_pool_t *pool, apr_status_t err, + const char *description) + { +- request_rec *r; + void *vr; + + apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); +- r = vr; +- +- /* sure we got r, but don't call ap_log_rerror() because we don't +- * have r->headers_in and possibly other storage referenced by +- * ap_log_rerror() +- */ +- ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, APLOGNO(01241) "%s", description); ++ if (vr) { ++ request_rec *r = vr; ++ ++ /* sure we got r, but don't call ap_log_rerror() because we don't ++ * have r->headers_in and possibly other storage referenced by ++ * ap_log_rerror() ++ */ ++ ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, APLOGNO(01241) "%s", description); ++ } ++ else { ++ const char *logstr; ++ ++ logstr = apr_psprintf(pool, APLOGNO(01241) "error spawning CGI child: %s (%pm)\n", ++ description, &err); ++ fputs(logstr, stderr); ++ fflush(stderr); ++ } + } + + static int cgid_server(void *data) +@@ -670,7 +775,7 @@ + } + + while (!daemon_should_exit) { +- int errfileno = STDERR_FILENO; ++ int errfileno; + char *argv0 = NULL; + char **env = NULL; + const char * const *argv; +@@ -710,7 +815,7 @@ + r = apr_pcalloc(ptrans, sizeof(request_rec)); + procnew = apr_pcalloc(ptrans, sizeof(*procnew)); + r->pool = ptrans; +- stat = get_req(sd2, r, &argv0, &env, &cgid_req); ++ stat = get_req(sd2, r, &argv0, &env, &errfileno, &cgid_req); + if (stat != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, stat, + main_server, APLOGNO(01248) +@@ -742,6 +847,16 @@ + continue; + } + ++ if (errfileno == 0) { ++ errfileno = STDERR_FILENO; ++ } ++ else { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, main_server, ++ "using passed fd %d as stderr", errfileno); ++ /* Limit the received fd lifetime to pool lifetime */ ++ apr_pool_cleanup_register(ptrans, (void *)((long)errfileno), ++ close_unix_socket, close_unix_socket); ++ } + apr_os_file_put(&r->server->error_log, &errfileno, 0, r->pool); + apr_os_file_put(&inout, &sd2, 0, r->pool); + +@@ -801,7 +916,10 @@ + close(sd2); + } + else { +- apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); ++ if (errfileno == STDERR_FILENO) { ++ /* Used by cgid_child_errfn without fd-passing. */ ++ apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); ++ } + + argv = (const char * const *)create_argv(r->pool, NULL, NULL, NULL, argv0, r->args); + +@@ -1101,6 +1219,33 @@ + return ret; + } + ++/* Soak up stderr from a script and redirect it to the error log. ++ * TODO: log_scripterror() and this could move to cgi_common.h. */ ++static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err) ++{ ++ char argsbuffer[HUGE_STRING_LEN]; ++ char *newline; ++ apr_status_t rv; ++ cgid_server_conf *conf = ap_get_module_config(r->server->module_config, &cgid_module); ++ ++ while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN, ++ script_err)) == APR_SUCCESS) { ++ ++ newline = strchr(argsbuffer, '\n'); ++ if (newline) { ++ char *prev = newline - 1; ++ if (prev >= argsbuffer && *prev == '\r') { ++ newline = prev; ++ } ++ ++ *newline = '\0'; ++ } ++ log_scripterror(r, conf, r->status, 0, argsbuffer); ++ } ++ ++ return rv; ++} ++ + static int log_script(request_rec *r, cgid_server_conf * conf, int ret, + char *dbuf, const char *sbuf, apr_bucket_brigade *bb, + apr_file_t *script_err) +@@ -1206,6 +1351,13 @@ + return ret; + } + ++/* Pull in CGI bucket implementation. */ ++#define cgi_server_conf cgid_server_conf ++#ifdef HAVE_CGID_FDPASSING ++#define WANT_CGI_BUCKET ++#endif ++#include "cgi_common.h" ++ + static int connect_to_daemon(int *sdptr, request_rec *r, + cgid_server_conf *conf) + { +@@ -1272,23 +1424,6 @@ + return OK; + } + +-static void discard_script_output(apr_bucket_brigade *bb) +-{ +- apr_bucket *e; +- const char *buf; +- apr_size_t len; +- +- for (e = APR_BRIGADE_FIRST(bb); +- e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e); +- e = APR_BRIGADE_FIRST(bb)) +- { +- if (apr_bucket_read(e, &buf, &len, APR_BLOCK_READ)) { +- break; +- } +- apr_bucket_delete(e); +- } +-} +- + /**************************************************************** + * + * Actual cgid handling... +@@ -1393,6 +1528,7 @@ + + static int cgid_handler(request_rec *r) + { ++ conn_rec *c = r->connection; + int retval, nph, dbpos; + char *argv0, *dbuf; + apr_bucket_brigade *bb; +@@ -1402,10 +1538,11 @@ + int seen_eos, child_stopped_reading; + int sd; + char **env; +- apr_file_t *tempsock; ++ apr_file_t *tempsock, *script_err, *errpipe_out; + struct cleanup_script_info *info; + apr_status_t rv; + cgid_dirconf *dc; ++ apr_interval_time_t timeout; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; +@@ -1414,7 +1551,7 @@ + conf = ap_get_module_config(r->server->module_config, &cgid_module); + dc = ap_get_module_config(r->per_dir_config, &cgid_module); + +- ++ timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; + is_included = !strcmp(r->protocol, "INCLUDED"); + + if ((argv0 = strrchr(r->filename, '/')) != NULL) { +@@ -1467,6 +1604,17 @@ + } + */ + ++#ifdef HAVE_CGID_FDPASSING ++ rv = apr_file_pipe_create(&script_err, &errpipe_out, r->pool); ++ if (rv) { ++ return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, rv, APLOGNO(10176) ++ "could not create pipe for stderr"); ++ } ++#else ++ script_err = NULL; ++ errpipe_out = NULL; ++#endif ++ + /* + * httpd core function used to add common environment variables like + * DOCUMENT_ROOT. +@@ -1479,12 +1627,16 @@ + return retval; + } + +- rv = send_req(sd, r, argv0, env, CGI_REQ); ++ rv = send_req(sd, errpipe_out, r, argv0, env, CGI_REQ); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01268) + "write to cgi daemon process"); + } + ++ /* The write-end of the pipe is only used by the server, so close ++ * it here. */ ++ if (errpipe_out) apr_file_close(errpipe_out); ++ + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; + info->r = r; +@@ -1506,12 +1658,7 @@ + */ + + apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); +- if (dc->timeout > 0) { +- apr_file_pipe_timeout_set(tempsock, dc->timeout); +- } +- else { +- apr_file_pipe_timeout_set(tempsock, r->server->timeout); +- } ++ apr_file_pipe_timeout_set(tempsock, timeout); + apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); + + /* Transfer any put/post args, CERN style... +@@ -1603,114 +1750,19 @@ + */ + shutdown(sd, 1); + +- /* Handle script return... */ +- if (!nph) { +- conn_rec *c = r->connection; +- const char *location; +- char sbuf[MAX_STRING_LEN]; +- int ret; +- +- bb = apr_brigade_create(r->pool, c->bucket_alloc); +- b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- +- if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, +- APLOG_MODULE_INDEX))) +- { +- ret = log_script(r, conf, ret, dbuf, sbuf, bb, NULL); +- +- /* +- * ret could be HTTP_NOT_MODIFIED in the case that the CGI script +- * does not set an explicit status and ap_meets_conditions, which +- * is called by ap_scan_script_header_err_brigade, detects that +- * the conditions of the requests are met and the response is +- * not modified. +- * In this case set r->status and return OK in order to prevent +- * running through the error processing stack as this would +- * break with mod_cache, if the conditions had been set by +- * mod_cache itself to validate a stale entity. +- * BTW: We circumvent the error processing stack anyway if the +- * CGI script set an explicit status code (whatever it is) and +- * the only possible values for ret here are: +- * +- * HTTP_NOT_MODIFIED (set by ap_meets_conditions) +- * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) +- * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the +- * processing of the response of the CGI script, e.g broken headers +- * or a crashed CGI process). +- */ +- if (ret == HTTP_NOT_MODIFIED) { +- r->status = ret; +- return OK; +- } +- +- return ret; +- } +- +- location = apr_table_get(r->headers_out, "Location"); +- +- if (location && location[0] == '/' && r->status == 200) { +- +- /* Soak up all the script output */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- /* This redirect needs to be a GET no matter what the original +- * method was. +- */ +- r->method = "GET"; +- r->method_number = M_GET; +- +- /* We already read the message body (if any), so don't allow +- * the redirected request to think it has one. We can ignore +- * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. +- */ +- apr_table_unset(r->headers_in, "Content-Length"); +- +- ap_internal_redirect_handler(location, r); +- return OK; +- } +- else if (location && r->status == 200) { +- /* XXX: Note that if a script wants to produce its own Redirect +- * body, it now has to explicitly *say* "Status: 302" +- */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- return HTTP_MOVED_TEMPORARILY; +- } +- +- rv = ap_pass_brigade(r->output_filters, bb); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, +- "Failed to flush CGI output to client"); +- } +- } +- +- if (nph) { +- conn_rec *c = r->connection; +- struct ap_filter_t *cur; +- +- /* get rid of all filters up through protocol... since we +- * haven't parsed off the headers, there is no way they can +- * work +- */ +- +- cur = r->proto_output_filters; +- while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { +- cur = cur->next; +- } +- r->output_filters = r->proto_output_filters = cur; +- +- bb = apr_brigade_create(r->pool, c->bucket_alloc); +- b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- ap_pass_brigade(r->output_filters, bb); +- } ++ bb = apr_brigade_create(r->pool, c->bucket_alloc); ++#ifdef HAVE_CGID_FDPASSING ++ b = cgi_bucket_create(r, dc->timeout, tempsock, script_err, c->bucket_alloc); ++ if (b == NULL) ++ return HTTP_INTERNAL_SERVER_ERROR; /* should call log_scripterror() w/ _UNAVAILABLE? */ ++#else ++ b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); ++#endif ++ APR_BRIGADE_INSERT_TAIL(bb, b); ++ b = apr_bucket_eos_create(c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(bb, b); + +- return OK; /* NOT r->status, even if it has changed. */ ++ return cgi_handle_response(r, nph, bb, timeout, conf, dbuf, script_err); + } + + +@@ -1827,7 +1879,7 @@ + return retval; + } + +- send_req(sd, r, command, env, SSI_REQ); ++ send_req(sd, NULL, r, command, env, SSI_REQ); + + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; |