ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, int error) { ngx_int_t rc; ngx_uint_t err, i, msie_padding; ngx_buf_t *b; ngx_chain_t *out, **ll, *cl; ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; rc = ngx_http_discard_body(r); if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { error = NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.status = error; if (r->keepalive != 0) { 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_HTTP_INTERNAL_SERVER_ERROR: r->keepalive = 0; } } if (r->lingering_close == 1) { switch (error) { case NGX_HTTP_BAD_REQUEST: case NGX_HTTP_TO_HTTPS: r->lingering_close = 0; } } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->err_ctx == NULL && clcf->error_pages) { err_page = clcf->error_pages->elts; for (i = 0; i < clcf->error_pages->nelts; i++) { if (err_page[i].status == error) { if (err_page[i].overwrite) { r->err_status = err_page[i].overwrite; } else { r->err_status = error; } r->err_ctx = r->ctx; return ngx_http_internal_redirect(r, &err_page[i].uri, NULL); } } } if (error < NGX_HTTP_BAD_REQUEST) { /* 3XX */ err = error - NGX_HTTP_MOVED_PERMANENTLY; } else if (error < NGX_HTTP_NGX_CODES) { /* 4XX */ err = error - NGX_HTTP_BAD_REQUEST + 3; } else { /* 49X, 5XX */ err = error - NGX_HTTP_NGX_CODES + 3 + 17; switch (error) { case NGX_HTTP_TO_HTTPS: r->headers_out.status = NGX_HTTP_BAD_REQUEST; error = NGX_HTTP_BAD_REQUEST; break; case NGX_HTTP_INVALID_HOST: r->headers_out.status = NGX_HTTP_NOT_FOUND; error = NGX_HTTP_NOT_FOUND; break; } } msie_padding = 0; if (error_pages[err].len) { r->headers_out.content_length_n = error_pages[err].len + sizeof(error_tail) - 1; if (clcf->msie_padding && r->headers_in.msie && r->http_version >= NGX_HTTP_VERSION_10 && error >= NGX_HTTP_BAD_REQUEST && error != NGX_HTTP_REQUEST_URI_TOO_LARGE) { r->headers_out.content_length_n += sizeof(msie_stub) - 1; msie_padding = 1; } r->headers_out.content_type = ngx_list_push(&r->headers_out.headers); if (r->headers_out.content_type == NULL) { return NGX_ERROR; } r->headers_out.content_type->key.len = sizeof("Content-Type") - 1; r->headers_out.content_type->key.data = (u_char *) "Content-Type"; r->headers_out.content_type->value.len = sizeof("text/html") - 1; r->headers_out.content_type->value.data = (u_char *) "text/html"; } else { r->headers_out.content_length_n = -1; } if (r->headers_out.content_length) { r->headers_out.content_length->key.len = 0; r->headers_out.content_length = NULL; } rc = ngx_http_send_header(r); if (rc == NGX_ERROR || r->header_only) { return rc; } if (error_pages[err].len == 0) { return NGX_OK; } out = NULL; ll = NULL; if (!(b = ngx_calloc_buf(r->pool))) { return NGX_ERROR; } b->memory = 1; b->pos = error_pages[err].data; b->last = error_pages[err].data + error_pages[err].len; ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); ngx_chain_add_link(out, ll, cl); if (!(b = ngx_calloc_buf(r->pool))) { return NGX_ERROR; } b->memory = 1; b->pos = error_tail; b->last = error_tail + sizeof(error_tail) - 1; ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); ngx_chain_add_link(out, ll, cl); if (msie_padding) { if (!(b = ngx_calloc_buf(r->pool))) { return NGX_ERROR; } b->memory = 1; b->pos = msie_stub; b->last = msie_stub + sizeof(msie_stub) - 1; ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); ngx_chain_add_link(out, ll, cl); } b->last_buf = 1; return ngx_http_output_filter(r, out); }
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) { ngx_int_t rc; ngx_uint_t i, err; ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http special response: %d, \"%V\"", error, &r->uri); rc = ngx_http_discard_body(r); if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { error = NGX_HTTP_INTERNAL_SERVER_ERROR; } r->err_status = error; if (r->keepalive != 0) { 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: r->keepalive = 0; } } if (r->lingering_close == 1) { 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) { 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]); } } } 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; r->header_only = 1; } else if (error == NGX_HTTP_NO_CONTENT) { /* 204 */ err = 0; } else if (error < NGX_HTTP_BAD_REQUEST) { /* 3XX */ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; } else if (error < NGX_HTTP_OWN_CODES) { /* 4XX */ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300; } else { /* 49X, 5XX */ err = error - NGX_HTTP_OWN_CODES + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300 + NGX_HTTP_LEVEL_400; switch (error) { case NGX_HTTP_TO_HTTPS: case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: r->err_status = NGX_HTTP_BAD_REQUEST; break; } } return ngx_http_send_special_response(r, clcf, err); }
static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r) { u_char *last; ngx_fd_t fd; ngx_int_t rc; ngx_uint_t level; ngx_str_t name, location; ngx_err_t err; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out; ngx_file_info_t fi; ngx_http_cleanup_t *file_cleanup, *redirect_cleanup; ngx_http_log_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; ngx_http_static_loc_conf_t *slcf; #if (NGX_HTTP_CACHE) uint32_t file_crc, redirect_crc; ngx_http_cache_t *file, *redirect; #endif if (r->uri.data[r->uri.len - 1] == '/') { return NGX_DECLINED; } if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; } rc = ngx_http_discard_body(r); if (rc != NGX_OK && rc != NGX_AGAIN) { return rc; } #if (NGX_HTTP_CACHE) /* * there is a valid cached open file, i.e by the index handler, * and it should be already registered in r->cleanup */ if (r->cache && !r->cache->expired) { return ngx_http_send_cached(r); } #endif log = r->connection->log; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* * make a file name, reserve 2 bytes for a trailing '/' * in a possible redirect and for the last '\0' */ if (clcf->alias) { name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2 - clcf->name.len); if (name.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } last = ngx_cpymem(name.data, clcf->root.data, clcf->root.len); last = ngx_cpystrn(last, r->uri.data + clcf->name.len, r->uri.len + 1 - clcf->name.len); name.len = last - name.data; location.data = ngx_palloc(r->pool, r->uri.len + 2); if (location.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1); #if 0 /* * aliases usually have trailling "/", * set it in the start of the possible redirect */ if (*location.data != '/') { location.data--; } #endif location.len = last - location.data + 1; } else { name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2); if (name.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } location.data = ngx_cpymem(name.data, clcf->root.data, clcf->root.len); last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1); name.len = last - name.data; location.len = last - location.data + 1; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http filename: \"%s\"", name.data); /* allocate cleanups */ if (!(file_cleanup = ngx_push_array(&r->cleanup))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } file_cleanup->valid = 0; slcf = ngx_http_get_module_loc_conf(r, ngx_http_static_module); if (slcf->redirect_cache) { if (!(redirect_cleanup = ngx_push_array(&r->cleanup))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } redirect_cleanup->valid = 0; } else { redirect_cleanup = NULL; } #if (NGX_HTTP_CACHE) /* look up an open files cache */ if (clcf->open_files) { file = ngx_http_cache_get(clcf->open_files, file_cleanup, &name, &file_crc); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http open file cache get: " PTR_FMT, file); if (file && !file->expired) { r->cache = file; return ngx_http_send_cached(r); } } else { file = NULL; } /* look up an redirect cache */ if (slcf->redirect_cache) { redirect = ngx_http_cache_get(slcf->redirect_cache, redirect_cleanup, &name, &redirect_crc); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http redirect cache get: " PTR_FMT, redirect); if (redirect && !redirect->expired) { /* * We do not copy a cached value so the cache entry is locked * until the end of the request. In a single threaded model * the redirected request should complete before other event * will be processed. In a multithreaded model this locking * should keep more popular redirects in cache. */ if (!(r->headers_out.location = ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.location->value = redirect->data.value; return NGX_HTTP_MOVED_PERMANENTLY; } } else { redirect = NULL; } #endif /* open file */ #if (WIN9X) /* TODO: redirect cache */ if (ngx_win32_version < NGX_WIN_NT) { /* * there is no way to open a file or a directory in Win9X with * one syscall because Win9X has no FILE_FLAG_BACKUP_SEMANTICS flag * so we need to check its type before the opening */ if (ngx_file_info(name.data, &fi) == NGX_FILE_ERROR) { err = ngx_errno; ngx_log_error(NGX_LOG_ERR, log, err, ngx_file_info_n " \"%s\" failed", name.data); if (err == NGX_ENOENT || err == NGX_ENOTDIR) { return NGX_HTTP_NOT_FOUND; } else if (err == NGX_EACCES) { return NGX_HTTP_FORBIDDEN; } else { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ngx_is_dir(&fi)) { ngx_log_debug(log, "HTTP DIR: '%s'" _ name.data); if (!(r->headers_out.location = ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } *last++ = '/'; *last = '\0'; r->headers_out.location->value.len = last - location; r->headers_out.location->value.data = location; return NGX_HTTP_MOVED_PERMANENTLY; } } #endif fd = ngx_open_file(name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN); if (fd == NGX_INVALID_FILE) { err = ngx_errno; if (err == NGX_ENOENT || err == NGX_ENOTDIR) { level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; } else if (err == NGX_EACCES) { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; } else { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_log_error(level, log, err, ngx_open_file_n " \"%s\" failed", name.data); return rc; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd); if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, ngx_fd_info_n " \"%s\" failed", name.data); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_is_dir(&fi)) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir"); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } *last++ = '/'; *last = '\0'; r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.location->value = location; #if (NGX_HTTP_CACHE) if (slcf->redirect_cache) { if (redirect) { if (location.len == redirect->data.value.len && ngx_memcmp(redirect->data.value.data, location.data, location.len) == 0) { redirect->accessed = ngx_cached_time; redirect->updated = ngx_cached_time; /* * we can unlock the cache entry because * we have the local copy anyway */ ngx_http_cache_unlock(slcf->redirect_cache, redirect, log); redirect_cleanup->valid = 0; return NGX_HTTP_MOVED_PERMANENTLY; } } location.len++; redirect = ngx_http_cache_alloc(slcf->redirect_cache, redirect, redirect_cleanup, &name, redirect_crc, &location, log); location.len--; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http redirect cache alloc: " PTR_FMT, redirect); if (redirect) { redirect->fd = NGX_INVALID_FILE; redirect->accessed = ngx_cached_time; redirect->last_modified = 0; redirect->updated = ngx_cached_time; redirect->memory = 1; ngx_http_cache_unlock(slcf->redirect_cache, redirect, log); redirect_cleanup->valid = 0; } } #endif return NGX_HTTP_MOVED_PERMANENTLY; } #if !(WIN32) /* the not regular files are probably Unix specific */ if (!ngx_is_file(&fi)) { ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, "%s is not a regular file", name.data); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } return NGX_HTTP_NOT_FOUND; } #endif #if (NGX_HTTP_CACHE) if (clcf->open_files) { #if (NGX_USE_HTTP_FILE_CACHE_UNIQ) if (file && file->uniq == ngx_file_uniq(&fi)) { if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } file->accessed = ngx_cached_time; file->updated = ngx_cached_time; file->expired = 0; r->cache = file; return ngx_http_send_cached(r); } else { if (file) { ngx_http_cache_unlock(clcf->open_files, file, log); file = NULL; } file = ngx_http_cache_alloc(clcf->open_files, file, file_cleanup, &name, file_crc, NULL, log); if (file) { file->uniq = ngx_file_uniq(&fi); } } #else file = ngx_http_cache_alloc(clcf->open_files, file, file_cleanup, &name, file_crc, NULL, log); #endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http open file cache alloc: " PTR_FMT, file); if (file) { file->fd = fd; file->data.size = ngx_file_size(&fi); file->accessed = ngx_cached_time; file->last_modified = ngx_file_mtime(&fi); file->updated = ngx_cached_time; r->cache = file; } return ngx_http_send_cached(r); } #endif ctx = log->data; ctx->action = "sending response to client"; file_cleanup->data.file.fd = fd; file_cleanup->data.file.name = name.data; file_cleanup->valid = 1; file_cleanup->cache = 0; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = ngx_file_size(&fi); r->headers_out.last_modified_time = ngx_file_mtime(&fi); if (r->headers_out.content_length_n == 0) { r->header_only = 1; } if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } #if (NGX_SUPPRESS_WARN) b = NULL; #endif if (!r->header_only) { /* we need to allocate all before the header would be sent */ if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (!(b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->filter_allow_ranges = 1; } rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } b->in_file = 1; if (!r->main) { b->last_buf = 1; } b->file_pos = 0; b->file_last = ngx_file_size(&fi); b->file->fd = fd; b->file->log = log; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); }