# HG changeset patch # User Igor Sysoev # Date 1238616000 -14400 # Node ID 09b703ae3ba5bd75b8a269d1f2dfe137f858b97d # Parent 3d40b0260a84d8c2bf80c8bd57ac73202c9b1e94 nginx 0.6.36 *) Change: now the "Invalid argument" error returned by setsockopt(TCP_NODELAY) on Solaris, is ignored. *) Change: now POSTs without "Content-Length" header line are allowed. *) Feature: the "try_files" directive. *) Feature: the --with-pcre option in the configure. *) Feature: the "if_modified_since" directive. *) Feature: the "$cookie_..." variables. *) Feature: the "$arg_..." variables. *) Bugfix: compatibility with Tru64 UNIX. Thanks to Dustin Marquess. *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for asymmetric ciphers. Thanks to Marcin Gozdalik. *) Bugfix: in a redirect rewrite directive original arguments were concatenated with new arguments by a "?" rather than an "&"; the bug had appeared in 0.1.18. Thanks to Maxim Dounin. *) Bugfix: nginx could not be built on AIX. *) Bugfix: a double response might be returned if the epoll or rtsig methods are used and a redirect was returned to a request with body. Thanks to Eden Li. *) Bugfix: a segmentation fault might occur in worker process if "resolver" directive was used in SMTP proxy. *) Bugfix: fastcgi_store stored files not always. *) Bugfix: nginx did not process a FastCGI server response, if the server send too many messages to stderr before response. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,49 @@ +Changes with nginx 0.6.36 02 Apr 2009 + + *) Change: now the "Invalid argument" error returned by + setsockopt(TCP_NODELAY) on Solaris, is ignored. + + *) Change: now POSTs without "Content-Length" header line are allowed. + + *) Feature: the "try_files" directive. + + *) Feature: the --with-pcre option in the configure. + + *) Feature: the "if_modified_since" directive. + + *) Feature: the "$cookie_..." variables. + + *) Feature: the "$arg_..." variables. + + *) Bugfix: compatibility with Tru64 UNIX. + Thanks to Dustin Marquess. + + *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for + asymmetric ciphers. + Thanks to Marcin Gozdalik. + + *) Bugfix: in a redirect rewrite directive original arguments were + concatenated with new arguments by a "?" rather than an "&"; + the bug had appeared in 0.1.18. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built on AIX. + + *) Bugfix: a double response might be returned if the epoll or rtsig + methods are used and a redirect was returned to a request with + body. + Thanks to Eden Li. + + *) Bugfix: a segmentation fault might occur in worker process if + "resolver" directive was used in SMTP proxy. + + *) Bugfix: fastcgi_store stored files not always. + + *) Bugfix: nginx did not process a FastCGI server response, if the + server send too many messages to stderr before response. + + Changes with nginx 0.6.35 26 Jan 2009 *) Bugfix: in shared memory allocations if nginx was built without @@ -3018,8 +3063,8 @@ Changes with nginx 0.1.18 *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not inherited. - *) Bugfix: in the redirect rewrite directive the arguments were - concatenated with URI by the "&" rather than the "?". + *) Bugfix: in a redirect rewrite directive arguments were concatenated + with URI by an "&" rather than a "?". *) Bugfix: the lines without trailing ";" in the file being included by the ngx_http_geo_module were silently ignored. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,49 @@ +Изменения в nginx 0.6.36 02.04.2009 + + *) Изменение: ошибка "Invalid argument", возвращаемая + setsockopt(TCP_NODELAY) на Solaris, теперь игнорируется. + + *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в + заголовке запроса. + + *) Добавление: директива try_files. + + *) Добавление: параметр --with-pcre в configure. + + *) Добавление: директива if_modified_since. + + *) Добавление: переменные "$cookie_...". + + *) Добавление: переменные "$arg_...". + + *) Исправление: совместимость с Tru64 UNIX. + Спасибо Dustin Marquess. + + *) Исправление: директива ssl_engine не использовала SSL-акселератор + для асимметричных шифров. + Спасибо Marcin Gozdalik. + + *) Исправление: в директиве rewrite, возвращающей редирект, старые + аргументы присоединялись к новым через символ "?" вместо "&"; + ошибка появилась в 0.1.18. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался на AIX. + + *) Исправление: если на запрос с телом возвращался редирект, то ответ + мог быть двойным при использовании методов epoll или rtsig. + Спасибо Eden Li. + + *) Исправление: при использовании директивы resolver в SMTP + прокси-сервере в рабочем процессе мог произойти segmentation fault. + + *) Исправление: fastcgi_store не всегда сохранял файлы. + + *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед + ответом сервер передавал много сообщений в stderr. + + Изменения в nginx 0.6.35 26.01.2009 *) Исправление: ошибки выделения больших блоков в разделяемой памяти, diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -215,6 +215,7 @@ do --with-debug) NGX_DEBUG=YES ;; --without-pcre) USE_PCRE=DISABLED ;; + --with-pcre) USE_PCRE=YES ;; --with-pcre=*) PCRE="$value" ;; --with-pcre-opt=*) PCRE_OPT="$value" ;; @@ -333,7 +334,8 @@ cat << END pentium, pentiumpro, pentium3, pentium4, athlon, opteron, sparc32, sparc64, ppc64 - --without-pcre disable PCRE libarary usage + --without-pcre disable PCRE library usage + --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional options for PCRE building 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,8 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.6.35" +#define nginx_version 006036 +#define NGINX_VERSION "0.6.36" #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 @@ -201,14 +201,14 @@ done: if (filename) { ngx_free(cf->conf_file->buffer->start); - cf->conf_file = prev; - if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", cf->conf_file->file.name.data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -853,31 +853,47 @@ void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vsnprintf(errstr, last - errstr, fmt, args); va_end(args); - *buf = '\0'; + if (err) { + + if (p > last - 50) { + + /* leave a space for an error code */ - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + p = last - 50; + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + +#if (NGX_WIN32) + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + p = ngx_snprintf(p, last - p, " (%d: ", err); +#endif + + p = ngx_strerror_r(err, p, last - p); + + *p++ = ')'; } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -779,12 +779,16 @@ ngx_connection_error(ngx_connection_t *c { ngx_uint_t level; - if (err == NGX_ECONNRESET - && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) - { + if (err == NGX_ECONNRESET && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) { return 0; } +#if (NGX_SOLARIS) + if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) { + return 0; + } +#endif + if (err == 0 || err == NGX_ECONNRESET #if !(NGX_WIN32) @@ -800,6 +804,7 @@ ngx_connection_error(ngx_connection_t *c { switch (c->log_error) { + case NGX_ERROR_IGNORE_EINVAL: case NGX_ERROR_IGNORE_ECONNRESET: case NGX_ERROR_INFO: level = NGX_LOG_INFO; diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -71,10 +71,11 @@ struct ngx_listening_s { typedef enum { - NGX_ERROR_CRIT = 0, + NGX_ERROR_ALERT = 0, NGX_ERROR_ERR, NGX_ERROR_INFO, - NGX_ERROR_IGNORE_ECONNRESET + NGX_ERROR_IGNORE_ECONNRESET, + NGX_ERROR_IGNORE_EINVAL } ngx_connection_log_error_e; @@ -135,7 +136,7 @@ struct ngx_connection_s { unsigned buffered:8; - unsigned log_error:2; /* ngx_connection_log_error_e */ + unsigned log_error:3; /* ngx_connection_log_error_e */ unsigned single_connection:1; unsigned unexpected_eof:1; 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 @@ -127,18 +127,10 @@ ngx_log_error_core(ngx_uint_t level, ngx } #if (NGX_WIN32) - - if ((unsigned) err >= 0x80000000) { - p = ngx_snprintf(p, last - p, " (%Xd: ", err); - - } else { - p = ngx_snprintf(p, last - p, " (%d: ", err); - } - + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); #else - p = ngx_snprintf(p, last - p, " (%d: ", err); - #endif p = ngx_strerror_r(err, p, last - p); @@ -310,7 +302,10 @@ ngx_set_error_log_levels(ngx_conf_t *cf, } } - if (log->log_level == NGX_LOG_DEBUG) { + if (log->log_level == 0) { + log->log_level = NGX_LOG_ERR; + + } else if (log->log_level == NGX_LOG_DEBUG) { log->log_level = NGX_LOG_DEBUG_ALL; } 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 @@ -578,6 +578,7 @@ failed: ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { + u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; @@ -601,19 +602,21 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); - ctx->name.len = rn->nlen; - ctx->name.data = ngx_resolver_dup(r, rn->name, rn->nlen); - if (ctx->name.data == NULL) { + name = ngx_resolver_dup(r, rn->name, rn->nlen); + if (name == NULL) { goto failed; } + ctx->name.len = rn->nlen; + ctx->name.data = name; + /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); - ngx_resolver_free(r, ctx->name.data); + ngx_resolver_free(r, name); return NGX_OK; } @@ -623,7 +626,9 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx ctx->next = rn->waiting; rn->waiting = ctx; - return NGX_AGAIN; + /* unlock addr mutex */ + + return NGX_OK; } ngx_queue_remove(&rn->queue); @@ -1306,7 +1311,7 @@ ngx_resolver_process_a(ngx_resolver_t *r ctx->handler(ctx); } - if (naddrs) { + if (naddrs > 1) { ngx_resolver_free(r, addrs); } @@ -1483,20 +1488,23 @@ ngx_resolver_process_ptr(ngx_resolver_t goto short_response; } - len -= 2; - if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); - if (len != (size_t) rn->nlen || ngx_strncmp(name.data, rn->name, len) != 0) + if (name.len != (size_t) rn->nlen + || ngx_strncmp(name.data, rn->name, name.len) != 0) { - ngx_resolver_free(r, rn->name); + if (rn->nlen) { + ngx_resolver_free(r, rn->name); + } + + rn->nlen = (u_short) name.len; rn->name = name.data; - name.data = ngx_resolver_dup(r, rn->name, len); + name.data = ngx_resolver_dup(r, rn->name, name.len); if (name.data == NULL) { goto failed; } diff --git a/src/core/ngx_shmtx.h b/src/core/ngx_shmtx.h --- a/src/core/ngx_shmtx.h +++ b/src/core/ngx_shmtx.h @@ -57,7 +57,15 @@ ngx_shmtx_trylock(ngx_shmtx_t *mtx) return 0; } - ngx_log_abort(err, ngx_trylock_fd_n " failed"); +#if __osf__ /* Tru64 UNIX */ + + if (err == NGX_EACCESS) { + return 0; + } + +#endif + + ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name); return 0; } @@ -74,7 +82,7 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx) return; } - ngx_log_abort(err, ngx_lock_fd_n " failed"); + ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name); } @@ -89,7 +97,7 @@ ngx_shmtx_unlock(ngx_shmtx_t *mtx) return; } - ngx_log_abort(err, ngx_unlock_fd_n " failed"); + ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -52,8 +52,24 @@ typedef struct { #define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2) +#define ngx_strlen(s) strlen((const char *) s) + #define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) -#define ngx_strlen(s) strlen((const char *) s) + +static ngx_inline u_char * +ngx_strlchr(u_char *p, u_char *last, u_char c) +{ + while (p < last) { + + if (*p == c) { + return p; + } + + p++; + } + + return NULL; +} /* 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 @@ -10,7 +10,7 @@ typedef struct { - ngx_str_t engine; + ngx_uint_t engine; /* unsigned engine:1; */ } ngx_openssl_conf_t; @@ -37,26 +37,17 @@ static void ngx_ssl_session_rbtree_inser ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); -static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); -#if !(NGX_SSL_ENGINE) -static char *ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -#endif - static ngx_command_t ngx_openssl_commands[] = { { ngx_string("ssl_engine"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, -#if (NGX_SSL_ENGINE) - ngx_conf_set_str_slot, -#else - ngx_openssl_noengine, -#endif + ngx_openssl_engine, 0, - offsetof(ngx_openssl_conf_t, engine), + 0, NULL }, ngx_null_command @@ -66,7 +57,7 @@ static ngx_command_t ngx_openssl_comman static ngx_core_module_t ngx_openssl_module_ctx = { ngx_string("openssl"), ngx_openssl_create_conf, - ngx_openssl_init_conf + NULL }; @@ -1921,8 +1912,7 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc /* * set by ngx_pcalloc(): * - * oscf->engine.len = 0; - * oscf->engine.data = NULL; + * oscf->engine = 0; */ return oscf; @@ -1930,53 +1920,54 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc static char * -ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf) +ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { #if (NGX_SSL_ENGINE) ngx_openssl_conf_t *oscf = conf; - ENGINE *engine; - - if (oscf->engine.len == 0) { - return NGX_CONF_OK; + ENGINE *engine; + ngx_str_t *value; + + if (oscf->engine) { + return "is duplicate"; } - engine = ENGINE_by_id((const char *) oscf->engine.data); + oscf->engine = 1; + + value = cf->args->elts; + + engine = ENGINE_by_id((const char *) value[1].data); if (engine == NULL) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, - "ENGINE_by_id(\"%V\") failed", &oscf->engine); + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + "ENGINE_by_id(\"%V\") failed", &value[1]); return NGX_CONF_ERROR; } if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed", - &oscf->engine); + &value[1]); + + ENGINE_free(engine); + return NGX_CONF_ERROR; } ENGINE_free(engine); -#endif - return NGX_CONF_OK; -} - - -#if !(NGX_SSL_ENGINE) - -static char * -ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"ssl_engine\" directive is available only in " "OpenSSL 0.9.7 and higher,"); return NGX_CONF_ERROR; -} #endif +} static void diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -299,6 +299,11 @@ ngx_http_autoindex_handler(ngx_http_requ if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_de_info_n " \"%s\" failed", filename); + + if (err == NGX_EACCES) { + continue; + } + return ngx_http_autoindex_error(r, &dir, &path); } diff --git a/src/http/modules/ngx_http_not_modified_filter_module.c b/src/http/modules/ngx_http_not_modified_filter_module.c --- a/src/http/modules/ngx_http_not_modified_filter_module.c +++ b/src/http/modules/ngx_http_not_modified_filter_module.c @@ -50,7 +50,8 @@ static ngx_http_output_header_filter_pt static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { - time_t ims; + time_t ims; + ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main @@ -60,23 +61,32 @@ ngx_int_t ngx_http_not_modified_header_f return ngx_http_next_header_filter(r); } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) { + return ngx_http_next_header_filter(r); + } + ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http ims:%d lm:%d", ims, r->headers_out.last_modified_time); - /* - * I think that the equality of the dates is correcter - */ + if (ims != r->headers_out.last_modified_time) { - if (ims == r->headers_out.last_modified_time) { - r->headers_out.status = NGX_HTTP_NOT_MODIFIED; - r->headers_out.content_type.len = 0; - ngx_http_clear_content_length(r); - ngx_http_clear_accept_ranges(r); + if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT + || ims < r->headers_out.last_modified_time) + { + return ngx_http_next_header_filter(r); + } } + r->headers_out.status = NGX_HTTP_NOT_MODIFIED; + r->headers_out.content_type.len = 0; + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + return ngx_http_next_header_filter(r); } 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.35'; +our $VERSION = '0.6.36'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -406,7 +406,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; - n = use_rewrite + use_access + 1; /* find config phase */ + n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; @@ -475,6 +475,15 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma continue; + case NGX_HTTP_TRY_FILES_PHASE: + if (cmcf->try_files) { + ph->checker = ngx_http_core_try_files_phase; + n++; + ph++; + } + + continue; + case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; @@ -896,8 +905,8 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma if (in_addr[i].hash.buckets == NULL && (in_addr[i].wc_head == NULL || in_addr[i].wc_head->hash.buckets == NULL) - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL)) + && (in_addr[i].wc_tail == NULL + || in_addr[i].wc_tail->hash.buckets == NULL)) { continue; } diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -71,6 +71,8 @@ ngx_int_t ngx_http_parse_unsafe_uri(ngx_ ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); +void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, + ngx_str_t *args); ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -62,6 +62,8 @@ static char *ngx_http_core_limit_except( void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, @@ -114,6 +116,14 @@ static ngx_conf_enum_t ngx_http_core_sa }; +static ngx_conf_enum_t ngx_http_core_if_modified_since[] = { + { ngx_string("off"), NGX_HTTP_IMS_OFF }, + { ngx_string("exact"), NGX_HTTP_IMS_EXACT }, + { ngx_string("before"), NGX_HTTP_IMS_BEFORE }, + { ngx_null_string, 0 } +}; + + #if (NGX_HTTP_GZIP) static ngx_conf_enum_t ngx_http_gzip_http_version[] = { @@ -507,6 +517,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, server_tokens), NULL }, + { ngx_string("if_modified_since"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, if_modified_since), + &ngx_http_core_if_modified_since }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -515,6 +532,13 @@ static ngx_command_t ngx_http_core_comm 0, NULL }, + { ngx_string("try_files"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_core_try_files, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("post_action"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -997,6 +1021,185 @@ ngx_http_core_post_access_phase(ngx_http ngx_int_t +ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph) +{ + size_t len, root, alias, reserve, allocated; + u_char *p, *name; + ngx_str_t path, args; + ngx_uint_t test_dir; + ngx_http_try_file_t *tf; + ngx_open_file_info_t of; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_len_code_pt lcode; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try files phase: %ui", r->phase_handler); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->try_files == NULL) { + r->phase_handler++; + return NGX_AGAIN; + } + + allocated = 0; + root = 0; + name = NULL; + /* suppress MSVC warning */ + path.data = NULL; + + tf = clcf->try_files; + + alias = clcf->alias ? clcf->name.len : 0; + + for ( ;; ) { + + if (tf->lengths) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = tf->lengths->elts; + e.request = r; + + /* 1 is for terminating '\0' as in static names */ + len = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + } else { + len = tf->name.len; + } + + /* 16 bytes are preallocation */ + reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16; + + if (reserve > allocated) { + + /* we just need to allocate path and to copy a root */ + + if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + name = path.data + root; + allocated = path.len - root - (r->uri.len - alias); + } + + if (tf->values == NULL) { + + /* tf->name.len includes the terminating '\0' */ + + ngx_memcpy(name, tf->name.data, tf->name.len); + + path.len = (name + tf->name.len - 1) - path.data; + + } else { + e.ip = tf->values->elts; + e.pos = name; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + path.len = e.pos - path.data; + + *e.pos = '\0'; + + if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) { + ngx_memcpy(name, name + alias, len - alias); + path.len -= alias; + } + } + + test_dir = tf->test_dir; + + tf++; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use file: \"%s\" \"%s\"", name, path.data); + + if (tf->lengths == NULL && tf->name.len == 0) { + + path.len -= root; + path.data += root; + + if (path.data[0] == '@') { + (void) ngx_http_named_location(r, &path); + + } else { + ngx_http_split_args(r, &path, &args); + + (void) ngx_http_internal_redirect(r, &path, &args); + } + + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.errors = clcf->open_file_cache_errors; + of.events = clcf->open_file_cache_events; + + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + ngx_open_file_n " \"%s\" failed", path.data); + } + + continue; + } + + if (of.is_dir && !test_dir) { + continue; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_palloc(r->pool, r->uri.len); + if (r->uri.data == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + p = ngx_copy(r->uri.data, clcf->name.data, alias); + ngx_memcpy(p, name, path.len); + } + + if (ngx_http_set_exten(r) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try file uri: \"%V\"", &r->uri); + + r->phase_handler++; + return NGX_AGAIN; + } + + /* not reached */ +} + + +ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { @@ -2672,6 +2875,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t * lcf->default_type = { 0, NULL }; * lcf->err_log = NULL; * lcf->error_pages = NULL; + * lcf->try_files = NULL; * lcf->client_body_path = NULL; * lcf->regex = NULL; * lcf->exact_match = 0; @@ -2684,6 +2888,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; lcf->satisfy = NGX_CONF_UNSET_UINT; + lcf->if_modified_since = NGX_CONF_UNSET_UINT; lcf->internal = NGX_CONF_UNSET; lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; @@ -2870,6 +3075,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, + NGX_HTTP_IMS_EXACT); ngx_conf_merge_value(conf->internal, prev->internal, 0); ngx_conf_merge_value(conf->client_body_in_file_only, prev->client_body_in_file_only, 0); @@ -3588,6 +3795,71 @@ ngx_http_core_error_page(ngx_conf_t *cf, static char * +ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + ngx_uint_t i, n; + ngx_http_try_file_t *tf; + ngx_http_script_compile_t sc; + ngx_http_core_main_conf_t *cmcf; + + if (clcf->try_files) { + return "is duplicate"; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->try_files = 1; + + tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t)); + if (tf == NULL) { + return NGX_CONF_ERROR; + } + + clcf->try_files = tf; + + value = cf->args->elts; + + for (i = 0; i < cf->args->nelts - 1; i++) { + + tf[i].name = value[i + 1]; + + if (tf[i].name.data[tf[i].name.len - 1] == '/') { + tf[i].test_dir = 1; + tf[i].name.len--; + tf[i].name.data[tf[i].name.len] = '\0'; + } + + n = ngx_http_script_variables_count(&tf[i].name); + + if (n) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &tf[i].name; + sc.lengths = &tf[i].lengths; + sc.values = &tf[i].values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + /* add trailing '\0' to length */ + tf[i].name.len++; + } + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *lcf = conf; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -28,6 +28,11 @@ #define NGX_HTTP_SATISFY_ANY 1 +#define NGX_HTTP_IMS_OFF 0 +#define NGX_HTTP_IMS_EXACT 1 +#define NGX_HTTP_IMS_BEFORE 2 + + typedef struct { unsigned default_server:1; unsigned bind:1; @@ -74,6 +79,7 @@ typedef enum { NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, + NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE @@ -122,6 +128,8 @@ typedef struct { ngx_hash_keys_arrays_t *variables_keys; + ngx_uint_t try_files; /* unsigned try_files:1 */ + ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; @@ -231,6 +239,14 @@ typedef struct { } ngx_http_err_page_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; + ngx_uint_t test_dir; /* unsigned test_dir:1; */ +} ngx_http_try_file_t; + + typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; struct ngx_http_core_loc_conf_s { @@ -292,6 +308,7 @@ struct ngx_http_core_loc_conf_s { time_t keepalive_header; /* keepalive_timeout */ ngx_uint_t satisfy; /* satisfy */ + ngx_uint_t if_modified_since; /* if_modified_since */ ngx_flag_t internal; /* internal */ ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ @@ -319,6 +336,7 @@ struct ngx_http_core_loc_conf_s { #endif ngx_array_t *error_pages; /* error_page */ + ngx_http_try_file_t *try_files; /* try_files */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ @@ -350,6 +368,8 @@ ngx_int_t ngx_http_core_access_phase(ngx ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1467,3 +1467,39 @@ ngx_http_parse_multi_header_lines(ngx_ar return NGX_DECLINED; } + + +void +ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) +{ + u_char ch, *p, *last; + + p = uri->data; + + last = p + uri->len; + + args->len = 0; + + while (p < last) { + + ch = *p++; + + if (ch == '?') { + args->len = last - p; + args->data = p; + + uri->len = p - 1 - uri->data; + + if (ngx_strlchr(p, last, '\0') != NULL) { + r->zero_in_uri = 1; + } + + return; + } + + if (ch == '\0') { + r->zero_in_uri = 1; + continue; + } + } +} 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 @@ -650,6 +650,7 @@ ngx_http_process_request_line(ngx_event_ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; + *r->request_end = '\0'; if (r->args_start) { @@ -1387,9 +1388,7 @@ ngx_http_process_request_header(ngx_http } } - if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT) - && r->headers_in.content_length_n == -1) - { + if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent %V method without \"Content-Length\" header", &r->method_name); @@ -2091,6 +2090,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler"); if (r->discard_body) { + r->write_event_handler = ngx_http_request_empty_handler; r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); return; @@ -2264,8 +2264,15 @@ ngx_http_set_keepalive(ngx_http_request_ (const void *) &tcp_nodelay, sizeof(int)) == -1) { +#if (NGX_SOLARIS) + /* Solaris returns EINVAL if a socket has been shut down */ + c->log_error = NGX_ERROR_IGNORE_EINVAL; +#endif + ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); + + c->log_error = NGX_ERROR_INFO; ngx_http_close_connection(c); return; } diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -244,10 +244,21 @@ ngx_http_script_compile(ngx_http_script_ name.data = &sc->source->data[i]; - while (i < sc->source->len - && sc->source->data[i] != '$' - && !(sc->source->data[i] == '?' && sc->compile_args)) - { + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + if (sc->source->data[i] == '?') { + + sc->args = 1; + + if (sc->compile_args) { + break; + } + } + i++; name.len++; } 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 @@ -1099,62 +1099,59 @@ ngx_http_upstream_process_header(ngx_eve #endif } - n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); - - if (n == NGX_AGAIN) { + for ( ;; ) { + + n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); + + if (n == NGX_AGAIN) { #if 0 - ngx_add_timer(rev, u->read_timeout); + ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } - return; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream prematurely closed connection"); - } - - if (n == NGX_ERROR || n == 0) { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); - return; - } - - u->buffer.last += n; + u->buffer.last += n; #if 0 - u->valid_header_in = 0; - - u->peer.cached = 0; -#endif - - rc = u->process_header(r); - - if (rc == NGX_AGAIN) { -#if 0 - ngx_add_timer(rev, u->read_timeout); + u->valid_header_in = 0; + + u->peer.cached = 0; #endif - if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); - - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); - return; + rc = u->process_header(r); + + if (rc == NGX_AGAIN) { + + if (u->buffer.pos == u->buffer.end) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent too big header"); + + ngx_http_upstream_next(r, u, + NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } + + continue; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - return; + break; } if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { @@ -1962,6 +1959,7 @@ ngx_http_upstream_process_downstream(ngx static void ngx_http_upstream_process_body(ngx_event_t *ev) { + ngx_uint_t del; ngx_temp_file_t *tf; ngx_event_pipe_t *p; ngx_connection_t *c, *downstream; @@ -2057,20 +2055,25 @@ ngx_http_upstream_process_body(ngx_event if (u->store) { + del = p->upstream_error; + tf = u->pipe->temp_file; - if (p->upstream_eof - && u->headers_in.status_n == NGX_HTTP_OK - && (u->headers_in.content_length_n == -1 - || (u->headers_in.content_length_n == tf->offset))) - { - ngx_http_upstream_store(r, u); - - } else if ((p->upstream_error - || (p->upstream_eof - && u->headers_in.status_n != NGX_HTTP_OK)) - && tf->file.fd != NGX_INVALID_FILE) - { + if (p->upstream_eof || p->upstream_done) { + + if (u->headers_in.status_n == NGX_HTTP_OK + && (u->headers_in.content_length_n == -1 + || (u->headers_in.content_length_n == tf->offset))) + { + ngx_http_upstream_store(r, u); + + } else { + del = 1; + } + } + + if (del && tf->file.fd != NGX_INVALID_FILE) { + if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -26,6 +26,10 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -481,6 +485,24 @@ ngx_http_get_variable(ngx_http_request_t return NULL; } + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { + + if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + + if (ngx_strncmp(name->data, "arg_", 4) == 0) { + + if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + vv->not_found = 1; if (nowarn == 0) { @@ -712,6 +734,90 @@ ngx_http_variable_unknown_header(ngx_htt static ngx_int_t +ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + s.len = name->len - (sizeof("cookie_") - 1); + s.data = name->data + sizeof("cookie_") - 1; + + if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie) + == NGX_DECLINED) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + u_char *p, *arg; + size_t len; + + if (r->args.len == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = name->len - 1 - (sizeof("arg_") - 1); + arg = name->data + sizeof("arg_") - 1; + + for (p = r->args.data; *p && *p != ' '; p++) { + + /* + * although r->args.data is not null-terminated by itself, + * however, there is null in the end of request line + */ + + p = ngx_strcasestrn(p, (char *) arg, len); + + if (p == NULL) { + v->not_found = 1; + return NGX_OK; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') { + + v->data = p + len + 2; + + p = (u_char *) ngx_strchr(p, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + v->len = p - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -1396,6 +1502,20 @@ ngx_http_variables_init_vars(ngx_conf_t continue; } + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { + v[i].get_handler = ngx_http_variable_argument; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unknown \"%V\" variable", &v[i].name); diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -12,6 +12,7 @@ static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); +static void ngx_mail_smtp_resolve_name(ngx_event_t *rev); static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); @@ -88,9 +89,8 @@ ngx_mail_smtp_init_session(ngx_mail_sess static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) { - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; + ngx_connection_t *c; + ngx_mail_session_t *s; s = ctx->data; c = s->connection; @@ -131,6 +131,23 @@ ngx_mail_smtp_resolve_addr_handler(ngx_r ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "address resolved: %V", &s->host); + c->read->handler = ngx_mail_smtp_resolve_name; + + ngx_post_event(c->read, &ngx_posted_events); +} + + +static void +ngx_mail_smtp_resolve_name(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ctx = ngx_resolve_start(cscf->resolver, NULL); diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c --- a/src/os/unix/ngx_errno.c +++ b/src/os/unix/ngx_errno.c @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char * /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c --- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -494,10 +494,16 @@ ngx_process_get_status(void) } if (WTERMSIG(status)) { +#ifdef WCOREDUMP ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d%s", process, pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); +#else + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d", + process, pid, WTERMSIG(status)); +#endif } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,