# HG changeset patch # User Igor Sysoev # Date 1337025600 -14400 # Node ID 4dcaf40cc70234b030b650147f309f76c8a5dbbb # Parent 1e5c7a976f4831cb4814825ba6b51e4780ce0804 nginx 1.3.0 *) Feature: the "debug_connection" directive now supports IPv6 addresses and the "unix:" parameter. *) Feature: the "set_real_ip_from" directive and the "proxy" parameter of the "geo" directive now support IPv6 addresses. *) Feature: the "real_ip_recursive", "geoip_proxy", and "geoip_proxy_recursive" directives. *) Feature: the "proxy_recursive" parameter of the "geo" directive. *) Bugfix: a segmentation fault might occur in a worker process if the "resolver" directive was used. *) Bugfix: a segmentation fault might occur in a worker process if the "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives were used and backend returned incorrect response. *) Bugfix: a segmentation fault might occur in a worker process if the "rewrite" directive was used and new request arguments in a replacement used variables. *) Bugfix: nginx might hog CPU if the open file resource limit was reached. *) Bugfix: nginx might loop infinitely over backends if the "proxy_next_upstream" directive with the "http_404" parameter was used and there were backup servers specified in an upstream block. *) Bugfix: adding the "down" parameter of the "server" directive might cause unneeded client redistribution among backend servers if the "ip_hash" directive was used. *) Bugfix: socket leak. Thanks to Yichun Zhang. *) Bugfix: in the ngx_http_fastcgi_module. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,45 @@ +Changes with nginx 1.3.0 15 May 2012 + + *) Feature: the "debug_connection" directive now supports IPv6 addresses + and the "unix:" parameter. + + *) Feature: the "set_real_ip_from" directive and the "proxy" parameter + of the "geo" directive now support IPv6 addresses. + + *) Feature: the "real_ip_recursive", "geoip_proxy", and + "geoip_proxy_recursive" directives. + + *) Feature: the "proxy_recursive" parameter of the "geo" directive. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "resolver" directive was used. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives were used and + backend returned incorrect response. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "rewrite" directive was used and new request arguments in a + replacement used variables. + + *) Bugfix: nginx might hog CPU if the open file resource limit was + reached. + + *) Bugfix: nginx might loop infinitely over backends if the + "proxy_next_upstream" directive with the "http_404" parameter was + used and there were backup servers specified in an upstream block. + + *) Bugfix: adding the "down" parameter of the "server" directive might + cause unneeded client redistribution among backend servers if the + "ip_hash" directive was used. + + *) Bugfix: socket leak. + Thanks to Yichun Zhang. + + *) Bugfix: in the ngx_http_fastcgi_module. + + Changes with nginx 1.2.0 23 Apr 2012 *) Bugfix: a segmentation fault might occur in a worker process if the diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,45 @@ +Изменения в nginx 1.3.0 15.05.2012 + + *) Добавление: директива debug_connection теперь поддерживает + IPv6-адреса и параметр "unix:". + + *) Добавление: директива set_real_ip_from и параметр proxy директивы geo + теперь поддерживают IPv6-адреса. + + *) Добавление: директивы real_ip_recursive, geoip_proxy и + geoip_proxy_recursive. + + *) Добавление: параметр proxy_recursive директивы geo. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовалась директива resolver. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовались директивы fastcgi_pass, scgi_pass или uwsgi_pass + и бэкенд возвращал некорректный ответ. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовалась директива rewrite и в новых аргументах запроса в + строке замены использовались переменные. + + *) Исправление: nginx мог нагружать процессор, если было достигнуто + ограничение на количество открытых файлов. + + *) Исправление: при использовании директивы proxy_next_upstream с + параметром http_404 nginx мог бесконечно перебирать бэкенды, если в + блоке upstream был хотя бы один сервер с флагом backup. + + *) Исправление: при использовании директивы ip_hash установка параметра + down директивы server могла приводить к ненужному перераспределению + клиентов между бэкендами. + + *) Исправление: утечки сокетов. + Спасибо Yichun Zhang. + + *) Исправление: в модуле ngx_http_fastcgi_module. + + Изменения в nginx 1.2.0 23.04.2012 *) Исправление: в рабочем процессе мог произойти segmentation fault, diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1002000 -#define NGINX_VERSION "1.2.0" +#define nginx_version 1003000 +#define NGINX_VERSION "1.3.0" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" 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 @@ -513,8 +513,10 @@ ngx_resolve_name_locked(ngx_resolver_t * /* lock alloc mutex */ - ngx_resolver_free_locked(r, rn->query); - rn->query = NULL; + if (rn->query) { + ngx_resolver_free_locked(r, rn->query); + rn->query = NULL; + } if (rn->cnlen) { ngx_resolver_free_locked(r, rn->u.cname); @@ -1409,6 +1411,9 @@ ngx_resolver_process_a(ngx_resolver_t *r ngx_resolver_free(r, addrs); } + ngx_resolver_free(r, rn->query); + rn->query = NULL; + return; } else if (cname) { @@ -1441,6 +1446,9 @@ ngx_resolver_process_a(ngx_resolver_t *r (void) ngx_resolve_name_locked(r, ctx); } + ngx_resolver_free(r, rn->query); + rn->query = NULL; + return; } @@ -1834,6 +1842,10 @@ ngx_resolver_create_name_query(ngx_resol p--; *p-- = '\0'; + if (ctx->name.len == 0) { + return NGX_DECLINED; + } + for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) { if (*s != '.') { *p = *s; diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -1064,38 +1064,34 @@ ngx_event_debug_connection(ngx_conf_t *c ngx_int_t rc; ngx_str_t *value; - ngx_event_debug_t *dc; struct hostent *h; - ngx_cidr_t cidr; + ngx_cidr_t *cidr; value = cf->args->elts; - dc = ngx_array_push(&ecf->debug_connection); - if (dc == NULL) { + cidr = ngx_array_push(&ecf->debug_connection); + if (cidr == NULL) { return NGX_CONF_ERROR; } - rc = ngx_ptocidr(&value[1], &cidr); +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], cidr); if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", &value[1]); - rc = NGX_OK; + return NGX_CONF_OK; } if (rc == NGX_OK) { - - /* AF_INET only */ - - if (cidr.family != AF_INET) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"debug_connection\" supports IPv4 only"); - return NGX_CONF_ERROR; - } - - dc->mask = cidr.u.in.mask; - dc->addr = cidr.u.in.addr; - return NGX_CONF_OK; } @@ -1107,8 +1103,9 @@ ngx_event_debug_connection(ngx_conf_t *c return NGX_CONF_ERROR; } - dc->mask = 0xffffffff; - dc->addr = *(in_addr_t *)(h->h_addr_list[0]); + cidr->family = AF_INET; + cidr->u.in.mask = 0xffffffff; + cidr->u.in.addr = *(in_addr_t *)(h->h_addr_list[0]); #else @@ -1142,7 +1139,7 @@ ngx_event_core_create_conf(ngx_cycle_t * #if (NGX_DEBUG) if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4, - sizeof(ngx_event_debug_t)) == NGX_ERROR) + sizeof(ngx_cidr_t)) == NGX_ERROR) { return NULL; } diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -222,12 +222,6 @@ struct ngx_event_aio_s { typedef struct { - in_addr_t mask; - in_addr_t addr; -} ngx_event_debug_t; - - -typedef struct { ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -21,6 +21,7 @@ ngx_event_accept(ngx_event_t *ev) socklen_t socklen; ngx_err_t err; ngx_log_t *log; + ngx_uint_t level; ngx_socket_t s; ngx_event_t *rev, *wev; ngx_listening_t *ls; @@ -31,6 +32,14 @@ ngx_event_accept(ngx_event_t *ev) static ngx_uint_t use_accept4 = 1; #endif + if (ev->timedout) { + if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) { + return; + } + + ev->timedout = 0; + } + ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module); if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { @@ -70,10 +79,17 @@ ngx_event_accept(ngx_event_t *ev) return; } + level = NGX_LOG_ALERT; + + if (err == NGX_ECONNABORTED) { + level = NGX_LOG_ERR; + + } else if (err == NGX_EMFILE || err == NGX_ENFILE) { + level = NGX_LOG_CRIT; + } + #if (NGX_HAVE_ACCEPT4) - ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ? - NGX_LOG_ERR : NGX_LOG_ALERT), - ev->log, err, + ngx_log_error(level, ev->log, err, use_accept4 ? "accept4() failed" : "accept() failed"); if (use_accept4 && err == NGX_ENOSYS) { @@ -82,9 +98,7 @@ ngx_event_accept(ngx_event_t *ev) continue; } #else - ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ? - NGX_LOG_ERR : NGX_LOG_ALERT), - ev->log, err, "accept() failed"); + ngx_log_error(level, ev->log, err, "accept() failed"); #endif if (err == NGX_ECONNABORTED) { @@ -97,6 +111,26 @@ ngx_event_accept(ngx_event_t *ev) } } + if (err == NGX_EMFILE || err == NGX_ENFILE) { + if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle) + != NGX_OK) + { + return; + } + + if (ngx_use_accept_mutex) { + if (ngx_accept_mutex_held) { + ngx_shmtx_unlock(&ngx_accept_mutex); + ngx_accept_mutex_held = 0; + } + + ngx_accept_disabled = 1; + + } else { + ngx_add_timer(ev, ecf->accept_mutex_delay); + } + } + return; } @@ -252,17 +286,56 @@ ngx_event_accept(ngx_event_t *ev) #if (NGX_DEBUG) { - in_addr_t i; - ngx_event_debug_t *dc; - struct sockaddr_in *sin; + struct sockaddr_in *sin; + ngx_cidr_t *cidr; + ngx_uint_t i; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_uint_t n; +#endif + + cidr = ecf->debug_connection.elts; + for (i = 0; i < ecf->debug_connection.nelts; i++) { + if (cidr[i].family != c->sockaddr->sa_family) { + goto next; + } + + switch (cidr[i].family) { - sin = (struct sockaddr_in *) sa; - dc = ecf->debug_connection.elts; - for (i = 0; i < ecf->debug_connection.nelts; i++) { - if ((sin->sin_addr.s_addr & dc[i].mask) == dc[i].addr) { - log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->sockaddr; + for (n = 0; n < 16; n++) { + if ((sin6->sin6_addr.s6_addr[n] + & cidr[i].u.in6.mask.s6_addr[n]) + != cidr[i].u.in6.addr.s6_addr[n]) + { + goto next; + } + } + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->sockaddr; + if ((sin->sin_addr.s_addr & cidr[i].u.in.mask) + != cidr[i].u.in.addr) + { + goto next; + } break; } + + log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL; + break; + + next: + continue; } } @@ -344,6 +417,10 @@ ngx_enable_accept_events(ngx_cycle_t *cy c = ls[i].connection; + if (c->read->active) { + continue; + } + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { if (ngx_add_conn(c) == NGX_ERROR) { 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 @@ -995,6 +995,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, } in->buf->pos += n; + c->sent += n; if (in->buf->pos == in->buf->last) { in = in->next; 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 @@ -619,6 +619,7 @@ ngx_http_fastcgi_handler(ngx_http_reques u->process_header = ngx_http_fastcgi_process_header; u->abort_request = ngx_http_fastcgi_abort_request; u->finalize_request = ngx_http_fastcgi_finalize_request; + r->state = 0; u->buffering = 1; @@ -1194,6 +1195,8 @@ ngx_http_fastcgi_reinit_request(ngx_http f->fastcgi_stdout = 0; f->large_stderr = 0; + r->state = 0; + return NGX_OK; } @@ -1353,7 +1356,11 @@ ngx_http_fastcgi_process_header(ngx_http } } else { - f->state = ngx_http_fastcgi_st_version; + if (f->padding) { + f->state = ngx_http_fastcgi_st_padding; + } else { + f->state = ngx_http_fastcgi_st_version; + } } continue; @@ -1686,7 +1693,12 @@ ngx_http_fastcgi_input_filter(ngx_event_ } if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { - f->state = ngx_http_fastcgi_st_version; + + if (f->padding) { + f->state = ngx_http_fastcgi_st_padding; + } else { + f->state = ngx_http_fastcgi_st_version; + } if (!flcf->keep_conn) { p->upstream_done = 1; @@ -1699,7 +1711,13 @@ ngx_http_fastcgi_input_filter(ngx_event_ } if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { - f->state = ngx_http_fastcgi_st_version; + + if (f->padding) { + f->state = ngx_http_fastcgi_st_padding; + } else { + f->state = ngx_http_fastcgi_st_version; + } + p->upstream_done = 1; if (flcf->keep_conn) { @@ -1772,7 +1790,11 @@ ngx_http_fastcgi_input_filter(ngx_event_ } } else { - f->state = ngx_http_fastcgi_st_version; + if (f->padding) { + f->state = ngx_http_fastcgi_st_padding; + } else { + f->state = ngx_http_fastcgi_st_version; + } } continue; diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- a/src/http/modules/ngx_http_flv_module.c +++ b/src/http/modules/ngx_http_flv_module.c @@ -235,7 +235,7 @@ ngx_http_flv_handler(ngx_http_request_t b->file_last = of.size; b->in_file = b->file_last ? 1: 0; - b->last_buf = 1; + b->last_buf = (r == r->main) ? 1 : 0; b->last_in_chain = 1; b->file->fd = of.fd; 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 @@ -51,6 +51,7 @@ typedef struct { unsigned outside_entries:1; unsigned allow_binary_include:1; unsigned binary_include:1; + unsigned proxy_recursive:1; } ngx_http_geo_conf_ctx_t; @@ -61,6 +62,7 @@ typedef struct { } u; ngx_array_t *proxies; + unsigned proxy_recursive:1; ngx_int_t index; } ngx_http_geo_ctx_t; @@ -68,8 +70,8 @@ typedef struct { static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx); -static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r, - ngx_http_geo_ctx_t *ctx); +static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr); 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, @@ -212,87 +214,60 @@ ngx_http_geo_range_variable(ngx_http_req static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) { - u_char *p, *ip; - size_t len; - in_addr_t addr; - ngx_uint_t i, n; - ngx_in_cidr_t *proxies; - ngx_table_elt_t *xfwd; + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + struct sockaddr_in *sin; - addr = ngx_http_geo_real_addr(r, ctx); + if (ngx_http_geo_real_addr(r, ctx, &addr) != NGX_OK) { + return INADDR_NONE; + } xfwd = r->headers_in.x_forwarded_for; - if (xfwd == NULL || ctx->proxies == NULL) { - return addr; + if (xfwd != NULL && ctx->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, + xfwd->value.len, ctx->proxies, + ctx->proxy_recursive); } - proxies = ctx->proxies->elts; - n = ctx->proxies->nelts; +#if (NGX_HAVE_INET6) - for (i = 0; i < n; i++) { - if ((addr & proxies[i].mask) == proxies[i].addr) { - - len = xfwd->value.len; - ip = xfwd->value.data; + if (addr.sockaddr->sa_family == AF_INET6) { + struct in6_addr *inaddr6; - for (p = ip + len - 1; p > ip; p--) { - if (*p == ' ' || *p == ',') { - p++; - len -= p - ip; - ip = p; - break; - } - } + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - return ntohl(ngx_inet_addr(ip, len)); + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + return ntohl(*(in_addr_t *) &inaddr6->s6_addr[12]); } } - return addr; +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); } -static in_addr_t -ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +static ngx_int_t +ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx, + ngx_addr_t *addr) { - struct sockaddr_in *sin; ngx_http_variable_value_t *v; -#if (NGX_HAVE_INET6) - u_char *p; - in_addr_t addr; - struct sockaddr_in6 *sin6; -#endif if (ctx->index == -1) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo started: %V", &r->connection->addr_text); - switch (r->connection->sockaddr->sa_family) { - - case AF_INET: - sin = (struct sockaddr_in *) r->connection->sockaddr; - return ntohl(sin->sin_addr.s_addr); - -#if (NGX_HAVE_INET6) - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + addr->sockaddr = r->connection->sockaddr; + addr->socklen = r->connection->socklen; + /* addr->name = r->connection->addr_text; */ - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - p = sin6->sin6_addr.s6_addr; - addr = p[12] << 24; - addr += p[13] << 16; - addr += p[14] << 8; - addr += p[15]; - - return addr; - } - -#endif - } - - return INADDR_NONE; + return NGX_OK; } v = ngx_http_get_flushed_variable(r, ctx->index); @@ -301,13 +276,17 @@ ngx_http_geo_real_addr(ngx_http_request_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo not found"); - return 0; + return NGX_ERROR; } 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)); + if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) { + return NGX_OK; + } + + return NGX_ERROR; } @@ -388,6 +367,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c *cf = save; geo->proxies = ctx.proxies; + geo->proxy_recursive = ctx.proxy_recursive; if (ctx.high.low) { @@ -493,6 +473,12 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command goto done; } + + else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) { + ctx->proxy_recursive = 1; + rv = NGX_CONF_OK; + goto done; + } } if (cf->args->nelts != 2) { @@ -926,6 +912,7 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht } if (ngx_strcmp(value[0].data, "default") == 0) { + /* cidr.family = AF_INET; */ cidr.u.in.addr = 0; cidr.u.in.mask = 0; net = &value[0]; @@ -944,6 +931,15 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht return NGX_CONF_ERROR; } + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"geo\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + + cidr.u.in.addr = ntohl(cidr.u.in.addr); + cidr.u.in.mask = ntohl(cidr.u.in.mask); + if (del) { if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask) @@ -1052,10 +1048,10 @@ static char * ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr) { - ngx_in_cidr_t *c; + ngx_cidr_t *c; if (ctx->proxies == NULL) { - ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t)); + ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t)); if (ctx->proxies == NULL) { return NGX_CONF_ERROR; } @@ -1066,8 +1062,7 @@ ngx_http_geo_add_proxy(ngx_conf_t *cf, n return NGX_CONF_ERROR; } - c->addr = cidr->u.in.addr; - c->mask = cidr->u.in.mask; + *c = *cidr; return NGX_CONF_OK; } @@ -1079,6 +1074,7 @@ ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_int_t rc; if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr->family = AF_INET; cidr->u.in.addr = 0xffffffff; cidr->u.in.mask = 0xffffffff; @@ -1092,19 +1088,11 @@ ngx_http_geo_cidr_value(ngx_conf_t *cf, return NGX_ERROR; } - if (cidr->family != AF_INET) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only"); - return NGX_ERROR; - } - if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", net); } - cidr->u.in.addr = ntohl(cidr->u.in.addr); - cidr->u.in.mask = ntohl(cidr->u.in.mask); - return NGX_OK; } diff --git a/src/http/modules/ngx_http_geoip_module.c b/src/http/modules/ngx_http_geoip_module.c --- a/src/http/modules/ngx_http_geoip_module.c +++ b/src/http/modules/ngx_http_geoip_module.c @@ -14,20 +14,24 @@ typedef struct { - GeoIP *country; - GeoIP *org; - GeoIP *city; + GeoIP *country; + GeoIP *org; + GeoIP *city; + ngx_array_t *proxies; /* array of ngx_cidr_t */ + ngx_flag_t proxy_recursive; } ngx_http_geoip_conf_t; typedef struct { - ngx_str_t *name; - uintptr_t data; + ngx_str_t *name; + uintptr_t data; } ngx_http_geoip_var_t; typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, u_long addr); +static u_long ngx_http_geoip_addr(ngx_http_request_t *r, + ngx_http_geoip_conf_t *gcf); static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r, @@ -44,12 +48,17 @@ static GeoIPRecord *ngx_http_geoip_get_c static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf); static void *ngx_http_geoip_create_conf(ngx_conf_t *cf); +static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf); static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, + ngx_cidr_t *cidr); static void ngx_http_geoip_cleanup(void *data); @@ -76,6 +85,20 @@ static ngx_command_t ngx_http_geoip_com 0, NULL }, + { ngx_string("geoip_proxy"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_geoip_proxy, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_proxy_recursive"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_geoip_conf_t, proxy_recursive), + NULL }, + ngx_null_command }; @@ -85,7 +108,7 @@ static ngx_http_module_t ngx_http_geoip NULL, /* postconfiguration */ ngx_http_geoip_create_conf, /* create main configuration */ - NULL, /* init main configuration */ + ngx_http_geoip_init_conf, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ @@ -182,40 +205,44 @@ static ngx_http_variable_t ngx_http_geo static u_long -ngx_http_geoip_addr(ngx_http_request_t *r) +ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - u_char *p; - u_long addr; - struct sockaddr_in6 *sin6; -#endif + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + struct sockaddr_in *sin; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ - switch (r->connection->sockaddr->sa_family) { + xfwd = r->headers_in.x_forwarded_for; - case AF_INET: - sin = (struct sockaddr_in *) r->connection->sockaddr; - return ntohl(sin->sin_addr.s_addr); + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, + xfwd->value.len, gcf->proxies, + gcf->proxy_recursive); + } #if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + if (addr.sockaddr->sa_family == AF_INET6) { + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - p = sin6->sin6_addr.s6_addr; - addr = p[12] << 24; - addr += p[13] << 16; - addr += p[14] << 8; - addr += p[15]; - - return addr; + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + return ntohl(*(in_addr_t *) &inaddr6->s6_addr[12]); } + } #endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; } - return INADDR_NONE; + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); } @@ -235,7 +262,7 @@ ngx_http_geoip_country_variable(ngx_http goto not_found; } - val = handler(gcf->country, ngx_http_geoip_addr(r)); + val = handler(gcf->country, ngx_http_geoip_addr(r, gcf)); if (val == NULL) { goto not_found; @@ -273,7 +300,7 @@ ngx_http_geoip_org_variable(ngx_http_req goto not_found; } - val = handler(gcf->org, ngx_http_geoip_addr(r)); + val = handler(gcf->org, ngx_http_geoip_addr(r, gcf)); if (val == NULL) { goto not_found; @@ -453,7 +480,7 @@ ngx_http_geoip_get_city_record(ngx_http_ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module); if (gcf->city) { - return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r)); + return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf)); } return NULL; @@ -490,6 +517,8 @@ ngx_http_geoip_create_conf(ngx_conf_t *c return NULL; } + conf->proxy_recursive = NGX_CONF_UNSET; + cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; @@ -503,6 +532,17 @@ ngx_http_geoip_create_conf(ngx_conf_t *c static char * +ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_geoip_conf_t *gcf = conf; + + ngx_conf_init_value(gcf->proxy_recursive, 0); + + return NGX_CONF_OK; +} + + +static char * ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_geoip_conf_t *gcf = conf; @@ -652,6 +692,66 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ } +static char * +ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + ngx_cidr_t cidr, *c; + + value = cf->args->elts; + + if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (gcf->proxies == NULL) { + gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t)); + if (gcf->proxies == NULL) { + return NGX_CONF_ERROR; + } + } + + c = ngx_array_push(gcf->proxies); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = cidr; + + return NGX_CONF_OK; +} + +static ngx_int_t +ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) +{ + ngx_int_t rc; + + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr->family = AF_INET; + cidr->u.in.addr = 0xffffffff; + cidr->u.in.mask = 0xffffffff; + + return NGX_OK; + } + + rc = ngx_ptocidr(net, cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", net); + } + + return NGX_OK; +} + + static void ngx_http_geoip_cleanup(void *data) { diff --git a/src/http/modules/ngx_http_gzip_static_module.c b/src/http/modules/ngx_http_gzip_static_module.c --- a/src/http/modules/ngx_http_gzip_static_module.c +++ b/src/http/modules/ngx_http_gzip_static_module.c @@ -245,7 +245,7 @@ ngx_http_gzip_static_handler(ngx_http_re b->file_last = of.size; b->in_file = b->file_last ? 1 : 0; - b->last_buf = 1; + b->last_buf = (r == r->main) ? 1 : 0; b->last_in_chain = 1; b->file->fd = of.fd; diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -616,7 +616,7 @@ ngx_http_mp4_handler(ngx_http_request_t b->file_last = of.size; b->in_file = b->file_last ? 1 : 0; - b->last_buf = 1; + b->last_buf = (r == r->main) ? 1 : 0; b->last_in_chain = 1; b->file->fd = of.fd; diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -16,13 +16,11 @@ typedef struct { - ngx_array_t *from; /* array of ngx_in_cidr_t */ + ngx_array_t *from; /* array of ngx_cidr_t */ ngx_uint_t type; ngx_uint_t hash; ngx_str_t header; -#if (NGX_HAVE_UNIX_DOMAIN) - ngx_uint_t unixsock; /* unsigned unixsock:2; */ -#endif + ngx_flag_t recursive; } ngx_http_realip_loc_conf_t; @@ -35,8 +33,8 @@ typedef struct { static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r); -static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r, u_char *ip, - size_t len); +static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r, + ngx_addr_t *addr); static void ngx_http_realip_cleanup(void *data); static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -63,6 +61,13 @@ static ngx_command_t ngx_http_realip_co 0, NULL }, + { ngx_string("real_ip_recursive"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_realip_loc_conf_t, recursive), + NULL }, + ngx_null_command }; @@ -105,10 +110,9 @@ ngx_http_realip_handler(ngx_http_request u_char *ip, *p; size_t len; ngx_uint_t i, hash; + ngx_addr_t addr; ngx_list_part_t *part; ngx_table_elt_t *header; - struct sockaddr_in *sin; - ngx_in_cidr_t *from; ngx_connection_t *c; ngx_http_realip_ctx_t *ctx; ngx_http_realip_loc_conf_t *rlcf; @@ -121,12 +125,7 @@ ngx_http_realip_handler(ngx_http_request rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module); - if (rlcf->from == NULL -#if (NGX_HAVE_UNIX_DOMAIN) - && !rlcf->unixsock -#endif - ) - { + if (rlcf->from == NULL) { return NGX_DECLINED; } @@ -152,15 +151,6 @@ ngx_http_realip_handler(ngx_http_request len = r->headers_in.x_forwarded_for->value.len; ip = r->headers_in.x_forwarded_for->value.data; - for (p = ip + len - 1; p > ip; p--) { - if (*p == ' ' || *p == ',') { - p++; - len -= p - ip; - ip = p; - break; - } - } - break; default: /* NGX_HTTP_REALIP_HEADER */ @@ -204,42 +194,27 @@ found: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "realip: \"%s\"", ip); - /* AF_INET only */ - - if (c->sockaddr->sa_family == AF_INET) { - sin = (struct sockaddr_in *) c->sockaddr; - - from = rlcf->from->elts; - for (i = 0; i < rlcf->from->nelts; i++) { - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "realip: %08XD %08XD %08XD", - sin->sin_addr.s_addr, from[i].mask, from[i].addr); + addr.sockaddr = c->sockaddr; + addr.socklen = c->socklen; + /* addr.name = c->addr_text; */ - if ((sin->sin_addr.s_addr & from[i].mask) == from[i].addr) { - return ngx_http_realip_set_addr(r, ip, len); - } - } + if (ngx_http_get_forwarded_addr(r, &addr, ip, len, rlcf->from, + rlcf->recursive) + == NGX_OK) + { + return ngx_http_realip_set_addr(r, &addr); } -#if (NGX_HAVE_UNIX_DOMAIN) - - if (c->sockaddr->sa_family == AF_UNIX && rlcf->unixsock) { - return ngx_http_realip_set_addr(r, ip, len); - } - -#endif - return NGX_DECLINED; } static ngx_int_t -ngx_http_realip_set_addr(ngx_http_request_t *r, u_char *ip, size_t len) +ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr) { + size_t len; u_char *p; - ngx_int_t rc; - ngx_addr_t addr; + u_char text[NGX_SOCKADDR_STRLEN]; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_realip_ctx_t *ctx; @@ -254,15 +229,9 @@ ngx_http_realip_set_addr(ngx_http_reques c = r->connection; - rc = ngx_parse_addr(c->pool, &addr, ip, len); - - switch (rc) { - case NGX_DECLINED: - return NGX_DECLINED; - case NGX_ERROR: + len = ngx_sock_ntop(addr->sockaddr, text, NGX_SOCKADDR_STRLEN, 0); + if (len == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; - default: /* NGX_OK */ - break; } p = ngx_pnalloc(c->pool, len); @@ -270,7 +239,7 @@ ngx_http_realip_set_addr(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_memcpy(p, ip, len); + ngx_memcpy(p, text, len); cln->handler = ngx_http_realip_cleanup; @@ -279,8 +248,8 @@ ngx_http_realip_set_addr(ngx_http_reques ctx->socklen = c->socklen; ctx->addr_text = c->addr_text; - c->sockaddr = addr.sockaddr; - c->socklen = addr.socklen; + c->sockaddr = addr->sockaddr; + c->socklen = addr->socklen; c->addr_text.len = len; c->addr_text.data = p; @@ -310,34 +279,33 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx ngx_int_t rc; ngx_str_t *value; - ngx_cidr_t cidr; - ngx_in_cidr_t *from; + ngx_cidr_t *cidr; value = cf->args->elts; + if (rlcf->from == NULL) { + rlcf->from = ngx_array_create(cf->pool, 2, + sizeof(ngx_cidr_t)); + if (rlcf->from == NULL) { + return NGX_CONF_ERROR; + } + } + + cidr = ngx_array_push(rlcf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + #if (NGX_HAVE_UNIX_DOMAIN) if (ngx_strcmp(value[1].data, "unix:") == 0) { - rlcf->unixsock = 1; + cidr->family = AF_UNIX; return NGX_CONF_OK; } #endif - if (rlcf->from == NULL) { - rlcf->from = ngx_array_create(cf->pool, 2, - sizeof(ngx_in_cidr_t)); - if (rlcf->from == NULL) { - return NGX_CONF_ERROR; - } - } - - from = ngx_array_push(rlcf->from); - if (from == NULL) { - return NGX_CONF_ERROR; - } - - rc = ngx_ptocidr(&value[1], &cidr); + rc = ngx_ptocidr(&value[1], cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", @@ -345,20 +313,11 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx return NGX_CONF_ERROR; } - if (cidr.family != AF_INET) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"set_real_ip_from\" supports IPv4 only"); - return NGX_CONF_ERROR; - } - if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", &value[1]); } - from->mask = cidr.u.in.mask; - from->addr = cidr.u.in.addr; - return NGX_CONF_OK; } @@ -409,9 +368,7 @@ ngx_http_realip_create_loc_conf(ngx_conf */ conf->type = NGX_CONF_UNSET_UINT; -#if (NGX_HAVE_UNIX_DOMAIN) - conf->unixsock = 2; -#endif + conf->recursive = NGX_CONF_UNSET; return conf; } @@ -427,13 +384,8 @@ ngx_http_realip_merge_loc_conf(ngx_conf_ conf->from = prev->from; } -#if (NGX_HAVE_UNIX_DOMAIN) - if (conf->unixsock == 2) { - conf->unixsock = (prev->unixsock == 2) ? 0 : prev->unixsock; - } -#endif - ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP); + ngx_conf_merge_value(conf->recursive, prev->recursive, 0); if (conf->header.len == 0) { conf->hash = prev->hash; diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -434,6 +434,7 @@ ngx_http_scgi_handler(ngx_http_request_t u->process_header = ngx_http_scgi_process_status_line; u->abort_request = ngx_http_scgi_abort_request; u->finalize_request = ngx_http_scgi_finalize_request; + r->state = 0; u->buffering = scf->upstream.buffering; @@ -843,6 +844,7 @@ ngx_http_scgi_reinit_request(ngx_http_re status->end = NULL; r->upstream->process_header = ngx_http_scgi_process_status_line; + r->state = 0; return NGX_OK; } diff --git a/src/http/modules/ngx_http_stub_status_module.c b/src/http/modules/ngx_http_stub_status_module.c --- a/src/http/modules/ngx_http_stub_status_module.c +++ b/src/http/modules/ngx_http_stub_status_module.c @@ -121,7 +121,7 @@ static ngx_int_t ngx_http_status_handler r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; - b->last_buf = 1; + b->last_buf = (r == r->main) ? 1 : 0; rc = ngx_http_send_header(r); diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -467,6 +467,7 @@ ngx_http_uwsgi_handler(ngx_http_request_ u->process_header = ngx_http_uwsgi_process_status_line; u->abort_request = ngx_http_uwsgi_abort_request; u->finalize_request = ngx_http_uwsgi_finalize_request; + r->state = 0; u->buffering = uwcf->upstream.buffering; @@ -883,6 +884,7 @@ ngx_http_uwsgi_reinit_request(ngx_http_r status->end = NULL; r->upstream->process_header = ngx_http_uwsgi_process_status_line; + r->state = 0; return NGX_OK; } diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -50,7 +50,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '1.2.0'; +our $VERSION = '1.3.0'; require XSLoader; XSLoader::load('nginx', $VERSION); 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 @@ -2599,6 +2599,7 @@ ngx_http_named_location(ngx_http_request r->phase_handler = cmcf->phase_engine.location_rewrite_index; + r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); return NGX_DONE; @@ -2698,6 +2699,109 @@ ngx_http_set_disable_symlinks(ngx_http_r } +ngx_int_t +ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr, + u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive) +{ + u_char *p; + in_addr_t inaddr; + ngx_addr_t paddr; + ngx_cidr_t *cidr; + ngx_uint_t family, i; +#if (NGX_HAVE_INET6) + ngx_uint_t n; + struct in6_addr *inaddr6; +#endif + +#if (NGX_SUPPRESS_WARN) + inaddr = 0; +#if (NGX_HAVE_INET6) + inaddr6 = NULL; +#endif +#endif + + family = addr->sockaddr->sa_family; + + if (family == AF_INET) { + inaddr = ((struct sockaddr_in *) addr->sockaddr)->sin_addr.s_addr; + } + +#if (NGX_HAVE_INET6) + else if (family == AF_INET6) { + inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + family = AF_INET; + inaddr = *(in_addr_t *) &inaddr6->s6_addr[12]; + } + } +#endif + + for (cidr = proxies->elts, i = 0; i < proxies->nelts; i++) { + if (cidr[i].family != family) { + goto next; + } + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + for (n = 0; n < 16; n++) { + if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n]) + != cidr[i].u.in6.addr.s6_addr[n]) + { + goto next; + } + } + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) { + goto next; + } + break; + } + + for (p = xff + xfflen - 1; p > xff; p--, xfflen--) { + if (*p != ' ' && *p != ',') { + break; + } + } + + for ( /* void */ ; p > xff; p--) { + if (*p == ' ' || *p == ',') { + p++; + break; + } + } + + if (ngx_parse_addr(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) { + return NGX_DECLINED; + } + + *addr = paddr; + + if (recursive && p > xff) { + (void) ngx_http_get_forwarded_addr(r, addr, xff, p - 1 - xff, + proxies, 1); + } + + return NGX_OK; + + next: + continue; + } + + return NGX_DECLINED; +} + + static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { 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 @@ -513,6 +513,9 @@ ngx_int_t ngx_http_write_filter(ngx_http ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of); +ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr, + u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive); + extern ngx_module_t ngx_http_core_module; 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 @@ -1933,6 +1933,7 @@ ngx_http_finalize_request(ngx_http_reque if (rc == NGX_OK && r->filter_finalize) { c->error = 1; + ngx_http_finalize_connection(r); return; } @@ -2001,14 +2002,6 @@ ngx_http_finalize_request(ngx_http_reque return; } -#if (NGX_DEBUG) - if (r != c->data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http finalize non-active request: \"%V?%V\"", - &r->uri, &r->args); - } -#endif - pr = r->parent; if (r == c->data) { @@ -2042,6 +2035,10 @@ ngx_http_finalize_request(ngx_http_reque } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + r->write_event_handler = ngx_http_request_finalizer; if (r->waited) { 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 @@ -1043,7 +1043,6 @@ ngx_http_script_regex_start_code(ngx_htt } e->buf.len = len; - e->is_args = le.is_args; } if (code->add_args && r->args.len) { diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -12,8 +12,8 @@ static ngx_int_t ngx_http_upstream_cmp_servers(const void *one, const void *two); -static ngx_uint_t -ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers); +static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer( + ngx_http_upstream_rr_peer_data_t *rrp); #if (NGX_HTTP_SSL) @@ -80,8 +80,9 @@ ngx_http_upstream_init_round_robin(ngx_c peers->peer[n].max_fails = server[i].max_fails; peers->peer[n].fail_timeout = server[i].fail_timeout; peers->peer[n].down = server[i].down; - peers->peer[n].weight = server[i].down ? 0 : server[i].weight; - peers->peer[n].current_weight = peers->peer[n].weight; + peers->peer[n].weight = server[i].weight; + peers->peer[n].effective_weight = server[i].weight; + peers->peer[n].current_weight = 0; n++; } } @@ -131,7 +132,8 @@ ngx_http_upstream_init_round_robin(ngx_c backup->peer[n].socklen = server[i].addrs[j].socklen; backup->peer[n].name = server[i].addrs[j].name; backup->peer[n].weight = server[i].weight; - backup->peer[n].current_weight = server[i].weight; + backup->peer[n].effective_weight = server[i].weight; + backup->peer[n].current_weight = 0; backup->peer[n].max_fails = server[i].max_fails; backup->peer[n].fail_timeout = server[i].fail_timeout; backup->peer[n].down = server[i].down; @@ -190,7 +192,8 @@ ngx_http_upstream_init_round_robin(ngx_c peers->peer[i].socklen = u.addrs[i].socklen; peers->peer[i].name = u.addrs[i].name; peers->peer[i].weight = 1; - peers->peer[i].current_weight = 1; + peers->peer[i].effective_weight = 1; + peers->peer[i].current_weight = 0; peers->peer[i].max_fails = 1; peers->peer[i].fail_timeout = 10; } @@ -306,7 +309,8 @@ ngx_http_upstream_create_round_robin_pee peers->peer[0].socklen = ur->socklen; peers->peer[0].name = ur->host; peers->peer[0].weight = 1; - peers->peer[0].current_weight = 1; + peers->peer[0].effective_weight = 1; + peers->peer[0].current_weight = 0; peers->peer[0].max_fails = 1; peers->peer[0].fail_timeout = 10; @@ -338,7 +342,8 @@ ngx_http_upstream_create_round_robin_pee peers->peer[i].name.len = len; peers->peer[i].name.data = p; peers->peer[i].weight = 1; - peers->peer[i].current_weight = 1; + peers->peer[i].effective_weight = 1; + peers->peer[i].current_weight = 0; peers->peer[i].max_fails = 1; peers->peer[i].fail_timeout = 10; } @@ -378,8 +383,6 @@ ngx_http_upstream_get_round_robin_peer(n { ngx_http_upstream_rr_peer_data_t *rrp = data; - time_t now; - uintptr_t m; ngx_int_t rc; ngx_uint_t i, n; ngx_connection_t *c; @@ -389,8 +392,6 @@ ngx_http_upstream_get_round_robin_peer(n ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get rr peer, try: %ui", pc->tries); - now = ngx_time(); - /* ngx_lock_mutex(rrp->peers->mutex); */ if (rrp->peers->last_cached) { @@ -423,118 +424,15 @@ ngx_http_upstream_get_round_robin_peer(n /* there are several peers */ - if (pc->tries == rrp->peers->number) { - - /* it's a first try - get a current peer */ - - i = pc->tries; - - for ( ;; ) { - rrp->current = ngx_http_upstream_get_peer(rrp->peers); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "get rr peer, current: %ui %i", - rrp->current, - rrp->peers->peer[rrp->current].current_weight); - - n = rrp->current / (8 * sizeof(uintptr_t)); - m = (uintptr_t) 1 << rrp->current % (8 * sizeof(uintptr_t)); - - if (!(rrp->tried[n] & m)) { - peer = &rrp->peers->peer[rrp->current]; - - if (!peer->down) { - - if (peer->max_fails == 0 - || peer->fails < peer->max_fails) - { - break; - } - - if (now - peer->checked > peer->fail_timeout) { - peer->checked = now; - break; - } - - peer->current_weight = 0; - - } else { - rrp->tried[n] |= m; - } - - pc->tries--; - } - - if (pc->tries == 0) { - goto failed; - } - - if (--i == 0) { - ngx_log_error(NGX_LOG_ALERT, pc->log, 0, - "round robin upstream stuck on %ui tries", - pc->tries); - goto failed; - } - } + peer = ngx_http_upstream_get_peer(rrp); - peer->current_weight--; - - } else { - - i = pc->tries; - - for ( ;; ) { - n = rrp->current / (8 * sizeof(uintptr_t)); - m = (uintptr_t) 1 << rrp->current % (8 * sizeof(uintptr_t)); - - if (!(rrp->tried[n] & m)) { - - peer = &rrp->peers->peer[rrp->current]; - - if (!peer->down) { - - if (peer->max_fails == 0 - || peer->fails < peer->max_fails) - { - break; - } - - if (now - peer->checked > peer->fail_timeout) { - peer->checked = now; - break; - } - - peer->current_weight = 0; - - } else { - rrp->tried[n] |= m; - } - - pc->tries--; - } - - rrp->current++; - - if (rrp->current >= rrp->peers->number) { - rrp->current = 0; - } - - if (pc->tries == 0) { - goto failed; - } - - if (--i == 0) { - ngx_log_error(NGX_LOG_ALERT, pc->log, 0, - "round robin upstream stuck on %ui tries", - pc->tries); - goto failed; - } - } - - peer->current_weight--; + if (peer == NULL) { + goto failed; } - rrp->tried[n] |= m; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get rr peer, current: %ui %i", + rrp->current, peer->current_weight); } pc->sockaddr = peer->sockaddr; @@ -545,11 +443,6 @@ ngx_http_upstream_get_round_robin_peer(n if (pc->tries == 1 && rrp->peers->next) { pc->tries += rrp->peers->next->number; - - n = rrp->peers->next->number / (8 * sizeof(uintptr_t)) + 1; - for (i = 0; i < n; i++) { - rrp->tried[i] = 0; - } } return NGX_OK; @@ -595,56 +488,71 @@ failed: } -static ngx_uint_t -ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers) +static ngx_http_upstream_rr_peer_t * +ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) { - ngx_uint_t i, n, reset = 0; - ngx_http_upstream_rr_peer_t *peer; - - peer = &peers->peer[0]; - - for ( ;; ) { + time_t now; + uintptr_t m; + ngx_int_t total; + ngx_uint_t i, n; + ngx_http_upstream_rr_peer_t *peer, *best; - for (i = 0; i < peers->number; i++) { + now = ngx_time(); - if (peer[i].current_weight <= 0) { - continue; - } - - n = i; - - while (i < peers->number - 1) { + best = NULL; + total = 0; - i++; + for (i = 0; i < rrp->peers->number; i++) { - if (peer[i].current_weight <= 0) { - continue; - } + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); - if (peer[n].current_weight * 1000 / peer[i].current_weight - > peer[n].weight * 1000 / peer[i].weight) - { - return n; - } + if (rrp->tried[n] & m) { + continue; + } - n = i; - } + peer = &rrp->peers->peer[i]; - if (peer[i].current_weight > 0) { - n = i; - } - - return n; + if (peer->down) { + continue; } - if (reset++) { - return 0; + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + peer->current_weight += peer->effective_weight; + total += peer->effective_weight; + + if (peer->effective_weight < peer->weight) { + peer->effective_weight++; } - for (i = 0; i < peers->number; i++) { - peer[i].current_weight = peer[i].weight; + if (best == NULL || peer->current_weight > best->current_weight) { + best = peer; } } + + if (best == NULL) { + return NULL; + } + + i = best - &rrp->peers->peer[0]; + + rrp->current = i; + + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + rrp->tried[n] |= m; + + best->current_weight -= total; + best->checked = now; + + return best; } @@ -683,15 +591,15 @@ ngx_http_upstream_free_round_robin_peer( peer->checked = now; if (peer->max_fails) { - peer->current_weight -= peer->weight / peer->max_fails; + peer->effective_weight -= peer->weight / peer->max_fails; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free rr peer failed: %ui %i", - rrp->current, peer->current_weight); + rrp->current, peer->effective_weight); - if (peer->current_weight < 0) { - peer->current_weight = 0; + if (peer->effective_weight < 0) { + peer->effective_weight = 0; } /* ngx_unlock_mutex(rrp->peers->mutex); */ @@ -705,12 +613,6 @@ ngx_http_upstream_free_round_robin_peer( } } - rrp->current++; - - if (rrp->current >= rrp->peers->number) { - rrp->current = 0; - } - if (pc->tries) { pc->tries--; } diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -20,6 +20,7 @@ typedef struct { ngx_str_t name; ngx_int_t current_weight; + ngx_int_t effective_weight; ngx_int_t weight; ngx_uint_t fails; 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 @@ -29,6 +29,8 @@ typedef int ngx_err_t; #define NGX_ENOTDIR ENOTDIR #define NGX_EISDIR EISDIR #define NGX_EINVAL EINVAL +#define NGX_ENFILE ENFILE +#define NGX_EMFILE EMFILE #define NGX_ENOSPC ENOSPC #define NGX_EPIPE EPIPE #define NGX_EINPROGRESS EINPROGRESS