概述 在nginx中可以通过error_page指令指定错误页面。和error_page 相关的还有recursive_error_pages指令,该指令制定是否递归解析error_page指令的错误页面,例如如下配置,如果recursive_error_pages 为off;则404会解析为405,如果为on,则会解析到406. error_page 404 =405 /uri; error_page 405 =406 /uri; 代码分析 配置解析 error_page是在ngx_http_core_module模块中实现的, 有ngx_http_core_error_page函数处理配置信息。 // 每一个错误码(code)将会对应一个这样的结构体 typedef struct { ngx_int_t status; // code ngx_int_t overwrite; // response ngx_http_complex_value_t value; // uri ngx_str_t args; // rui的args } ngx_http_err_page_t; // 格式:error_page code ... [=[response]] uri; static char * ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf = conf; u_char *p; ngx_int_t overwrite; ngx_str_t *value, uri, args; ngx_uint_t i, n; ngx_http_err_page_t *err; ngx_http_complex_value_t cv; ngx_http_compile_complex_value_t ccv; if (clcf->error_pages == NULL) { clcf->error_pages = ngx_array_create(cf->pool, 4, sizeof(ngx_http_err_page_t)); if (clcf->error_pages == NULL) { return NGX_CONF_ERROR; } } value = cf->args->elts; i = cf->args->nelts - 2; if (value[i].data[0] == '=') { if (i == 1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); return NGX_CONF_ERROR; } if (value[i].len > 1) { overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1); if (overwrite == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); return NGX_CONF_ERROR; } } else { overwrite = 0; } n = 2; } else { overwrite = -1; n = 1; } uri = value[cf->args->nelts - 1]; ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &uri; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } ngx_str_null(&args); if (cv.lengths == NULL && uri.len && uri.data[0] == '/') { p = (u_char *) ngx_strchr(uri.data, '?'); if (p) { cv.value.len = p - uri.data; cv.value.data = uri.data; p++; args.len = (uri.data + uri.len) - p; args.data = p; } } for (i = 1; i < cf->args->nelts - n; i++) { err = ngx_array_push(clcf->error_pages); if (err == NULL) { return NGX_CONF_ERROR; } err->status = ngx_atoi(value[i].data, value[i].len); if (err->status == NGX_ERROR || err->status == 499) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); return NGX_CONF_ERROR; } if (err->status < 300 || err->status > 599) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "value \"%V\" must be between 300 and 599", &value[i]); return NGX_CONF_ERROR; } err->overwrite = overwrite; if (overwrite == -1) { switch (err->status) { case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: err->overwrite = NGX_HTTP_BAD_REQUEST; } } err->value = cv; err->args = args; } return NGX_CONF_OK; } 请求处理 错误页面跳转会调用ngx_http_special_response_handler函数,该函数会调用ngx_http_send_error_page发送错误页面。 ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) { ngx_uint_t i, err; ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http special response: %i, \"%V?%V\"", error, &r->uri, &r->args); r->err_status = error; if (r->keepalive) { switch (error) { case NGX_HTTP_BAD_REQUEST: case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: case NGX_HTTP_REQUEST_URI_TOO_LARGE: case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: case NGX_HTTP_INTERNAL_SERVER_ERROR: case NGX_HTTP_NOT_IMPLEMENTED: r->keepalive = 0; } } if (r->lingering_close) { switch (error) { case NGX_HTTP_BAD_REQUEST: case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: r->lingering_close = 0; } } r->headers_out.content_type.len = 0; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (!r->error_page && clcf->error_pages && r->uri_changes != 0) { // 是否递归解析错误页面 if (clcf->recursive_error_pages == 0) { r->error_page = 1; } err_page = clcf->error_pages->elts; for (i = 0; i < clcf->error_pages->nelts; i++) { if (err_page[i].status == error) { return ngx_http_send_error_page(r, &err_page[i]); } } } r->expect_tested = 1; if (ngx_http_discard_request_body(r) != NGX_OK) { r->keepalive = 0; } if (clcf->msie_refresh && r->headers_in.msie && (error == NGX_HTTP_MOVED_PERMANENTLY || error == NGX_HTTP_MOVED_TEMPORARILY)) { return ngx_http_send_refresh(r); } if (error == NGX_HTTP_CREATED) { /* 201 */ err = 0; } else if (error == NGX_HTTP_NO_CONTENT) { /* 204 */ err = 0; } else if (error >= NGX_HTTP_MOVED_PERMANENTLY && error < NGX_HTTP_LAST_3XX) { /* 3XX */ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; } else if (error >= NGX_HTTP_BAD_REQUEST && error < NGX_HTTP_LAST_4XX) { /* 4XX */ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX; } else if (error >= NGX_HTTP_NGINX_CODES && error < NGX_HTTP_LAST_5XX) { /* 49X, 5XX */ err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX; switch (error) { case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: case NGX_HTTP_REQUEST_HEADER_TOO_LARGE: r->err_status = NGX_HTTP_BAD_REQUEST; } } else { /* unknown code, zero body */ err = 0; } return ngx_http_send_special_response(r, clcf, err); } static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page) { ngx_int_t overwrite; ngx_str_t uri, args; ngx_table_elt_t *location; ngx_http_core_loc_conf_t *clcf; overwrite = err_page->overwrite; if (overwrite && overwrite != NGX_HTTP_OK) { r->expect_tested = 1; } // 覆盖的状态码 if (overwrite >= 0) { r->err_status = overwrite; } if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) { return NGX_ERROR; } if (uri.len && uri.data[0] == '/') { if (err_page->value.lengths) { ngx_http_split_args(r, &uri, &args); } else { args = err_page->args; } if (r->method != NGX_HTTP_HEAD) { r->method = NGX_HTTP_GET; r->method_name = ngx_http_core_get_method; } return ngx_http_internal_redirect(r, &uri, &args); } if (uri.len && uri.data[0] == '@') { return ngx_http_named_location(r, &uri); } location = ngx_list_push(&r->headers_out.headers); if (location == NULL) { return NGX_ERROR; } if (overwrite != NGX_HTTP_MOVED_PERMANENTLY && overwrite != NGX_HTTP_MOVED_TEMPORARILY && overwrite != NGX_HTTP_SEE_OTHER && overwrite != NGX_HTTP_TEMPORARY_REDIRECT && overwrite != NGX_HTTP_PERMANENT_REDIRECT) { r->err_status = NGX_HTTP_MOVED_TEMPORARILY; } location->hash = 1; ngx_str_set(&location->key, "Location"); location->value = uri; ngx_http_clear_location(r); r->headers_out.location = location; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->msie_refresh && r->headers_in.msie) { return ngx_http_send_refresh(r); } return ngx_http_send_special_response(r, clcf, r->err_status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX); } FROM:https://github.com/vislee/leevis.com/issues/131 转载请注明:爱开源 » error_page 指令代码分析
概述
在nginx中可以通过error_page指令指定错误页面。和error_page 相关的还有recursive_error_pages指令,该指令制定是否递归解析error_page指令的错误页面,例如如下配置,如果recursive_error_pages 为off;则404会解析为405,如果为on,则会解析到406.
代码分析
配置解析
error_page是在ngx_http_core_module模块中实现的, 有ngx_http_core_error_page函数处理配置信息。
请求处理
错误页面跳转会调用ngx_http_special_response_handler函数,该函数会调用ngx_http_send_error_page发送错误页面。