- if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
- "Premature end of script headers: %s", r->filename);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
-
- /* Delete terminal (CR?)LF */
-
- p = strlen(w);
- /* Indeed, the host's '\n':
- '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
- -- whatever the script generates.
- */
- if (p > 0 && w[p - 1] == '\n') {
- if (p > 1 && w[p - 2] == CR) {
- w[p - 2] = '\0';
- }
- else {
- w[p - 1] = '\0';
- }
- }
-
- /*
- * If we've finished reading the headers, check to make sure any
- * HTTP/1.1 conditions are met. If so, we're done; normal processing
- * will handle the script's output. If not, just return the error.
- * The appropriate thing to do would be to send the script process a
- * SIGPIPE to let it know we're ignoring it, close the channel to the
- * script process, and *then* return the failed-to-meet-condition
- * error. Otherwise we'd be waiting for the script to finish
- * blithering before telling the client the output was no good.
- * However, we don't have the information to do that, so we have to
- * leave it to an upper layer.
- */
- if (w[0] == '\0') {
- int cond_status = OK;
-
- if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
- cond_status = ap_meets_conditions(r);
- }
- apr_table_overlap(r->err_headers_out, merge,
- APR_OVERLAP_TABLES_MERGE);
- if (!apr_is_empty_table(cookie_table)) {
- /* the cookies have already been copied to the cookie_table */
- apr_table_unset(r->err_headers_out, "Set-Cookie");
- r->err_headers_out = apr_table_overlay(r->pool,
- r->err_headers_out, cookie_table);
- }
- return cond_status;
- }
-
- /* if we see a bogus header don't ignore it. Shout and scream */
+ int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data);
+ if (rv == 0) {
+ const char *msg = "Premature end of script headers";
+ if (first_header)
+ msg = "End of script output before headers";
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ "%s: %s", msg,
+ apr_filepath_name_get(r->filename));
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ else if (rv == -1) {
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ "Script timed out before returning headers: %s",
+ apr_filepath_name_get(r->filename));
+ return HTTP_GATEWAY_TIME_OUT;
+ }
+
+ /* Delete terminal (CR?)LF */
+
+ p = strlen(w);
+ /* Indeed, the host's '\n':
+ '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
+ -- whatever the script generates.
+ */
+ if (p > 0 && w[p - 1] == '\n') {
+ if (p > 1 && w[p - 2] == CR) {
+ w[p - 2] = '\0';
+ }
+ else {
+ w[p - 1] = '\0';
+ }
+ }
+
+ /*
+ * If we've finished reading the headers, check to make sure any
+ * HTTP/1.1 conditions are met. If so, we're done; normal processing
+ * will handle the script's output. If not, just return the error.
+ * The appropriate thing to do would be to send the script process a
+ * SIGPIPE to let it know we're ignoring it, close the channel to the
+ * script process, and *then* return the failed-to-meet-condition
+ * error. Otherwise we'd be waiting for the script to finish
+ * blithering before telling the client the output was no good.
+ * However, we don't have the information to do that, so we have to
+ * leave it to an upper layer.
+ */
+ if (w[0] == '\0') {
+ int cond_status = OK;
+
+ /* PR#38070: This fails because it gets confused when a
+ * CGI Status header overrides ap_meets_conditions.
+ *
+ * We can fix that by dropping ap_meets_conditions when
+ * Status has been set. Since this is the only place
+ * cgi_status gets used, let's test it explicitly.
+ *
+ * The alternative would be to ignore CGI Status when
+ * ap_meets_conditions returns anything interesting.
+ * That would be safer wrt HTTP, but would break CGI.
+ */
+ if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
+ cond_status = ap_meets_conditions(r);
+ }
+ apr_table_overlap(r->err_headers_out, merge,
+ APR_OVERLAP_TABLES_MERGE);
+ if (!apr_is_empty_table(cookie_table)) {
+ /* the cookies have already been copied to the cookie_table */
+ apr_table_unset(r->err_headers_out, "Set-Cookie");
+ r->err_headers_out = apr_table_overlay(r->pool,
+ r->err_headers_out, cookie_table);
+ }
+ return cond_status;
+ }
+
+ if (trace_log) {
+ if (first_header)
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r,
+ "Headers from script '%s':",
+ apr_filepath_name_get(r->filename));
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, " %s", w);
+ }
+
+ /* if we see a bogus header don't ignore it. Shout and scream */