[core] server.error-handler new directive for error pages (fixes #2702)
server.error-handler preserves HTTP status error code when error page is static, and allows dynamic handlers to change HTTP status code when error page is provided by dynamic handler. server.error-handler intercepts all HTTP status codes >= 400 except when the content is generated by a dynamic handler (cgi, ssi, fastcgi, scgi, proxy, lua). The request method is unconditionally changed to GET for the request to service the error handler, and the original request method is later restored (for logging purposes). request body from the original request, if present, is discarded. server.error-handler is somewhat similar to server.error-handler-404, but server.error-handler-404 is now deprecated, intercepts only 404 and 403 HTTP status codes, and returns 200 OK for static error pages, a source of confusion for some admins. On the other hand, the new server.error-handler, when set, will intercept all HTTP status error codes >= 400. server.error-handler takes precedence over server.error-handler-404 when both are set. NOTE: a major difference between server.error-handler and the now-deprecated server.error-handler-404 is that the values of the non-standard CGI environment variables REQUEST_URI and REDIRECT_URI have been swapped. Since REDIRECT_STATUS is the original HTTP status code, REDIRECT_URI is now the original request, and REQUEST_URI is the current request (e.g. the URI/URL to the error handler). The prior behavior -- which reversed REQUEST_URI and REDIRECT_URI values from those described above -- is preserved for server.error-handler-404. Additionally, REDIRECT_STATUS is now available to mod_magnet, which continues to have access to request.uri and request.orig_uri. See further discussion at https://redmine.lighttpd.net/issues/2702 and https://redmine.lighttpd.net/issues/1828 github: closes #36
This commit is contained in:
parent
87b172e70e
commit
dbdab5dbc9
|
@ -245,6 +245,7 @@ typedef struct {
|
|||
buffer *document_root;
|
||||
buffer *server_name;
|
||||
buffer *error_handler;
|
||||
buffer *error_handler_404;
|
||||
buffer *server_tag;
|
||||
buffer *dirlist_encoding;
|
||||
buffer *errorfile_prefix;
|
||||
|
@ -447,6 +448,7 @@ typedef struct {
|
|||
|
||||
/* error-handler */
|
||||
int error_handler_saved_status;
|
||||
http_method_t error_handler_saved_method;
|
||||
|
||||
struct server_socket *srv_socket; /* reference to the server-socket */
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ static int config_insert(server *srv) {
|
|||
|
||||
{ "server.max-read-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
|
||||
{ "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */
|
||||
{ "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
|
||||
{ "server.error-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
|
||||
{ "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */
|
||||
#ifdef HAVE_LSTAT
|
||||
{ "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */
|
||||
|
@ -112,6 +112,7 @@ static int config_insert(server *srv) {
|
|||
{ "server.upload-temp-file-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 68 */
|
||||
{ "mimetype.xattr-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 69 */
|
||||
{ "server.listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 70 */
|
||||
{ "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 71 */
|
||||
|
||||
{ "server.host",
|
||||
"use server.bind instead",
|
||||
|
@ -193,6 +194,7 @@ static int config_insert(server *srv) {
|
|||
s->ssl_pemfile = buffer_init();
|
||||
s->ssl_ca_file = buffer_init();
|
||||
s->error_handler = buffer_init();
|
||||
s->error_handler_404 = buffer_init();
|
||||
s->server_tag = buffer_init();
|
||||
s->ssl_cipher_list = buffer_init();
|
||||
s->ssl_dh_file = buffer_init();
|
||||
|
@ -288,6 +290,7 @@ static int config_insert(server *srv) {
|
|||
cv[66].destination = &(s->ssl_honor_cipher_order);
|
||||
cv[67].destination = &(s->ssl_empty_fragments);
|
||||
cv[70].destination = &(s->listen_backlog);
|
||||
cv[71].destination = s->error_handler_404;
|
||||
|
||||
srv->config_storage[i] = s;
|
||||
|
||||
|
@ -336,6 +339,7 @@ int config_setup_connection(server *srv, connection *con) {
|
|||
PATCH(max_write_idle);
|
||||
PATCH(use_xattr);
|
||||
PATCH(error_handler);
|
||||
PATCH(error_handler_404);
|
||||
PATCH(errorfile_prefix);
|
||||
#ifdef HAVE_LSTAT
|
||||
PATCH(follow_symlink);
|
||||
|
@ -410,8 +414,10 @@ int config_patch_connection(server *srv, connection *con) {
|
|||
PATCH(document_root);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.range-requests"))) {
|
||||
PATCH(range_requests);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) {
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler"))) {
|
||||
PATCH(error_handler);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) {
|
||||
PATCH(error_handler_404);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.errorfile-prefix"))) {
|
||||
PATCH(errorfile_prefix);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.assign"))) {
|
||||
|
|
|
@ -632,6 +632,8 @@ int connection_reset(server *srv, connection *con) {
|
|||
|
||||
con->header_len = 0;
|
||||
con->error_handler_saved_status = 0;
|
||||
/*con->error_handler_saved_method = HTTP_METHOD_UNSET;*/
|
||||
/*(error_handler_saved_method value is not valid unless error_handler_saved_status is set)*/
|
||||
|
||||
config_setup_connection(srv, con);
|
||||
|
||||
|
@ -1021,19 +1023,30 @@ int connection_state_machine(server *srv, connection *con) {
|
|||
|
||||
switch (r = http_response_prepare(srv, con)) {
|
||||
case HANDLER_FINISHED:
|
||||
if (con->error_handler_saved_status > 0) {
|
||||
con->request.http_method = con->error_handler_saved_method;
|
||||
}
|
||||
if (con->mode == DIRECT) {
|
||||
if (con->error_handler_saved_status) {
|
||||
if (con->http_status == 404 || con->http_status == 403) {
|
||||
/* error-handler-404 is a 404 */
|
||||
if (con->error_handler_saved_status > 0) {
|
||||
con->http_status = con->error_handler_saved_status;
|
||||
} else if (con->http_status == 404 || con->http_status == 403) {
|
||||
/* error-handler-404 is a 404 */
|
||||
con->http_status = -con->error_handler_saved_status;
|
||||
} else {
|
||||
/* error-handler-404 is back and has generated content */
|
||||
/* if Status: was set, take it otherwise use 200 */
|
||||
}
|
||||
} else if (con->http_status == 404 || con->http_status == 403) {
|
||||
/* 404 error-handler */
|
||||
|
||||
} else if (con->http_status >= 400) {
|
||||
buffer *error_handler = NULL;
|
||||
if (!buffer_string_is_empty(con->conf.error_handler)) {
|
||||
error_handler = con->conf.error_handler;
|
||||
} else if ((con->http_status == 404 || con->http_status == 403)
|
||||
&& !buffer_string_is_empty(con->conf.error_handler_404)) {
|
||||
error_handler = con->conf.error_handler_404;
|
||||
}
|
||||
|
||||
if (error_handler) {
|
||||
/* call error-handler */
|
||||
|
||||
/* set REDIRECT_STATUS to save current HTTP status code
|
||||
|
@ -1047,10 +1060,40 @@ int connection_state_machine(server *srv, connection *con) {
|
|||
buffer_append_int(ds->value, con->http_status);
|
||||
array_insert_unique(con->environment, (data_unset *)ds);
|
||||
|
||||
con->error_handler_saved_status = con->http_status;
|
||||
if (error_handler == con->conf.error_handler) {
|
||||
plugins_call_connection_reset(srv, con);
|
||||
|
||||
if (con->request.content_length) {
|
||||
if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
|
||||
con->keep_alive = 0;
|
||||
}
|
||||
con->request.content_length = 0;
|
||||
chunkqueue_reset(con->request_content_queue);
|
||||
}
|
||||
|
||||
con->is_writable = 1;
|
||||
con->file_finished = 0;
|
||||
con->file_started = 0;
|
||||
con->got_response = 0;
|
||||
con->parsed_response = 0;
|
||||
con->response.keep_alive = 0;
|
||||
con->response.content_length = -1;
|
||||
con->response.transfer_encoding = 0;
|
||||
array_reset(con->response.headers);
|
||||
chunkqueue_reset(con->write_queue);
|
||||
|
||||
array_set_key_value(con->environment, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.orig_uri));
|
||||
con->error_handler_saved_status = con->http_status;
|
||||
con->error_handler_saved_method = con->request.http_method;
|
||||
|
||||
con->request.http_method = HTTP_METHOD_GET;
|
||||
} else { /*(preserve behavior for server.error-handler-404)*/
|
||||
array_set_key_value(con->environment, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(error_handler));
|
||||
con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/
|
||||
}
|
||||
con->http_status = 0;
|
||||
|
||||
buffer_copy_buffer(con->request.uri, con->conf.error_handler);
|
||||
buffer_copy_buffer(con->request.uri, error_handler);
|
||||
buffer_reset(con->physical.path);
|
||||
connection_handle_errdoc_init(con);
|
||||
|
||||
|
|
|
@ -1083,7 +1083,9 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_
|
|||
} else {
|
||||
cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
|
||||
}
|
||||
if (!buffer_string_is_empty(con->request.orig_uri)) {
|
||||
if (con->error_handler_saved_status >= 0) {
|
||||
cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.uri));
|
||||
} else {
|
||||
cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
|
||||
}
|
||||
/* set REDIRECT_STATUS for php compiled with --force-redirect
|
||||
|
|
|
@ -1864,6 +1864,7 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) {
|
|||
fcgi_extension_host *host= hctx->host;
|
||||
|
||||
connection *con = hctx->remote_conn;
|
||||
buffer * const req_uri = (con->error_handler_saved_status >= 0) ? con->request.uri : con->request.orig_uri;
|
||||
server_socket *srv_sock = con->srv_socket;
|
||||
|
||||
sock_addr our_addr;
|
||||
|
@ -2026,26 +2027,24 @@ static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) {
|
|||
* /index/list
|
||||
*
|
||||
*/
|
||||
|
||||
if ('/' != host->strip_request_uri->ptr[buffer_string_length(host->strip_request_uri) - 1]) {
|
||||
/* fix the user-input to have / as last char */
|
||||
buffer_append_string_len(host->strip_request_uri, CONST_STR_LEN("/"));
|
||||
}
|
||||
|
||||
if (buffer_string_length(con->request.orig_uri) >= buffer_string_length(host->strip_request_uri) &&
|
||||
0 == strncmp(con->request.orig_uri->ptr, host->strip_request_uri->ptr, buffer_string_length(host->strip_request_uri))) {
|
||||
if (buffer_string_length(req_uri) >= buffer_string_length(host->strip_request_uri) &&
|
||||
0 == strncmp(req_uri->ptr, host->strip_request_uri->ptr, buffer_string_length(host->strip_request_uri))) {
|
||||
/* the left is the same */
|
||||
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"),
|
||||
con->request.orig_uri->ptr + (buffer_string_length(host->strip_request_uri) - 1),
|
||||
buffer_string_length(con->request.orig_uri) - (buffer_string_length(host->strip_request_uri) - 1)), con);
|
||||
req_uri->ptr + (buffer_string_length(host->strip_request_uri) - 1),
|
||||
buffer_string_length(req_uri) - (buffer_string_length(host->strip_request_uri) - 1)), con)
|
||||
} else {
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con)
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(req_uri)),con)
|
||||
}
|
||||
} else {
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con)
|
||||
}
|
||||
if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)),con)
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(req_uri)),con)
|
||||
}
|
||||
if (!buffer_string_is_empty(con->uri.query)) {
|
||||
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)),con)
|
||||
|
|
|
@ -1027,6 +1027,7 @@ static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, bu
|
|||
|
||||
static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) {
|
||||
size_t i;
|
||||
handler_t ret = HANDLER_GO_ON;
|
||||
|
||||
/* no filename set */
|
||||
if (files->used == 0) return HANDLER_GO_ON;
|
||||
|
@ -1034,18 +1035,25 @@ static handler_t magnet_attract_array(server *srv, connection *con, plugin_data
|
|||
/**
|
||||
* execute all files and jump out on the first !HANDLER_GO_ON
|
||||
*/
|
||||
for (i = 0; i < files->used; i++) {
|
||||
for (i = 0; i < files->used && ret == HANDLER_GO_ON; i++) {
|
||||
data_string *ds = (data_string *)files->data[i];
|
||||
handler_t ret;
|
||||
|
||||
if (buffer_string_is_empty(ds->value)) continue;
|
||||
|
||||
ret = magnet_attract(srv, con, p, ds->value);
|
||||
|
||||
if (ret != HANDLER_GO_ON) return ret;
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
if (con->error_handler_saved_status) {
|
||||
/* retrieve (possibly modified) REDIRECT_STATUS and store as number */
|
||||
unsigned long x;
|
||||
data_string * const ds = (data_string *)array_get_element(con->environment, "REDIRECT_STATUS");
|
||||
if (ds && (x = strtoul(ds->value->ptr, NULL, 10)) < 1000)
|
||||
/*(simplified validity check x < 1000)*/
|
||||
con->error_handler_saved_status =
|
||||
con->error_handler_saved_status > 0 ? (int)x : -(int)x;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
URIHANDLER_FUNC(mod_magnet_uri_handler) {
|
||||
|
|
|
@ -1648,9 +1648,10 @@ static int scgi_create_env(server *srv, handler_ctx *hctx) {
|
|||
scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
|
||||
}
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
|
||||
if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri));
|
||||
if (con->error_handler_saved_status >= 0) {
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.uri));
|
||||
} else {
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
|
||||
}
|
||||
if (!buffer_string_is_empty(con->uri.query)) {
|
||||
scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
|
||||
|
|
|
@ -343,6 +343,7 @@ static void server_free(server *srv) {
|
|||
buffer_free(s->ssl_dh_file);
|
||||
buffer_free(s->ssl_ec_curve);
|
||||
buffer_free(s->error_handler);
|
||||
buffer_free(s->error_handler_404);
|
||||
buffer_free(s->errorfile_prefix);
|
||||
array_free(s->mimetypes);
|
||||
buffer_free(s->ssl_verifyclient_username);
|
||||
|
|
|
@ -37,6 +37,9 @@ cgi.assign = (
|
|||
$HTTP["url"] =~ "^/static/" {
|
||||
server.error-handler-404 = "/404.html"
|
||||
}
|
||||
else $HTTP["url"] =~ "^/dynamic/redirect_status/" {
|
||||
server.error-handler = "/404.pl"
|
||||
}
|
||||
else $HTTP["url"] =~ "." {
|
||||
server.error-handler-404 = "/404.pl"
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ BEGIN {
|
|||
|
||||
use strict;
|
||||
use IO::Socket;
|
||||
use Test::More tests => 8;
|
||||
use Test::More tests => 9;
|
||||
use LightyTest;
|
||||
|
||||
my $tf = LightyTest->new();
|
||||
|
@ -58,6 +58,13 @@ EOF
|
|||
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, 'HTTP-Content' => "Not found here\n" } ];
|
||||
ok($tf->handle_http($t) == 0, '404 handler => dynamic(404)');
|
||||
|
||||
$t->{REQUEST} = ( <<EOF
|
||||
GET /dynamic/redirect_status/ HTTP/1.0
|
||||
EOF
|
||||
);
|
||||
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, 'HTTP-Content' => "REDIRECT_STATUS\n" } ];
|
||||
ok($tf->handle_http($t) == 0, 'error handler => dynamic(REDIRECT_STATUS)');
|
||||
|
||||
$t->{REQUEST} = ( <<EOF
|
||||
GET /dynamic/nostatus/notfound HTTP/1.0
|
||||
EOF
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
use CGI qw/:standard/;
|
||||
|
||||
my $cgi = new CGI;
|
||||
my $request_uri = $ENV{'REQUEST_URI'};
|
||||
print (STDERR "REQUEST_URI: $request_uri\n");
|
||||
my $request_uri = $ENV{'REQUEST_URI'}; # server.error-handler-404
|
||||
my $redirect_uri= $ENV{'REDIRECT_URI'}; # server.error-handler
|
||||
print (STDERR "REQUEST_URI: $request_uri\n");
|
||||
print (STDERR "REDIRECT_URI: $redirect_uri\n");
|
||||
|
||||
if ($request_uri =~ m/^\/dynamic\/200\// ) {
|
||||
print header ( -status => 200,
|
||||
|
@ -28,6 +30,11 @@ elsif ($request_uri =~ m/^\/send404\.pl/ ) {
|
|||
elsif ($request_uri =~ m/^\/dynamic\/nostatus\// ) {
|
||||
print ("found here\n");
|
||||
}
|
||||
elsif ($redirect_uri =~ m/^\/dynamic\/redirect_status\// ) {
|
||||
print header ( -status => $ENV{'REDIRECT_STATUS'},
|
||||
-type => 'text/plain');
|
||||
print ("REDIRECT_STATUS\n");
|
||||
}
|
||||
else {
|
||||
print header ( -status => 500,
|
||||
-type => 'text/plain');
|
||||
|
|
Loading…
Reference in New Issue