# HG changeset patch # User Igor Sysoev # Date 1227128400 -10800 # Node ID fc497c1dfb7c38ddec71f75a5dd1528d58d7dd19 # Parent 15c4ba3bc2fad0e15329058ac6f18c5cc879510e nginx 0.6.33 *) Feature: now nginx returns the 405 status code for POST method requesting a static file only if the file exists. *) Workaround: compatibility with glibc 2.3. Thanks to Eric Benson and Maxim Dounin. *) Bugfix: the resolver did not understand big DNS responses. Thanks to Zyb. *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" error. *) Bugfix: the ngx_http_charset_module did not understand quoted charset name received from backend. *) Bugfix: if the "max_fails=0" parameter was used in upstream with several servers, then a worker process exited on a SIGFPE signal. Thanks to Maxim Dounin. *) Bugfix: the $r->header_in() method did not return value of the "Host", "User-Agent", and "Connection" request header lines; the bug had appeared in 0.6.32. *) Bugfix: a full response was returned for request method HEAD while redirection via an "error_page" directive. *) Bugfix: if a directory has search only rights and the first index file was absent, then nginx returned the 500 status code. *) Bugfix: of recursive error_page for 500 status code. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,38 @@ +Changes with nginx 0.6.33 20 Nov 2008 + + *) Feature: now nginx returns the 405 status code for POST method + requesting a static file only if the file exists. + + *) Workaround: compatibility with glibc 2.3. + Thanks to Eric Benson and Maxim Dounin. + + *) Bugfix: the resolver did not understand big DNS responses. + Thanks to Zyb. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error. + + *) Bugfix: the ngx_http_charset_module did not understand quoted + charset name received from backend. + + *) Bugfix: if the "max_fails=0" parameter was used in upstream with + several servers, then a worker process exited on a SIGFPE signal. + Thanks to Maxim Dounin. + + *) Bugfix: the $r->header_in() method did not return value of the + "Host", "User-Agent", and "Connection" request header lines; the bug + had appeared in 0.6.32. + + *) Bugfix: a full response was returned for request method HEAD while + redirection via an "error_page" directive. + + *) Bugfix: if a directory has search only rights and the first index + file was absent, then nginx returned the 500 status code. + + *) Bugfix: of recursive error_page for 500 status code. + + Changes with nginx 0.6.32 07 Jul 2008 *) Change: the "none" parameter in the "ssl_session_cache" directive; @@ -274,8 +308,7 @@ Changes with nginx 0.6.22 Changes with nginx 0.6.21 03 Dec 2007 *) Change: if variable values used in a "proxy_pass" directive contain - IP-addresses only, then a "resolver" directive is not mandatory. - resolver + IP-addresses only, then a "resolver" directive is not mandatory. *) Bugfix: a segmentation fault might occur in worker process if a "proxy_pass" directive with URI-part was used; the bug had appeared @@ -1427,7 +1460,7 @@ Changes with nginx 0.3.55 *) Bugfix: if the request contained "//" or "/./" and escaped symbols after them, then the proxied request was sent unescaped. - *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now + *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now returns all "Cookie" header lines. *) Bugfix: a segmentation fault occurred if diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,39 @@ +Изменения в nginx 0.6.33 20.11.2008 + + *) Добавление: теперь nginx возвращает код 405 для метода POST при + запросе статического файла, только если файл существует. + + *) Изменение: совместимость с glibc 2.3. + Спасибо Eric Benson и Максиму Дунину. + + *) Исправление: resolver не понимал большие DNS-ответы. + Спасибо Zyb. + + *) Исправление: при использовании HTTPS запросы могли завершаться с + ошибкой "bad write retry". + + *) Исправление: модуль ngx_http_charset_module не понимал название + кодировки в кавычках, полученное от бэкенда. + + *) Исправление: при использовании параметра max_fails=0 в upstream'е с + несколькими серверами рабочий процесс выходил по сигналу SIGFPE. + Спасибо Максиму Дунину. + + *) Исправление: метод $r->header_in() не возвращал значения строк + "Host", "User-Agent", и "Connection" из заголовка запроса; ошибка + появилась в 0.6.32. + + *) Исправление: при перенаправлении запроса с методом HEAD с помощью + директивы error_page возвращался полный ответ. + + *) Исправление: если у каталога были права доступа только на поиск + файлов и первый индексный файл отсутствовал, то nginx возвращал + ошибку 500. + + *) Исправление: рекурсивной error_page для 500 ошибки. + + Изменения в nginx 0.6.32 07.07.2008 *) Изменение: параметр "none" в директиве ssl_session_cache; теперь @@ -1461,7 +1496,7 @@ закодированные символы в виде "%XX", то проксируемый запрос передавался незакодированным. - *) Исправление: метод $r->headers_in("Cookie") модуля + *) Исправление: метод $r->header_in("Cookie") модуля ngx_http_perl_module теперь возвращает все строки "Cookie" в заголовке запроса. diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -192,6 +192,8 @@ static char **ngx_os_environ; int ngx_cdecl main(int argc, char *const *argv) { + char *p; + ssize_t n; ngx_int_t i; ngx_log_t *log; ngx_cycle_t *cycle, init_cycle; @@ -241,23 +243,30 @@ main(int argc, char *const *argv) } if (ngx_show_version) { - ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF, - sizeof("nginx version: " NGINX_VER CRLF) - 1); + + p = "nginx version: " NGINX_VER CRLF; + n = sizeof("nginx version: " NGINX_VER CRLF) - 1; + + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } if (ngx_show_configure) { #ifdef NGX_COMPILER - ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF, - sizeof("built by " NGX_COMPILER CRLF) - 1); + p = "built by " NGX_COMPILER CRLF; + n = sizeof("built by " NGX_COMPILER CRLF) - 1; + + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } #endif -#ifndef __WATCOMC__ - - /* OpenWatcomC could not build the long NGX_CONFIGURE string */ + p = "configure arguments: " NGX_CONFIGURE CRLF; + n = sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1; - ngx_write_fd(ngx_stderr_fileno, - "configure arguments: " NGX_CONFIGURE CRLF, - sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1); -#endif + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } } if (!ngx_test_config) { diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.6.32" +#define NGINX_VERSION "0.6.33" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -806,6 +806,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n static void ngx_conf_flush_files(ngx_cycle_t *cycle) { + ssize_t n, len; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; @@ -826,11 +827,24 @@ ngx_conf_flush_files(ngx_cycle_t *cycle) i = 0; } - if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) { + len = file[i].pos - file[i].buffer; + + if (file[i].buffer == NULL || len == 0) { continue; } - ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer); + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } } } diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -970,6 +970,7 @@ ngx_test_lockfile(u_char *file, ngx_log_ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { + ssize_t n, len; ngx_fd_t fd; ngx_uint_t i; ngx_list_part_t *part; @@ -993,9 +994,23 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx continue; } - if (file[i].buffer && file[i].pos - file[i].buffer != 0) { - ngx_write_fd(file[i].fd, file[i].buffer, - file[i].pos - file[i].buffer); + len = file[i].pos - file[i].buffer; + + if (file[i].buffer && len != 0) { + + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } + file[i].pos = file[i].buffer; } diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c --- a/src/core/ngx_log.c +++ b/src/core/ngx_log.c @@ -158,7 +158,7 @@ ngx_log_error_core(ngx_uint_t level, ngx ngx_linefeed(p); - ngx_write_fd(log->file->fd, errstr, p - errstr); + (void) ngx_write_fd(log->file->fd, errstr, p - errstr); } diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -1836,7 +1836,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx } if (n & 0xc0) { - n = (n & 0x3f << 8) + *p; + n = ((n & 0x3f) << 8) + *p; p = &buf[n]; } else { @@ -1886,7 +1886,7 @@ done: } } else { - n = (n & 0x3f << 8) + *src; + n = ((n & 0x3f) << 8) + *src; src = &buf[n]; n = *src++; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -187,13 +187,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_ SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); } - /* - * we need this option because in ngx_ssl_send_chain() - * we may switch to a buffered write and may copy leftover part of - * previously unbuffered data to our internal buffer - */ - SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_read_ahead(ssl->ctx, 1); return NGX_OK; @@ -776,14 +769,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ssize_t send, size; ngx_buf_t *buf; - if (!c->ssl->buffer - || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED))) - { - /* - * we avoid a buffer copy if - * we do not need to buffer the output - * or the incoming buf is a single and our buffer is empty - */ + if (!c->ssl->buffer) { while (in) { if (ngx_buf_special(in->buf)) { diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -306,6 +306,19 @@ ngx_http_index_test_dir(ngx_http_request return ngx_http_index_error(r, dir.data, NGX_ENOENT); } + if (of.err == NGX_EACCES) { + + *last = c; + + /* + * ngx_http_index_test_dir() is called after the first index + * file testing has returned an error distinct from NGX_EACCES. + * This means that directory searching is allowed. + */ + + return NGX_OK; + } + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, ngx_open_file_n " \"%s\" failed", dir.data); } diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c --- a/src/http/modules/ngx_http_static_module.c +++ b/src/http/modules/ngx_http_static_module.c @@ -58,7 +58,7 @@ ngx_http_static_handler(ngx_http_request ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; - if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_HTTP_NOT_ALLOWED; } @@ -71,12 +71,6 @@ ngx_http_static_handler(ngx_http_request return NGX_DECLINED; } - rc = ngx_http_discard_request_body(r); - - if (rc != NGX_OK) { - return rc; - } - log = r->connection->log; /* @@ -186,6 +180,16 @@ ngx_http_static_handler(ngx_http_request #endif + if (r->method & NGX_HTTP_POST) { + return NGX_HTTP_NOT_ALLOWED; + } + + rc = ngx_http_discard_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + log->action = "sending response to client"; r->headers_out.status = NGX_HTTP_OK; diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.6.32'; +our $VERSION = '0.6.33'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -74,15 +74,18 @@ static char *ngx_http_client_errors[] = ngx_http_header_t ngx_http_headers_in[] = { - { ngx_string("Host"), 0, ngx_http_process_host }, - - { ngx_string("Connection"), 0, ngx_http_process_connection }, + { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), + ngx_http_process_host }, + + { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), + ngx_http_process_connection }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), ngx_http_process_unique_header_line }, - { ngx_string("User-Agent"), 0, ngx_http_process_user_agent }, + { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), + ngx_http_process_user_agent }, { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer), ngx_http_process_header_line }, diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -327,7 +327,6 @@ static ngx_str_t ngx_http_get_name = { 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; @@ -335,12 +334,6 @@ ngx_http_special_response_handler(ngx_ht ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http special response: %d, \"%V\"", error, &r->uri); - rc = ngx_http_discard_request_body(r); - - if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { - error = NGX_HTTP_INTERNAL_SERVER_ERROR; - } - r->err_status = error; if (r->keepalive != 0) { @@ -370,7 +363,7 @@ ngx_http_special_response_handler(ngx_ht clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (!r->error_page && clcf->error_pages) { + if (!r->error_page && clcf->error_pages && r->uri_changes != 0) { if (clcf->recursive_error_pages == 0) { r->error_page = 1; @@ -385,6 +378,10 @@ ngx_http_special_response_handler(ngx_ht } } + if (ngx_http_discard_request_body(r) != NGX_OK) { + error = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (clcf->msie_refresh && r->headers_in.msie && (error == NGX_HTTP_MOVED_PERMANENTLY @@ -492,8 +489,10 @@ ngx_http_send_error_page(ngx_http_reques if (uri->data[0] == '/') { - r->method = NGX_HTTP_GET; - r->method_name = ngx_http_get_name; + if (r->method != NGX_HTTP_HEAD) { + r->method = NGX_HTTP_GET; + r->method_name = ngx_http_get_name; + } return ngx_http_internal_redirect(r, uri, args); } diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2625,7 +2625,17 @@ ngx_http_upstream_copy_content_type(ngx_ r->headers_out.content_type_len = last - h->value.data; - r->headers_out.charset.len = h->value.data + h->value.len - p; + if (*p == '"') { + p++; + } + + last = h->value.data + h->value.len; + + if (*(last - 1) == '"') { + last--; + } + + r->headers_out.charset.len = last - p; r->headers_out.charset.data = p; return NGX_OK; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -645,7 +645,9 @@ ngx_http_upstream_free_round_robin_peer( peer->fails++; peer->accessed = now; - peer->current_weight -= peer->weight / peer->max_fails; + if (peer->max_fails) { + peer->current_weight -= peer->weight / peer->max_fails; + } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free rr peer failed: %ui %i", diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -68,7 +68,17 @@ ssize_t ngx_write_chain_to_file(ngx_file #define ngx_read_fd read #define ngx_read_fd_n "read()" -#define ngx_write_fd write +/* + * we use inlined function instead of simple #define + * because glibc 2.3 sets warn_unused_result attribute for write() + * and in this case gcc 4.3 ignores (void) cast + */ +static ngx_inline ssize_t +ngx_write_fd(ngx_fd_t fd, void *buf, size_t n) +{ + return write(fd, buf, n); +} + #define ngx_write_fd_n "write()" #define ngx_linefeed(p) *p++ = LF;