# HG changeset patch # User Igor Sysoev # Date 1229288400 -10800 # Node ID dac47e9ef0d5fc6ec8cd1e0ff433c86e96d1a358 # Parent 3b8e9d1bc9bb09ec584983982aed45d7466feee1 nginx 0.7.27 *) Feature: the "try_files" directive. *) Feature: variables support in the "fastcgi_pass" directive. *) Feature: now the $geo variable may get an address from a variable. Thanks to Andrei Nigmatulin. *) Feature: now a location's modifier may be used without space before name. *) Feature: the $upstream_response_length variable. *) Bugfix: now a "add_header" directive does not add an empty value. *) Bugfix: if zero length static file was requested, then nginx just closed connection; the bug had appeared in 0.7.25. *) Bugfix: a MOVE method could not move file in non-existent directory. *) Bugfix: a segmentation fault occurred in worker process, if no one named location was defined in server, but some one was used in an error_page directive. Thanks to Sergey Bochenkov. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,32 @@ +Changes with nginx 0.7.27 15 Dec 2008 + + *) Feature: the "try_files" directive. + + *) Feature: variables support in the "fastcgi_pass" directive. + + *) Feature: now the $geo variable may get an address from a + variable. + Thanks to Andrei Nigmatulin. + + *) Feature: now a location's modifier may be used without space before + name. + + *) Feature: the $upstream_response_length variable. + + *) Bugfix: now a "add_header" directive does not add an empty value. + + *) Bugfix: if zero length static file was requested, then nginx just + closed connection; the bug had appeared in 0.7.25. + + *) Bugfix: a MOVE method could not move file in non-existent directory. + + *) Bugfix: a segmentation fault occurred in worker process, if no one + named location was defined in server, but some one was used in an + error_page directive. + Thanks to Sergey Bochenkov. + + Changes with nginx 0.7.26 08 Dec 2008 *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,33 @@ +Изменения в nginx 0.7.27 15.12.2008 + + *) Добавление: директива try_files. + + *) Добавление: директива fastcgi_pass поддерживает переменные. + + *) Добавление: теперь директива geo может брать адрес из переменной. + Спасибо Андрею Нигматулину. + + *) Добавление: теперь модификатор location'а можно указывать без + пробела перед названием. + + *) Добавление: переменная $upstream_response_length. + + *) Исправление: теперь директива add_header не добавляет пустое + значение. + + *) Исправление: при запросе файла нулевой длины nginx закрывал + соединение, ничего не передав; ошибка появилась в 0.7.25. + + *) Исправление: метод MOVE не мог перемещать файл в несуществующий + каталог. + + *) Исправление: если в сервере не был описан ни один именованный + location, но такой location использовался в директиве error_page, то + в рабочем процессе происходил segmentation fault. + Спасибо Сергею Боченкову. + + Изменения в nginx 0.7.26 08.12.2008 *) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25. 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.7.26" +#define NGINX_VERSION "0.7.27" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -487,11 +487,13 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ #if !(NGX_WIN32) - if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, - ngx_change_file_access_n " \"%s\" failed", src->data); - err = 0; - goto failed; + if (ext->access) { + if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_change_file_access_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } } #endif @@ -517,7 +519,7 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ goto failed; } - err = ngx_create_full_path(to->data, ngx_dir_access(ext->access)); + err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access)); if (err) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, @@ -561,12 +563,14 @@ failed: } } - if (err) { + if (err && ext->log_rename_error) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, ngx_rename_file_n " \"%s\" to \"%s\" failed", src->data, to->data); } + ext->rename_error = err; + return NGX_ERROR; } diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -60,11 +60,14 @@ typedef struct { typedef struct { ngx_uint_t access; + ngx_uint_t path_access; time_t time; ngx_fd_t fd; + ngx_err_t rename_error; unsigned create_path:1; unsigned delete_file:1; + unsigned log_rename_error:1; ngx_log_t *log; } ngx_ext_rename_file_t; 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); 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 @@ -498,11 +498,11 @@ ngx_ssl_handshake(ngx_connection_t *c) if (n == 1) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -569,7 +569,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -581,7 +581,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -767,7 +767,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->ssl->saved_write_handler = NULL; c->write->ready = 1; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -795,7 +795,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->write->ready = 0; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -1019,7 +1019,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->ssl->saved_read_handler = NULL; c->read->ready = 1; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1047,7 +1047,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->read->ready = 0; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1147,11 +1147,11 @@ ngx_ssl_shutdown(ngx_connection_t *c) c->read->handler = ngx_ssl_shutdown_handler; c->write->handler = ngx_ssl_shutdown_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -56,7 +56,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; - if (ngx_handle_read_event(rev, flags) == NGX_ERROR) { + if (ngx_handle_read_event(rev, flags) != NGX_OK) { return NGX_ABORT; } @@ -70,7 +70,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) { wev = p->downstream->write; - if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { return NGX_ABORT; } diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -246,9 +246,11 @@ ngx_http_dav_put_handler(ngx_http_reques dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); ext.access = dlcf->access; + ext.path_access = dlcf->access; ext.time = -1; ext.create_path = dlcf->create_full_put_path; ext.delete_file = 1; + ext.log_rename_error = 1; ext.log = r->connection->log; if (r->headers_in.date) { @@ -521,6 +523,7 @@ ngx_http_dav_copy_move_handler(ngx_http_ ngx_tree_ctx_t tree; ngx_file_info_t fi; ngx_table_elt_t *dest, *over; + ngx_ext_rename_file_t ext; ngx_http_dav_copy_ctx_t copy; ngx_http_dav_loc_conf_t *dlcf; @@ -781,9 +784,32 @@ overwrite_done: } else { if (r->method == NGX_HTTP_MOVE) { - if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) { + + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + ext.access = 0; + ext.path_access = dlcf->access; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 0; + ext.log_rename_error = 0; + ext.log = r->connection->log; + + if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) { return NGX_HTTP_NO_CONTENT; } + + if (ext.rename_error != NGX_EXDEV) { + + if (ext.rename_error) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, + ext.rename_error, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + path.data, copy.path.data); + } + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -19,6 +19,9 @@ typedef struct { ngx_array_t *params; ngx_array_t *params_source; ngx_array_t *catch_stderr; + + ngx_array_t *fastcgi_lengths; + ngx_array_t *fastcgi_values; } ngx_http_fastcgi_loc_conf_t; @@ -103,6 +106,8 @@ typedef struct { } ngx_http_fastcgi_request_start_t; +static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r, + ngx_http_fastcgi_loc_conf_t *flcf); static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r); @@ -414,14 +419,25 @@ ngx_http_fastcgi_handler(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + ngx_http_set_ctx(r, NULL, ngx_http_fastcgi_module); u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); if (u == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = flcf->upstream.schema; + r->upstream = u; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + if (flcf->fastcgi_lengths) { + if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + u->schema.len = sizeof("fastcgi://") - 1; + u->schema.data = (u_char *) "fastcgi://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -449,8 +465,6 @@ ngx_http_fastcgi_handler(ngx_http_reques u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; - r->upstream = u; - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -462,6 +476,50 @@ ngx_http_fastcgi_handler(ngx_http_reques static ngx_int_t +ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) +{ + ngx_url_t u; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0, + flcf->fastcgi_values->elts) + == NULL) + { + return NGX_ERROR; + } + + u.no_resolve = 1; + + if (ngx_parse_url(r->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "%s in upstream \"%V\"", u.err, &u.url); + } + + return NGX_ERROR; + } + + if (u.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &u.url); + return NGX_ERROR; + } + + r->upstream->resolved = ngx_pcalloc(r->pool, + sizeof(ngx_http_upstream_resolved_t)); + if (r->upstream->resolved == NULL) { + return NGX_ERROR; + } + + r->upstream->resolved->host = u.host; + r->upstream->resolved->port = u.port; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r) { off_t file_pos; @@ -1631,7 +1689,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1861,7 +1918,11 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; + } + + if (conf->fastcgi_lengths == NULL) { + conf->fastcgi_lengths = prev->fastcgi_lengths; + conf->fastcgi_values = prev->fastcgi_values; } if (conf->params_source == NULL) { @@ -2044,35 +2105,56 @@ ngx_http_fastcgi_script_name_variable(ng static char * ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_fastcgi_loc_conf_t *lcf = conf; - - ngx_url_t u; - ngx_str_t *value; - ngx_http_core_loc_conf_t *clcf; - - if (lcf->upstream.schema.len) { + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_url_t u; + ngx_str_t *value, *url; + ngx_uint_t n; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_compile_t sc; + + if (flcf->upstream.upstream || flcf->fastcgi_lengths) { return "is duplicate"; } + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_fastcgi_handler; + value = cf->args->elts; + url = &value[1]; + + n = ngx_http_script_variables_count(url); + + if (n) { + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = url; + sc.lengths = &flcf->fastcgi_lengths; + sc.values = &flcf->fastcgi_values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + ngx_memzero(&u, sizeof(ngx_url_t)); u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (flcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("fastcgi://") - 1; - lcf->upstream.schema.data = (u_char *) "fastcgi://"; - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - clcf->handler = ngx_http_fastcgi_handler; - if (clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -10,36 +10,48 @@ typedef struct { - u_short start; - u_short end; - ngx_http_variable_value_t *value; + u_short start; + u_short end; + ngx_http_variable_value_t *value; } ngx_http_geo_range_t; typedef struct { - ngx_http_geo_range_t *ranges; - ngx_uint_t n; + ngx_http_geo_range_t *ranges; + ngx_uint_t n; } ngx_http_geo_low_ranges_t; typedef struct { - ngx_http_geo_low_ranges_t low[0x10000]; - ngx_http_variable_value_t *default_value; + ngx_http_geo_low_ranges_t low[0x10000]; + ngx_http_variable_value_t *default_value; } ngx_http_geo_high_ranges_t; typedef struct { - ngx_http_variable_value_t *value; - ngx_str_t *net; - ngx_http_geo_high_ranges_t *high; - ngx_radix_tree_t *tree; - ngx_rbtree_t rbtree; - ngx_rbtree_node_t sentinel; - ngx_pool_t *pool; - ngx_pool_t *temp_pool; + ngx_http_variable_value_t *value; + ngx_str_t *net; + ngx_http_geo_high_ranges_t *high; + ngx_radix_tree_t *tree; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; } ngx_http_geo_conf_ctx_t; +typedef struct { + union { + ngx_radix_tree_t *tree; + ngx_http_geo_high_ranges_t *high; + } u; + + ngx_int_t index; +} ngx_http_geo_ctx_t; + + +static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx); static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, @@ -57,7 +69,7 @@ static ngx_http_variable_value_t *ngx_ht static ngx_command_t ngx_http_geo_commands[] = { { ngx_string("geo"), - NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_geo_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, @@ -104,23 +116,17 @@ static ngx_int_t ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; - struct sockaddr_in *sin; ngx_http_variable_value_t *vv; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo started"); - - sin = (struct sockaddr_in *) r->connection->sockaddr; - vv = (ngx_http_variable_value_t *) - ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); + ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); *v = *vv; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %v", &r->connection->addr_text, v); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); return NGX_OK; } @@ -130,27 +136,21 @@ static ngx_int_t ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data; + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; in_addr_t addr; ngx_uint_t i, n; - struct sockaddr_in *sin; ngx_http_geo_range_t *range; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo started"); - - sin = (struct sockaddr_in *) r->connection->sockaddr; + *v = *ctx->u.high->default_value; - *v = *high->default_value; + addr = ngx_http_geo_addr(r, ctx); - addr = ntohl(sin->sin_addr.s_addr); - - range = high->low[addr >> 16].ranges; + range = ctx->u.high->low[addr >> 16].ranges; n = addr & 0xffff; - for (i = 0; i < high->low[addr >> 16].n; i++) { + for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { if (n >= (ngx_uint_t) range[i].start && n <= (ngx_uint_t) range[i].end) { @@ -158,13 +158,43 @@ ngx_http_geo_range_variable(ngx_http_req } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %v", &r->connection->addr_text, v); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); return NGX_OK; } +static in_addr_t +ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +{ + struct sockaddr_in *sin; + ngx_http_variable_value_t *v; + + if (ctx->index == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %V", &r->connection->addr_text); + + sin = (struct sockaddr_in *) r->connection->sockaddr; + return ntohl(sin->sin_addr.s_addr); + } + + v = ngx_http_get_flushed_variable(r, ctx->index); + + if (v == NULL || v->not_found) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo not found"); + + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %v", v); + + return ntohl(ngx_inet_addr(v->data, v->len)); +} + + static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -176,19 +206,33 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c ngx_pool_t *pool; ngx_array_t *a; ngx_http_variable_t *var; + ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; value = cf->args->elts; + geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t)); + if (geo == NULL) { + return NGX_CONF_ERROR; + } + name = value[1]; + name.len--; + name.data++; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "\"%V\" variable name should start with '$'", - &value[1]); - } else { + if (cf->args->nelts == 3) { + + geo->index = ngx_http_get_variable_index(cf, &name); + if (geo->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + name = value[2]; name.len--; name.data++; + + } else { + geo->index = -1; } var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); @@ -244,8 +288,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); } + geo->u.high = ctx.high; + var->get_handler = ngx_http_geo_range_variable; - var->data = (uintptr_t) ctx.high; + var->data = (uintptr_t) geo; ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); @@ -262,8 +308,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c } } + geo->u.tree = ctx.tree; + var->get_handler = ngx_http_geo_cidr_variable; - var->data = (uintptr_t) ctx.tree; + var->data = (uintptr_t) geo; ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); @@ -644,22 +692,29 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht del = 0; } - rc = ngx_ptocidr(net, &cidrin); + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidrin.addr = 0xffffffff; + cidrin.mask = 0xffffffff; - if (rc == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid network \"%V\"", net); - return NGX_CONF_ERROR; - } + } else { + rc = ngx_ptocidr(net, &cidrin); - if (rc == NGX_DONE) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "low address bits of %V are meaningless", net); + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid network \"%V\"", net); + return NGX_CONF_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + net); + } + + cidrin.addr = ntohl(cidrin.addr); + cidrin.mask = ntohl(cidrin.mask); } - cidrin.addr = ntohl(cidrin.addr); - cidrin.mask = ntohl(cidrin.mask); - if (del) { if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask) != NGX_OK) diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -325,15 +325,17 @@ ngx_http_add_header(ngx_http_request_t * { ngx_table_elt_t *h; - h = ngx_list_push(&r->headers_out.headers); - if (h == NULL) { - return NGX_ERROR; + if (value->len) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = hv->value.hash; + h->key = hv->value.key; + h->value = *value; } - h->hash = hv->value.hash; - h->key = hv->value.key; - h->value = *value; - return NGX_OK; } 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 @@ -99,7 +99,6 @@ ngx_http_index_handler(ngx_http_request_ size_t len, nlen, root, allocated; ngx_int_t rc; ngx_str_t path, uri; - ngx_log_t *log; ngx_uint_t i, dir_tested; ngx_http_index_t *index; ngx_open_file_info_t of; @@ -122,8 +121,6 @@ ngx_http_index_handler(ngx_http_request_ return NGX_DECLINED; } - log = r->connection->log; - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -206,7 +203,8 @@ ngx_http_index_handler(ngx_http_request_ *e.pos++ = '\0'; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "open index \"%V\"", &path); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); @@ -219,7 +217,7 @@ ngx_http_index_handler(ngx_http_request_ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, ngx_open_file_n " \"%s\" failed", path.data); if (of.err == 0) { @@ -244,7 +242,7 @@ ngx_http_index_handler(ngx_http_request_ continue; } - ngx_log_error(NGX_LOG_ERR, log, of.err, + ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err, ngx_open_file_n " \"%s\" failed", path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -183,7 +183,8 @@ ngx_http_memcached_handler(ngx_http_requ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = mlcf->upstream.schema; + u->schema.len = sizeof("memcached://") - 1; + u->schema.data = (u_char *) "memcached://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -521,7 +522,6 @@ ngx_http_memcached_create_loc_conf(ngx_c * conf->upstream.bufs.num = 0; * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; */ @@ -584,7 +584,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; } if (conf->index == NGX_CONF_UNSET) { @@ -598,13 +597,13 @@ ngx_http_memcached_merge_loc_conf(ngx_co static char * ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_memcached_loc_conf_t *lcf = conf; + ngx_http_memcached_loc_conf_t *mlcf = conf; ngx_str_t *value; ngx_url_t u; ngx_http_core_loc_conf_t *clcf; - if (lcf->upstream.schema.len) { + if (mlcf->upstream.upstream) { return "is duplicate"; } @@ -615,14 +614,11 @@ ngx_http_memcached_pass(ngx_conf_t *cf, u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (mlcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("memcached://") - 1; - lcf->upstream.schema.data = (u_char *) "memcached://"; - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_memcached_handler; @@ -631,9 +627,9 @@ ngx_http_memcached_pass(ngx_conf_t *cf, clcf->auto_redirect = 1; } - lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); + mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); - if (lcf->index == NGX_ERROR) { + if (mlcf->index == NGX_ERROR) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -32,6 +32,7 @@ struct ngx_http_proxy_redirect_s { typedef struct { + ngx_str_t schema; ngx_str_t host_header; ngx_str_t port; ngx_str_t uri; @@ -480,7 +481,7 @@ ngx_http_proxy_handler(ngx_http_request_ if (plcf->proxy_lengths == 0) { ctx->vars = plcf->vars; - u->schema = plcf->upstream.schema; + u->schema = plcf->vars.schema; #if (NGX_HTTP_SSL) u->ssl = (plcf->upstream.ssl != NULL); #endif @@ -1631,7 +1632,6 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1931,9 +1931,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->vars = prev->vars; - conf->upstream.schema = prev->upstream.schema; } @@ -2216,7 +2214,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; - if (plcf->upstream.schema.len) { + if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is duplicate"; } @@ -2296,8 +2294,8 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ return NGX_CONF_ERROR; } - plcf->upstream.schema.len = add; - plcf->upstream.schema.data = url->data; + plcf->vars.schema.len = add; + plcf->vars.schema.data = url->data; plcf->location = clcf->name; clcf->handler = ngx_http_proxy_handler; 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 @@ -217,7 +217,7 @@ ngx_http_static_handler(ngx_http_request return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (of.size == 0) { + if (r != r->main && of.size == 0) { return ngx_http_send_header(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.7.26'; +our $VERSION = '0.7.27'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -285,7 +285,7 @@ ngx_http_perl_sleep_handler(ngx_http_req return; } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } } 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 @@ -489,7 +489,7 @@ ngx_http_init_phase_handlers(ngx_conf_t 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; @@ -558,6 +558,15 @@ ngx_http_init_phase_handlers(ngx_conf_t 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; 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 @@ -39,6 +39,8 @@ static char *ngx_http_core_server(ngx_co void *dummy); static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); +static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless); static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -56,6 +58,8 @@ static char *ngx_http_core_directio(ngx_ 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, @@ -537,6 +541,13 @@ static ngx_command_t ngx_http_core_comm 0, NULL }, + { ngx_string("try_files"), + 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, @@ -1030,6 +1041,177 @@ 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; + ssize_t reserve, allocated; + u_char *p, *name; + ngx_str_t path; + 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; + path.len = 0; + 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; + e.flushed = 1; + + /* 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; + } + + reserve = len - r->uri.len; + + /* 16 bytes are preallocation */ + reserve = reserve < 16 ? 16 : reserve + 16; + + reserve += alias; + + 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; + + 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; + } + } + + tf++; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use file: \"%s\"", name); + + 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 { + (void) ngx_http_internal_redirect(r, &path, NULL); + } + + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.directio = clcf->directio; + 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; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_pnalloc(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); + } + + 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) { @@ -2015,33 +2197,37 @@ ngx_http_named_location(ngx_http_request cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - for (clcfp = cscf->named_locations; *clcfp; clcfp++) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "test location: \"%V\"", &(*clcfp)->name); - - if (name->len != (*clcfp)->name.len - || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) - { - continue; + if (cscf->named_locations) { + + for (clcfp = cscf->named_locations; *clcfp; clcfp++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "test location: \"%V\"", &(*clcfp)->name); + + if (name->len != (*clcfp)->name.len + || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "using location: %V \"%V?%V\"", + name, &r->uri, &r->args); + + r->internal = 1; + r->content_handler = NULL; + r->loc_conf = (*clcfp)->loc_conf; + + ngx_http_update_location_config(r); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + r->phase_handler = cmcf->phase_engine.location_rewrite_index; + + ngx_http_core_run_phases(r); + + return NGX_DONE; } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "using location: %V \"%V?%V\"", name, &r->uri, &r->args); - - r->internal = 1; - r->content_handler = NULL; - r->loc_conf = (*clcfp)->loc_conf; - - ngx_http_update_location_config(r); - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - r->phase_handler = cmcf->phase_engine.location_rewrite_index; - - ngx_http_core_run_phases(r); - - return NGX_DONE; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, @@ -2182,8 +2368,10 @@ static char * ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; + u_char *mod; + size_t len; + ngx_str_t *value, *name; ngx_uint_t i; - ngx_str_t *value; ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; @@ -2225,45 +2413,32 @@ ngx_http_core_location(ngx_conf_t *cf, n value = cf->args->elts; if (cf->args->nelts == 3) { - if (value[1].len == 1 && value[1].data[0] == '=') { - clcf->name = value[2]; + + len = value[1].len; + mod = value[1].data; + name = &value[2]; + + if (len == 1 && mod[0] == '=') { + + clcf->name = *name; clcf->exact_match = 1; - } else if (value[1].len == 2 - && value[1].data[0] == '^' - && value[1].data[1] == '~') - { - clcf->name = value[2]; + } else if (len == 2 && mod[0] == '^' && mod[1] == '~') { + + clcf->name = *name; clcf->noregex = 1; - } else if ((value[1].len == 1 && value[1].data[0] == '~') - || (value[1].len == 2 - && value[1].data[0] == '~' - && value[1].data[1] == '*')) - { -#if (NGX_PCRE) - ngx_str_t err; - u_char errstr[NGX_MAX_CONF_ERRSTR]; - - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; - - clcf->regex = ngx_regex_compile(&value[2], - value[1].len == 2 ? NGX_REGEX_CASELESS: 0, - cf->pool, &err); - - if (clcf->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + } else if (len == 1 && mod[0] == '~') { + + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } - clcf->name = value[2]; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" " - "requires PCRE library", &value[2]); - return NGX_CONF_ERROR; -#endif + } else if (len == 2 && mod[0] == '~' && mod[1] == '*') { + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2273,10 +2448,47 @@ ngx_http_core_location(ngx_conf_t *cf, n } else { - clcf->name = value[1]; - - if (value[1].data[0] == '@') { - clcf->named = 1; + name = &value[1]; + + if (name->data[0] == '=') { + + clcf->name.len = name->len - 1; + clcf->name.data = name->data + 1; + clcf->exact_match = 1; + + } else if (name->data[0] == '^' && name->data[1] == '~') { + + clcf->name.len = name->len - 2; + clcf->name.data = name->data + 2; + clcf->noregex = 1; + + } else if (name->data[0] == '~') { + + name->len--; + name->data++; + + if (name->data[0] == '*') { + + name->len--; + name->data++; + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + } else { + + clcf->name = *name; + + if (name->data[0] == '@') { + clcf->named = 1; + } } } @@ -2314,13 +2526,13 @@ ngx_http_core_location(ngx_conf_t *cf, n return NGX_CONF_ERROR; } + len = pclcf->name.len; + #if (NGX_PCRE) if (clcf->regex == NULL - && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #else - if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #endif { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2346,6 +2558,40 @@ ngx_http_core_location(ngx_conf_t *cf, n } +static ngx_int_t +ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf, + ngx_str_t *regex, ngx_uint_t caseless) +{ +#if (NGX_PCRE) + ngx_str_t err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0, + cf->pool, &err); + + if (clcf->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_ERROR; + } + + clcf->name = *regex; + + return NGX_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" requires PCRE library", + regex); + return NGX_ERROR; + +#endif +} + + static char * ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -2644,6 +2890,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; @@ -3619,6 +3866,65 @@ 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]; + + 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 @@ -81,6 +81,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 @@ -129,6 +130,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; @@ -238,6 +241,13 @@ typedef struct { } ngx_http_err_page_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; +} ngx_http_try_file_t; + + struct ngx_http_core_loc_conf_s { ngx_str_t name; /* location name */ @@ -328,6 +338,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 */ @@ -386,6 +397,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_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -215,7 +215,7 @@ ngx_http_init_connection(ngx_connection_ ngx_add_timer(rev, c->listening->post_accept_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { #if (NGX_STAT_STUB) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif @@ -504,7 +504,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_add_timer(rev, c->listening->post_accept_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } @@ -1038,7 +1038,7 @@ ngx_http_read_request_header(ngx_http_re ngx_add_timer(rev, cscf->client_header_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1883,8 +1883,6 @@ ngx_http_finalize_request(ngx_http_reque "http wake parent request: \"%V?%V\"", &pr->uri, &pr->args); - ngx_http_run_posted_requests(c); - return; } @@ -1973,7 +1971,7 @@ ngx_http_set_write_handler(ngx_http_requ ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); return NGX_ERROR; } @@ -2014,7 +2012,7 @@ ngx_http_writer(ngx_http_request_t *r) if (!wev->ready) { ngx_add_timer(wev, clcf->send_timeout); - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -2026,7 +2024,7 @@ ngx_http_writer(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer delayed"); - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -2050,7 +2048,7 @@ ngx_http_writer(ngx_http_request_t *r) ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -2085,9 +2083,7 @@ ngx_http_block_reading(ngx_http_request_ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && r->connection->read->active) { - if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) - == NGX_ERROR) - { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } @@ -2149,7 +2145,7 @@ ngx_http_test_reading(ngx_http_request_t if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { - if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } @@ -2243,7 +2239,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_add_timer(rev, clcf->keepalive_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2330,7 +2326,7 @@ ngx_http_set_keepalive(ngx_http_request_ rev->handler = ngx_http_keepalive_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2455,7 +2451,7 @@ ngx_http_keepalive_handler(ngx_event_t * c->log_error = NGX_ERROR_INFO; if (n == NGX_AGAIN) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } @@ -2508,7 +2504,7 @@ ngx_http_set_lingering_close(ngx_http_re r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2517,7 +2513,7 @@ ngx_http_set_lingering_close(ngx_http_re wev->handler = ngx_http_empty_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2576,7 +2572,7 @@ ngx_http_lingering_close_handler(ngx_eve } while (rev->ready); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -327,7 +327,7 @@ ngx_http_do_read_client_request_body(ngx clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -462,7 +462,7 @@ ngx_http_discard_request_body(ngx_http_r r->read_event_handler = ngx_http_read_discarded_request_body_handler; - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -519,7 +519,7 @@ ngx_http_read_discarded_request_body_han /* rc == NGX_AGAIN */ - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c->error = 1; ngx_http_finalize_request(r, rc); 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 @@ -430,20 +430,23 @@ ngx_http_script_copy_len_code(ngx_http_s void ngx_http_script_copy_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_script_copy_code_t *code; code = (ngx_http_script_copy_code_t *) e->ip; + p = e->pos; + if (!e->skip) { - e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t), + e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t), code->len); } e->ip += sizeof(ngx_http_script_copy_code_t) + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script copy: \"%V\"", &e->buf); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script copy: \"%*s\"", e->pos - p, p); } @@ -475,6 +478,7 @@ ngx_http_script_copy_var_len_code(ngx_ht void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_variable_value_t *value; ngx_http_script_var_code_t *code; @@ -492,11 +496,12 @@ ngx_http_script_copy_var_code(ngx_http_s } if (value && !value->not_found) { - e->pos = ngx_copy(e->pos, value->data, value->len); + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script var: \"%V\"", &e->buf); + "http script var: \"%*s\"", e->pos - p, p); } } } @@ -532,29 +537,32 @@ ngx_http_script_copy_capture_len_code(ng void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_script_copy_capture_code_t *code; code = (ngx_http_script_copy_capture_code_t *) e->ip; e->ip += sizeof(ngx_http_script_copy_capture_code_t); + p = e->pos; + if (code->n < e->ncaptures) { if ((e->is_args || e->quote) && (e->request->quoted_uri || e->request->plus_in_uri)) { - e->pos = (u_char *) ngx_escape_uri(e->pos, + e->pos = (u_char *) ngx_escape_uri(p, &e->line.data[e->captures[code->n]], e->captures[code->n + 1] - e->captures[code->n], NGX_ESCAPE_ARGS); } else { - e->pos = ngx_copy(e->pos, + e->pos = ngx_copy(p, &e->line.data[e->captures[code->n]], e->captures[code->n + 1] - e->captures[code->n]); } } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script capture: \"%V\"", &e->buf); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script capture: \"%*s\"", e->pos - p, p); } @@ -612,7 +620,7 @@ ngx_http_script_regex_start_code(ngx_htt rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures); if (rc == NGX_REGEX_NO_MATCHED) { - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" does not match \"%V\"", &code->name, &e->line); @@ -650,7 +658,7 @@ ngx_http_script_regex_start_code(ngx_htt return; } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" matches \"%V\"", &code->name, &e->line); } @@ -786,7 +794,7 @@ ngx_http_script_regex_end_code(ngx_http_ e->buf.len = e->pos - e->buf.data; - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten redirect: \"%V\"", &e->buf); } @@ -828,7 +836,7 @@ ngx_http_script_regex_end_code(ngx_http_ } } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten data: \"%V\", args: \"%V\"", &e->buf, &r->args); @@ -928,8 +936,8 @@ ngx_http_script_equal_code(ngx_http_scri e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { *res = ngx_http_variable_true_value; return; @@ -956,8 +964,8 @@ ngx_http_script_not_equal_code(ngx_http_ e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script not equal: no"); @@ -1163,9 +1171,6 @@ ngx_http_script_set_var_code(ngx_http_sc ngx_http_request_t *r; ngx_http_script_var_code_t *code; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script set var"); - code = (ngx_http_script_var_code_t *) e->ip; e->ip += sizeof(ngx_http_script_var_code_t); @@ -1179,6 +1184,20 @@ ngx_http_script_set_var_code(ngx_http_sc r->variables[code->index].no_cacheable = 0; r->variables[code->index].not_found = 0; r->variables[code->index].data = e->sp->data; + +#if (NGX_DEBUG) + { + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + v = cmcf->variables.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script set $%V", &v[code->index].name); + } +#endif } 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 @@ -20,19 +20,24 @@ static ngx_int_t ngx_http_upstream_reini ngx_http_upstream_t *u); static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_send_request_handler(ngx_event_t *wev); -static void ngx_http_upstream_process_header(ngx_event_t *rev); +static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_header(ngx_http_request_t *r, + ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c); -static void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev); +static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_non_buffered_upstream(ngx_event_t *ev); +static void + ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, ngx_uint_t do_write); @@ -40,11 +45,13 @@ static ngx_int_t ngx_http_upstream_non_b static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes); static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_upstream(ngx_event_t *rev); +static void ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_process_request(ngx_http_request_t *r); static void ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_dummy_handler(ngx_event_t *wev); +static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type); static void ngx_http_upstream_cleanup(void *data); @@ -89,6 +96,8 @@ static ngx_int_t ngx_http_upstream_statu ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_response_length_variable( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, @@ -289,6 +298,10 @@ static ngx_http_variable_t ngx_http_ups ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("upstream_response_length"), NULL, + ngx_http_upstream_response_length_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -538,10 +551,10 @@ ngx_http_upstream_handler(ngx_event_t *e "http upstream request: \"%V?%V\"", &r->uri, &r->args); if (ev->write) { - u->write_event_handler(ev); + u->write_event_handler(r, u); } else { - u->read_event_handler(ev); + u->read_event_handler(r, u); } ngx_http_run_posted_requests(c); @@ -996,8 +1009,7 @@ ngx_http_upstream_send_request(ngx_http_ if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->send_timeout); - if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR) - { + if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1034,14 +1046,14 @@ ngx_http_upstream_send_request(ngx_http_ * it's better to do here because we postpone header buffer allocation */ - ngx_http_upstream_process_header(c->read); + ngx_http_upstream_process_header(r, u); return; } #endif u->write_event_handler = ngx_http_upstream_dummy_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1050,20 +1062,17 @@ ngx_http_upstream_send_request(ngx_http_ static void -ngx_http_upstream_send_request_handler(ngx_event_t *wev) +ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = wev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream send request handler"); - if (wev->timedout) { + if (c->write->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1080,7 +1089,7 @@ ngx_http_upstream_send_request_handler(n if (u->header_sent) { u->write_event_handler = ngx_http_upstream_dummy_handler; - (void) ngx_handle_write_event(wev, 0); + (void) ngx_handle_write_event(c->write, 0); return; } @@ -1090,7 +1099,7 @@ ngx_http_upstream_send_request_handler(n static void -ngx_http_upstream_process_header(ngx_event_t *rev) +ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) { ssize_t n; ngx_int_t rc; @@ -1099,21 +1108,17 @@ ngx_http_upstream_process_header(ngx_eve ngx_list_part_t *part; ngx_table_elt_t *h; ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; - c = rev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process header"); c->log->action = "reading response header from upstream"; - if (rev->timedout) { + if (c->read->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1164,7 +1169,7 @@ ngx_http_upstream_process_header(ngx_eve ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1174,7 +1179,7 @@ ngx_http_upstream_process_header(ngx_eve } if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, + ngx_log_error(NGX_LOG_ERR, c->log, 0, "upstream prematurely closed connection"); } @@ -1196,7 +1201,7 @@ ngx_http_upstream_process_header(ngx_eve if (rc == NGX_AGAIN) { if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, + ngx_log_error(NGX_LOG_ERR, c->log, 0, "upstream sent too big header"); ngx_http_upstream_next(r, u, @@ -1380,6 +1385,8 @@ ngx_http_upstream_process_header(ngx_eve if (n) { u->buffer.last -= n; + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; @@ -1393,7 +1400,7 @@ ngx_http_upstream_process_header(ngx_eve u->read_event_handler = ngx_http_upstream_process_body_in_memory; - ngx_http_upstream_process_body_in_memory(rev); + ngx_http_upstream_process_body_in_memory(r, u); } @@ -1537,18 +1544,17 @@ ngx_http_upstream_test_connect(ngx_conne static void -ngx_http_upstream_process_body_in_memory(ngx_event_t *rev) +ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = rev->data; - r = c->data; - u = r->upstream; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; + + c = u->peer.connection; + rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process body on memory"); @@ -1583,6 +1589,8 @@ ngx_http_upstream_process_body_in_memory return; } + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; @@ -1593,7 +1601,7 @@ ngx_http_upstream_process_body_in_memory } } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -1611,7 +1619,7 @@ static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) { int tcp_nodelay; - ssize_t size; + ssize_t n; ngx_int_t rc; ngx_event_pipe_t *p; ngx_connection_t *c; @@ -1683,12 +1691,14 @@ ngx_http_upstream_send_response(ngx_http c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - size = u->buffer.last - u->buffer.pos; - - if (size) { + n = u->buffer.last - u->buffer.pos; + + if (n) { u->buffer.last = u->buffer.pos; - if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) { + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; } @@ -1705,8 +1715,7 @@ ngx_http_upstream_send_response(ngx_http } if (u->peer.connection->read->ready) { - ngx_http_upstream_process_non_buffered_upstream( - u->peer.connection->read); + ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -1839,7 +1848,7 @@ ngx_http_upstream_send_response(ngx_http u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; - ngx_http_upstream_process_upstream(u->peer.connection->read); + ngx_http_upstream_process_upstream(r, u); } @@ -1871,22 +1880,19 @@ ngx_http_upstream_process_non_buffered_d static void -ngx_http_upstream_process_non_buffered_upstream(ngx_event_t *rev) +ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = rev->data; - r = c->data; - u = r->upstream; + ngx_connection_t *c; + + c = u->peer.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process non buffered upstream"); c->log->action = "reading upstream"; - if (rev->timedout) { + if (c->read->timedout) { ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1966,6 +1972,8 @@ ngx_http_upstream_process_non_buffered_r } if (n > 0) { + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -2142,31 +2150,26 @@ ngx_http_upstream_process_downstream(ngx static void -ngx_http_upstream_process_upstream(ngx_event_t *rev) +ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_event_pipe_t *p; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = rev->data; - r = c->data; - u = r->upstream; - p = u->pipe; + ngx_connection_t *c; + + c = u->peer.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process upstream"); c->log->action = "reading upstream"; - if (rev->timedout) { - p->upstream_error = 1; + if (c->read->timedout) { + u->pipe->upstream_error = 1; ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); } else { c = r->connection; - if (ngx_event_pipe(p, 0) == NGX_ABORT) { + if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { if (c->destroyed) { return; @@ -2299,9 +2302,11 @@ ngx_http_upstream_store(ngx_http_request } ext.access = u->conf->store_access; + ext.path_access = u->conf->store_access; ext.time = -1; ext.create_path = 1; ext.delete_file = 1; + ext.log_rename_error = 1; ext.log = r->connection->log; if (u->headers_in.last_modified) { @@ -2337,9 +2342,9 @@ ngx_http_upstream_store(ngx_http_request static void -ngx_http_upstream_dummy_handler(ngx_event_t *wev) +ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream dummy handler"); } @@ -2489,6 +2494,10 @@ ngx_http_upstream_finalize_request(ngx_h tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; u->state->response_msec = tp->msec - u->state->response_msec; + + if (u->pipe) { + u->state->response_length = u->pipe->read_length; + } } u->finalize_request(r, rc); @@ -3132,6 +3141,66 @@ ngx_http_upstream_response_time_variable } +static ngx_int_t +ngx_http_upstream_response_length_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = r->upstream_states->elts; + + for ( ;; ) { + p = ngx_sprintf(p, "%O", state[i].response_length); + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -45,6 +45,7 @@ typedef struct { ngx_uint_t status; time_t response_sec; ngx_uint_t response_msec; + off_t response_length; ngx_str_t *peer; } ngx_http_upstream_state_t; @@ -144,8 +145,6 @@ typedef struct { ngx_array_t *hide_headers; ngx_array_t *pass_headers; - ngx_str_t schema; - ngx_array_t *store_lengths; ngx_array_t *store_values; @@ -216,9 +215,13 @@ typedef struct { } ngx_http_upstream_resolved_t; +typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r, + ngx_http_upstream_t *u); + + struct ngx_http_upstream_s { - ngx_event_handler_pt read_event_handler; - ngx_event_handler_pt write_event_handler; + ngx_http_upstream_handler_pt read_event_handler; + ngx_http_upstream_handler_pt write_event_handler; ngx_peer_connection_t peer; diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -268,7 +268,7 @@ ngx_mail_auth_http_write_handler(ngx_eve ngx_del_timer(wev); } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); @@ -894,7 +894,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t return; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -902,7 +902,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t } if (rev->active) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } } @@ -1102,7 +1102,7 @@ ngx_mail_auth_http_block_read(ngx_event_ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth http block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -513,7 +513,7 @@ ngx_mail_send(ngx_event_t *wev) } if (s->out.len == 0) { - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -552,7 +552,7 @@ ngx_mail_send(ngx_event_t *wev) ngx_add_timer(c->write, cscf->timeout); - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -579,7 +579,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c --- a/src/mail/ngx_mail_imap_handler.c +++ b/src/mail/ngx_mail_imap_handler.c @@ -46,7 +46,7 @@ ngx_mail_imap_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c --- a/src/mail/ngx_mail_pop3_handler.c +++ b/src/mail/ngx_mail_pop3_handler.c @@ -67,7 +67,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -201,7 +201,7 @@ ngx_mail_proxy_block_read(ngx_event_t *r ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -664,7 +664,7 @@ ngx_mail_proxy_dummy_handler(ngx_event_t ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler"); - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { c = wev->data; s = c->data; @@ -947,22 +947,22 @@ ngx_mail_proxy_handler(ngx_event_t *ev) return; } - if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(dst->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(src->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(src->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } 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 @@ -228,7 +228,7 @@ ngx_mail_smtp_greeting(ngx_mail_session_ timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; ngx_add_timer(c->read, timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -270,7 +270,7 @@ ngx_mail_smtp_invalid_pipelining(ngx_eve ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -819,7 +819,7 @@ ngx_mail_smtp_discard_command(ngx_mail_s } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h --- a/src/os/unix/ngx_errno.h +++ b/src/os/unix/ngx_errno.h @@ -23,6 +23,7 @@ typedef int ngx_err_t; #define NGX_EACCES EACCES #define NGX_EBUSY EBUSY #define NGX_EEXIST EEXIST +#define NGX_EXDEV EXDEV #define NGX_ENOTDIR ENOTDIR #define NGX_EISDIR EISDIR #define NGX_EINVAL EINVAL