Index: modules/proxy/mod_proxy.c =================================================================== --- modules/proxy/mod_proxy.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy.c (date 1579882358258) @@ -1583,6 +1583,7 @@ 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_intercept_codes = apr_array_make(p, 10, sizeof(proxy_status_code)); new->preserve_host_set = 0; new->preserve_host = 0; new->interpolate_env = -1; /* unset */ @@ -1613,6 +1614,8 @@ = 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_intercept_codes + = apr_array_append(p, base->error_intercept_codes, add->error_intercept_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 @@ -2122,15 +2125,38 @@ return NULL; } + 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) { + proxy_status_code *sc; + if (!apr_isdigit(arg[0])) + return "ProxyErrorOverride: status codes to intercept must be numeric"; + + int code = strtol(arg, NULL, 10); + if (!ap_is_HTTP_ERROR(code)) + return "ProxyErrorOverride: status codes to intercept must be valid HTTP Status Codes >=400 && <600"; + + sc = apr_array_push(conf->error_intercept_codes); + sc->status_code = code; + } + 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 +2740,7 @@ "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"), Index: modules/proxy/mod_proxy_uwsgi.c =================================================================== --- modules/proxy/mod_proxy_uwsgi.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy_uwsgi.c (date 1579874244945) @@ -362,9 +362,9 @@ #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 (dconf->error_override && ap_is_proxy_error_code(dconf, r->status)) { #else - if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (conf->error_override && ap_is_proxy_error_code(conf, r->status)) { #endif int status = r->status; r->status = HTTP_OK; Index: modules/proxy/mod_proxy_fcgi.c =================================================================== --- modules/proxy/mod_proxy_fcgi.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy_fcgi.c (date 1579874347058) @@ -772,7 +772,7 @@ } if (conf->error_override - && ap_is_HTTP_ERROR(r->status) && ap_is_initial_req(r)) { + && ap_is_proxy_error_code(conf, r->status) && ap_is_initial_req(r)) { /* * set script_error_status to discard * everything after the headers Index: modules/proxy/mod_proxy_ajp.c =================================================================== --- modules/proxy/mod_proxy_ajp.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy_ajp.c (date 1579874336285) @@ -474,7 +474,7 @@ /* 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 (!conf->error_override || !ap_is_proxy_error_code(conf, r->status)) { /* AJP13_SEND_BODY_CHUNK with zero length * is explicit flush message */ @@ -497,8 +497,8 @@ * 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 (conf->error_override && !ap_is_proxy_error_code(conf, r->status) + && ap_is_proxy_error_code(conf, original_status)) { r->status = original_status; r->status_line = original_status_line; } @@ -547,7 +547,7 @@ if (status != APR_SUCCESS) { backend_failed = 1; } - if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) { + if (!conf->error_override || !ap_is_proxy_error_code(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 @@ conn->worker->cp->addr, conn->worker->s->hostname_ex); - if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (conf->error_override && ap_is_proxy_error_code(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 Index: modules/proxy/mod_proxy_http.c =================================================================== --- modules/proxy/mod_proxy_http.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy_http.c (date 1579874406388) @@ -1630,7 +1630,7 @@ */ /* PR 41646: get HEAD right with ProxyErrorOverride */ - if (ap_is_HTTP_ERROR(proxy_status) && dconf->error_override) { + if (dconf->error_override && ap_is_proxy_error_code(dconf, proxy_status)) { if (proxy_status == HTTP_UNAUTHORIZED) { const char *buf; const char *wa = "WWW-Authenticate"; @@ -1723,7 +1723,7 @@ * 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 (!dconf->error_override || !ap_is_proxy_error_code(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 @@ * 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 (dconf->error_override && !ap_is_proxy_error_code(dconf, proxy_status) + && ap_is_proxy_error_code(dconf, original_status)) { r->status = original_status; r->status_line = original_status_line; } Index: modules/proxy/proxy_util.c =================================================================== --- modules/proxy/proxy_util.c (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/proxy_util.c (date 1579875189605) @@ -3377,6 +3377,35 @@ return lb_workers_limit; } +/* + * @param conf proxy directory configuration + * @param code http status code + * @return 1 if code is in the list of error-codes to override, 0 otherwise + */ +static int ap_is_intercept_error_code(proxy_dir_conf *conf, int code) +{ + if (apr_is_empty_array(conf->error_intercept_codes)) + return 0; + + proxy_status_code *list = (proxy_status_code *) conf->error_intercept_codes->elts; + + int i; + for (i = 0; i < conf->error_intercept_codes->nelts; i++) { + if (code == list[i].status_code) + return 1; + } + + return 0; +} + +PROXY_DECLARE(int) ap_is_proxy_error_code(proxy_dir_conf *conf, int code) +{ + if (apr_is_empty_array(conf->error_intercept_codes)) + return ap_is_HTTP_ERROR(code); + + return ap_is_intercept_error_code(conf, code); +} + /* deprecated - to be removed in v2.6 */ PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, apr_bucket_brigade *brigade) Index: docs/manual/mod/mod_proxy.xml =================================================================== --- docs/manual/mod/mod_proxy.xml (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ docs/manual/mod/mod_proxy.xml (date 1579882675108) @@ -2029,7 +2029,7 @@ ProxyErrorOverride Override error pages for proxied content -ProxyErrorOverride On|Off +ProxyErrorOverride Off|On [code ...] ProxyErrorOverride Off server configvirtual host directory @@ -2046,6 +2046,26 @@

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 + +
Index: modules/proxy/mod_proxy.h =================================================================== --- modules/proxy/mod_proxy.h (revision e5810edcbb25cd3e39fc998ade3480f563d830ae) +++ modules/proxy/mod_proxy.h (date 1579875012332) @@ -201,6 +201,9 @@ unsigned int ppinherit_set:1; } proxy_server_conf; +typedef struct { + unsigned int status_code; +} proxy_status_code; typedef struct { const char *p; /* The path */ @@ -240,6 +243,8 @@ /** Named back references */ apr_array_header_t *refs; + apr_array_header_t *error_intercept_codes; + unsigned int forward_100_continue:1; unsigned int forward_100_continue_set:1; } proxy_dir_conf; @@ -1278,6 +1283,17 @@ */ int ap_proxy_lb_workers(void); +/** + * Checks if the specified code is an http error code. + * When the given proxy directory configuration has custom intercept-codes configured, + * only those are considered. Otherwise code is compared against the default error-codes. + * + * @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_is_proxy_error_code(proxy_dir_conf *conf, int code); + /** * Return the port number of a known scheme (eg: http -> 80). * @param scheme scheme to test