From: Eric Covener Date: Sat, 11 Apr 2020 21:19:08 +0000 (+0000) Subject: PR63628: individual status codes for ProxyErrorOverride. X-Git-Tag: 2.5.0-alpha2-ci-test-only~1532 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a545608f8355fba22eee1edf86a63f0be785f595;p=thirdparty%2Fapache%2Fhttpd.git PR63628: individual status codes for ProxyErrorOverride. Support specifying the http status codes to be considered by ProxyErrorOverride Submitted By: Martin Drößler Committed By: covener git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1876404 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index b8e8f907592..9c5f2588771 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) mod_proxy: Allow ProxyErrorOverride to be restricted to specific status + codes. PR63628. [Martin Drößler ] + *) configtest: Issue a warning for non-existent directories in config sections. PR63079. [Stephane Blondon ]. diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 6672ca44058..99659a06e64 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -2029,11 +2029,12 @@ header for proxied requests ProxyErrorOverride Override error pages for proxied content -ProxyErrorOverride On|Off +ProxyErrorOverride Off|On [code ...] ProxyErrorOverride Off server configvirtual host directory +The list of status codes was added in 2.5.1

This directive is useful for reverse-proxy setups where you want to @@ -2046,6 +2047,26 @@ header for proxied requests

This directive does not affect the processing of informational (1xx), normal success (2xx), or redirect (3xx) responses.

+ +

By default ProxyErrorOverride affects all responses with codes between 400 (including) + and 600 (excluding).

+ + Example for default behavior + + ProxyErrorOverride On + + + +

To change the default behavior, you can specify the status codes to consider, separated by spaces. + If you do so, all other status codes will be ignored. + You can only specify status codes, that are considered error codes: between 400 (including) + and 600 (excluding).

+ + Example for custom status codes + + ProxyErrorOverride On 403 405 500 501 502 503 504 + +
diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 30c156fbfa7..53dee4c025f 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -625,6 +625,7 @@ * 20200331.0 (2.5.1-dev) Remove ap_request_core_filter() and * ap_request_core_filter_handle. * 20200331.1 (2.5.1-dev) Add flushed:1 to request_rec + * 20200331.2 (2.5.1-dev) Add ap_proxy_should_override to mod_proxy.h */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index bb973e121fd..7294e5a0b3a 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -1583,6 +1583,7 @@ static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) new->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); new->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias)); new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); + new->error_override_codes = apr_array_make(p, 10, sizeof(int)); new->preserve_host_set = 0; new->preserve_host = 0; new->interpolate_env = -1; /* unset */ @@ -1613,6 +1614,8 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) = apr_array_append(p, base->cookie_paths, add->cookie_paths); new->cookie_domains = apr_array_append(p, base->cookie_domains, add->cookie_domains); + new->error_override_codes + = apr_array_append(p, base->error_override_codes, add->error_override_codes); new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env : add->interpolate_env; new->preserve_host = (add->preserve_host_set == 0) ? base->preserve_host @@ -2123,14 +2126,39 @@ static const char * } static const char * - set_proxy_error_override(cmd_parms *parms, void *dconf, int flag) + set_proxy_error_override(cmd_parms *parms, void *dconf, const char *arg) { proxy_dir_conf *conf = dconf; - conf->error_override = flag; - conf->error_override_set = 1; + if (strcasecmp(arg, "Off") == 0) { + conf->error_override = 0; + conf->error_override_set = 1; + } + else if (strcasecmp(arg, "On") == 0) { + conf->error_override = 1; + conf->error_override_set = 1; + } + else if (conf->error_override_set == 1) { + int *newcode; + int argcode; + if (!apr_isdigit(arg[0])) + return "ProxyErrorOverride: status codes to intercept must be numeric"; + if (!conf->error_override) + return "ProxyErrorOverride: status codes must follow a value of 'on'"; + + argcode = strtol(arg, NULL, 10); + if (!ap_is_HTTP_ERROR(argcode)) + return "ProxyErrorOverride: status codes to intercept must be valid HTTP Status Codes >=400 && <600"; + + newcode = apr_array_push(conf->error_override_codes); + *newcode = argcode; + } + else + return "ProxyErrorOverride first parameter must be one of: off | on"; + return NULL; } + static const char * add_proxy_http_headers(cmd_parms *parms, void *dconf, int flag) { @@ -2714,7 +2742,7 @@ static const command_rec proxy_cmds[] = "The default intranet domain name (in absence of a domain in the URL)"), AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF, "Configure Via: proxy header header to one of: on | off | block | full"), - AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF, + AP_INIT_ITERATE("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF, "use our error handling pages instead of the servers' we are proxying"), AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF|ACCESS_CONF, "on if we should preserve host header while proxying"), diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 7d501bf5d0a..bb47d9a01fc 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -201,7 +201,6 @@ typedef struct { unsigned int ppinherit_set:1; } proxy_server_conf; - typedef struct { const char *p; /* The path */ ap_regex_t *r; /* Is this a regex? */ @@ -242,6 +241,8 @@ typedef struct { unsigned int forward_100_continue:1; unsigned int forward_100_continue_set:1; + + apr_array_header_t *error_override_codes; } proxy_dir_conf; /* if we interpolate env vars per-request, we'll need a per-request @@ -1278,6 +1279,15 @@ PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket); */ int ap_proxy_lb_workers(void); +/** + * Returns 1 if a response with the given status should be overridden. + * + * @param conf proxy directory configuration + * @param code http status code + * @return 1 if code is considered an error-code, 0 otherwise + */ +PROXY_DECLARE(int) ap_proxy_should_override(proxy_dir_conf *conf, int code); + /** * Return the port number of a known scheme (eg: http -> 80). * @param scheme scheme to test diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c index 54e2d1a70d5..9799c784bbe 100644 --- a/modules/proxy/mod_proxy_ajp.c +++ b/modules/proxy/mod_proxy_ajp.c @@ -474,7 +474,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, /* If we are overriding the errors, we can't put the content * of the page into the brigade. */ - if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) { + if (!ap_proxy_should_override(conf, r->status)) { /* AJP13_SEND_BODY_CHUNK with zero length * is explicit flush message */ @@ -497,8 +497,8 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, * error status so that an underlying error (eg HTTP_NOT_FOUND) * doesn't become an HTTP_OK. */ - if (conf->error_override && !ap_is_HTTP_ERROR(r->status) - && ap_is_HTTP_ERROR(original_status)) { + if (!ap_proxy_should_override(conf, r->status) + && ap_proxy_should_override(conf, original_status)) { r->status = original_status; r->status_line = original_status_line; } @@ -547,7 +547,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, if (status != APR_SUCCESS) { backend_failed = 1; } - if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) { + if (!ap_proxy_should_override(conf, r->status)) { e = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(output_brigade, e); if (ap_pass_brigade(r->output_filters, @@ -642,7 +642,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, conn->worker->cp->addr, conn->worker->s->hostname_ex); - if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (ap_proxy_should_override(conf, r->status)) { /* clear r->status for override error, otherwise ErrorDocument * thinks that this is a recursive error, and doesn't find the * custom error page diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c index 2e97408f3f7..77ad661c273 100644 --- a/modules/proxy/mod_proxy_fcgi.c +++ b/modules/proxy/mod_proxy_fcgi.c @@ -771,8 +771,7 @@ recv_again: } } - if (conf->error_override - && ap_is_HTTP_ERROR(r->status) && ap_is_initial_req(r)) { + if (ap_proxy_should_override(conf, r->status) && ap_is_initial_req(r)) { /* * set script_error_status to discard * everything after the headers diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index c5e1777623e..3f356d40e75 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -1630,7 +1630,7 @@ int ap_proxy_http_process_response(proxy_http_req_t *req) */ /* PR 41646: get HEAD right with ProxyErrorOverride */ - if (ap_is_HTTP_ERROR(proxy_status) && dconf->error_override) { + if (ap_proxy_should_override(dconf, proxy_status)) { if (proxy_status == HTTP_UNAUTHORIZED) { const char *buf; const char *wa = "WWW-Authenticate"; @@ -1723,7 +1723,7 @@ int ap_proxy_http_process_response(proxy_http_req_t *req) * if we are overriding the errors, we can't put the content * of the page into the brigade */ - if (!dconf->error_override || !ap_is_HTTP_ERROR(proxy_status)) { + if (!ap_proxy_should_override(dconf, proxy_status)) { /* read the body, pass it to the output filters */ apr_read_type_e mode = APR_NONBLOCK_READ; int finish = FALSE; @@ -1733,8 +1733,8 @@ int ap_proxy_http_process_response(proxy_http_req_t *req) * error status so that an underlying error (eg HTTP_NOT_FOUND) * doesn't become an HTTP_OK. */ - if (dconf->error_override && !ap_is_HTTP_ERROR(proxy_status) - && ap_is_HTTP_ERROR(original_status)) { + if (!ap_proxy_should_override(dconf, proxy_status) + && ap_proxy_should_override(dconf, original_status)) { r->status = original_status; r->status_line = original_status_line; } diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c index bce4fc21b80..98b8579d0b7 100644 --- a/modules/proxy/mod_proxy_uwsgi.c +++ b/modules/proxy/mod_proxy_uwsgi.c @@ -362,9 +362,9 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, #if AP_MODULE_MAGIC_AT_LEAST(20101106,0) dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - if (dconf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (ap_proxy_should_override(dconf, r->status)) { #else - if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (ap_proxy_should_override(conf, r->status)) { #endif int status = r->status; r->status = HTTP_OK; diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 1fcf52f6b0a..bb12774a553 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -3377,6 +3377,33 @@ int ap_proxy_lb_workers(void) return lb_workers_limit; } +static int error_code_overridden(proxy_dir_conf *conf, int code) +{ + int i; + int *list = (int *) conf->error_override_codes->elts; + + if (apr_is_empty_array(conf->error_override_codes)) + return 0; + + for (i = 0; i < conf->error_override_codes->nelts; i++) { + if (code == list[i]) + return 1; + } + + return 0; +} + +PROXY_DECLARE(int) ap_proxy_should_override(proxy_dir_conf *conf, int code) +{ + if (!conf->error_override) + return 0; + + if (apr_is_empty_array(conf->error_override_codes)) + return ap_is_HTTP_ERROR(code); + + return error_code_overridden(conf, code); +} + /* deprecated - to be removed in v2.6 */ PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, apr_bucket_brigade *brigade)