diff -Nru nginx-1.10.1/CHANGES nginx-1.10.3/CHANGES --- nginx-1.10.1/CHANGES 2016-05-31 09:47:07.000000000 -0400 +++ nginx-1.10.3/CHANGES 2017-01-31 10:01:17.000000000 -0500 @@ -1,4 +1,65 @@ +Changes with nginx 1.10.3 31 Jan 2017 + + *) Bugfix: in the "add_after_body" directive when used with the + "sub_filter" directive. + + *) Bugfix: unix domain listen sockets might not be inherited during + binary upgrade on Linux. + + *) Bugfix: graceful shutdown of old worker processes might require + infinite time when using HTTP/2. + + *) Bugfix: when using HTTP/2 and the "limit_req" or "auth_request" + directives client request body might be corrupted; the bug had + appeared in 1.10.2. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2; the bug had appeared in 1.10.2. + + *) Bugfix: an incorrect response might be returned when using the + "sendfile" directive on FreeBSD and macOS; the bug had appeared in + 1.7.8. + + *) Bugfix: a truncated response might be stored in cache when using the + "aio_write" directive. + + *) Bugfix: a socket leak might occur when using the "aio_write" + directive. + + +Changes with nginx 1.10.2 18 Oct 2016 + + *) Change: the "421 Misdirected Request" response now used when + rejecting requests to a virtual server different from one negotiated + during an SSL handshake; this improves interoperability with some + HTTP/2 clients when using client certificates. + + *) Change: HTTP/2 clients can now start sending request body + immediately; the "http2_body_preread_size" directive controls size of + the buffer used before nginx will start reading client request body. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2 and the "proxy_request_buffering" directive. + + *) Bugfix: the "Content-Length" request header line was always added to + requests passed to backends, including requests without body, when + using HTTP/2. + + *) Bugfix: "http request count is zero" alerts might appear in logs when + using HTTP/2. + + *) Bugfix: unnecessary buffering might occur when using the "sub_filter" + directive; the issue had appeared in 1.9.4. + + *) Bugfix: socket leak when using HTTP/2. + + *) Bugfix: an incorrect response might be returned when using the "aio + threads" and "sendfile" directives; the bug had appeared in 1.9.13. + + *) Workaround: OpenSSL 1.1.0 compatibility. + + Changes with nginx 1.10.1 31 May 2016 *) Security: a segmentation fault might occur in a worker process while diff -Nru nginx-1.10.1/CHANGES.ru nginx-1.10.3/CHANGES.ru --- nginx-1.10.1/CHANGES.ru 2016-05-31 09:47:05.000000000 -0400 +++ nginx-1.10.3/CHANGES.ru 2017-01-31 10:01:15.000000000 -0500 @@ -1,4 +1,66 @@ +Изменения в nginx 1.10.3 31.01.2017 + + *) Исправление: в директиве add_after_body при использовании совместно с + директивой sub_filter. + + *) Исправление: unix domain listen-сокеты могли не наследоваться при + обновлении исполняемого файла на Linux. + + *) Исправление: плавное завершение старых рабочих процессов могло + занимать бесконечное время при использовании HTTP/2. + + *) Исправление: при использовании HTTP/2 и директив limit_req или + auth_request тело запроса могло быть повреждено; ошибка появилась в + 1.10.2. + + *) Исправление: при использовании HTTP/2 в рабочем процессе мог + произойти segmentation fault; ошибка появилась в 1.10.2. + + *) Исправление: при использовании директивы sendfile на FreeBSD и macOS + мог возвращаться некорректный ответ; ошибка появилась в 1.7.8. + + *) Исправление: при использовании директивы aio_write ответ мог + сохраняться в кэш не полностью. + + *) Исправление: при использовании директивы aio_write могла происходить + утечка сокетов. + + +Изменения в nginx 1.10.2 18.10.2016 + + *) Изменение: при попытке запросить виртуальный сервер, отличающийся от + согласованного в процессе SSL handshake, теперь возвращается ответ + "421 Misdirected Request"; это улучшает совместимость с некоторыми + HTTP/2-клиентами в случае использования клиентских сертификатов. + + *) Изменение: HTTP/2-клиенты теперь могут сразу присылать тело запроса; + директива http2_body_preread_size позволяет указать размер буфера, + который будет использоваться до того, как nginx начнёт читать тело. + + *) Исправление: при использовании HTTP/2 и директивы + proxy_request_buffering в рабочем процессе мог произойти segmentation + fault. + + *) Исправление: при использовании HTTP/2 к запросам, передаваемым на + бэкенд, всегда добавлялась строка заголовка "Content-Length", даже + если у запроса не было тела. + + *) Исправление: при использовании HTTP/2 в логах могли появляться + сообщения "http request count is zero". + + *) Исправление: при использовании директивы sub_filter могло + буферизироваться больше данных, чем это необходимо; проблема + появилась в 1.9.4. + + *) Исправление: утечки сокетов при использовании HTTP/2. + + *) Исправление: при использовании директив "aio threads" и sendfile мог + возвращаться некорректный ответ; ошибка появилась в 1.9.13. + + *) Изменение: совместимость с OpenSSL 1.1.0. + + Изменения в nginx 1.10.1 31.05.2016 *) Безопасность: при записи тела специально созданного запроса во diff -Nru nginx-1.10.1/debian/changelog nginx-1.10.3/debian/changelog --- nginx-1.10.1/debian/changelog 2016-10-27 10:14:26.000000000 -0400 +++ nginx-1.10.3/debian/changelog 2017-02-11 16:26:40.000000000 -0500 @@ -1,3 +1,15 @@ +nginx (1.10.3-0ubuntu0.16.10.1) yakkety; urgency=medium + + * Stable Release Update (LP: #1663937) + * New upstream release (1.10.3) - full changelog available at upstream + website - http://nginx.org/en/CHANGES-1.10 + * All Ubuntu specific changes from 1.10.1-0ubuntu1 through 1.10.1-0ubuntu1.2 + remain included. + * Additional changes: + * debian/patches/ubuntu-branding.patch: Refreshed Ubuntu Branding patch. + + -- Thomas Ward Sat, 11 Feb 2017 16:18:21 -0500 + nginx (1.10.1-0ubuntu1.2) yakkety-security; urgency=medium * SECURITY REGRESSION: postinst upgrade failure (LP: #1637058) diff -Nru nginx-1.10.1/debian/patches/ubuntu-branding.patch nginx-1.10.3/debian/patches/ubuntu-branding.patch --- nginx-1.10.1/debian/patches/ubuntu-branding.patch 2016-05-31 19:15:58.000000000 -0400 +++ nginx-1.10.3/debian/patches/ubuntu-branding.patch 2017-02-11 16:21:19.000000000 -0500 @@ -1,7 +1,7 @@ Description: Add Ubuntu token to NGINX_VER Author: Adam Conrad Forwarded: not-needed -Last-Update: 2016-05-31 +Last-Update: 2017-02-11 Index: b/src/core/nginx.h =================================================================== @@ -9,8 +9,8 @@ +++ b/src/core/nginx.h @@ -11,7 +11,7 @@ - #define nginx_version 1010001 - #define NGINX_VERSION "1.10.1" + #define nginx_version 1010003 + #define NGINX_VERSION "1.10.3" -#define NGINX_VER "nginx/" NGINX_VERSION +#define NGINX_VER "nginx/" NGINX_VERSION " (Ubuntu)" diff -Nru nginx-1.10.1/src/core/nginx.h nginx-1.10.3/src/core/nginx.h --- nginx-1.10.1/src/core/nginx.h 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/core/nginx.h 2017-01-31 10:01:11.000000000 -0500 @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1010001 -#define NGINX_VERSION "1.10.1" +#define nginx_version 1010003 +#define NGINX_VERSION "1.10.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff -Nru nginx-1.10.1/src/core/ngx_buf.c nginx-1.10.3/src/core/ngx_buf.c --- nginx-1.10.1/src/core/ngx_buf.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/core/ngx_buf.c 2017-01-31 10:01:11.000000000 -0500 @@ -244,6 +244,9 @@ if (aligned <= cl->buf->file_last) { size = aligned - cl->buf->file_pos; } + + total += size; + break; } total += size; diff -Nru nginx-1.10.1/src/core/ngx_inet.c nginx-1.10.3/src/core/ngx_inet.c --- nginx-1.10.1/src/core/ngx_inet.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/core/ngx_inet.c 2017-01-31 10:01:11.000000000 -0500 @@ -1213,6 +1213,7 @@ struct sockaddr_in6 *sin61, *sin62; #endif #if (NGX_HAVE_UNIX_DOMAIN) + size_t len; struct sockaddr_un *saun1, *saun2; #endif @@ -1242,15 +1243,21 @@ #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: - /* TODO length */ - saun1 = (struct sockaddr_un *) sa1; saun2 = (struct sockaddr_un *) sa2; - if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, - sizeof(saun1->sun_path)) - != 0) - { + if (slen1 < slen2) { + len = slen1 - offsetof(struct sockaddr_un, sun_path); + + } else { + len = slen2 - offsetof(struct sockaddr_un, sun_path); + } + + if (len > sizeof(saun1->sun_path)) { + len = sizeof(saun1->sun_path); + } + + if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, len) != 0) { return NGX_DECLINED; } diff -Nru nginx-1.10.1/src/event/ngx_event_openssl.c nginx-1.10.3/src/event/ngx_event_openssl.c --- nginx-1.10.1/src/event/ngx_event_openssl.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/event/ngx_event_openssl.c 2017-01-31 10:01:11.000000000 -0500 @@ -951,6 +951,8 @@ return NGX_ERROR; } +#if OPENSSL_VERSION_NUMBER < 0x10100005L + dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); @@ -960,6 +962,23 @@ return NGX_ERROR; } +#else + { + BIGNUM *p, *g; + + p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); + g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); + + if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed"); + DH_free(dh); + BN_free(p); + BN_free(g); + return NGX_ERROR; + } + } +#endif + SSL_CTX_set_tmp_dh(ssl->ctx, dh); DH_free(dh); @@ -1938,7 +1957,9 @@ || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */ || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */ || n == SSL_R_LENGTH_MISMATCH /* 159 */ +#ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ +#endif || n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */ || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */ || n == SSL_R_NO_SHARED_CIPHER /* 193 */ @@ -2898,7 +2919,7 @@ ngx_ssl_session_ticket_md(), NULL); ngx_memcpy(name, key[0].name, 16); - return 0; + return 1; } else { /* decrypt session ticket */ diff -Nru nginx-1.10.1/src/event/ngx_event_pipe.c nginx-1.10.3/src/event/ngx_event_pipe.c --- nginx-1.10.1/src/event/ngx_event_pipe.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/event/ngx_event_pipe.c 2017-01-31 10:01:11.000000000 -0500 @@ -113,11 +113,24 @@ } #if (NGX_THREADS) + if (p->aio) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe read upstream: aio"); return NGX_AGAIN; } + + if (p->writing) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe read upstream: writing"); + + rc = ngx_event_pipe_write_chain_to_temp_file(p); + + if (rc != NGX_OK) { + return rc; + } + } + #endif ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, @@ -815,10 +828,12 @@ } #if (NGX_THREADS) - p->temp_file->thread_write = p->thread_handler ? 1 : 0; - p->temp_file->file.thread_task = p->thread_task; - p->temp_file->file.thread_handler = p->thread_handler; - p->temp_file->file.thread_ctx = p->thread_ctx; + if (p->thread_handler) { + p->temp_file->thread_write = 1; + p->temp_file->file.thread_task = p->thread_task; + p->temp_file->file.thread_handler = p->thread_handler; + p->temp_file->file.thread_ctx = p->thread_ctx; + } #endif n = ngx_write_chain_to_temp_file(p->temp_file, out); diff -Nru nginx-1.10.1/src/http/modules/ngx_http_addition_filter_module.c nginx-1.10.3/src/http/modules/ngx_http_addition_filter_module.c --- nginx-1.10.1/src/http/modules/ngx_http_addition_filter_module.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/modules/ngx_http_addition_filter_module.c 2017-01-31 10:01:11.000000000 -0500 @@ -171,6 +171,7 @@ for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { cl->buf->last_buf = 0; + cl->buf->last_in_chain = 1; cl->buf->sync = 1; last = 1; } diff -Nru nginx-1.10.1/src/http/modules/ngx_http_sub_filter_module.c nginx-1.10.3/src/http/modules/ngx_http_sub_filter_module.c --- nginx-1.10.1/src/http/modules/ngx_http_sub_filter_module.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/modules/ngx_http_sub_filter_module.c 2017-01-31 10:01:11.000000000 -0500 @@ -83,7 +83,9 @@ static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx); static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, - ngx_http_sub_ctx_t *ctx); + ngx_http_sub_ctx_t *ctx, ngx_uint_t flush); +static ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, + ngx_str_t *m); static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -285,6 +287,7 @@ ngx_int_t rc; ngx_buf_t *b; ngx_str_t *sub; + ngx_uint_t flush, last; ngx_chain_t *cl; ngx_http_sub_ctx_t *ctx; ngx_http_sub_match_t *match; @@ -326,6 +329,9 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http sub filter \"%V\"", &r->uri); + flush = 0; + last = 0; + while (ctx->in || ctx->buf) { if (ctx->buf == NULL) { @@ -334,11 +340,19 @@ ctx->pos = ctx->buf->pos; } + if (ctx->buf->flush || ctx->buf->recycled) { + flush = 1; + } + + if (ctx->in == NULL) { + last = flush; + } + b = NULL; while (ctx->pos < ctx->buf->last) { - rc = ngx_http_sub_parse(r, ctx); + rc = ngx_http_sub_parse(r, ctx, last); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "parse: %i, looked: \"%V\" %p-%p", @@ -590,9 +604,10 @@ static ngx_int_t -ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) +ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx, + ngx_uint_t flush) { - u_char *p, *last, *pat, *pat_end, c; + u_char *p, c; ngx_str_t *m; ngx_int_t offset, start, next, end, len, rc; ngx_uint_t shift, i, j; @@ -602,6 +617,7 @@ slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); tables = ctx->tables; + match = ctx->matches->elts; offset = ctx->offset; end = ctx->buf->last - ctx->pos; @@ -628,7 +644,6 @@ /* a potential match */ start = offset - (ngx_int_t) tables->min_match_len + 1; - match = ctx->matches->elts; i = ngx_max(tables->index[c], ctx->index); j = tables->index[c + 1]; @@ -641,41 +656,15 @@ m = &match[i].match; - pat = m->data; - pat_end = m->data + m->len; - - if (start >= 0) { - p = ctx->pos + start; + rc = ngx_http_sub_match(ctx, start, m); - } else { - last = ctx->looked.data + ctx->looked.len; - p = last + start; - - while (p < last && pat < pat_end) { - if (ngx_tolower(*p) != *pat) { - goto next; - } - - p++; - pat++; - } - - p = ctx->pos; - } - - while (p < ctx->buf->last && pat < pat_end) { - if (ngx_tolower(*p) != *pat) { - goto next; - } - - p++; - pat++; + if (rc == NGX_DECLINED) { + goto next; } ctx->index = i; - if (pat != pat_end) { - /* partial match */ + if (rc == NGX_AGAIN) { goto again; } @@ -695,6 +684,26 @@ ctx->index = 0; } + if (flush) { + for ( ;; ) { + start = offset - (ngx_int_t) tables->min_match_len + 1; + + if (start >= end) { + break; + } + + for (i = 0; i < ctx->matches->nelts; i++) { + m = &match[i].match; + + if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) { + goto again; + } + } + + offset++; + } + } + again: ctx->offset = offset; @@ -731,6 +740,51 @@ } +static ngx_int_t +ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m) +{ + u_char *p, *last, *pat, *pat_end; + + pat = m->data; + pat_end = m->data + m->len; + + if (start >= 0) { + p = ctx->pos + start; + + } else { + last = ctx->looked.data + ctx->looked.len; + p = last + start; + + while (p < last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + p = ctx->pos; + } + + while (p < ctx->buf->last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + if (pat != pat_end) { + /* partial match */ + return NGX_AGAIN; + } + + return NGX_OK; +} + + static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { diff -Nru nginx-1.10.1/src/http/ngx_http_header_filter_module.c nginx-1.10.3/src/http/ngx_http_header_filter_module.c --- nginx-1.10.1/src/http/ngx_http_header_filter_module.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/ngx_http_header_filter_module.c 2017-01-31 10:01:11.000000000 -0500 @@ -95,17 +95,17 @@ ngx_string("414 Request-URI Too Large"), ngx_string("415 Unsupported Media Type"), ngx_string("416 Requested Range Not Satisfiable"), + ngx_null_string, /* "417 Expectation Failed" */ + ngx_null_string, /* "418 unused" */ + ngx_null_string, /* "419 unused" */ + ngx_null_string, /* "420 unused" */ + ngx_string("421 Misdirected Request"), - /* ngx_null_string, */ /* "417 Expectation Failed" */ - /* ngx_null_string, */ /* "418 unused" */ - /* ngx_null_string, */ /* "419 unused" */ - /* ngx_null_string, */ /* "420 unused" */ - /* ngx_null_string, */ /* "421 unused" */ /* ngx_null_string, */ /* "422 Unprocessable Entity" */ /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ -#define NGX_HTTP_LAST_4XX 417 +#define NGX_HTTP_LAST_4XX 422 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string("500 Internal Server Error"), @@ -113,10 +113,10 @@ ngx_string("502 Bad Gateway"), ngx_string("503 Service Temporarily Unavailable"), ngx_string("504 Gateway Time-out"), - ngx_null_string, /* "505 HTTP Version Not Supported" */ ngx_null_string, /* "506 Variant Also Negotiates" */ ngx_string("507 Insufficient Storage"), + /* ngx_null_string, */ /* "508 unused" */ /* ngx_null_string, */ /* "509 unused" */ /* ngx_null_string, */ /* "510 Not Extended" */ diff -Nru nginx-1.10.1/src/http/ngx_http_request.c nginx-1.10.3/src/http/ngx_http_request.c --- nginx-1.10.1/src/http/ngx_http_request.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/ngx_http_request.c 2017-01-31 10:01:11.000000000 -0500 @@ -2065,7 +2065,7 @@ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client attempted to request the server name " "different from that one was negotiated"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); return NGX_ERROR; } } diff -Nru nginx-1.10.1/src/http/ngx_http_request.h nginx-1.10.3/src/http/ngx_http_request.h --- nginx-1.10.1/src/http/ngx_http_request.h 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/ngx_http_request.h 2017-01-31 10:01:11.000000000 -0500 @@ -95,6 +95,7 @@ #define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 +#define NGX_HTTP_MISDIRECTED_REQUEST 421 /* Our own HTTP codes */ diff -Nru nginx-1.10.1/src/http/ngx_http_special_response.c nginx-1.10.3/src/http/ngx_http_special_response.c --- nginx-1.10.1/src/http/ngx_http_special_response.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/ngx_http_special_response.c 2017-01-31 10:01:11.000000000 -0500 @@ -210,6 +210,14 @@ ; +static char ngx_http_error_421_page[] = +"" CRLF +"421 Misdirected Request" CRLF +"" CRLF +"

421 Misdirected Request

" CRLF +; + + static char ngx_http_error_494_page[] = "" CRLF "400 Request Header Or Cookie Too Large" @@ -334,8 +342,13 @@ ngx_string(ngx_http_error_414_page), ngx_string(ngx_http_error_415_page), ngx_string(ngx_http_error_416_page), + ngx_null_string, /* 417 */ + ngx_null_string, /* 418 */ + ngx_null_string, /* 419 */ + ngx_null_string, /* 420 */ + ngx_string(ngx_http_error_421_page), -#define NGX_HTTP_LAST_4XX 417 +#define NGX_HTTP_LAST_4XX 422 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string(ngx_http_error_494_page), /* 494, request header too large */ diff -Nru nginx-1.10.1/src/http/ngx_http_upstream.c nginx-1.10.3/src/http/ngx_http_upstream.c --- nginx-1.10.1/src/http/ngx_http_upstream.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/ngx_http_upstream.c 2017-01-31 10:01:12.000000000 -0500 @@ -3744,9 +3744,24 @@ p = u->pipe; #if (NGX_THREADS) + + if (p->writing && !p->aio) { + + /* + * make sure to call ngx_event_pipe() + * if there is an incomplete aio write + */ + + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + if (p->writing) { return; } + #endif if (u->peer.connection) { diff -Nru nginx-1.10.1/src/http/v2/ngx_http_v2.c nginx-1.10.3/src/http/v2/ngx_http_v2.c --- nginx-1.10.1/src/http/v2/ngx_http_v2.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/v2/ngx_http_v2.c 2017-01-31 10:01:12.000000000 -0500 @@ -48,11 +48,6 @@ #define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) -#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) -#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 - -#define NGX_HTTP_V2_INITIAL_WINDOW 0 - #define NGX_HTTP_V2_ROOT (void *) -1 @@ -141,6 +136,8 @@ ngx_uint_t sid, size_t window); static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t status); +static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame( ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type, @@ -289,7 +286,6 @@ : ngx_http_v2_state_preface; ngx_queue_init(&h2c->waiting); - ngx_queue_init(&h2c->posted); ngx_queue_init(&h2c->dependencies); ngx_queue_init(&h2c->closed); @@ -298,6 +294,8 @@ rev->handler = ngx_http_v2_read_handler; c->write->handler = ngx_http_v2_write_handler; + c->idle = 1; + ngx_http_v2_read_handler(rev); } @@ -325,6 +323,25 @@ h2c->blocked = 1; + if (c->close) { + c->close = 0; + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + h2c->blocked = 0; + + return; + } + h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); @@ -397,9 +414,7 @@ ngx_http_v2_write_handler(ngx_event_t *wev) { ngx_int_t rc; - ngx_queue_t *q; ngx_connection_t *c; - ngx_http_v2_stream_t *stream; ngx_http_v2_connection_t *h2c; c = wev->data; @@ -415,33 +430,23 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write handler"); - h2c->blocked = 1; + if (h2c->last_out == NULL && !c->buffered) { - rc = ngx_http_v2_send_output_queue(h2c); + if (wev->timer_set) { + ngx_del_timer(wev); + } - if (rc == NGX_ERROR) { - ngx_http_v2_finalize_connection(h2c, 0); + ngx_http_v2_handle_connection(h2c); return; } - while (!ngx_queue_empty(&h2c->posted)) { - q = ngx_queue_head(&h2c->posted); - - ngx_queue_remove(q); - - stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - - stream->handled = 0; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "run http2 stream %ui", stream->node->id); - - wev = stream->request->connection->write; + h2c->blocked = 1; - wev->active = 0; - wev->ready = 1; + rc = ngx_http_v2_send_output_queue(h2c); - wev->handler(wev); + if (rc == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; } h2c->blocked = 0; @@ -473,7 +478,7 @@ wev = c->write; if (!wev->ready) { - return NGX_OK; + return NGX_AGAIN; } cl = NULL; @@ -544,15 +549,6 @@ c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - if (cl) { - ngx_add_timer(wev, clcf->send_timeout); - - } else { - if (wev->timer_set) { - ngx_del_timer(wev); - } - } - for ( /* void */ ; out; out = fn) { fn = out->next; @@ -577,6 +573,15 @@ h2c->last_out = frame; + if (!wev->ready) { + ngx_add_timer(wev, clcf->send_timeout); + return NGX_AGAIN; + } + + if (wev->timer_set) { + ngx_del_timer(wev); + } + return NGX_OK; error: @@ -594,7 +599,8 @@ static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) { - ngx_connection_t *c; + ngx_int_t rc; + ngx_connection_t *c; ngx_http_v2_srv_conf_t *h2scf; if (h2c->last_out || h2c->processing) { @@ -609,6 +615,26 @@ } if (c->buffered) { + h2c->blocked = 1; + + rc = ngx_http_v2_send_output_queue(h2c); + + h2c->blocked = 0; + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_AGAIN) { + return; + } + + /* rc == NGX_OK */ + } + + if (h2c->goaway) { + ngx_http_close_connection(c); return; } @@ -619,11 +645,6 @@ return; } - if (ngx_terminate || ngx_exiting) { - ngx_http_close_connection(c); - return; - } - ngx_destroy_pool(h2c->pool); h2c->pool = NULL; @@ -637,7 +658,6 @@ #endif c->destroyed = 1; - c->idle = 1; ngx_reusable_connection(c, 1); c->write->handler = ngx_http_empty_handler; @@ -879,8 +899,6 @@ return ngx_http_v2_state_skip_padded(h2c, pos, end); } - stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; - h2c->state.stream = stream; return ngx_http_v2_state_read_data(h2c, pos, end); @@ -891,10 +909,12 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t size; - ngx_int_t rc; - ngx_uint_t last; - ngx_http_v2_stream_t *stream; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; stream = h2c->state.stream; @@ -913,17 +933,42 @@ if (size >= h2c->state.length) { size = h2c->state.length; - last = stream->in_closed; - - } else { - last = 0; + stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; } - rc = ngx_http_v2_process_request_body(stream->request, pos, size, last); + r = stream->request; - if (rc != NGX_OK) { - stream->skip_data = 1; - ngx_http_finalize_request(stream->request, rc); + if (r->request_body) { + rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); + + if (rc != NGX_OK) { + stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + } + + } else if (size) { + buf = stream->preread; + + if (buf == NULL) { + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + buf = ngx_create_temp_buf(r->pool, h2scf->preread_size); + if (buf == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->preread = buf; + } + + if (size > (size_t) (buf->end - buf->last)) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "http2 preread buffer overflow"); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + buf->last = ngx_cpymem(buf->last, pos, size); } pos += size; @@ -981,6 +1026,12 @@ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + if (h2c->goaway) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 HEADERS frame"); + return ngx_http_v2_state_skip(h2c, pos, end); + } + if ((size_t) (end - pos) < size) { return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_headers); @@ -1058,7 +1109,9 @@ goto rst_stream; } - if (!h2c->settings_ack && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)) + if (!h2c->settings_ack + && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) + && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client sent stream with data " @@ -2164,7 +2217,7 @@ stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - stream->handled = 0; + stream->waiting = 0; wev = stream->request->connection->write; @@ -2434,8 +2487,7 @@ buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - NGX_HTTP_V2_INITIAL_WINDOW); + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); @@ -2643,6 +2695,7 @@ ngx_http_log_ctx_t *ctx; ngx_http_request_t *r; ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; ngx_http_core_srv_conf_t *cscf; fc = h2c->free_fake_connections; @@ -2756,8 +2809,10 @@ stream->request = r; stream->connection = h2c; + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + stream->send_window = h2c->init_window; - stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW; + stream->recv_window = h2scf->preread_size; h2c->processing++; @@ -3400,7 +3455,9 @@ return; } - r->headers_in.chunked = (r->headers_in.content_length_n == -1); + if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) { + r->headers_in.chunked = 1; + } ngx_http_process_request(r); } @@ -3411,7 +3468,11 @@ ngx_http_client_body_handler_pt post_handler) { off_t len; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; ngx_http_v2_connection_t *h2c; @@ -3444,28 +3505,43 @@ r->request_body = rb; + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); len = r->headers_in.content_length_n; if (r->request_body_no_buffering && !stream->in_closed) { - r->request_body_in_file_only = 0; if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { len = clcf->client_body_buffer_size; } + /* + * We need a room to store data up to the stream's initial window size, + * at least until this window will be exhausted. + */ + + if (len < (off_t) h2scf->preread_size) { + len = h2scf->preread_size; + } + if (len > NGX_HTTP_V2_MAX_WINDOW) { len = NGX_HTTP_V2_MAX_WINDOW; } - } - if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size - && !r->request_body_in_file_only) + rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); + + } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size + && !r->request_body_in_file_only) { rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); } else { + if (stream->preread) { + /* enforce writing preread buffer to file */ + r->request_body_in_file_only = 1; + } + rb->buf = ngx_calloc_buf(r->pool); if (rb->buf != NULL) { @@ -3478,22 +3554,44 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } + buf = stream->preread; + if (stream->in_closed) { r->request_body_no_buffering = 0; + + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 1); + ngx_pfree(r->pool, buf->start); + return rc; + } + return ngx_http_v2_process_request_body(r, NULL, 0, 1); } - if (len) { - if (r->request_body_no_buffering) { - stream->recv_window = (size_t) len; + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 0); - } else { - stream->no_flow_control = 1; - stream->recv_window = NGX_HTTP_V2_MAX_WINDOW; + ngx_pfree(r->pool, buf->start); + + if (rc != NGX_OK) { + stream->skip_data = 1; + return rc; } + } + + if (r->request_body_no_buffering) { + size = (size_t) len - h2scf->preread_size; - if (ngx_http_v2_send_window_update(stream->connection, stream->node->id, - stream->recv_window) + } else { + stream->no_flow_control = 1; + size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window; + } + + if (size) { + if (ngx_http_v2_send_window_update(stream->connection, + stream->node->id, size) == NGX_ERROR) { stream->skip_data = 1; @@ -3508,9 +3606,13 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } } + + stream->recv_window += size; } - ngx_add_timer(r->connection->read, clcf->client_body_timeout); + if (!buf) { + ngx_add_timer(r->connection->read, clcf->client_body_timeout); + } r->read_event_handler = ngx_http_v2_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; @@ -3529,13 +3631,8 @@ ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; - rb = r->request_body; - - if (rb == NULL) { - return NGX_OK; - } - fc = r->connection; + rb = r->request_body; buf = rb->buf; if (size) { @@ -3579,7 +3676,7 @@ rb->buf = NULL; } - if (r->headers_in.content_length_n == -1) { + if (r->headers_in.chunked) { r->headers_in.content_length_n = rb->received; } @@ -3789,7 +3886,14 @@ window -= h2c->state.length; } - if (window == stream->recv_window) { + if (window <= stream->recv_window) { + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "http2 negative window update"); + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return NGX_AGAIN; } @@ -3824,6 +3928,10 @@ ngx_event_t *rev; ngx_connection_t *fc; + if (stream->rst_sent) { + return NGX_OK; + } + if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status) == NGX_ERROR) { @@ -3831,6 +3939,7 @@ } stream->rst_sent = 1; + stream->skip_data = 1; fc = stream->request->connection; fc->error = 1; @@ -3862,6 +3971,7 @@ if (stream->queued) { fc->write->handler = ngx_http_v2_close_stream_handler; + fc->read->handler = ngx_http_empty_handler; return; } @@ -4062,7 +4172,6 @@ #endif c->destroyed = 0; - c->idle = 0; ngx_reusable_connection(c, 0); h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, @@ -4097,16 +4206,14 @@ h2c->blocked = 1; - if (!c->error && ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { - (void) ngx_http_v2_send_output_queue(h2c); + if (!c->error && !h2c->goaway) { + if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { + (void) ngx_http_v2_send_output_queue(h2c); + } } c->error = 1; - if (h2c->state.stream) { - ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST); - } - if (!h2c->processing) { ngx_http_close_connection(c); return; @@ -4131,7 +4238,7 @@ continue; } - stream->handled = 0; + stream->waiting = 0; r = stream->request; fc = r->connection; diff -Nru nginx-1.10.1/src/http/v2/ngx_http_v2_filter_module.c nginx-1.10.3/src/http/v2/ngx_http_v2_filter_module.c --- nginx-1.10.1/src/http/v2/ngx_http_v2_filter_module.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/v2/ngx_http_v2_filter_module.c 2017-01-31 10:01:12.000000000 -0500 @@ -169,6 +169,12 @@ return NGX_OK; } + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } @@ -259,8 +265,6 @@ len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } - fc = r->connection; - if (r->headers_out.location && r->headers_out.location->value.len) { if (r->headers_out.location->value.data[0] == '/') { @@ -1118,11 +1122,11 @@ ngx_queue_t *q; ngx_http_v2_stream_t *s; - if (stream->handled) { + if (stream->waiting) { return; } - stream->handled = 1; + stream->waiting = 1; for (q = ngx_queue_last(&h2c->waiting); q != ngx_queue_sentinel(&h2c->waiting); @@ -1313,18 +1317,29 @@ ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream) { - ngx_event_t *wev; + ngx_event_t *wev; + ngx_connection_t *fc; - if (stream->handled || stream->blocked || stream->exhausted) { + if (stream->waiting || stream->blocked) { return; } - wev = stream->request->connection->write; + fc = stream->request->connection; - if (!wev->delayed) { - stream->handled = 1; - ngx_queue_insert_tail(&h2c->posted, &stream->queue); + if (!fc->error && stream->exhausted) { + return; } + + wev = fc->write; + + wev->active = 0; + wev->ready = 1; + + if (!fc->error && wev->delayed) { + return; + } + + ngx_post_event(wev, &ngx_posted_events); } @@ -1334,11 +1349,13 @@ ngx_http_v2_stream_t *stream = data; size_t window; + ngx_event_t *wev; + ngx_queue_t *q; ngx_http_v2_out_frame_t *frame, **fn; ngx_http_v2_connection_t *h2c; - if (stream->handled) { - stream->handled = 0; + if (stream->waiting) { + stream->waiting = 0; ngx_queue_remove(&stream->queue); } @@ -1372,9 +1389,26 @@ fn = &frame->next; } - if (h2c->send_window == 0 && window && !ngx_queue_empty(&h2c->waiting)) { - ngx_queue_add(&h2c->posted, &h2c->waiting); - ngx_queue_init(&h2c->waiting); + if (h2c->send_window == 0 && window) { + + while (!ngx_queue_empty(&h2c->waiting)) { + q = ngx_queue_head(&h2c->waiting); + + ngx_queue_remove(q); + + stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); + + stream->waiting = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + ngx_post_event(wev, &ngx_posted_events); + } + } } h2c->send_window += window; diff -Nru nginx-1.10.1/src/http/v2/ngx_http_v2.h nginx-1.10.3/src/http/v2/ngx_http_v2.h --- nginx-1.10.1/src/http/v2/ngx_http_v2.h 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/v2/ngx_http_v2.h 2017-01-31 10:01:12.000000000 -0500 @@ -46,6 +46,9 @@ #define NGX_HTTP_V2_PADDED_FLAG 0x08 #define NGX_HTTP_V2_PRIORITY_FLAG 0x20 +#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) +#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 + typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; typedef struct ngx_http_v2_node_s ngx_http_v2_node_t; @@ -134,7 +137,6 @@ ngx_http_v2_out_frame_t *last_out; - ngx_queue_t posted; ngx_queue_t dependencies; ngx_queue_t closed; @@ -143,6 +145,7 @@ unsigned closed_nodes:8; unsigned settings_ack:1; unsigned blocked:1; + unsigned goaway:1; }; @@ -174,6 +177,8 @@ ssize_t send_window; size_t recv_window; + ngx_buf_t *preread; + ngx_http_v2_out_frame_t *free_frames; ngx_chain_t *free_frame_headers; ngx_chain_t *free_bufs; @@ -186,7 +191,7 @@ ngx_pool_t *pool; - unsigned handled:1; + unsigned waiting:1; unsigned blocked:1; unsigned exhausted:1; unsigned in_closed:1; diff -Nru nginx-1.10.1/src/http/v2/ngx_http_v2_module.c nginx-1.10.3/src/http/v2/ngx_http_v2_module.c --- nginx-1.10.1/src/http/v2/ngx_http_v2_module.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/v2/ngx_http_v2_module.c 2017-01-31 10:01:12.000000000 -0500 @@ -30,6 +30,7 @@ static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data); @@ -41,6 +42,8 @@ { ngx_http_v2_recv_buffer_size }; static ngx_conf_post_t ngx_http_v2_pool_size_post = { ngx_http_v2_pool_size }; +static ngx_conf_post_t ngx_http_v2_preread_size_post = + { ngx_http_v2_preread_size }; static ngx_conf_post_t ngx_http_v2_streams_index_mask_post = { ngx_http_v2_streams_index_mask }; static ngx_conf_post_t ngx_http_v2_chunk_size_post = @@ -84,6 +87,13 @@ offsetof(ngx_http_v2_srv_conf_t, max_header_size), NULL }, + { ngx_string("http2_body_preread_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_v2_srv_conf_t, preread_size), + &ngx_http_v2_preread_size_post }, + { ngx_string("http2_streams_index_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -316,6 +326,8 @@ h2scf->max_field_size = NGX_CONF_UNSET_SIZE; h2scf->max_header_size = NGX_CONF_UNSET_SIZE; + h2scf->preread_size = NGX_CONF_UNSET_SIZE; + h2scf->streams_index_mask = NGX_CONF_UNSET_UINT; h2scf->recv_timeout = NGX_CONF_UNSET_MSEC; @@ -341,6 +353,8 @@ ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size, 16384); + ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536); + ngx_conf_merge_uint_value(conf->streams_index_mask, prev->streams_index_mask, 32 - 1); @@ -414,6 +428,23 @@ return NGX_CONF_ERROR; } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp > NGX_HTTP_V2_MAX_WINDOW) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the maximum body preread buffer size is %uz", + NGX_HTTP_V2_MAX_WINDOW); + + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } diff -Nru nginx-1.10.1/src/http/v2/ngx_http_v2_module.h nginx-1.10.3/src/http/v2/ngx_http_v2_module.h --- nginx-1.10.1/src/http/v2/ngx_http_v2_module.h 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/http/v2/ngx_http_v2_module.h 2017-01-31 10:01:12.000000000 -0500 @@ -25,6 +25,7 @@ ngx_uint_t concurrent_streams; size_t max_field_size; size_t max_header_size; + size_t preread_size; ngx_uint_t streams_index_mask; ngx_msec_t recv_timeout; ngx_msec_t idle_timeout; diff -Nru nginx-1.10.1/src/os/unix/ngx_darwin_sendfile_chain.c nginx-1.10.3/src/os/unix/ngx_darwin_sendfile_chain.c --- nginx-1.10.1/src/os/unix/ngx_darwin_sendfile_chain.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/os/unix/ngx_darwin_sendfile_chain.c 2017-01-31 10:01:12.000000000 -0500 @@ -98,7 +98,7 @@ send += file_size; - if (header.count == 0) { + if (header.count == 0 && send < limit) { /* * create the trailer iovec and coalesce the neighbouring bufs diff -Nru nginx-1.10.1/src/os/unix/ngx_freebsd_sendfile_chain.c nginx-1.10.3/src/os/unix/ngx_freebsd_sendfile_chain.c --- nginx-1.10.1/src/os/unix/ngx_freebsd_sendfile_chain.c 2016-05-31 09:47:02.000000000 -0400 +++ nginx-1.10.3/src/os/unix/ngx_freebsd_sendfile_chain.c 2017-01-31 10:01:12.000000000 -0500 @@ -114,15 +114,23 @@ send += file_size; - /* create the trailer iovec and coalesce the neighbouring bufs */ + if (send < limit) { - cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, c->log); + /* + * create the trailer iovec and coalesce the neighbouring bufs + */ + + cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, + c->log); + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } - if (cl == NGX_CHAIN_ERROR) { - return NGX_CHAIN_ERROR; - } + send += trailer.size; - send += trailer.size; + } else { + trailer.count = 0; + } if (ngx_freebsd_use_tcp_nopush && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)