From d2ce3a1fcb5e0f7f27d22293832ced78b2cc744d Mon Sep 17 00:00:00 2001 From: nginx Date: Tue, 8 Apr 2014 14:31:42 +0000 Subject: Changes with nginx 1.5.13 08 Apr 2014 *) Change: improved hash table handling; the default values of the "variables_hash_max_size" and "types_hash_bucket_size" were changed to 1024 and 64 respectively. *) Feature: the ngx_http_mp4_module now supports the "end" argument. *) Feature: byte ranges support in the ngx_http_mp4_module and while saving responses to cache. *) Bugfix: alerts "ngx_slab_alloc() failed: no memory" no longer logged when using shared memory in the "ssl_session_cache" directive and in the ngx_http_limit_req_module. *) Bugfix: the "underscores_in_headers" directive did not allow underscore as a first character of a header. Thanks to Piotr Sikora. *) Bugfix: cache manager might hog CPU on exit in nginx/Windows. *) Bugfix: nginx/Windows terminated abnormally if the "ssl_session_cache" directive was used with the "shared" parameter. *) Bugfix: in the ngx_http_spdy_module. --- CHANGES | 27 + CHANGES.ru | 28 + auto/cc/msvc | 2 +- src/core/nginx.h | 4 +- src/core/ngx_hash.c | 15 +- src/core/ngx_slab.c | 6 +- src/core/ngx_slab.h | 2 + src/event/ngx_event_openssl.c | 10 +- src/http/modules/ngx_http_limit_req_module.c | 4 + src/http/modules/ngx_http_mp4_module.c | 754 +++++++++++++++++++----- src/http/modules/ngx_http_range_filter_module.c | 5 +- src/http/ngx_http_core_module.c | 22 +- src/http/ngx_http_parse.c | 13 + src/http/ngx_http_request.h | 1 + src/http/ngx_http_spdy.c | 83 ++- src/http/ngx_http_spdy_filter_module.c | 75 +-- src/http/ngx_http_spdy_module.c | 4 +- src/http/ngx_http_upstream.c | 5 + src/mail/ngx_mail_handler.c | 7 + src/os/unix/ngx_files.h | 2 + 20 files changed, 815 insertions(+), 254 deletions(-) diff --git a/CHANGES b/CHANGES index a38f93754..215cb5501 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,31 @@ +Changes with nginx 1.5.13 08 Apr 2014 + + *) Change: improved hash table handling; the default values of the + "variables_hash_max_size" and "types_hash_bucket_size" were changed + to 1024 and 64 respectively. + + *) Feature: the ngx_http_mp4_module now supports the "end" argument. + + *) Feature: byte ranges support in the ngx_http_mp4_module and while + saving responses to cache. + + *) Bugfix: alerts "ngx_slab_alloc() failed: no memory" no longer logged + when using shared memory in the "ssl_session_cache" directive and in + the ngx_http_limit_req_module. + + *) Bugfix: the "underscores_in_headers" directive did not allow + underscore as a first character of a header. + Thanks to Piotr Sikora. + + *) Bugfix: cache manager might hog CPU on exit in nginx/Windows. + + *) Bugfix: nginx/Windows terminated abnormally if the + "ssl_session_cache" directive was used with the "shared" parameter. + + *) Bugfix: in the ngx_http_spdy_module. + + Changes with nginx 1.5.12 18 Mar 2014 *) Security: a heap memory buffer overflow might occur in a worker diff --git a/CHANGES.ru b/CHANGES.ru index 139934be0..6b6f14e08 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,32 @@ +Изменения в nginx 1.5.13 08.04.2014 + + *) Изменение: улучшена обработка хэш-таблиц; в директивах + variables_hash_max_size и types_hash_bucket_size значения по + умолчанию изменены на 1024 и 64 соответственно. + + *) Добавление: модуль ngx_http_mp4_module теперь понимает аргумент end. + + *) Добавление: поддержка byte ranges модулем ngx_http_mp4_module и при + сохранении ответов в кэш. + + *) Исправление: теперь nginx не пишет в лог сообщения "ngx_slab_alloc() + failed: no memory" при использовании разделяемой памяти в + ssl_session_cache и в модуле ngx_http_limit_req_module. + + *) Исправление: директива underscores_in_headers не разрешала + подчёркивание в первом символе заголовка. + Спасибо Piotr Sikora. + + *) Исправление: cache manager мог нагружать процессор при выходе в + nginx/Windows. + + *) Исправление: при использовании ssl_session_cache с параметром shared + рабочий процесс nginx/Windows завершался аварийно. + + *) Исправление: в модуле ngx_http_spdy_module. + + Изменения в nginx 1.5.12 18.03.2014 *) Безопасность: при обработке специально созданного запроса модулем diff --git a/auto/cc/msvc b/auto/cc/msvc index 6cb8b3d58..393ba3214 100644 --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -106,7 +106,7 @@ fi # precompiled headers CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch" -CORE_LINK="$NGX_OBJS/ngx_pch.obj" +CORE_LINK="$CORE_LINK $NGX_OBJS/ngx_pch.obj" NGX_PCH="$NGX_OBJS/ngx_config.pch" NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch" NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch" diff --git a/src/core/nginx.h b/src/core/nginx.h index 17dda595e..dc88100b9 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1005012 -#define NGINX_VERSION "1.5.12" +#define nginx_version 1005013 +#define NGINX_VERSION "1.5.13" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c index b53294502..c7bfed709 100644 --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -282,7 +282,7 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) start = hinit->max_size - 1000; } - for (size = start; size < hinit->max_size; size++) { + for (size = start; size <= hinit->max_size; size++) { ngx_memzero(test, size * sizeof(u_short)); @@ -312,15 +312,12 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) continue; } - ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, - "could not build the %s, you should increase " - "either %s_max_size: %i or %s_bucket_size: %i", + ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0, + "could not build optimal %s, you should increase " + "either %s_max_size: %i or %s_bucket_size: %i; " + "ignoring %s_bucket_size", hinit->name, hinit->name, hinit->max_size, - hinit->name, hinit->bucket_size); - - ngx_free(test); - - return NGX_ERROR; + hinit->name, hinit->bucket_size, hinit->name); found: diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c index 6d18abb72..be7927ce0 100644 --- a/src/core/ngx_slab.c +++ b/src/core/ngx_slab.c @@ -129,6 +129,7 @@ ngx_slab_init(ngx_slab_pool_t *pool) pool->pages->slab = pages; } + pool->log_nomem = 1; pool->log_ctx = &pool->zero; pool->zero = '\0'; } @@ -658,7 +659,10 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages) } } - ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory"); + if (pool->log_nomem) { + ngx_slab_error(pool, NGX_LOG_CRIT, + "ngx_slab_alloc() failed: no memory"); + } return NULL; } diff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h index c5e420bfa..5735e3bb3 100644 --- a/src/core/ngx_slab.h +++ b/src/core/ngx_slab.h @@ -39,6 +39,8 @@ typedef struct { u_char *log_ctx; u_char zero; + unsigned log_nomem:1; + void *data; void *addr; } ngx_slab_pool_t; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 28e7aa509..d8dd3d358 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1804,13 +1804,13 @@ ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) return NGX_OK; } + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + if (shm_zone->shm.exists) { - shm_zone->data = data; + shm_zone->data = shpool->data; return NGX_OK; } - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; - cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t)); if (cache == NULL) { return NGX_ERROR; @@ -1834,6 +1834,8 @@ ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z", &shm_zone->shm.name); + shpool->log_nomem = 0; + return NGX_OK; } @@ -1986,7 +1988,7 @@ failed: ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "could not add new SSL session to the session cache"); + "could not allocate new session%s", shpool->log_ctx); return 0; } diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c index d4c1ff6c3..74f7fdaa7 100644 --- a/src/http/modules/ngx_http_limit_req_module.c +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -451,6 +451,8 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "could not allocate node%s", ctx->shpool->log_ctx); return NGX_ERROR; } } @@ -674,6 +676,8 @@ ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z", &shm_zone->shm.name); + ctx->shpool->log_nomem = 0; + return NGX_OK; } diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 426a0b97f..8f439ba92 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -27,14 +27,15 @@ #define NGX_HTTP_MP4_CTTS_ATOM 15 #define NGX_HTTP_MP4_CTTS_DATA 16 #define NGX_HTTP_MP4_STSC_ATOM 17 -#define NGX_HTTP_MP4_STSC_CHUNK 18 +#define NGX_HTTP_MP4_STSC_START 18 #define NGX_HTTP_MP4_STSC_DATA 19 -#define NGX_HTTP_MP4_STSZ_ATOM 20 -#define NGX_HTTP_MP4_STSZ_DATA 21 -#define NGX_HTTP_MP4_STCO_ATOM 22 -#define NGX_HTTP_MP4_STCO_DATA 23 -#define NGX_HTTP_MP4_CO64_ATOM 24 -#define NGX_HTTP_MP4_CO64_DATA 25 +#define NGX_HTTP_MP4_STSC_END 20 +#define NGX_HTTP_MP4_STSZ_ATOM 21 +#define NGX_HTTP_MP4_STSZ_DATA 22 +#define NGX_HTTP_MP4_STCO_ATOM 23 +#define NGX_HTTP_MP4_STCO_DATA 24 +#define NGX_HTTP_MP4_CO64_ATOM 25 +#define NGX_HTTP_MP4_CO64_DATA 26 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA @@ -62,10 +63,15 @@ typedef struct { uint32_t chunks; ngx_uint_t start_sample; + ngx_uint_t end_sample; ngx_uint_t start_chunk; - ngx_uint_t chunk_samples; - uint64_t chunk_samples_size; + ngx_uint_t end_chunk; + ngx_uint_t start_chunk_samples; + ngx_uint_t end_chunk_samples; + uint64_t start_chunk_samples_size; + uint64_t end_chunk_samples_size; off_t start_offset; + off_t end_offset; size_t tkhd_size; size_t mdhd_size; @@ -95,7 +101,8 @@ typedef struct { ngx_buf_t ctts_atom_buf; ngx_buf_t ctts_data_buf; ngx_buf_t stsc_atom_buf; - ngx_buf_t stsc_chunk_buf; + ngx_buf_t stsc_start_chunk_buf; + ngx_buf_t stsc_end_chunk_buf; ngx_buf_t stsc_data_buf; ngx_buf_t stsz_atom_buf; ngx_buf_t stsz_data_buf; @@ -104,7 +111,8 @@ typedef struct { ngx_buf_t co64_atom_buf; ngx_buf_t co64_data_buf; - ngx_mp4_stsc_entry_t stsc_chunk_entry; + ngx_mp4_stsc_entry_t stsc_start_chunk_entry; + ngx_mp4_stsc_entry_t stsc_end_chunk_entry; } ngx_http_mp4_trak_t; @@ -121,6 +129,7 @@ typedef struct { off_t end; off_t content_length; ngx_uint_t start; + ngx_uint_t length; uint32_t timescale; ngx_http_request_t *request; ngx_array_t trak; @@ -219,7 +228,7 @@ static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, - off_t start_offset); + off_t start_offset, off_t end_offset); static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, @@ -258,18 +267,26 @@ static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak); +static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start); static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak); +static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start); static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak); +static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start); static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak); +static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start); static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size); static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, @@ -403,8 +420,8 @@ ngx_http_mp4_handler(ngx_http_request_t *r) { u_char *last; size_t root; - ngx_int_t rc, start; - ngx_uint_t level; + ngx_int_t rc, start, end; + ngx_uint_t level, length; ngx_str_t path, value; ngx_log_t *log; ngx_buf_t *b; @@ -509,6 +526,7 @@ ngx_http_mp4_handler(ngx_http_request_t *r) r->allow_ranges = 1; start = -1; + length = 0; r->headers_out.content_length_n = of.size; mp4 = NULL; b = NULL; @@ -530,10 +548,30 @@ ngx_http_mp4_handler(ngx_http_request_t *r) start = -1; } } + + if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) { + + ngx_set_errno(0); + end = (int) (strtod((char *) value.data, NULL) * 1000); + + if (ngx_errno != 0) { + end = -1; + } + + if (end > 0) { + if (start < 0) { + start = 0; + } + + if (end > start) { + length = end - start; + } + } + } } if (start >= 0) { - r->allow_ranges = 0; + r->single_range = 1; mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t)); if (mp4 == NULL) { @@ -545,6 +583,7 @@ ngx_http_mp4_handler(ngx_http_request_t *r) mp4->file.log = r->connection->log; mp4->end = of.size; mp4->start = (ngx_uint_t) start; + mp4->length = length; mp4->request = r; switch (ngx_http_mp4_process(mp4)) { @@ -650,15 +689,15 @@ ngx_http_mp4_handler(ngx_http_request_t *r) static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) { - off_t start_offset, adjustment; + off_t start_offset, end_offset, adjustment; ngx_int_t rc; ngx_uint_t i, j; ngx_chain_t **prev; ngx_http_mp4_trak_t *trak; ngx_http_mp4_conf_t *conf; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "mp4 start:%ui", mp4->start); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 start:%ui, length:%ui", mp4->start, mp4->length); conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module); @@ -700,6 +739,7 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) } start_offset = mp4->end; + end_offset = 0; trak = mp4->trak.elts; for (i = 0; i < mp4->trak.nelts; i++) { @@ -747,6 +787,10 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) start_offset = trak[i].start_offset; } + if (end_offset < trak[i].end_offset) { + end_offset = trak[i].end_offset; + } + *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM]; prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next; @@ -758,6 +802,10 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) } } + if (end_offset < start_offset) { + end_offset = start_offset; + } + mp4->moov_size += 8; ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size); @@ -774,7 +822,7 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) } adjustment = mp4->ftyp_size + mp4->moov_size - + ngx_http_mp4_update_mdat_atom(mp4, start_offset) + + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset) - start_offset; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, @@ -1018,7 +1066,7 @@ ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) no_mdat = (mp4->mdat_atom.buf == NULL); - if (no_mdat && mp4->start == 0) { + if (no_mdat && mp4->start == 0 && mp4->length == 0) { /* * send original file if moov atom resides before * mdat atom and client requests integral file @@ -1117,7 +1165,8 @@ ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) static size_t -ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset) +ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset, + off_t end_offset) { off_t atom_data_size; u_char *atom_header; @@ -1125,8 +1174,9 @@ ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset) uint64_t atom_size; ngx_buf_t *atom; - atom_data_size = mp4->mdat_data.buf->file_last - start_offset; + atom_data_size = end_offset - start_offset; mp4->mdat_data.buf->file_pos = start_offset; + mp4->mdat_data.buf->file_last = end_offset; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mdat new offset @%O:%O", start_offset, atom_data_size); @@ -1208,7 +1258,7 @@ ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) u_char *atom_header; size_t atom_size; uint32_t timescale; - uint64_t duration, start_time; + uint64_t duration, start_time, length_time; ngx_buf_t *atom; ngx_mp4_mvhd_atom_t *mvhd_atom; ngx_mp4_mvhd64_atom_t *mvhd64_atom; @@ -1262,6 +1312,14 @@ ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) duration -= start_time; + if (mp4->length) { + length_time = (uint64_t) mp4->length * timescale / 1000; + + if (duration > length_time) { + duration = length_time; + } + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mvhd new duration:%uL, time:%.3fs", duration, (double) duration / timescale); @@ -1407,7 +1465,7 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) { u_char *atom_header; size_t atom_size; - uint64_t duration, start_time; + uint64_t duration, start_time, length_time; ngx_buf_t *atom; ngx_http_mp4_trak_t *trak; ngx_mp4_tkhd_atom_t *tkhd_atom; @@ -1449,7 +1507,7 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) start_time = (uint64_t) mp4->start * mp4->timescale / 1000; - if (duration < start_time) { + if (duration <= start_time) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "tkhd duration is less than start time"); return NGX_DECLINED; @@ -1457,6 +1515,14 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) duration -= start_time; + if (mp4->length) { + length_time = (uint64_t) mp4->length * mp4->timescale / 1000; + + if (duration > length_time) { + duration = length_time; + } + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "tkhd new duration:%uL, time:%.3fs", duration, (double) duration / mp4->timescale); @@ -1558,7 +1624,7 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) u_char *atom_header; size_t atom_size; uint32_t timescale; - uint64_t duration, start_time; + uint64_t duration, start_time, length_time; ngx_buf_t *atom; ngx_http_mp4_trak_t *trak; ngx_mp4_mdhd_atom_t *mdhd_atom; @@ -1602,7 +1668,7 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) start_time = (uint64_t) mp4->start * timescale / 1000; - if (duration < start_time) { + if (duration <= start_time) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mdhd duration is less than start time"); return NGX_DECLINED; @@ -1610,6 +1676,14 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) duration -= start_time; + if (mp4->length) { + length_time = (uint64_t) mp4->length * timescale / 1000; + + if (duration > length_time) { + duration = length_time; + } + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mdhd new duration:%uL, time:%.3fs", duration, (double) duration / timescale); @@ -1981,13 +2055,9 @@ static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { - size_t atom_size; - uint32_t entries, count, duration; - uint64_t start_time; - ngx_buf_t *atom, *data; - ngx_uint_t start_sample; - ngx_mp4_stts_atom_t *stts_atom; - ngx_mp4_stts_entry_t *entry, *end; + size_t atom_size; + ngx_buf_t *atom, *data; + ngx_mp4_stts_atom_t *stts_atom; /* * mdia.minf.stbl.stts updating requires trak->timescale @@ -2006,12 +2076,60 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, return NGX_ERROR; } - entries = trak->time_to_sample_entries; - start_time = (uint64_t) mp4->start * trak->timescale / 1000; + if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) { + return NGX_ERROR; + } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "time-to-sample start_time:%uL", start_time); + "time-to-sample entries:%uD", trak->time_to_sample_entries); + + atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos); + trak->size += atom_size; + + atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf; + stts_atom = (ngx_mp4_stts_atom_t *) atom->pos; + ngx_mp4_set_32value(stts_atom->size, atom_size); + ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start) +{ + uint32_t count, duration, rest; + uint64_t start_time; + ngx_buf_t *data; + ngx_uint_t start_sample, entries, start_sec; + ngx_mp4_stts_entry_t *entry, *end; + + if (start) { + start_sec = mp4->start; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stts crop start_time:%ui", start_sec); + + } else if (mp4->length) { + start_sec = mp4->length; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stts crop end_time:%ui", start_sec); + + } else { + return NGX_OK; + } + + data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf; + + start_time = (uint64_t) start_sec * trak->timescale / 1000; + + entries = trak->time_to_sample_entries; start_sample = 0; entry = (ngx_mp4_stts_entry_t *) data->pos; end = (ngx_mp4_stts_entry_t *) data->last; @@ -2020,13 +2138,13 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, count = ngx_mp4_get_32value(entry->count); duration = ngx_mp4_get_32value(entry->duration); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "count:%uD, duration:%uD", count, duration); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "time:%uL, count:%uD, duration:%uD", + start_time, count, duration); if (start_time < (uint64_t) count * duration) { start_sample += (ngx_uint_t) (start_time / duration); - count -= (uint32_t) (start_time / duration); - ngx_mp4_set_32value(entry->count, count); + rest = (uint32_t) (start_time / duration); goto found; } @@ -2036,27 +2154,44 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, entry++; } - ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, - "start time is out mp4 stts samples in \"%s\"", - mp4->file.name.data); + if (start) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "start time is out mp4 stts samples in \"%s\"", + mp4->file.name.data); - return NGX_ERROR; + return NGX_ERROR; + + } else { + trak->end_sample = trak->start_sample + start_sample; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "end_sample:%ui", trak->end_sample); + + return NGX_OK; + } found: - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start_sample:%ui, new count:%uD", start_sample, count); + if (start) { + ngx_mp4_set_32value(entry->count, count - rest); + data->pos = (u_char *) entry; + trak->time_to_sample_entries = entries; + trak->start_sample = start_sample; - trak->start_sample = start_sample; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "start_sample:%ui, new count:%uD", + trak->start_sample, count - rest); - data->pos = (u_char *) entry; - atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos); - trak->size += atom_size; + } else { + ngx_mp4_set_32value(entry->count, rest); + data->last = (u_char *) (entry + 1); + trak->time_to_sample_entries -= entries - 1; + trak->end_sample = trak->start_sample + start_sample; - atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf; - stts_atom = (ngx_mp4_stts_atom_t *) atom->pos; - ngx_mp4_set_32value(stts_atom->size, atom_size); - ngx_mp4_set_32value(stts_atom->entries, entries); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "end_sample:%ui, new count:%uD", + trak->end_sample, rest); + } return NGX_OK; } @@ -2138,7 +2273,7 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { size_t atom_size; - uint32_t entries, sample, start_sample, *entry, *end; + uint32_t sample, start_sample, *entry, *end; ngx_buf_t *atom, *data; ngx_http_mp4_stss_atom_t *stss_atom; @@ -2157,18 +2292,79 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, return NGX_OK; } + ngx_http_mp4_crop_stss_data(mp4, trak, 1); + ngx_http_mp4_crop_stss_data(mp4, trak, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "sync sample entries:%uD", trak->sync_samples_entries); + + if (trak->sync_samples_entries) { + entry = (uint32_t *) data->pos; + end = (uint32_t *) data->last; + + start_sample = trak->start_sample; + + while (entry < end) { + sample = ngx_mp4_get_32value(entry); + sample -= start_sample; + ngx_mp4_set_32value(entry, sample); + entry++; + } + + } else { + trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL; + } + + atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos); + trak->size += atom_size; + + atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf; + stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos; + + ngx_mp4_set_32value(stss_atom->size, atom_size); + ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries); + + return NGX_OK; +} + + +static void +ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start) +{ + uint32_t sample, start_sample, *entry, *end; + ngx_buf_t *data; + ngx_uint_t entries; + /* sync samples starts from 1 */ - start_sample = trak->start_sample + 1; - entries = trak->sync_samples_entries; + if (start) { + start_sample = trak->start_sample + 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stss crop start_sample:%uD", start_sample); + + } else if (mp4->length) { + start_sample = trak->end_sample + 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stss crop end_sample:%uD", start_sample); + + } else { + return; + } + + data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf; + + entries = trak->sync_samples_entries; entry = (uint32_t *) data->pos; end = (uint32_t *) data->last; while (entry < end) { sample = ngx_mp4_get_32value(entry); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start:%uD, sync:%uD", start_sample, sample); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "sync:%uD", sample); if (sample >= start_sample) { goto found; @@ -2179,31 +2375,18 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start sample is out of mp4 stss atom"); + "sample is out of mp4 stss atom"); found: - data->pos = (u_char *) entry; - - start_sample = trak->start_sample; + if (start) { + data->pos = (u_char *) entry; + trak->sync_samples_entries = entries; - while (entry < end) { - sample = ngx_mp4_get_32value(entry); - sample -= start_sample; - ngx_mp4_set_32value(entry, sample); - entry++; + } else { + data->last = (u_char *) entry; + trak->sync_samples_entries -= entries; } - - atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos); - trak->size += atom_size; - - atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf; - stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos; - - ngx_mp4_set_32value(stss_atom->size, atom_size); - ngx_mp4_set_32value(stss_atom->entries, entries); - - return NGX_OK; } @@ -2287,11 +2470,9 @@ static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { - size_t atom_size; - uint32_t entries, count, start_sample; - ngx_buf_t *atom, *data; - ngx_mp4_ctts_atom_t *ctts_atom; - ngx_mp4_ctts_entry_t *entry, *end; + size_t atom_size; + ngx_buf_t *atom, *data; + ngx_mp4_ctts_atom_t *ctts_atom; /* * mdia.minf.stbl.ctts updating requires trak->start_sample @@ -2308,8 +2489,61 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, return; } + ngx_http_mp4_crop_ctts_data(mp4, trak, 1); + ngx_http_mp4_crop_ctts_data(mp4, trak, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "composition offset entries:%uD", + trak->composition_offset_entries); + + if (trak->composition_offset_entries == 0) { + trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL; + trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL; + return; + } + + atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos); + trak->size += atom_size; + + atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf; + ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos; + + ngx_mp4_set_32value(ctts_atom->size, atom_size); + ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries); + + return; +} + + +static void +ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start) +{ + uint32_t count, start_sample, rest; + ngx_buf_t *data; + ngx_uint_t entries; + ngx_mp4_ctts_entry_t *entry, *end; + /* sync samples starts from 1 */ - start_sample = trak->start_sample + 1; + + if (start) { + start_sample = trak->start_sample + 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 ctts crop start_sample:%uD", start_sample); + + } else if (mp4->length) { + start_sample = trak->end_sample - trak->start_sample + 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 ctts crop end_sample:%uD", start_sample); + + } else { + return; + } + + data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf; + entries = trak->composition_offset_entries; entry = (ngx_mp4_ctts_entry_t *) data->pos; end = (ngx_mp4_ctts_entry_t *) data->last; @@ -2318,12 +2552,11 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, count = ngx_mp4_get_32value(entry->count); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start:%uD, count:%uD, offset:%uD", + "sample:%uD, count:%uD, offset:%uD", start_sample, count, ngx_mp4_get_32value(entry->offset)); if (start_sample <= count) { - count -= (start_sample - 1); - ngx_mp4_set_32value(entry->count, count); + rest = start_sample - 1; goto found; } @@ -2332,24 +2565,25 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, entry++; } - trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL; - trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL; + if (start) { + data->pos = (u_char *) end; + trak->composition_offset_entries = 0; + } return; found: - data->pos = (u_char *) entry; - atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos); - trak->size += atom_size; + if (start) { + ngx_mp4_set_32value(entry->count, count - rest); + data->pos = (u_char *) entry; + trak->composition_offset_entries = entries; - atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf; - ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos; - - ngx_mp4_set_32value(ctts_atom->size, atom_size); - ngx_mp4_set_32value(ctts_atom->entries, entries); - - return; + } else { + ngx_mp4_set_32value(entry->count, rest); + data->last = (u_char *) (entry + 1); + trak->composition_offset_entries -= entries - 1; + } } @@ -2428,11 +2662,10 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { size_t atom_size; - uint32_t start_sample, entries, chunk, samples, id, - next_chunk, n; - ngx_buf_t *atom, *data, *buf; + uint32_t chunk; + ngx_buf_t *atom, *data; ngx_mp4_stsc_atom_t *stsc_atom; - ngx_mp4_stsc_entry_t *entry, *first, *end; + ngx_mp4_stsc_entry_t *entry, *end; /* * mdia.minf.stbl.stsc updating requires trak->start_sample @@ -2459,15 +2692,97 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, return NGX_ERROR; } - start_sample = (uint32_t) trak->start_sample; + if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "sample-to-chunk entries:%uD", + trak->sample_to_chunk_entries); + + entry = (ngx_mp4_stsc_entry_t *) data->pos; + end = (ngx_mp4_stsc_entry_t *) data->last; + + while (entry < end) { + chunk = ngx_mp4_get_32value(entry->chunk); + chunk -= trak->start_chunk; + ngx_mp4_set_32value(entry->chunk, chunk); + entry++; + } + + atom_size = sizeof(ngx_mp4_stsc_atom_t) + + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t); + + trak->size += atom_size; + + atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf; + stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos; + + ngx_mp4_set_32value(stsc_atom->size, atom_size); + ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, ngx_uint_t start) +{ + uint32_t start_sample, chunk, samples, id, next_chunk, n, + prev_samples; + ngx_buf_t *data, *buf; + ngx_uint_t entries, target_chunk, chunk_samples; + ngx_mp4_stsc_entry_t *entry, *end, *first; + entries = trak->sample_to_chunk_entries - 1; + if (start) { + start_sample = (uint32_t) trak->start_sample; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stsc crop start_sample:%uD", start_sample); + + } else if (mp4->length) { + start_sample = (uint32_t) (trak->end_sample - trak->start_sample); + samples = 0; + + data = trak->out[NGX_HTTP_MP4_STSC_START].buf; + + if (data) { + entry = (ngx_mp4_stsc_entry_t *) data->pos; + samples = ngx_mp4_get_32value(entry->samples); + entries--; + + if (samples > start_sample) { + samples = start_sample; + ngx_mp4_set_32value(entry->samples, samples); + } + + start_sample -= samples; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stsc crop end_sample:%uD, ext_samples:%uD", + start_sample, samples); + + } else { + return NGX_OK; + } + + data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf; + entry = (ngx_mp4_stsc_entry_t *) data->pos; end = (ngx_mp4_stsc_entry_t *) data->last; chunk = ngx_mp4_get_32value(entry->chunk); samples = ngx_mp4_get_32value(entry->samples); id = ngx_mp4_get_32value(entry->id); + prev_samples = 0; entry++; while (entry < end) { @@ -2475,7 +2790,7 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, next_chunk = ngx_mp4_get_32value(entry->chunk); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start_sample:%uD, chunk:%uD, chunks:%uD, " + "sample:%uD, chunk:%uD, chunks:%uD, " "samples:%uD, id:%uD", start_sample, chunk, next_chunk - chunk, samples, id); @@ -2487,6 +2802,7 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, start_sample -= n; + prev_samples = samples; chunk = next_chunk; samples = ngx_mp4_get_32value(entry->samples); id = ngx_mp4_get_32value(entry->id); @@ -2497,15 +2813,15 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, next_chunk = trak->chunks + 1; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD", + "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD", start_sample, chunk, next_chunk - chunk, samples); n = (next_chunk - chunk) * samples; if (start_sample > n) { ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, - "start time is out mp4 stsc chunks in \"%s\"", - mp4->file.name.data); + "%s time is out mp4 stsc chunks in \"%s\"", + start ? "start" : "end", mp4->file.name.data); return NGX_ERROR; } @@ -2521,59 +2837,91 @@ found: return NGX_ERROR; } - trak->start_chunk = chunk - 1; + target_chunk = chunk - 1; + target_chunk += start_sample / samples; + chunk_samples = start_sample % samples; - trak->start_chunk += start_sample / samples; - trak->chunk_samples = start_sample % samples; + if (start) { + data->pos = (u_char *) entry; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start chunk:%ui, samples:%uD", - trak->start_chunk, trak->chunk_samples); + trak->sample_to_chunk_entries = entries; + trak->start_chunk = target_chunk; + trak->start_chunk_samples = chunk_samples; + + ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1); + + samples -= chunk_samples; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "start_chunk:%ui, start_chunk_samples:%ui", + trak->start_chunk, trak->start_chunk_samples); + + } else { + if (start_sample) { + data->last = (u_char *) (entry + 1); + trak->sample_to_chunk_entries -= entries - 1; + trak->end_chunk_samples = samples; - data->pos = (u_char *) entry; - atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos); + } else { + data->last = (u_char *) entry; + trak->sample_to_chunk_entries -= entries; + trak->end_chunk_samples = prev_samples; + } - ngx_mp4_set_32value(entry->chunk, 1); + if (chunk_samples) { + trak->end_chunk = target_chunk + 1; + trak->end_chunk_samples = chunk_samples; - if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) { + } else { + trak->end_chunk = target_chunk; + } - /* last chunk in the entry */ + samples = chunk_samples; + next_chunk = chunk + 1; - ngx_mp4_set_32value(entry->samples, samples - trak->chunk_samples); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "end_chunk:%ui, end_chunk_samples:%ui", + trak->end_chunk, trak->end_chunk_samples); + } - } else if (trak->chunk_samples) { + if (chunk_samples && next_chunk - target_chunk == 2) { - first = &trak->stsc_chunk_entry; + ngx_mp4_set_32value(entry->samples, samples); + + } else if (chunk_samples && start) { + + first = &trak->stsc_start_chunk_entry; ngx_mp4_set_32value(first->chunk, 1); - ngx_mp4_set_32value(first->samples, samples - trak->chunk_samples); + ngx_mp4_set_32value(first->samples, samples); ngx_mp4_set_32value(first->id, id); - buf = &trak->stsc_chunk_buf; + buf = &trak->stsc_start_chunk_buf; buf->temporary = 1; buf->pos = (u_char *) first; buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t); - trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf; + trak->out[NGX_HTTP_MP4_STSC_START].buf = buf; - ngx_mp4_set_32value(entry->chunk, 2); + ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2); - entries++; - atom_size += sizeof(ngx_mp4_stsc_entry_t); - } + trak->sample_to_chunk_entries++; - while (++entry < end) { - chunk = ngx_mp4_get_32value(entry->chunk); - chunk -= trak->start_chunk; - ngx_mp4_set_32value(entry->chunk, chunk); - } + } else if (chunk_samples) { - trak->size += atom_size; + first = &trak->stsc_end_chunk_entry; + ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk); + ngx_mp4_set_32value(first->samples, samples); + ngx_mp4_set_32value(first->id, id); - atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf; - stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos; + buf = &trak->stsc_end_chunk_buf; + buf->temporary = 1; + buf->pos = (u_char *) first; + buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t); - ngx_mp4_set_32value(stsc_atom->size, atom_size); - ngx_mp4_set_32value(stsc_atom->entries, entries); + trak->out[NGX_HTTP_MP4_STSC_END].buf = buf; + + trak->sample_to_chunk_entries++; + } return NGX_OK; } @@ -2669,7 +3017,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { size_t atom_size; - uint32_t *pos, *end; + uint32_t *pos, *end, entries; ngx_buf_t *atom, *data; ngx_mp4_stsz_atom_t *stsz_atom; @@ -2685,22 +3033,47 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf; if (data) { - if (trak->start_sample > trak->sample_sizes_entries) { + entries = trak->sample_sizes_entries; + + if (trak->start_sample > entries) { ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, "start time is out mp4 stsz samples in \"%s\"", mp4->file.name.data); return NGX_ERROR; } + entries -= trak->start_sample; data->pos += trak->start_sample * sizeof(uint32_t); end = (uint32_t *) data->pos; - for (pos = end - trak->chunk_samples; pos < end; pos++) { - trak->chunk_samples_size += ngx_mp4_get_32value(pos); + for (pos = end - trak->start_chunk_samples; pos < end; pos++) { + trak->start_chunk_samples_size += ngx_mp4_get_32value(pos); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "chunk samples sizes:%uL", trak->chunk_samples_size); + "chunk samples sizes:%uL", + trak->start_chunk_samples_size); + + if (mp4->length) { + if (trak->end_sample - trak->start_sample > entries) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "end time is out mp4 stsz samples in \"%s\"", + mp4->file.name.data); + return NGX_ERROR; + } + + entries = trak->end_sample - trak->start_sample; + data->last = data->pos + entries * sizeof(uint32_t); + end = (uint32_t *) data->last; + + for (pos = end - trak->end_chunk_samples; pos < end; pos++) { + trak->end_chunk_samples_size += ngx_mp4_get_32value(pos); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 stsz end_chunk_samples_size:%uL", + trak->end_chunk_samples_size); + } atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); trak->size += atom_size; @@ -2709,8 +3082,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos; ngx_mp4_set_32value(stsz_atom->size, atom_size); - ngx_mp4_set_32value(stsz_atom->entries, - trak->sample_sizes_entries - trak->start_sample); + ngx_mp4_set_32value(stsz_atom->entries, entries); } return NGX_OK; @@ -2791,6 +3163,7 @@ ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { size_t atom_size; + uint32_t entries; ngx_buf_t *atom, *data; ngx_mp4_stco_atom_t *stco_atom; @@ -2820,21 +3193,53 @@ ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, } data->pos += trak->start_chunk * sizeof(uint32_t); - atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); - trak->size += atom_size; trak->start_offset = ngx_mp4_get_32value(data->pos); - trak->start_offset += trak->chunk_samples_size; + trak->start_offset += trak->start_chunk_samples_size; ngx_mp4_set_32value(data->pos, trak->start_offset); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start chunk offset:%uD", trak->start_offset); + "start chunk offset:%O", trak->start_offset); + + if (mp4->length) { + + if (trak->end_chunk > trak->chunks) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "end time is out mp4 stco chunks in \"%s\"", + mp4->file.name.data); + return NGX_ERROR; + } + + entries = trak->end_chunk - trak->start_chunk; + data->last = data->pos + entries * sizeof(uint32_t); + + if (entries) { + trak->end_offset = + ngx_mp4_get_32value(data->last - sizeof(uint32_t)); + trak->end_offset += trak->end_chunk_samples_size; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "end chunk offset:%O", trak->end_offset); + } + + } else { + entries = trak->chunks - trak->start_chunk; + trak->end_offset = mp4->mdat_data.buf->file_last; + } + + if (entries == 0) { + trak->start_offset = mp4->end; + trak->end_offset = 0; + } + + atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); + trak->size += atom_size; atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf; stco_atom = (ngx_mp4_stco_atom_t *) atom->pos; ngx_mp4_set_32value(stco_atom->size, atom_size); - ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk); + ngx_mp4_set_32value(stco_atom->entries, entries); return NGX_OK; } @@ -2942,6 +3347,7 @@ ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak) { size_t atom_size; + uint64_t entries; ngx_buf_t *atom, *data; ngx_mp4_co64_atom_t *co64_atom; @@ -2971,21 +3377,53 @@ ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, } data->pos += trak->start_chunk * sizeof(uint64_t); - atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); - trak->size += atom_size; trak->start_offset = ngx_mp4_get_64value(data->pos); - trak->start_offset += trak->chunk_samples_size; + trak->start_offset += trak->start_chunk_samples_size; ngx_mp4_set_64value(data->pos, trak->start_offset); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "start chunk offset:%uL", trak->start_offset); + "start chunk offset:%O", trak->start_offset); + + if (mp4->length) { + + if (trak->end_chunk > trak->chunks) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "end time is out mp4 co64 chunks in \"%s\"", + mp4->file.name.data); + return NGX_ERROR; + } + + entries = trak->end_chunk - trak->start_chunk; + data->last = data->pos + entries * sizeof(uint64_t); + + if (entries) { + trak->end_offset = + ngx_mp4_get_64value(data->last - sizeof(uint64_t)); + trak->end_offset += trak->end_chunk_samples_size; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "end chunk offset:%O", trak->end_offset); + } + + } else { + entries = trak->chunks - trak->start_chunk; + trak->end_offset = mp4->mdat_data.buf->file_last; + } + + if (entries == 0) { + trak->start_offset = mp4->end; + trak->end_offset = 0; + } + + atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); + trak->size += atom_size; atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf; co64_atom = (ngx_mp4_co64_atom_t *) atom->pos; ngx_mp4_set_32value(co64_atom->size, atom_size); - ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk); + ngx_mp4_set_32value(co64_atom->entries, entries); return NGX_OK; } diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c index bcc64fd30..6a65e4849 100644 --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -148,6 +148,7 @@ ngx_http_range_header_filter(ngx_http_request_t *r) { time_t if_range_time; ngx_str_t *if_range, *etag; + ngx_uint_t ranges; ngx_http_core_loc_conf_t *clcf; ngx_http_range_filter_ctx_t *ctx; @@ -227,7 +228,9 @@ parse: return NGX_ERROR; } - switch (ngx_http_range_parse(r, ctx, clcf->max_ranges)) { + ranges = r->single_range ? 1 : clcf->max_ranges; + + switch (ngx_http_range_parse(r, ctx, ranges)) { case NGX_OK: ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index b023bdbc8..bfaa90e76 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3432,25 +3432,16 @@ ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf) { ngx_http_core_main_conf_t *cmcf = conf; - if (cmcf->server_names_hash_max_size == NGX_CONF_UNSET_UINT) { - cmcf->server_names_hash_max_size = 512; - } - - if (cmcf->server_names_hash_bucket_size == NGX_CONF_UNSET_UINT) { - cmcf->server_names_hash_bucket_size = ngx_cacheline_size; - } + ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512); + ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size, + ngx_cacheline_size); cmcf->server_names_hash_bucket_size = ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size); - if (cmcf->variables_hash_max_size == NGX_CONF_UNSET_UINT) { - cmcf->variables_hash_max_size = 512; - } - - if (cmcf->variables_hash_bucket_size == NGX_CONF_UNSET_UINT) { - cmcf->variables_hash_bucket_size = 64; - } + ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024); + ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64); cmcf->variables_hash_bucket_size = ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); @@ -3719,8 +3710,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->types_hash_max_size, 1024); ngx_conf_merge_uint_value(conf->types_hash_bucket_size, - prev->types_hash_bucket_size, - ngx_cacheline_size); + prev->types_hash_bucket_size, 64); conf->types_hash_bucket_size = ngx_align(conf->types_hash_bucket_size, ngx_cacheline_size); diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 8c1a62a7b..02b4a0fd1 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -886,6 +886,19 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, break; } + if (ch == '_') { + if (allow_underscores) { + hash = ngx_hash(0, ch); + r->lowcase_header[0] = ch; + i = 1; + + } else { + r->invalid_header = 1; + } + + break; + } + if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_HEADER; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index a1295e799..705c4e904 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -528,6 +528,7 @@ struct ngx_http_request_s { unsigned filter_need_in_memory:1; unsigned filter_need_temporary:1; unsigned allow_ranges:1; + unsigned single_range:1; #if (NGX_STAT_STUB) unsigned stat_reading:1; diff --git a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c index 6e45f4b46..33f0f03ac 100644 --- a/src/http/ngx_http_spdy.c +++ b/src/http/ngx_http_spdy.c @@ -47,11 +47,11 @@ #define ngx_spdy_ctl_frame_check(h) \ - (((h) & 0xffffff00) == ngx_spdy_ctl_frame_head(0)) + (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0)) #define ngx_spdy_data_frame_check(h) \ (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31)) -#define ngx_spdy_ctl_frame_type(h) ((h) & 0x000000ff) +#define ngx_spdy_ctl_frame_type(h) ((h) & 0x0000ffff) #define ngx_spdy_frame_flags(p) ((p) >> 24) #define ngx_spdy_frame_length(p) ((p) & 0x00ffffff) #define ngx_spdy_frame_id(p) ((p) & 0x00ffffff) @@ -836,7 +836,8 @@ static u_char * ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end) { - uint32_t head, flen; + uint32_t head, flen; + ngx_uint_t type; if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { return ngx_http_spdy_state_save(sc, pos, end, @@ -859,7 +860,9 @@ ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, head, sc->flags, sc->length); if (ngx_spdy_ctl_frame_check(head)) { - switch (ngx_spdy_ctl_frame_type(head)) { + type = ngx_spdy_ctl_frame_type(head); + + switch (type) { case NGX_SPDY_SYN_STREAM: return ngx_http_spdy_state_syn_stream(sc, pos, end); @@ -885,7 +888,9 @@ ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, case NGX_SPDY_WINDOW_UPDATE: return ngx_http_spdy_state_window_update(sc, pos, end); - default: /* TODO logging */ + default: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, + "spdy control frame with unknown type %ui", type); return ngx_http_spdy_state_skip(sc, pos, end); } } @@ -1049,6 +1054,15 @@ ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, if (r->headers_in.headers.part.elts == NULL) { if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { + + if (complete) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent SYN_STREAM frame " + "with invalid HEADERS block"); + ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); + return ngx_http_spdy_state_protocol_error(sc); + } + return ngx_http_spdy_state_save(sc, pos, end, ngx_http_spdy_state_headers); } @@ -1187,10 +1201,10 @@ ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, } } - if (buf->pos != buf->last) { - /* TODO: improve error message */ - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "end %ui %p %p", complete, buf->pos, buf->last); + if (buf->pos != buf->last || sc->zstream_in.avail_in) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent SYN_STREAM frame " + "with invalid HEADERS block"); ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); return ngx_http_spdy_state_protocol_error(sc); } @@ -1489,7 +1503,6 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, ssize_t n; ngx_buf_t *buf; ngx_int_t rc; - ngx_uint_t complete; ngx_temp_file_t *tf; ngx_http_request_t *r; ngx_http_spdy_stream_t *stream; @@ -1514,13 +1527,8 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, size = end - pos; - if (size >= sc->length) { + if (size > sc->length) { size = sc->length; - complete = 1; - - } else { - sc->length -= size; - complete = 0; } r = stream->request; @@ -1562,6 +1570,8 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, } } + sc->length -= size; + if (tf) { buf->start = pos; buf->pos = pos; @@ -1590,7 +1600,7 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, r->request_length += size; } - if (!complete) { + if (sc->length) { return ngx_http_spdy_state_save(sc, pos, end, ngx_http_spdy_state_read_data); } @@ -1599,6 +1609,19 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, stream->in_closed = 1; + if (r->headers_in.content_length_n < 0) { + r->headers_in.content_length_n = rb->rest; + + } else if (r->headers_in.content_length_n != rb->rest) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed stream: " + "%O of %O bytes of request body received", + rb->rest, r->headers_in.content_length_n); + + stream->skip_data = NGX_SPDY_DATA_ERROR; + goto error; + } + if (tf) { ngx_memzero(buf, sizeof(ngx_buf_t)); @@ -1609,10 +1632,6 @@ ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos, rb->buf = NULL; } - if (r->headers_in.content_length_n < 0) { - r->headers_in.content_length_n = rb->rest; - } - if (rb->post_handler) { r->read_event_handler = ngx_http_block_reading; rb->post_handler(r); @@ -1872,18 +1891,20 @@ static u_char * ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler) { -#if 1 - if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) { + size_t size; + + size = end - pos; + + if (size > NGX_SPDY_STATE_BUFFER_SIZE) { ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, "spdy state buffer overflow: " - "%z bytes required", end - pos); + "%uz bytes required", size); return ngx_http_spdy_state_internal_error(sc); } -#endif ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE); - sc->buffer_used = end - pos; + sc->buffer_used = size; sc->handler = handler; sc->incomplete = 1; @@ -2941,6 +2962,16 @@ ngx_http_spdy_run_request(ngx_http_request_t *r) return; } + if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed stream"); + + r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; + } + ngx_http_process_request(r); } diff --git a/src/http/ngx_http_spdy_filter_module.c b/src/http/ngx_http_spdy_filter_module.c index 92c760243..559fb4aab 100644 --- a/src/http/ngx_http_spdy_filter_module.c +++ b/src/http/ngx_http_spdy_filter_module.c @@ -625,6 +625,20 @@ ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) r = fc->data; stream = r->spdy_stream; +#if (NGX_SUPPRESS_WARN) + size = 0; +#endif + + while (in) { + size = ngx_buf_size(in->buf); + + if (size || in->buf->last_buf) { + break; + } + + in = in->next; + } + if (in == NULL) { if (stream->queued) { @@ -638,8 +652,6 @@ ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) sc = stream->connection; - size = ngx_buf_size(in->buf); - if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) { fc->write->delayed = 1; return in; @@ -850,48 +862,45 @@ ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, "spdy:%ui create DATA frame %p: len:%uz flags:%ui", stream->id, frame, len, flags); - if (len || flags) { - - cl = ngx_chain_get_free_buf(stream->request->pool, - &stream->free_data_headers); - if (cl == NULL) { - return NULL; - } + cl = ngx_chain_get_free_buf(stream->request->pool, + &stream->free_data_headers); + if (cl == NULL) { + return NULL; + } - buf = cl->buf; + buf = cl->buf; - if (buf->start) { - p = buf->start; - buf->pos = p; + if (buf->start) { + p = buf->start; + buf->pos = p; - p += NGX_SPDY_SID_SIZE; + p += NGX_SPDY_SID_SIZE; - (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); + (void) ngx_spdy_frame_write_flags_and_len(p, flags, len); - } else { - p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE); - if (p == NULL) { - return NULL; - } + } else { + p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE); + if (p == NULL) { + return NULL; + } - buf->pos = p; - buf->start = p; + buf->pos = p; + buf->start = p; - p = ngx_spdy_frame_write_sid(p, stream->id); - p = ngx_spdy_frame_write_flags_and_len(p, flags, len); + p = ngx_spdy_frame_write_sid(p, stream->id); + p = ngx_spdy_frame_write_flags_and_len(p, flags, len); - buf->last = p; - buf->end = p; + buf->last = p; + buf->end = p; - buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; - buf->memory = 1; - } + buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; + buf->memory = 1; + } - cl->next = first; - first = cl; + cl->next = first; + first = cl; - last->buf->flush = 1; - } + last->buf->flush = 1; frame->first = first; frame->last = last; diff --git a/src/http/ngx_http_spdy_module.c b/src/http/ngx_http_spdy_module.c index 8307f15c8..5178a36f2 100644 --- a/src/http/ngx_http_spdy_module.c +++ b/src/http/ngx_http_spdy_module.c @@ -251,9 +251,7 @@ ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf) { ngx_http_spdy_main_conf_t *smcf = conf; - if (smcf->recv_buffer_size == NGX_CONF_UNSET_SIZE) { - smcf->recv_buffer_size = 256 * 1024; - } + ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024); return NGX_CONF_OK; } diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index cf9ca0d5c..040bda106 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -4183,7 +4183,12 @@ ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, if (r->cached) { r->allow_ranges = 1; return NGX_OK; + } + if (r->upstream->cacheable) { + r->allow_ranges = 1; + r->single_range = 1; + return NGX_OK; } #endif diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c index 9212a121c..47ddb0dcf 100644 --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -559,8 +559,13 @@ ngx_mail_send(ngx_event_t *wev) n = c->send(c, s->out.data, s->out.len); if (n > 0) { + s->out.data += n; s->out.len -= n; + if (s->out.len != 0) { + goto again; + } + if (wev->timer_set) { ngx_del_timer(wev); } @@ -584,6 +589,8 @@ ngx_mail_send(ngx_event_t *wev) /* n == NGX_AGAIN */ +again: + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ngx_add_timer(c->write, cscf->timeout); diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h index 4b2016500..a78ec9613 100644 --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -53,7 +53,9 @@ typedef struct { #ifdef __CYGWIN__ +#ifndef NGX_HAVE_CASELESS_FILESYSTEM #define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif #define ngx_open_file(name, mode, create, access) \ open((const char *) name, mode|create|O_BINARY, access) -- cgit v1.2.1