# HG changeset patch # User Igor Sysoev # Date 1301860800 -14400 # Node ID b9763778e212fc338790a00c19a41fd0e68e1923 # Parent fcda3d3eb4ffe2c79a3ead266eb2ce4bc6857ece nginx 0.9.7 *) Feature: now keepalive connections may be closed premature, if there are no free worker connections. Thanks to Maxim Dounin. *) Feature: the "rotate" parameter of the "image_filter" directive. Thanks to Adam Bocim. *) Bugfix: a case when a backend in "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives is given by expression and refers to a defined upstream. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,18 @@ +Changes with nginx 0.9.7 04 Apr 2011 + + *) Feature: now keepalive connections may be closed premature, if there + are no free worker connections. + Thanks to Maxim Dounin. + + *) Feature: the "rotate" parameter of the "image_filter" directive. + Thanks to Adam Bocim. + + *) Bugfix: a case when a backend in "fastcgi_pass", "scgi_pass", or + "uwsgi_pass" directives is given by expression and refers to a + defined upstream. + + Changes with nginx 0.9.6 21 Mar 2011 *) Feature: the "map" directive supports regular expressions as value diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,18 @@ +Изменения в nginx 0.9.7 04.04.2011 + + *) Добавление: теперь соединения в состоянии keepalive могут быть + закрыты преждевременно, если у воркера нет свободных соединений. + Спасибо Максиму Дунину. + + *) Добавление: параметр rotate директивы image_filter. + Спасибо Adam Bocim. + + *) Исправление: ситуации, когда бэкенд в директивах fastcgi_pass, + scgi_pass или uwsgi_pass задан выражением и ссылается на описанный + upstream. + + Изменения в nginx 0.9.6 21.03.2011 *) Добавление: директива map поддерживает регулярные выражения в diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,8 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 9006 -#define NGINX_VERSION "0.9.6" +#define nginx_version 9007 +#define NGINX_VERSION "0.9.7" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -12,6 +12,9 @@ ngx_os_io_t ngx_io; +static void ngx_drain_connections(void); + + ngx_listening_t * ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) { @@ -719,6 +722,11 @@ ngx_get_connection(ngx_socket_t s, ngx_l c = ngx_cycle->free_connections; if (c == NULL) { + ngx_drain_connections(); + c = ngx_cycle->free_connections; + } + + if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, "%ui worker_connections are not enough", ngx_cycle->connection_n); @@ -861,6 +869,8 @@ ngx_close_connection(ngx_connection_t *c #endif + ngx_reusable_connection(c, 0); + log_error = c->log_error; ngx_free_connection(c); @@ -900,6 +910,51 @@ ngx_close_connection(ngx_connection_t *c } +void +ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable) +{ + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "reusable connection: %ui", reusable); + + if (c->reusable) { + ngx_queue_remove(&c->queue); + } + + c->reusable = reusable; + + if (reusable) { + /* need cast as ngx_cycle is volatile */ + + ngx_queue_insert_head( + (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue); + } +} + + +static void +ngx_drain_connections(void) +{ + ngx_int_t i; + ngx_queue_t *q; + ngx_connection_t *c; + + for (i = 0; i < 32; i++) { + if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) { + break; + } + + q = ngx_queue_last(&ngx_cycle->reusable_connections_queue); + c = ngx_queue_data(q, ngx_connection_t, queue); + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "reusing connection"); + + c->close = 1; + c->read->handler(c->read); + } +} + + ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, ngx_uint_t port) diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -135,6 +135,8 @@ struct ngx_connection_s { ngx_buf_t *buffer; + ngx_queue_t queue; + ngx_atomic_uint_t number; ngx_uint_t requests; @@ -150,6 +152,7 @@ struct ngx_connection_s { unsigned destroyed:1; unsigned idle:1; + unsigned reusable:1; unsigned close:1; unsigned sendfile:1; @@ -186,5 +189,6 @@ ngx_int_t ngx_connection_error(ngx_conne ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log); void ngx_free_connection(ngx_connection_t *c); +void ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable); #endif /* _NGX_CONNECTION_H_INCLUDED_ */ diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -181,6 +181,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) cycle->listening.pool = pool; + ngx_queue_init(&cycle->reusable_connections_queue); + + cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); if (cycle->conf_ctx == NULL) { ngx_destroy_pool(pool); diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -44,6 +44,8 @@ struct ngx_cycle_s { ngx_connection_t *free_connections; ngx_uint_t free_connection_n; + ngx_queue_t reusable_connections_queue; + ngx_array_t listening; ngx_array_t pathes; ngx_list_t open_files; 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 @@ -636,12 +636,6 @@ ngx_http_fastcgi_eval(ngx_http_request_t return NGX_ERROR; } - if (url.no_port) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "no port in upstream \"%V\"", &url.url); - return NGX_ERROR; - } - u = r->upstream; u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); @@ -658,6 +652,7 @@ ngx_http_fastcgi_eval(ngx_http_request_t } else { u->resolved->host = url.host; u->resolved->port = url.port; + u->resolved->no_port = url.no_port; } return NGX_OK; diff --git a/src/http/modules/ngx_http_image_filter_module.c b/src/http/modules/ngx_http_image_filter_module.c --- a/src/http/modules/ngx_http_image_filter_module.c +++ b/src/http/modules/ngx_http_image_filter_module.c @@ -16,6 +16,7 @@ #define NGX_HTTP_IMAGE_SIZE 2 #define NGX_HTTP_IMAGE_RESIZE 3 #define NGX_HTTP_IMAGE_CROP 4 +#define NGX_HTTP_IMAGE_ROTATE 5 #define NGX_HTTP_IMAGE_START 0 @@ -38,12 +39,14 @@ typedef struct { ngx_uint_t filter; ngx_uint_t width; ngx_uint_t height; + ngx_uint_t angle; ngx_uint_t jpeg_quality; ngx_flag_t transparency; ngx_http_complex_value_t *wcv; ngx_http_complex_value_t *hcv; + ngx_http_complex_value_t *acv; ngx_http_complex_value_t *jqcv; size_t buffer_size; @@ -58,9 +61,9 @@ typedef struct { ngx_uint_t width; ngx_uint_t height; - ngx_uint_t max_width; ngx_uint_t max_height; + ngx_uint_t angle; ngx_uint_t phase; ngx_uint_t type; @@ -108,7 +111,7 @@ static ngx_int_t ngx_http_image_filter_i static ngx_command_t ngx_http_image_filter_commands[] = { { ngx_string("image_filter"), - NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13|NGX_CONF_TAKE2, ngx_http_image_filter, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -492,6 +495,17 @@ ngx_http_image_process(ngx_http_request_ return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL); } + ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle); + + if (conf->filter == NGX_HTTP_IMAGE_ROTATE) { + + if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) { + return NULL; + } + + return ngx_http_image_resize(r, ctx); + } + ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width); if (ctx->max_width == 0) { return NULL; @@ -506,6 +520,7 @@ ngx_http_image_process(ngx_http_request_ if (rc == NGX_OK && ctx->width <= ctx->max_width && ctx->height <= ctx->max_height + && ctx->angle == 0 && !ctx->force) { return ngx_http_image_asis(r, ctx); @@ -710,7 +725,7 @@ ngx_http_image_resize(ngx_http_request_t { int sx, sy, dx, dy, ox, oy, size, colors, palette, transparent, - red, green, blue; + red, green, blue, t; u_char *out; ngx_buf_t *b; ngx_uint_t resize; @@ -730,6 +745,7 @@ ngx_http_image_resize(ngx_http_request_t conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); if (!ctx->force + && ctx->angle == 0 && (ngx_uint_t) sx <= ctx->max_width && (ngx_uint_t) sy <= ctx->max_height) { @@ -781,6 +797,10 @@ transparent: resize = 1; + } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) { + + resize = 0; + } else { /* NGX_HTTP_IMAGE_CROP */ resize = 0; @@ -829,6 +849,38 @@ transparent: dst = src; } + if (ctx->angle) { + src = dst; + + switch (ctx->angle) { + + case 90: + case 270: + dst = ngx_http_image_new(r, dy, dx, palette); + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + gdImageCopyRotated(dst, src, dy/2, dx/2, 0, 0, dx, dy, ctx->angle); + gdImageDestroy(src); + break; + + case 180: + dst = ngx_http_image_new(r, dx, dy, palette); + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + gdImageCopyRotated(dst, src, dx/2, dy/2, 0, 0, dx, dy, ctx->angle); + gdImageDestroy(src); + break; + } + + t = dx; + dx = dy; + dy = t; + } + if (conf->filter == NGX_HTTP_IMAGE_CROP) { src = dst; @@ -1090,6 +1142,7 @@ ngx_http_image_filter_create_conf(ngx_co conf->filter = NGX_CONF_UNSET_UINT; conf->jpeg_quality = NGX_CONF_UNSET_UINT; + conf->angle = NGX_CONF_UNSET_UINT; conf->transparency = NGX_CONF_UNSET; conf->buffer_size = NGX_CONF_UNSET_SIZE; @@ -1124,6 +1177,11 @@ ngx_http_image_filter_merge_conf(ngx_con conf->jqcv = prev->jqcv; } + ngx_conf_merge_uint_value(conf->angle, prev->angle, 0); + if (conf->acv == NULL) { + conf->acv = prev->acv; + } + ngx_conf_merge_value(conf->transparency, prev->transparency, 1); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, @@ -1163,6 +1221,46 @@ ngx_http_image_filter(ngx_conf_t *cf, ng } return NGX_CONF_OK; + + } else if (cf->args->nelts == 3) { + + if (ngx_strcmp(value[i].data, "rotate") == 0) { + imcf->filter = NGX_HTTP_IMAGE_ROTATE; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[++i]; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths == NULL) { + n = ngx_http_image_filter_value(&value[i]); + + if (n != 90 && n != 180 && n != 270) { + goto failed; + } + + imcf->angle = (ngx_uint_t) n; + + } else { + imcf->acv = ngx_palloc(cf->pool, + sizeof(ngx_http_complex_value_t)); + if (imcf->acv == NULL) { + return NGX_CONF_ERROR; + } + + *imcf->acv = cv; + } + + return NGX_CONF_OK; + + } else { + goto failed; + } } if (ngx_strcmp(value[i].data, "resize") == 0) { 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 @@ -458,12 +458,6 @@ ngx_http_scgi_eval(ngx_http_request_t *r return NGX_ERROR; } - if (url.no_port) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "no port in upstream \"%V\"", &url.url); - return NGX_ERROR; - } - u = r->upstream; u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); @@ -480,6 +474,7 @@ ngx_http_scgi_eval(ngx_http_request_t *r } else { u->resolved->host = url.host; u->resolved->port = url.port; + u->resolved->no_port = url.no_port; } return NGX_OK; 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 @@ -491,12 +491,6 @@ ngx_http_uwsgi_eval(ngx_http_request_t * return NGX_ERROR; } - if (url.no_port) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "no port in upstream \"%V\"", &url.url); - return NGX_ERROR; - } - u = r->upstream; u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); @@ -513,6 +507,7 @@ ngx_http_uwsgi_eval(ngx_http_request_t * } else { u->resolved->host = url.host; u->resolved->port = url.port; + u->resolved->no_port = url.no_port; } 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 @@ -48,7 +48,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.9.6'; +our $VERSION = '0.9.7'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2594,6 +2594,7 @@ ngx_http_set_keepalive(ngx_http_request_ #endif c->idle = 1; + ngx_reusable_connection(c, 1); if (rev->ready) { ngx_post_event(rev, &ngx_posted_events); @@ -2703,6 +2704,7 @@ ngx_http_keepalive_handler(ngx_event_t * c->log->action = "reading client request line"; c->idle = 0; + ngx_reusable_connection(c, 0); ngx_http_init_request(rev); } 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 @@ -574,6 +574,14 @@ ngx_http_upstream_init_request(ngx_http_ } } + if (u->resolved->port == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", host); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + temp.name = *host; ctx = ngx_resolve_start(clcf->resolver, &temp);