diff options
author | nginx <nginx@nginx.org> | 2014-01-22 13:57:30 +0000 |
---|---|---|
committer | Jon Kolb <kolbyjack@gmail.com> | 2014-01-22 13:57:30 +0000 |
commit | 498c331cd1630965fd757859b69b4d8bf566a6d9 (patch) | |
tree | 5114b88974383b03805b5a6ff63b5837e8d03496 | |
parent | 80816f36cfa4dc95e2441e527b1ff915b105309c (diff) | |
download | nginx-498c331cd1630965fd757859b69b4d8bf566a6d9.tar.gz |
Changes with nginx 1.5.9 22 Jan 2014v1.5.9
*) Change: now nginx expects escaped URIs in "X-Accel-Redirect" headers.
*) Feature: the "ssl_buffer_size" directive.
*) Feature: the "limit_rate" directive can now be used to rate limit
responses sent in SPDY connections.
*) Feature: the "spdy_chunk_size" directive.
*) Feature: the "ssl_session_tickets" directive.
Thanks to Dirkjan Bussink.
*) Bugfix: the $ssl_session_id variable contained full session
serialized instead of just a session id.
Thanks to Ivan Ristić.
*) Bugfix: nginx incorrectly handled escaped "?" character in the
"include" SSI command.
*) Bugfix: the ngx_http_dav_module did not unescape destination URI of
the COPY and MOVE methods.
*) Bugfix: resolver did not understand domain names with a trailing dot.
Thanks to Yichun Zhang.
*) Bugfix: alerts "zero size buf in output" might appear in logs while
proxying; the bug had appeared in 1.3.9.
*) Bugfix: a segmentation fault might occur in a worker process if the
ngx_http_spdy_module was used.
*) Bugfix: proxied WebSocket connections might hang right after
handshake if the select, poll, or /dev/poll methods were used.
*) Bugfix: the "xclient" directive of the mail proxy module incorrectly
handled IPv6 client addresses.
35 files changed, 787 insertions, 405 deletions
@@ -1,4 +1,44 @@ +Changes with nginx 1.5.9 22 Jan 2014 + + *) Change: now nginx expects escaped URIs in "X-Accel-Redirect" headers. + + *) Feature: the "ssl_buffer_size" directive. + + *) Feature: the "limit_rate" directive can now be used to rate limit + responses sent in SPDY connections. + + *) Feature: the "spdy_chunk_size" directive. + + *) Feature: the "ssl_session_tickets" directive. + Thanks to Dirkjan Bussink. + + *) Bugfix: the $ssl_session_id variable contained full session + serialized instead of just a session id. + Thanks to Ivan Ristić. + + *) Bugfix: nginx incorrectly handled escaped "?" character in the + "include" SSI command. + + *) Bugfix: the ngx_http_dav_module did not unescape destination URI of + the COPY and MOVE methods. + + *) Bugfix: resolver did not understand domain names with a trailing dot. + Thanks to Yichun Zhang. + + *) Bugfix: alerts "zero size buf in output" might appear in logs while + proxying; the bug had appeared in 1.3.9. + + *) Bugfix: a segmentation fault might occur in a worker process if the + ngx_http_spdy_module was used. + + *) Bugfix: proxied WebSocket connections might hang right after + handshake if the select, poll, or /dev/poll methods were used. + + *) Bugfix: the "xclient" directive of the mail proxy module incorrectly + handled IPv6 client addresses. + + Changes with nginx 1.5.8 17 Dec 2013 *) Feature: IPv6 support in resolver. @@ -6218,7 +6258,7 @@ Changes with nginx 0.1.16 25 Jan 2005 *) Bugfix: the compressed response encrypted by SSL may not transferred complete. - *) Bugfix: the TCP-specific TCP_NODELAY, TCP_NOPSUH, and TCP_CORK + *) Bugfix: the TCP-specific TCP_NODELAY, TCP_NOPUSH, and TCP_CORK options, are not used for the unix domain sockets. *) Feature: the rewrite directive supports the arguments rewriting. diff --git a/CHANGES.ru b/CHANGES.ru index f50fd2aeb..580bba300 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,46 @@ +Изменения в nginx 1.5.9 22.01.2014 + + *) Изменение: теперь в заголовке X-Accel-Redirect nginx ожидает + закодированный URI. + + *) Добавление: директива ssl_buffer_size. + + *) Добавление: директиву limit_rate теперь можно использовать для + ограничения скорости передачи ответов клиенту в SPDY-соединениях. + + *) Добавление: директива spdy_chunk_size. + + *) Добавление: директива ssl_session_tickets. + Спасибо Dirkjan Bussink. + + *) Исправление: переменная $ssl_session_id содержала всю сессию в + сериализованном виде вместо её идентификатора. + Спасибо Ivan Ristić. + + *) Исправление: nginx неправильно обрабатывал закодированный символ "?" + в команде SSI include. + + *) Исправление: модуль ngx_http_dav_module не раскодировал целевой URI + при обработке методов COPY и MOVE. + + *) Исправление: resolver не понимал доменные имена с точкой в конце. + Спасибо Yichun Zhang. + + *) Исправление: при проксировании в логах могли появляться сообщения + "zero size buf in output"; ошибка появилась в 1.3.9. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовался модуль ngx_http_spdy_module. + + *) Исправление: при использовании методов обработки соединений select, + poll и /dev/poll проксируемые WebSocket-соединения могли зависать + сразу после открытия. + + *) Исправление: директива xclient почтового прокси-сервера некорректно + передавала IPv6-адреса. + + Изменения в nginx 1.5.8 17.12.2013 *) Добавление: теперь resolver поддерживает IPv6. @@ -2826,7 +2868,7 @@ умолчанию; ошибка появилась в 0.7.18. Спасибо Максиму Дунину. - *) Исправление: при проксировании SMPT nginx выдавал сообщение + *) Исправление: при проксировании SMTP nginx выдавал сообщение "250 2.0.0 OK" вместо "235 2.0.0 OK"; ошибка появилась в 0.7.22. Спасибо Максиму Дунину. @@ -6312,7 +6354,7 @@ *) Исправление: при использовании SSL сжатый ответ мог передаваться не до конца. - *) Исправление: опции TCP_NODELAY, TCP_NOPSUH и TCP_CORK, специфичные + *) Исправление: опции TCP_NODELAY, TCP_NOPUSH и TCP_CORK, специфичные для TCP сокетов, не используются для unix domain сокетов. *) Добавление: директива rewrite поддерживает перезаписывание @@ -1,6 +1,6 @@ /* - * Copyright (C) 2002-2013 Igor Sysoev - * Copyright (C) 2011-2013 Nginx, Inc. + * Copyright (C) 2002-2014 Igor Sysoev + * Copyright (C) 2011-2014 Nginx, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/core/nginx.h b/src/core/nginx.h index 220d55a29..76d43c6d4 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1005008 -#define NGINX_VERSION "1.5.8" +#define nginx_version 1005009 +#define NGINX_VERSION "1.5.9" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 57e26912d..08a547d42 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -199,7 +199,9 @@ ngx_set_inherited_sockets(ngx_cycle_t *cycle) #if (NGX_HAVE_SETFIB) - if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB, + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, (void *) &ls[i].setfib, &olen) == -1) { @@ -215,7 +217,9 @@ ngx_set_inherited_sockets(ngx_cycle_t *cycle) #if (NGX_HAVE_TCP_FASTOPEN) - if (getsockopt(ls[i].fastopen, IPPROTO_TCP, TCP_FASTOPEN, + olen = sizeof(int); + + if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN, (void *) &ls[i].fastopen, &olen) == -1) { diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index c7b3b315c..d9bc60a77 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -112,6 +112,7 @@ typedef enum { #define NGX_LOWLEVEL_BUFFERED 0x0f #define NGX_SSL_BUFFERED 0x01 +#define NGX_SPDY_BUFFERED 0x02 struct ngx_connection_s { @@ -171,12 +172,15 @@ struct ngx_connection_s { unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */ + unsigned need_last_buf:1; + #if (NGX_HAVE_IOCP) unsigned accept_context_updated:1; #endif #if (NGX_HAVE_AIO_SENDFILE) unsigned aio_sendfile:1; + unsigned busy_count:2; ngx_buf_t *busy_sendfile; #endif diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c index efbc24452..1f70f9eee 100644 --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -105,11 +105,14 @@ ngx_reset_pool(ngx_pool_t *pool) } } - pool->large = NULL; - for (p = pool; p; p = p->d.next) { p->d.last = (u_char *) p + sizeof(ngx_pool_t); + p->d.failed = 0; } + + pool->current = pool; + pool->chain = NULL; + pool->large = NULL; } diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index a446f98b6..abf82d85b 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -356,6 +356,10 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx) r = ctx->resolver; + if (ctx->name.len > 0 && ctx->name.data[ctx->name.len - 1] == '.') { + ctx->name.len--; + } + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve: \"%V\"", &ctx->name); @@ -678,6 +682,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) rn->code = 0; rn->cnlen = 0; rn->valid = 0; + rn->ttl = NGX_MAX_UINT32_VALUE; rn->waiting = ctx; ctx->state = NGX_AGAIN; @@ -871,6 +876,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx) rn->name = NULL; rn->nlen = 0; rn->valid = 0; + rn->ttl = NGX_MAX_UINT32_VALUE; rn->waiting = ctx; /* unlock addr mutex */ @@ -1574,7 +1580,6 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, i = ans; naddrs = 0; cname = NULL; - ttl = 0; for (a = 0; a < nan; a++) { @@ -1628,6 +1633,8 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ttl = 0; } + rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl); + i += sizeof(ngx_resolver_an_t); switch (type) { @@ -1694,8 +1701,8 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, - "resolver naddrs:%ui cname:%p ttl:%d", - naddrs, cname, ttl); + "resolver naddrs:%ui cname:%p ttl:%uD", + naddrs, cname, rn->ttl); if (naddrs) { @@ -1746,8 +1753,6 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, #endif } - rn->ttl = ttl; - n = 0; i = ans; @@ -1915,7 +1920,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, rn->cnlen = (u_short) name.len; rn->u.cname = name.data; - rn->valid = ngx_time() + (r->valid ? r->valid : ttl); + rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index ee6671350..9935fb098 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -190,6 +190,8 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) return NGX_ERROR; } + ssl->buffer_size = NGX_SSL_BUFSIZE; + /* client side options */ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG); @@ -726,6 +728,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) } sc->buffer = ((flags & NGX_SSL_BUFFER) != 0); + sc->buffer_size = ssl->buffer_size; sc->connection = SSL_new(ssl->ctx); @@ -1222,7 +1225,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) buf = c->ssl->buf; if (buf == NULL) { - buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE); + buf = ngx_create_temp_buf(c->pool, c->ssl->buffer_size); if (buf == NULL) { return NGX_CHAIN_ERROR; } @@ -1231,14 +1234,14 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) } if (buf->start == NULL) { - buf->start = ngx_palloc(c->pool, NGX_SSL_BUFSIZE); + buf->start = ngx_palloc(c->pool, c->ssl->buffer_size); if (buf->start == NULL) { return NGX_CHAIN_ERROR; } buf->pos = buf->start; buf->last = buf->start; - buf->end = buf->start + NGX_SSL_BUFSIZE; + buf->end = buf->start + c->ssl->buffer_size; } send = buf->last - buf->pos; @@ -2501,32 +2504,22 @@ ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { int len; - u_char *p, *buf; + u_char *buf; SSL_SESSION *sess; sess = SSL_get0_session(c->ssl->connection); - len = i2d_SSL_SESSION(sess, NULL); - - buf = ngx_alloc(len, c->log); - if (buf == NULL) { - return NGX_ERROR; - } + buf = sess->session_id; + len = sess->session_id_length; s->len = 2 * len; s->data = ngx_pnalloc(pool, 2 * len); if (s->data == NULL) { - ngx_free(buf); return NGX_ERROR; } - p = buf; - i2d_SSL_SESSION(sess, &p); - ngx_hex_dump(s->data, buf, len); - ngx_free(buf); - return NGX_OK; } diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 841e82569..907639e83 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -29,6 +29,7 @@ typedef struct { SSL_CTX *ctx; ngx_log_t *log; + size_t buffer_size; } ngx_ssl_t; @@ -37,6 +38,7 @@ typedef struct { ngx_int_t last; ngx_buf_t *buf; + size_t buffer_size; ngx_connection_handler_pt handler; diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c index 221455120..f694df075 100644 --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -233,6 +233,7 @@ ngx_http_autoindex_handler(ngx_http_request_t *r) r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_type_len = sizeof("text/html") - 1; ngx_str_set(&r->headers_out.content_type, "text/html"); + r->headers_out.content_type_lowcase = NULL; rc = ngx_http_send_header(r); diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c index 2a179922c..e7f9e9ae3 100644 --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -604,7 +604,7 @@ destination_done: duri.len = last - p; duri.data = p; - flags = 0; + flags = NGX_HTTP_LOG_UNSAFE; if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) { goto invalid_destination; diff --git a/src/http/modules/ngx_http_image_filter_module.c b/src/http/modules/ngx_http_image_filter_module.c index cc41ef0d5..c983b973b 100644 --- a/src/http/modules/ngx_http_image_filter_module.c +++ b/src/http/modules/ngx_http_image_filter_module.c @@ -572,6 +572,7 @@ ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) ngx_http_clean_header(r); r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_type_len = sizeof("application/json") - 1; ngx_str_set(&r->headers_out.content_type, "application/json"); r->headers_out.content_type_lowcase = NULL; diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index c44dc52c9..d900fb8d1 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -542,7 +542,7 @@ ngx_http_mp4_handler(ngx_http_request_t *r) mp4->file.fd = of.fd; mp4->file.name = path; - mp4->file.log = r->connection->log;; + mp4->file.log = r->connection->log; mp4->end = of.size; mp4->start = (ngx_uint_t) start; mp4->request = r; diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index d396b27c8..70f6ac1de 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -1734,7 +1734,7 @@ ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) sc.source = &value[1]; sc.lengths = &scf->upstream.store_lengths; sc.values = &scf->upstream.store_values; - sc.variables = ngx_http_script_variables_count(&value[1]);; + sc.variables = ngx_http_script_variables_count(&value[1]); sc.complete_lengths = 1; sc.complete_values = 1; diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index c70b17e09..a53cd1472 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -1982,8 +1982,6 @@ static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) { - u_char *dst, *src; - size_t len; ngx_int_t rc, key; ngx_str_t *uri, *file, *wait, *set, *stub, args; ngx_buf_t *b; @@ -2054,18 +2052,6 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, return rc; } - dst = uri->data; - src = uri->data; - - ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI); - - len = (uri->data + uri->len) - src; - if (len) { - dst = ngx_movemem(dst, src, len); - } - - uri->len = dst - uri->data; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ssi include: \"%V\"", uri); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 7ac96c621..1a2669065 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -111,6 +111,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { offsetof(ngx_http_ssl_srv_conf_t, ciphers), NULL }, + { ngx_string("ssl_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, buffer_size), + NULL }, + { ngx_string("ssl_verify_client"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -119,7 +126,7 @@ static ngx_command_t ngx_http_ssl_commands[] = { &ngx_http_ssl_verify }, { ngx_string("ssl_verify_depth"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, verify_depth), @@ -153,6 +160,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_session_tickets"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, session_tickets), + NULL }, + { ngx_string("ssl_session_ticket_key"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, @@ -424,10 +438,12 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) sscf->enable = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; + sscf->session_tickets = NGX_CONF_UNSET; sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; @@ -465,6 +481,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, + NGX_SSL_BUFSIZE); + ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); @@ -572,6 +591,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + conf->ssl.buffer_size = conf->buffer_size; + if (conf->verify) { if (conf->client_certificate.len == 0 && conf->verify != 3) { @@ -631,6 +652,14 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1); + +#ifdef SSL_OP_NO_TICKET + if (!conf->session_tickets) { + SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET); + } +#endif + ngx_conf_merge_ptr_value(conf->session_ticket_keys, prev->session_ticket_keys, NULL); diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 3feb4b6d2..ec2c62f6f 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -26,6 +26,8 @@ typedef struct { ngx_uint_t verify; ngx_uint_t verify_depth; + size_t buffer_size; + ssize_t builtin_session_cache; time_t session_timeout; @@ -42,6 +44,7 @@ typedef struct { ngx_shm_zone_t *shm_zone; + ngx_flag_t session_tickets; ngx_array_t *session_ticket_keys; ngx_flag_t stapling; diff --git a/src/http/modules/ngx_http_stub_status_module.c b/src/http/modules/ngx_http_stub_status_module.c index fe9132df8..b5ecd6d9e 100644 --- a/src/http/modules/ngx_http_stub_status_module.c +++ b/src/http/modules/ngx_http_stub_status_module.c @@ -98,7 +98,9 @@ static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r) return rc; } + r->headers_out.content_type_len = sizeof("text/plain") - 1; ngx_str_set(&r->headers_out.content_type, "text/plain"); + r->headers_out.content_type_lowcase = NULL; if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c index 0e5cf416a..041883fec 100644 --- a/src/http/modules/ngx_http_upstream_ip_hash_module.c +++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -197,33 +197,39 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); - if (!(iphp->rrp.tried[n] & m)) { + if (iphp->rrp.tried[n] & m) { + goto next; + } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "get ip hash peer, hash: %ui %04XA", p, m); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get ip hash peer, hash: %ui %04XA", p, m); - peer = &iphp->rrp.peers->peer[p]; + peer = &iphp->rrp.peers->peer[p]; - /* ngx_lock_mutex(iphp->rrp.peers->mutex); */ + /* ngx_lock_mutex(iphp->rrp.peers->mutex); */ - if (!peer->down) { + if (peer->down) { + goto next_try; + } - if (peer->max_fails == 0 || peer->fails < peer->max_fails) { - break; - } + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + goto next_try; + } - if (now - peer->checked > peer->fail_timeout) { - peer->checked = now; - break; - } - } + break; - iphp->rrp.tried[n] |= m; + next_try: - /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ + iphp->rrp.tried[n] |= m; - pc->tries--; - } + /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ + + pc->tries--; + + next: if (++iphp->tries >= 20) { return iphp->get_rr_peer(pc, &iphp->rrp); @@ -236,6 +242,10 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) pc->socklen = peer->socklen; pc->name = &peer->name; + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; + } + /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ iphp->rrp.tried[n] |= m; diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 0ddea57df..f55e606c8 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1916,7 +1916,7 @@ ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) sc.source = &value[1]; sc.lengths = &uwcf->upstream.store_lengths; sc.values = &uwcf->upstream.store_values; - sc.variables = ngx_http_script_variables_count(&value[1]);; + sc.variables = ngx_http_script_variables_count(&value[1]); sc.complete_lengths = 1; sc.complete_values = 1; diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c index 95bc0b835..3ad27b042 100644 --- a/src/http/ngx_http_copy_filter_module.c +++ b/src/http/ngx_http_copy_filter_module.c @@ -169,13 +169,15 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in) offset = c->busy_sendfile->file_pos; if (file->aio) { - c->aio_sendfile = (offset != file->aio->last_offset); + c->busy_count = (offset == file->aio->last_offset) ? + c->busy_count + 1 : 0; file->aio->last_offset = offset; - if (c->aio_sendfile == 0) { + if (c->busy_count > 2) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "sendfile(%V) returned busy again", &file->name); + c->aio_sendfile = 0; } } diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index cf89b8cfe..8c1a62a7b 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1780,17 +1780,21 @@ ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_uint_t *flags) { - u_char ch, *p; - size_t len; + u_char ch, *p, *src, *dst; + size_t len; + ngx_uint_t quoted; len = uri->len; p = uri->data; + quoted = 0; if (len == 0 || p[0] == '?') { goto unsafe; } - if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) { + if (p[0] == '.' && len > 1 && p[1] == '.' + && (len == 2 || ngx_path_separator(p[2]))) + { goto unsafe; } @@ -1798,6 +1802,11 @@ ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, ch = *p++; + if (ch == '%') { + quoted = 1; + continue; + } + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { continue; } @@ -1807,7 +1816,7 @@ ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, args->data = p; uri->len -= len; - return NGX_OK; + break; } if (ch == '\0') { @@ -1816,14 +1825,66 @@ ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, if (ngx_path_separator(ch) && len > 2) { - /* detect "/../" */ + /* detect "/../" and "/.." */ - if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) { + if (p[0] == '.' && p[1] == '.' + && (len == 3 || ngx_path_separator(p[2]))) + { goto unsafe; } } } + if (quoted) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "escaped URI: \"%V\"", uri); + + src = uri->data; + + dst = ngx_pnalloc(r->pool, uri->len); + if (dst == NULL) { + return NGX_ERROR; + } + + uri->data = dst; + + ngx_unescape_uri(&dst, &src, uri->len, 0); + + uri->len = dst - uri->data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "unescaped URI: \"%V\"", uri); + + len = uri->len; + p = uri->data; + + if (p[0] == '.' && len > 1 && p[1] == '.' + && (len == 2 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + + for ( /* void */ ; len; len--) { + + ch = *p++; + + if (ch == '\0') { + goto unsafe; + } + + if (ngx_path_separator(ch) && len > 2) { + + /* detect "/../" and "/.." */ + + if (p[0] == '.' && p[1] == '.' + && (len == 3 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + } + } + } + return NGX_OK; unsafe: diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 97df69c05..94cdbeed6 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -150,21 +150,27 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, goto done; } - cl = ngx_chain_get_free_buf(r->pool, &rb->free); - if (cl == NULL) { - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; - goto done; - } + if (rb->temp_file->file.offset != 0) { - b = cl->buf; + cl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (cl == NULL) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + goto done; + } - ngx_memzero(b, sizeof(ngx_buf_t)); + b = cl->buf; - b->in_file = 1; - b->file_last = rb->temp_file->file.offset; - b->file = &rb->temp_file->file; + ngx_memzero(b, sizeof(ngx_buf_t)); - rb->bufs = cl; + b->in_file = 1; + b->file_last = rb->temp_file->file.offset; + b->file = &rb->temp_file->file; + + rb->bufs = cl; + + } else { + rb->bufs = NULL; + } } post_handler(r); @@ -375,20 +381,26 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - cl = ngx_chain_get_free_buf(r->pool, &rb->free); - if (cl == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + if (rb->temp_file->file.offset != 0) { - b = cl->buf; + cl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - ngx_memzero(b, sizeof(ngx_buf_t)); + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->in_file = 1; + b->file_last = rb->temp_file->file.offset; + b->file = &rb->temp_file->file; - b->in_file = 1; - b->file_last = rb->temp_file->file.offset; - b->file = &rb->temp_file->file; + rb->bufs = cl; - rb->bufs = cl; + } else { + rb->bufs = NULL; + } } r->read_event_handler = ngx_http_block_reading; @@ -843,6 +855,10 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) for (cl = in; cl; cl = cl->next) { + if (rb->rest == 0) { + break; + } + tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; diff --git a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c index 2346ad7a1..27c8aa196 100644 --- a/src/http/ngx_http_spdy.c +++ b/src/http/ngx_http_spdy.c @@ -81,8 +81,6 @@ static void ngx_http_spdy_read_handler(ngx_event_t *rev); static void ngx_http_spdy_write_handler(ngx_event_t *wev); static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); -static u_char *ngx_http_spdy_state_detect_settings( - ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, @@ -101,8 +99,10 @@ static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); +#if 0 static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); +#endif static u_char *ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, @@ -235,7 +235,7 @@ ngx_http_spdy_init(ngx_event_t *rev) sc->connection = c; sc->http_connection = hc; - sc->handler = ngx_http_spdy_state_detect_settings; + sc->handler = ngx_http_spdy_state_head; sc->zstream_in.zalloc = ngx_http_spdy_zalloc; sc->zstream_in.zfree = ngx_http_spdy_zfree; @@ -297,6 +297,13 @@ ngx_http_spdy_init(ngx_event_t *rev) return; } + if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + ngx_queue_init(&sc->posted); + c->data = sc; rev->handler = ngx_http_spdy_read_handler; @@ -346,7 +353,7 @@ ngx_http_spdy_read_handler(ngx_event_t *rev) break; } - if (n == 0 && (sc->waiting || sc->processing)) { + if (n == 0 && (sc->incomplete || sc->processing)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed prematurely connection"); } @@ -360,7 +367,7 @@ ngx_http_spdy_read_handler(ngx_event_t *rev) end += n; sc->buffer_used = 0; - sc->waiting = 0; + sc->incomplete = 0; do { p = sc->handler(sc, p, end); @@ -378,6 +385,11 @@ ngx_http_spdy_read_handler(ngx_event_t *rev) return; } + if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) { + ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + sc->blocked = 0; if (sc->processing) { @@ -395,8 +407,9 @@ static void ngx_http_spdy_write_handler(ngx_event_t *wev) { ngx_int_t rc; + ngx_queue_t *q; ngx_connection_t *c; - ngx_http_spdy_stream_t *stream, *s, *sn; + ngx_http_spdy_stream_t *stream; ngx_http_spdy_connection_t *sc; c = wev->data; @@ -411,7 +424,7 @@ ngx_http_spdy_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); - sc->blocked = 2; + sc->blocked = 1; rc = ngx_http_spdy_send_output_queue(sc); @@ -420,20 +433,13 @@ ngx_http_spdy_write_handler(ngx_event_t *wev) return; } - stream = NULL; + while (!ngx_queue_empty(&sc->posted)) { + q = ngx_queue_head(&sc->posted); - for (s = sc->last_stream; s; s = sn) { - sn = s->next; - s->next = stream; - stream = s; - } + ngx_queue_remove(q); - sc->last_stream = NULL; + stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue); - sc->blocked = 1; - - for ( /* void */ ; stream; stream = sn) { - sn = stream->next; stream->handled = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -486,9 +492,9 @@ ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) out = frame; ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, - "spdy frame out: %p sid:%ui prio:%ui bl:%ui size:%uz", + "spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz", out, out->stream ? out->stream->id : 0, out->priority, - out->blocked, out->size); + out->blocked, out->length); } cl = c->send_chain(c, cl, 0); @@ -519,7 +525,9 @@ ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) } } - for ( /* void */ ; out; out = out->next) { + for ( /* void */ ; out; out = fn) { + fn = out->next; + if (out->handler(sc, out) != NGX_OK) { out->blocked = 1; out->priority = NGX_SPDY_HIGHEST_PRIORITY; @@ -527,9 +535,9 @@ ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "spdy frame sent: %p sid:%ui bl:%ui size:%uz", + "spdy frame sent: %p sid:%ui bl:%d len:%uz", out, out->stream ? out->stream->id : 0, - out->blocked, out->size); + out->blocked, out->length); } frame = NULL; @@ -569,7 +577,7 @@ ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ngx_http_spdy_module); - if (sc->waiting) { + if (sc->incomplete) { ngx_add_timer(c->read, sscf->recv_timeout); return; } @@ -607,38 +615,6 @@ ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) static u_char * -ngx_http_spdy_state_detect_settings(ngx_http_spdy_connection_t *sc, - u_char *pos, u_char *end) -{ - if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { - return ngx_http_spdy_state_save(sc, pos, end, - ngx_http_spdy_state_detect_settings); - } - - /* - * Since this is the first frame in a buffer, - * then it is properly aligned - */ - - if (*(uint32_t *) pos == htonl(ngx_spdy_ctl_frame_head(NGX_SPDY_SETTINGS))) - { - sc->length = ngx_spdy_frame_length(htonl(((uint32_t *) pos)[1])); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, - "spdy SETTINGS frame received, size: %uz", sc->length); - - pos += NGX_SPDY_FRAME_HEADER_SIZE; - - return ngx_http_spdy_state_settings(sc, pos, end); - } - - ngx_http_spdy_send_settings(sc); - - return ngx_http_spdy_state_head(sc, pos, end); -} - - -static u_char * ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end) { @@ -661,7 +637,7 @@ ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, pos += sizeof(uint32_t); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, - "spdy process frame head:%08Xd f:%ui l:%ui", + "spdy process frame head:%08XD f:%Xd l:%uz", head, sc->flags, sc->length); if (ngx_spdy_ctl_frame_check(head)) { @@ -859,14 +835,15 @@ ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, ngx_http_spdy_state_headers); } - sc->headers = ngx_spdy_frame_parse_uint16(buf->pos); + sc->entries = ngx_spdy_frame_parse_uint16(buf->pos); buf->pos += NGX_SPDY_NV_NUM_SIZE; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "spdy headers count: %ui", sc->headers); + "spdy HEADERS block consists of %ui entries", + sc->entries); - if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3, + if (ngx_list_init(&r->headers_in.headers, r->pool, sc->entries + 3, sizeof(ngx_table_elt_t)) != NGX_OK) { @@ -885,14 +862,14 @@ ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, } } - while (sc->headers) { + while (sc->entries) { rc = ngx_http_spdy_parse_header(r); switch (rc) { case NGX_DONE: - sc->headers--; + sc->entries--; case NGX_OK: break; @@ -1250,7 +1227,6 @@ ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ngx_uint_t sid, status; ngx_event_t *ev; ngx_connection_t *fc; - ngx_http_request_t *r; ngx_http_spdy_stream_t *stream; if (end - pos < NGX_SPDY_RST_STREAM_SIZE) { @@ -1259,7 +1235,10 @@ ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, } if (sc->length != NGX_SPDY_RST_STREAM_SIZE) { - /* TODO logging */ + ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, + "client sent RST_STREAM frame with incorrect length %uz", + sc->length); + return ngx_http_spdy_state_protocol_error(sc); } @@ -1274,55 +1253,42 @@ ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy RST_STREAM sid:%ui st:%ui", sid, status); + stream = ngx_http_spdy_get_stream_by_id(sc, sid); + if (stream == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "unknown stream, probably it has been closed already"); + return ngx_http_spdy_state_complete(sc, pos, end); + } - switch (status) { + stream->in_closed = 1; + stream->out_closed = 1; - case NGX_SPDY_PROTOCOL_ERROR: - /* TODO logging */ - return ngx_http_spdy_state_protocol_error(sc); + fc = stream->request->connection; + fc->error = 1; - case NGX_SPDY_INVALID_STREAM: - /* TODO */ - break; + switch (status) { - case NGX_SPDY_REFUSED_STREAM: - /* TODO */ + case NGX_SPDY_CANCEL: + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client canceled stream %ui", sid); break; - case NGX_SPDY_UNSUPPORTED_VERSION: - /* TODO logging */ - return ngx_http_spdy_state_protocol_error(sc); - - case NGX_SPDY_CANCEL: case NGX_SPDY_INTERNAL_ERROR: - stream = ngx_http_spdy_get_stream_by_id(sc, sid); - if (stream == NULL) { - /* TODO false cancel */ - break; - } - - stream->in_closed = 1; - stream->out_closed = 1; - - r = stream->request; - - fc = r->connection; - fc->error = 1; - - ev = fc->read; - ev->handler(ev); - + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client terminated stream %ui because of internal error", + sid); break; - case NGX_SPDY_FLOW_CONTROL_ERROR: - /* TODO logging */ - return ngx_http_spdy_state_protocol_error(sc); - default: - /* TODO */ - return ngx_http_spdy_state_protocol_error(sc); + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client terminated stream %ui with status %ui", + sid, status); + break; } + ev = fc->read; + ev->handler(ev); + return ngx_http_spdy_state_complete(sc, pos, end); } @@ -1391,66 +1357,51 @@ ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, } +#if 0 + static u_char * ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end) { - ngx_uint_t v; - ngx_http_spdy_srv_conf_t *sscf; - - if (sc->headers == 0) { + if (sc->entries == 0) { if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) { return ngx_http_spdy_state_save(sc, pos, end, ngx_http_spdy_state_settings); } - sc->headers = ngx_spdy_frame_parse_uint32(pos); + sc->entries = ngx_spdy_frame_parse_uint32(pos); pos += NGX_SPDY_SETTINGS_NUM_SIZE; sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE; - if (sc->length < sc->headers * NGX_SPDY_SETTINGS_PAIR_SIZE) { + if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) { /* TODO logging */ return ngx_http_spdy_state_protocol_error(sc); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy SETTINGS frame consists of %ui entries", - sc->headers); + sc->entries); } - while (sc->headers) { + while (sc->entries) { if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) { return ngx_http_spdy_state_save(sc, pos, end, ngx_http_spdy_state_settings); } - sc->headers--; - - if (pos[0] != NGX_SPDY_SETTINGS_MAX_STREAMS) { - pos += NGX_SPDY_SETTINGS_PAIR_SIZE; - sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; - continue; - } - - v = ngx_spdy_frame_parse_uint32(pos + NGX_SPDY_SETTINGS_IDF_SIZE); - - sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, - ngx_http_spdy_module); - - if (v != sscf->concurrent_streams) { - ngx_http_spdy_send_settings(sc); - } + sc->entries--; - return ngx_http_spdy_state_skip(sc, pos, end); + pos += NGX_SPDY_SETTINGS_PAIR_SIZE; + sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; } - ngx_http_spdy_send_settings(sc); - return ngx_http_spdy_state_complete(sc, pos, end); } +#endif + static u_char * ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos, @@ -1482,7 +1433,7 @@ ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) { ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, "spdy state buffer overflow: " - "%i bytes required", end - pos); + "%z bytes required", end - pos); return ngx_http_spdy_state_internal_error(sc); } #endif @@ -1491,7 +1442,7 @@ ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, sc->buffer_used = end - pos; sc->handler = handler; - sc->waiting = 1; + sc->incomplete = 1; return end; } @@ -1600,7 +1551,6 @@ ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) { u_char *p; ngx_buf_t *buf; - ngx_pool_t *pool; ngx_chain_t *cl; ngx_http_spdy_srv_conf_t *sscf; ngx_http_spdy_out_frame_t *frame; @@ -1608,21 +1558,19 @@ ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy create SETTINGS frame"); - pool = sc->connection->pool; - - frame = ngx_palloc(pool, sizeof(ngx_http_spdy_out_frame_t)); + frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); if (frame == NULL) { return NGX_ERROR; } - cl = ngx_alloc_chain_link(pool); + cl = ngx_alloc_chain_link(sc->pool); if (cl == NULL) { return NGX_ERROR; } - buf = ngx_create_temp_buf(pool, NGX_SPDY_FRAME_HEADER_SIZE - + NGX_SPDY_SETTINGS_NUM_SIZE - + NGX_SPDY_SETTINGS_PAIR_SIZE); + buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE + + NGX_SPDY_SETTINGS_NUM_SIZE + + NGX_SPDY_SETTINGS_PAIR_SIZE); if (buf == NULL) { return NGX_ERROR; } @@ -1635,11 +1583,9 @@ ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) frame->first = cl; frame->last = cl; frame->handler = ngx_http_spdy_settings_frame_handler; -#if (NGX_DEBUG) frame->stream = NULL; - frame->size = NGX_SPDY_FRAME_HEADER_SIZE - + NGX_SPDY_SETTINGS_NUM_SIZE - + NGX_SPDY_SETTINGS_PAIR_SIZE; +#if (NGX_DEBUG) + frame->length = NGX_SPDY_SETTINGS_NUM_SIZE + NGX_SPDY_SETTINGS_PAIR_SIZE; #endif frame->priority = NGX_SPDY_HIGHEST_PRIORITY; frame->blocked = 0; @@ -1653,8 +1599,7 @@ ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) p = ngx_spdy_frame_aligned_write_uint32(p, 1); p = ngx_spdy_frame_aligned_write_uint32(p, - NGX_SPDY_SETTINGS_MAX_STREAMS << 24 - | NGX_SPDY_SETTINGS_FLAG_PERSIST); + NGX_SPDY_SETTINGS_MAX_STREAMS << 24); sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ngx_http_spdy_module); @@ -1688,7 +1633,7 @@ ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc, static ngx_http_spdy_out_frame_t * -ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, +ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length, ngx_uint_t priority) { ngx_chain_t *cl; @@ -1697,7 +1642,7 @@ ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, frame = sc->free_ctl_frames; if (frame) { - sc->free_ctl_frames = frame->free; + sc->free_ctl_frames = frame->next; cl = frame->first; cl->buf->pos = cl->buf->start; @@ -1724,19 +1669,17 @@ ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, frame->first = cl; frame->last = cl; frame->handler = ngx_http_spdy_ctl_frame_handler; + frame->stream = NULL; } - frame->free = NULL; - #if (NGX_DEBUG) - if (size > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { + if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0, - "requested control frame is too big: %z", size); + "requested control frame is too big: %uz", length); return NULL; } - frame->stream = NULL; - frame->size = size; + frame->length = length; #endif frame->priority = priority; @@ -1758,7 +1701,7 @@ ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc, return NGX_AGAIN; } - frame->free = sc->free_ctl_frames; + frame->next = sc->free_ctl_frames; sc->free_ctl_frames = frame; return NGX_OK; @@ -2106,7 +2049,7 @@ ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r) } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "spdy large header alloc: %p %uz", + "spdy large header alloc: %p %z", buf->pos, buf->end - buf->last); old = r->header_in->pos; @@ -2644,9 +2587,21 @@ ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) sc = stream->connection; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, - "spdy close stream %ui, processing %ui", - stream->id, sc->processing); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy close stream %ui, queued %ui, processing %ui", + stream->id, stream->queued, sc->processing); + + if (stream->handled) { + stream->handled = 0; + ngx_queue_remove(&stream->queue); + } + + fc = stream->request->connection; + + if (stream->queued) { + fc->write->handler = ngx_http_spdy_close_stream_handler; + return; + } if (!stream->out_closed) { if (ngx_http_spdy_send_rst_stream(sc, stream->id, @@ -2658,6 +2613,10 @@ ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) } } + if (sc->stream == stream) { + sc->stream = NULL; + } + sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, ngx_http_spdy_module); @@ -2678,8 +2637,6 @@ ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) index = &s->index; } - fc = stream->request->connection; - ngx_http_free_request(stream->request, rc); ev = fc->read; @@ -2847,14 +2804,15 @@ ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, stream = sc->streams_index[i]; while (stream) { - r = stream->request; + stream->handled = 0; + r = stream->request; fc = r->connection; + fc->error = 1; - if (stream->waiting) { - r->blocked -= stream->waiting; - stream->waiting = 0; + if (stream->queued) { + stream->queued = 0; ev = fc->write; ev->delayed = 0; diff --git a/src/http/ngx_http_spdy.h b/src/http/ngx_http_spdy.h index c47243fb0..8caa5b0c4 100644 --- a/src/http/ngx_http_spdy.h +++ b/src/http/ngx_http_spdy.h @@ -96,18 +96,19 @@ struct ngx_http_spdy_connection_s { ngx_http_spdy_stream_t **streams_index; ngx_http_spdy_out_frame_t *last_out; - ngx_http_spdy_stream_t *last_stream; + + ngx_queue_t posted; ngx_http_spdy_stream_t *stream; - ngx_uint_t headers; + ngx_uint_t entries; size_t length; u_char flags; ngx_uint_t last_sid; - unsigned blocked:2; - unsigned waiting:1; /* FIXME better name */ + unsigned blocked:1; + unsigned incomplete:1; }; @@ -116,15 +117,19 @@ struct ngx_http_spdy_stream_s { ngx_http_request_t *request; ngx_http_spdy_connection_t *connection; ngx_http_spdy_stream_t *index; - ngx_http_spdy_stream_t *next; ngx_uint_t header_buffers; - ngx_uint_t waiting; + ngx_uint_t queued; + ngx_http_spdy_out_frame_t *free_frames; ngx_chain_t *free_data_headers; + ngx_chain_t *free_bufs; + + ngx_queue_t queue; unsigned priority:2; unsigned handled:1; + unsigned blocked:1; unsigned in_closed:1; unsigned out_closed:1; unsigned skip_data:2; @@ -138,10 +143,8 @@ struct ngx_http_spdy_out_frame_s { ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); - ngx_http_spdy_out_frame_t *free; - ngx_http_spdy_stream_t *stream; - size_t size; + size_t length; ngx_uint_t priority; unsigned blocked:1; diff --git a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c index 805177651..0ec9028ce 100644 --- a/src/http/ngx_http_spdy_filter_module.c +++ b/src/http/ngx_http_spdy_filter_module.c @@ -14,8 +14,6 @@ #include <zlib.h> -#define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED - #define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1) #define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1) @@ -29,12 +27,18 @@ #define ngx_http_spdy_nv_write_val(p, h) \ ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1) + +static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc, + ngx_chain_t *in, off_t limit); + static ngx_inline ngx_int_t ngx_http_spdy_filter_send( ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); +static ngx_chain_t *ngx_http_spdy_filter_get_shadow( + ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( - ngx_http_spdy_stream_t *stream, size_t len, ngx_uint_t flags, - ngx_chain_t *first, ngx_chain_t *last); + ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first, + ngx_chain_t *last); static ngx_int_t ngx_http_spdy_syn_frame_handler( ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); @@ -82,7 +86,6 @@ ngx_module_t ngx_http_spdy_filter_module = { static ngx_http_output_header_filter_pt ngx_http_next_header_filter; -static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -557,13 +560,14 @@ ngx_http_spdy_header_filter(ngx_http_request_t *r) r->header_size = len; + len -= NGX_SPDY_FRAME_HEADER_SIZE; + if (r->header_only) { b->last_buf = 1; - p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, - len - NGX_SPDY_FRAME_HEADER_SIZE); + p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len); + } else { - p = ngx_spdy_frame_write_flags_and_len(p, 0, - len - NGX_SPDY_FRAME_HEADER_SIZE); + p = ngx_spdy_frame_write_flags_and_len(p, 0, len); } (void) ngx_spdy_frame_write_sid(p, stream->id); @@ -584,21 +588,18 @@ ngx_http_spdy_header_filter(ngx_http_request_t *r) frame->first = cl; frame->last = cl; frame->handler = ngx_http_spdy_syn_frame_handler; - frame->free = NULL; frame->stream = stream; - frame->size = len; + frame->length = len; frame->priority = stream->priority; frame->blocked = 1; frame->fin = r->header_only; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, - "spdy:%ui create SYN_REPLY frame %p: size:%uz", - stream->id, frame, frame->size); + "spdy:%ui create SYN_REPLY frame %p: len:%uz", + stream->id, frame, frame->length); ngx_http_spdy_queue_blocked_frame(sc, frame); - r->blocked++; - cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; @@ -607,109 +608,208 @@ ngx_http_spdy_header_filter(ngx_http_request_t *r) cln->handler = ngx_http_spdy_filter_cleanup; cln->data = stream; - stream->waiting = 1; + stream->queued = 1; + + c->send_chain = ngx_http_spdy_send_chain; + c->need_last_buf = 1; return ngx_http_spdy_filter_send(c, stream); } -static ngx_int_t -ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +static ngx_chain_t * +ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) { - off_t size; - ngx_buf_t *b; - ngx_chain_t *cl, *ll, *out, **ln; + off_t size, offset; + size_t rest, frame_size; + ngx_chain_t *cl, *out, **ln; + ngx_http_request_t *r; ngx_http_spdy_stream_t *stream; + ngx_http_spdy_loc_conf_t *slcf; ngx_http_spdy_out_frame_t *frame; + r = fc->data; stream = r->spdy_stream; - if (stream == NULL) { - return ngx_http_next_body_filter(r, in); - } + if (in == NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "spdy body filter \"%V?%V\"", &r->uri, &r->args); + if (stream->queued) { + fc->write->delayed = 1; + } else { + fc->buffered &= ~NGX_SPDY_BUFFERED; + } - if (in == NULL || r->header_only) { + return NULL; + } - if (stream->waiting) { - return NGX_AGAIN; + size = ngx_buf_size(in->buf); + + if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; } - r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED; + cl->buf = in->buf; + in->buf = cl->buf->shadow; - return NGX_OK; + offset = ngx_buf_in_memory(in->buf) + ? (cl->buf->pos - in->buf->pos) + : (cl->buf->file_pos - in->buf->file_pos); + + cl->next = stream->free_bufs; + stream->free_bufs = cl; + + } else { + offset = 0; } - size = 0; - ln = &out; - ll = in; +#if (NGX_SUPPRESS_WARN) + cl = NULL; +#endif + + slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module); + + frame_size = (limit && limit <= (off_t) slcf->chunk_size) + ? (size_t) limit + : slcf->chunk_size; for ( ;; ) { - b = ll->buf; -#if 1 - if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "zero size buf in spdy body filter " - "t:%d r:%d f:%d %p %p-%p %p %O-%O", - b->temporary, - b->recycled, - b->in_file, - b->start, - b->pos, - b->last, - b->file, - b->file_pos, - b->file_last); - - ngx_debug_point(); - return NGX_ERROR; + ln = &out; + rest = frame_size; + + while ((off_t) rest >= size) { + + if (offset) { + cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, + offset, size); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + offset = 0; + + } else { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf = in->buf; + } + + *ln = cl; + ln = &cl->next; + + rest -= (size_t) size; + in = in->next; + + if (in == NULL) { + frame_size -= rest; + rest = 0; + break; + } + + size = ngx_buf_size(in->buf); } -#endif - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_ERROR; + + if (rest) { + cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, + offset, rest); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf->flush = 0; + cl->buf->last_buf = 0; + + *ln = cl; + + offset += rest; + size -= rest; + } + + frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size, + out, cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; } - size += ngx_buf_size(b); - cl->buf = b; + ngx_http_spdy_queue_frame(stream->connection, frame); - *ln = cl; - ln = &cl->next; + stream->queued++; - if (ll->next == NULL) { + if (in == NULL) { break; } - ll = ll->next; + if (limit) { + limit -= frame_size; + + if (limit == 0) { + break; + } + + if (limit < (off_t) slcf->chunk_size) { + frame_size = (size_t) limit; + } + } } - if (size > NGX_SPDY_MAX_FRAME_SIZE) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "FIXME: chain too big in spdy filter: %O", size); - return NGX_ERROR; + if (offset) { + cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + in->buf = cl->buf; + ngx_free_chain(r->pool, cl); } - frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, - b->last_buf, out, cl); - if (frame == NULL) { - return NGX_ERROR; + if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) { + return NGX_CHAIN_ERROR; } - ngx_http_spdy_queue_frame(stream->connection, frame); + return in; +} + + +static ngx_chain_t * +ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, + off_t offset, off_t size) +{ + ngx_buf_t *chunk; + ngx_chain_t *cl; + + cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); + if (cl == NULL) { + return NULL; + } + + chunk = cl->buf; + + ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); - stream->waiting++; + chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow; + chunk->shadow = buf; - r->main->blocked++; + if (ngx_buf_in_memory(chunk)) { + chunk->pos += offset; + chunk->last = chunk->pos + size; + } + + if (chunk->in_file) { + chunk->file_pos += offset; + chunk->file_last = chunk->file_pos + size; + } - return ngx_http_spdy_filter_send(r->connection, stream); + return cl; } static ngx_http_spdy_out_frame_t * ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, - size_t len, ngx_uint_t fin, ngx_chain_t *first, ngx_chain_t *last) + size_t len, ngx_chain_t *first, ngx_chain_t *last) { u_char *p; ngx_buf_t *buf; @@ -721,7 +821,7 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, frame = stream->free_frames; if (frame) { - stream->free_frames = frame->free; + stream->free_frames = frame->next; } else { frame = ngx_palloc(stream->request->pool, @@ -731,13 +831,13 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, } } - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, - "spdy:%ui create DATA frame %p: len:%uz fin:%ui", - stream->id, frame, len, fin); + flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0; - if (len || fin) { + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + "spdy:%ui create DATA frame %p: len:%uz flags:%ui", + stream->id, frame, len, flags); - flags = fin ? NGX_SPDY_FLAG_FIN : 0; + if (len || flags) { cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_data_headers); @@ -751,7 +851,7 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, p = buf->start; buf->pos = p; - p += sizeof(uint32_t); + p += NGX_SPDY_SID_SIZE; (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); @@ -770,7 +870,7 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, buf->last = p; buf->end = p; - buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module; + buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; buf->memory = 1; } @@ -781,12 +881,11 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, frame->first = first; frame->last = last; frame->handler = ngx_http_spdy_data_frame_handler; - frame->free = NULL; frame->stream = stream; - frame->size = NGX_SPDY_FRAME_HEADER_SIZE + len; + frame->length = len; frame->priority = stream->priority; frame->blocked = 0; - frame->fin = fin; + frame->fin = last->buf->last_buf; return frame; } @@ -795,18 +894,22 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, static ngx_inline ngx_int_t ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream) { + stream->blocked = 1; + if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) { fc->error = 1; return NGX_ERROR; } - if (stream->waiting) { - fc->buffered |= NGX_SPDY_WRITE_BUFFERED; + stream->blocked = 0; + + if (stream->queued) { + fc->buffered |= NGX_SPDY_BUFFERED; fc->write->delayed = 1; return NGX_AGAIN; } - fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED; + fc->buffered &= ~NGX_SPDY_BUFFERED; return NGX_OK; } @@ -844,6 +947,7 @@ static ngx_int_t ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame) { + ngx_buf_t *buf; ngx_chain_t *cl, *ln; ngx_http_spdy_stream_t *stream; @@ -851,7 +955,7 @@ ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, cl = frame->first; - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) { + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) { if (cl->buf->pos != cl->buf->last) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, @@ -874,6 +978,18 @@ ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, } for ( ;; ) { + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { + buf = cl->buf->shadow; + + if (ngx_buf_in_memory(buf)) { + buf->pos = cl->buf->pos; + } + + if (buf->in_file) { + buf->file_pos = cl->buf->file_pos; + } + } + if (ngx_buf_size(cl->buf) != 0) { if (cl != frame->first) { @@ -890,7 +1006,13 @@ ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, ln = cl->next; - ngx_free_chain(stream->request->pool, cl); + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { + cl->next = stream->free_bufs; + stream->free_bufs = cl; + + } else { + ngx_free_chain(stream->request->pool, cl); + } if (cl == frame->last) { goto done; @@ -922,17 +1044,16 @@ ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream, r = stream->request; - r->connection->sent += frame->size; - r->blocked--; + r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length; if (frame->fin) { stream->out_closed = 1; } - frame->free = stream->free_frames; + frame->next = stream->free_frames; stream->free_frames = frame; - stream->waiting--; + stream->queued--; } @@ -940,21 +1061,19 @@ static ngx_inline void ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream) { - ngx_connection_t *fc; - - fc = stream->request->connection; + ngx_event_t *wev; - fc->write->delayed = 0; - - if (stream->handled) { + if (stream->handled || stream->blocked) { return; } - if (sc->blocked == 2) { - stream->handled = 1; + wev = stream->request->connection->write; + + if (!wev->timer_set) { + wev->delayed = 0; - stream->next = sc->last_stream; - sc->last_stream = stream; + stream->handled = 1; + ngx_queue_insert_tail(&sc->posted, &stream->queue); } } @@ -964,15 +1083,12 @@ ngx_http_spdy_filter_cleanup(void *data) { ngx_http_spdy_stream_t *stream = data; - ngx_http_request_t *r; ngx_http_spdy_out_frame_t *frame, **fn; - if (stream->waiting == 0) { + if (stream->queued == 0) { return; } - r = stream->request; - fn = &stream->connection->last_out; for ( ;; ) { @@ -983,9 +1099,7 @@ ngx_http_spdy_filter_cleanup(void *data) } if (frame->stream == stream && !frame->blocked) { - - stream->waiting--; - r->blocked--; + stream->queued--; *fn = frame->next; continue; @@ -1002,8 +1116,5 @@ ngx_http_spdy_filter_init(ngx_conf_t *cf) ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_spdy_header_filter; - ngx_http_next_body_filter = ngx_http_top_body_filter; - ngx_http_top_body_filter = ngx_http_spdy_body_filter; - return NGX_OK; } diff --git a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c index 7f02a18ca..c706e3fe6 100644 --- a/src/http/ngx_http_spdy_module.c +++ b/src/http/ngx_http_spdy_module.c @@ -22,16 +22,19 @@ static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle); static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf); static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf); - static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf); static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); +static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data); static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = { @@ -44,6 +47,8 @@ static ngx_conf_post_t ngx_http_spdy_pool_size_post = { ngx_http_spdy_pool_size }; static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post = { ngx_http_spdy_streams_index_mask }; +static ngx_conf_post_t ngx_http_spdy_chunk_size_post = + { ngx_http_spdy_chunk_size }; static ngx_command_t ngx_http_spdy_commands[] = { @@ -97,6 +102,13 @@ static ngx_command_t ngx_http_spdy_commands[] = { offsetof(ngx_http_spdy_srv_conf_t, headers_comp), &ngx_http_spdy_headers_comp_bounds }, + { ngx_string("spdy_chunk_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_spdy_loc_conf_t, chunk_size), + &ngx_http_spdy_chunk_size_post }, + ngx_null_command }; @@ -111,8 +123,8 @@ static ngx_http_module_t ngx_http_spdy_module_ctx = { ngx_http_spdy_create_srv_conf, /* create server configuration */ ngx_http_spdy_merge_srv_conf, /* merge server configuration */ - NULL, /* create location configuration */ - NULL /* merge location configuration */ + ngx_http_spdy_create_loc_conf, /* create location configuration */ + ngx_http_spdy_merge_loc_conf /* merge location configuration */ }; @@ -296,6 +308,34 @@ ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } +static void * +ngx_http_spdy_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_spdy_loc_conf_t *slcf; + + slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t)); + if (slcf == NULL) { + return NULL; + } + + slcf->chunk_size = NGX_CONF_UNSET_SIZE; + + return slcf; +} + + +static char * +ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_spdy_loc_conf_t *prev = parent; + ngx_http_spdy_loc_conf_t *conf = child; + + ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024); + + return NGX_CONF_OK; +} + + static char * ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) { @@ -349,3 +389,22 @@ ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data) return NGX_CONF_OK; } + + +static char * +ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the spdy chunk size cannot be zero"); + return NGX_CONF_ERROR; + } + + if (*sp > NGX_SPDY_MAX_FRAME_SIZE) { + *sp = NGX_SPDY_MAX_FRAME_SIZE; + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_spdy_module.h b/src/http/ngx_http_spdy_module.h index 97a7a13a6..5242322b3 100644 --- a/src/http/ngx_http_spdy_module.h +++ b/src/http/ngx_http_spdy_module.h @@ -30,6 +30,11 @@ typedef struct { } ngx_http_spdy_srv_conf_t; +typedef struct { + size_t chunk_size; +} ngx_http_spdy_loc_conf_t; + + extern ngx_module_t ngx_http_spdy_module; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 8bf0ba218..0524144f2 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1994,7 +1994,7 @@ ngx_http_upstream_test_connect(ngx_connection_t *c) static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ngx_str_t *uri, args; + ngx_str_t uri, args; ngx_uint_t i, flags; ngx_list_part_t *part; ngx_table_elt_t *h; @@ -2035,11 +2035,11 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) } } - uri = &u->headers_in.x_accel_redirect->value; + uri = u->headers_in.x_accel_redirect->value; ngx_str_null(&args); flags = NGX_HTTP_LOG_UNSAFE; - if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { + if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_DONE; } @@ -2048,7 +2048,7 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) r->method = NGX_HTTP_GET; } - ngx_http_internal_redirect(r, uri, &args); + ngx_http_internal_redirect(r, &uri, &args); ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; } @@ -2560,11 +2560,7 @@ ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u) ngx_http_upstream_process_upgraded(r, 1, 1); } - if (c->read->ready - || r->header_in->pos != r->header_in->last) - { - ngx_http_upstream_process_upgraded(r, 0, 1); - } + ngx_http_upstream_process_upgraded(r, 0, 1); } @@ -3656,7 +3652,7 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r, return NGX_OK; } - if (r->cache->valid_sec != 0) { + if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) { return NGX_OK; } diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c index f74dbc8ab..83cb1fa1e 100644 --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -184,7 +184,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_AGAIN; } - if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) { + if (size == 0 + && !(c->buffered & NGX_LOWLEVEL_BUFFERED) + && !(last && c->need_last_buf)) + { if (last || flush) { for (cl = r->out; cl; /* void */) { ln = cl; diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c index 02222c122..41cbcf6e3 100644 --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -542,17 +542,40 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev) CRLF) - 1 + s->connection->addr_text.len + s->login.len + s->host.len; +#if (NGX_HAVE_INET6) + if (s->connection->sockaddr->sa_family == AF_INET6) { + line.len += sizeof("IPV6:") - 1; + } +#endif + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; } - line.len = ngx_sprintf(line.data, - "XCLIENT ADDR=%V%s%V NAME=%V" CRLF, - &s->connection->addr_text, - (s->login.len ? " LOGIN=" : ""), &s->login, &s->host) - - line.data; + p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1); + +#if (NGX_HAVE_INET6) + if (s->connection->sockaddr->sa_family == AF_INET6) { + p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1); + } +#endif + + p = ngx_copy(p, s->connection->addr_text.data, + s->connection->addr_text.len); + + if (s->login.len) { + p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); + p = ngx_copy(p, s->login.data, s->login.len); + } + + p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1); + p = ngx_copy(p, s->host.data, s->host.len); + + *p++ = CR; *p++ = LF; + + line.len = p - line.data; if (s->smtp_helo.len) { s->mail_state = ngx_smtp_xclient_helo; diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 94c015700..fe88f48e4 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -116,6 +116,13 @@ static ngx_command_t ngx_mail_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_session_tickets"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, session_tickets), + NULL }, + { ngx_string("ssl_session_ticket_key"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, @@ -191,6 +198,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->builtin_session_cache = NGX_CONF_UNSET; scf->session_timeout = NGX_CONF_UNSET; + scf->session_tickets = NGX_CONF_UNSET; scf->session_ticket_keys = NGX_CONF_UNSET_PTR; return scf; @@ -339,6 +347,15 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + ngx_conf_merge_value(conf->session_tickets, + prev->session_tickets, 1); + +#ifdef SSL_OP_NO_TICKET + if (!conf->session_tickets) { + SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET); + } +#endif + ngx_conf_merge_ptr_value(conf->session_ticket_keys, prev->session_ticket_keys, NULL); diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h index 54e057721..bef0e515a 100644 --- a/src/mail/ngx_mail_ssl_module.h +++ b/src/mail/ngx_mail_ssl_module.h @@ -41,6 +41,7 @@ typedef struct { ngx_shm_zone_t *shm_zone; + ngx_flag_t session_tickets; ngx_array_t *session_ticket_keys; u_char *file; diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c index 9d23a73d0..499039a7e 100644 --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -959,6 +959,8 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) "sigprocmask() failed"); } + srandom((ngx_pid << 16) ^ ngx_time()); + /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point |