changeset 300:cba14c1e2a4b NGINX_0_5_20

nginx 0.5.20 *) Feature: the "sendfile_max_chunk" directive. *) Feature: the "$http_...", "$sent_http_...", and "$upstream_http_..." variables may be changed using the "set" directive. *) Bugfix: a segmentation fault might occur in worker process if the SSI command 'if expr="$var = /"' was used. *) Bugfix: trailing boundary of multipart range response was transferred incorrectly. Thanks to Evan Miller. *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun Studio. Thanks to Andrei Nigmatulin. *) Bugfix: the ngx_http_perl_module could not built by Solaris make. Thanks to Andrei Nigmatulin.
author Igor Sysoev <http://sysoev.ru>
date Mon, 07 May 2007 00:00:00 +0400
parents 7d0d14dc5fd7
children a025840de07d
files CHANGES CHANGES.ru auto/lib/perl/make auto/options src/core/nginx.h src/core/ngx_palloc.h src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_parse.c src/http/ngx_http_variables.c src/http/ngx_http_write_filter_module.c src/os/unix/ngx_linux_sendfile_chain.c src/os/unix/ngx_sunpro_atomic_sparc64.h
diffstat 17 files changed, 200 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,26 @@
 
+Changes with nginx 0.5.20                                        07 May 2007
+
+    *) Feature: the "sendfile_max_chunk" directive.
+
+    *) Feature: the "$http_...", "$sent_http_...", and "$upstream_http_..." 
+       variables may be changed using the "set" directive.
+
+    *) Bugfix: a segmentation fault might occur in worker process if the 
+       SSI command 'if expr="$var = /"' was used.
+
+    *) Bugfix: trailing boundary of multipart range response was 
+       transferred incorrectly.
+       Thanks to Evan Miller.
+
+    *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun 
+       Studio.
+       Thanks to Andrei Nigmatulin.
+
+    *) Bugfix: the ngx_http_perl_module could not built by Solaris make.
+       Thanks to Andrei Nigmatulin.
+
+
 Changes with nginx 0.5.19                                        24 Apr 2007
 
     *) Change: now the $request_time variable has millisecond precision.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,27 @@
 
+Изменения в nginx 0.5.20                                          07.05.2007
+
+    *) Добавление: директива sendfile_max_chunk.
+
+    *) Добавление: переменные "$http_...", "$sent_http_..." и 
+       "$upstream_http_..." можно менять директивой set.
+
+    *) Исправление: при использовании SSI-команды 'if expr="$var = /"' в 
+       рабочем процессе мог произойти segmentation fault.
+
+    *) Исправление: завершающая строка multipart range ответа передавалась 
+       неверно.
+       Спасибо Evan Miller.
+
+    *) Исправление: nginx не работал на Solaris/sparc64, если был собран 
+       Sun Studio.
+       Спасибо Андрею Нигматулину.
+
+    *) Исправление: модуль ngx_http_perl_module не собирался make в 
+       Solaris.
+       Спасибо Андрею Нигматулину.
+
+
 Изменения в nginx 0.5.19                                          24.04.2007
 
     *) Изменение: значение переменной $request_time теперь записывается с 
--- a/auto/lib/perl/make
+++ b/auto/lib/perl/make
@@ -9,7 +9,6 @@ cat << END                              
 		src/http/modules/perl/nginx.xs				\
 		src/http/modules/perl/ngx_http_perl_module.h		\
 		$NGX_OBJS/src/http/modules/perl/Makefile
-
 	cp -p src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
 
 	cd $NGX_OBJS/src/http/modules/perl && make
@@ -20,7 +19,6 @@ cat << END                              
 $NGX_OBJS/src/http/modules/perl/Makefile:				\
 		src/http/modules/perl/Makefile.PL			\
 		src/http/modules/perl/nginx.pm
-
 	cp -p src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
 	cp -p src/http/modules/perl/typemap $NGX_OBJS/src/http/modules/perl/
 	cp -p src/http/modules/perl/Makefile.PL $NGX_OBJS/src/http/modules/perl/
--- a/auto/options
+++ b/auto/options
@@ -264,6 +264,7 @@ cat << END
   --with-http_sub_module             enable ngx_http_sub_module
   --with-http_dav_module             enable ngx_http_dav_module
   --with-http_flv_module             enable ngx_http_flv_module
+  --with-http_stub_status_module     enable ngx_http_stub_status_module
 
   --without-http_charset_module      disable ngx_http_charset_module
   --without-http_gzip_module         disable ngx_http_gzip_module
@@ -301,6 +302,8 @@ cat << END
   --with-mail                        enable IMAP4/POP3/SMTP proxy module
   --with-mail_ssl_module             enable ngx_mail_ssl_module
 
+  --add-module=PATH                  enable an external module
+
   --with-cc=PATH                     set path to C compiler
   --with-cpp=PATH                    set path to C preprocessor
   --with-cc-opt=OPTIONS              set additional options for C compiler
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.5.19"
+#define NGINX_VERSION      "0.5.20"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_palloc.h
+++ b/src/core/ngx_palloc.h
@@ -19,7 +19,9 @@
  */
 #define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)
 
-#define NGX_DEFAULT_POOL_SIZE   (16 * 1024)
+#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)
+#define NGX_MIN_POOL_SIZE                                                     \
+    (sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t))
 
 
 typedef void (*ngx_pool_cleanup_pt)(void *data);
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -636,7 +636,8 @@ ngx_http_range_body_filter(ngx_http_requ
         return NGX_ERROR;
     }
 
-    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10);
+    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
+                         sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
     *b->last++ = '-'; *b->last++ = '-';
     *b->last++ = CR; *b->last++ = LF;
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -935,7 +935,11 @@ ngx_http_rewrite_set(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
-    if (v->get_handler == NULL) {
+    if (v->get_handler == NULL
+        && ngx_strncasecmp(value[1].data, (u_char *) "http_", 5) != 0
+        && ngx_strncasecmp(value[1].data, (u_char *) "sent_http_", 10) != 0
+        && ngx_strncasecmp(value[1].data, (u_char *) "upstream_http_", 14) != 0)
+    {
         v->get_handler = ngx_http_rewrite_var;
         v->data = index;
     }
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -2376,7 +2376,7 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
         p++;
     }
 
-    if (p < last && *p == '/') {
+    if (p < last - 1 && *p == '/') {
         if (*(last - 1) != '/') {
             goto invalid_expression;
         }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.5.19';
+our $VERSION = '0.5.20';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -70,9 +70,13 @@ static char *ngx_http_core_internal(ngx_
     void *conf);
 
 static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);
 
 static ngx_conf_post_t  ngx_http_core_lowat_post =
-                                                 { ngx_http_core_lowat_check };
+    { ngx_http_core_lowat_check };
+
+static ngx_conf_post_handler_pt  ngx_http_core_pool_size_p =
+    ngx_http_core_pool_size;
 
 static ngx_conf_deprecated_t  ngx_conf_deprecated_optimize_host_names = {
     ngx_conf_deprecated, "optimize_host_names", "optimize_server_names"
@@ -129,14 +133,14 @@ static ngx_command_t  ngx_http_core_comm
       ngx_conf_set_size_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_core_srv_conf_t, connection_pool_size),
-      NULL },
+      &ngx_http_core_pool_size_p },
 
     { ngx_string("request_pool_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_core_srv_conf_t, request_pool_size),
-      NULL },
+      &ngx_http_core_pool_size_p },
 
     { ngx_string("client_header_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
@@ -295,6 +299,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, sendfile),
       NULL },
 
+    { ngx_string("sendfile_max_chunk"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),
+      NULL },
+
     { ngx_string("tcp_nopush"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -2191,6 +2202,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->internal = NGX_CONF_UNSET;
     lcf->client_body_in_file_only = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
+    lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;
     lcf->tcp_nopush = NGX_CONF_UNSET;
     lcf->tcp_nodelay = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET_MSEC;
@@ -2359,6 +2371,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_value(conf->client_body_in_file_only,
                               prev->client_body_in_file_only, 0);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
+    ngx_conf_merge_size_value(conf->sendfile_max_chunk,
+                              prev->sendfile_max_chunk, 0);
     ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
     ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);
 
@@ -3042,3 +3056,19 @@ ngx_http_core_lowat_check(ngx_conf_t *cf
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)
+{
+    size_t *sp = data;
+
+    if (*sp < NGX_MIN_POOL_SIZE) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "pool must be no less than %uz", NGX_MIN_POOL_SIZE);
+
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -245,6 +245,7 @@ struct ngx_http_core_loc_conf_s {
     size_t        send_lowat;              /* send_lowat */
     size_t        postpone_output;         /* postpone_output */
     size_t        limit_rate;              /* limit_rate */
+    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
 
     ngx_msec_t    client_body_timeout;     /* client_body_timeout */
     ngx_msec_t    send_timeout;            /* send_timeout */
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -32,6 +32,71 @@ static uint32_t  usual[] = {
 };
 
 
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
+        && m[4] == c4
+
+#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
+        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
+
+#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
+        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
+
+#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
+        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
+
+#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
+    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
+        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
+        && m[8] == c8
+
+#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
+
+#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
+    m[0] == c0 && m[1] == c1 && m[2] == c2
+
+#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
+    m[0] == c0 && m[2] == c2 && m[3] == c3
+
+#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
+
+#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
+        && m[4] == c4 && m[5] == c5
+
+#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
+        && m[4] == c4 && m[5] == c5 && m[6] == c6
+
+#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
+        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
+
+#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
+        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
+
+#endif
+
+
 /* gcc, icc, msvc and others compile these switches as an jump table */
 
 ngx_int_t
@@ -92,12 +157,12 @@ ngx_http_parse_request_line(ngx_http_req
                 switch (p - m) {
 
                 case 3:
-                    if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
+                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
                         r->method = NGX_HTTP_GET;
                         break;
                     }
 
-                    if (m[0] == 'P' && m[1] == 'U' && m[2] == 'T') {
+                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
                         r->method = NGX_HTTP_PUT;
                         break;
                     }
@@ -107,31 +172,29 @@ ngx_http_parse_request_line(ngx_http_req
                 case 4:
                     if (m[1] == 'O') {
 
-                        if (m[0] == 'P' && m[2] == 'S' && m[3] == 'T') {
+                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
                             r->method = NGX_HTTP_POST;
                             break;
                         }
 
-                        if (m[0] == 'C' && m[2] == 'P' && m[3] == 'Y') {
+                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
                             r->method = NGX_HTTP_COPY;
                             break;
                         }
 
-                        if (m[0] == 'M' && m[2] == 'V' && m[3] == 'E') {
+                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
                             r->method = NGX_HTTP_MOVE;
                             break;
                         }
 
-                        if (m[0] == 'L' && m[2] == 'C' && m[3] == 'K') {
+                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
                             r->method = NGX_HTTP_LOCK;
                             break;
                         }
 
                     } else {
 
-                        if (m[0] == 'H' && m[1] == 'E'
-                            && m[2] == 'A' && m[3] == 'D')
-                        {
+                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
                             r->method = NGX_HTTP_HEAD;
                             break;
                         }
@@ -140,31 +203,23 @@ ngx_http_parse_request_line(ngx_http_req
                     break;
 
                 case 5:
-                    if (m[0] == 'M' && m[1] == 'K'
-                        && m[2] == 'C' && m[3] == 'O' && m[4] == 'L')
-                    {
+                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
                         r->method = NGX_HTTP_MKCOL;
                     }
 
-                    if (m[0] == 'T' && m[1] == 'R'
-                        && m[2] == 'A' && m[3] == 'C' && m[4] == 'E')
-                    {
+                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
                         r->method = NGX_HTTP_TRACE;
                     }
 
                     break;
 
                 case 6:
-                    if (m[0] == 'D' && m[1] == 'E' && m[2] == 'L'
-                        && m[3] == 'E' && m[4] == 'T' && m[5] == 'E')
-                    {
+                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
                         r->method = NGX_HTTP_DELETE;
                         break;
                     }
 
-                    if (m[0] == 'U' && m[1] == 'N' && m[2] == 'L'
-                        && m[3] == 'O' && m[4] == 'C' && m[5] == 'K')
-                    {
+                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
                         r->method = NGX_HTTP_UNLOCK;
                         break;
                     }
@@ -172,9 +227,7 @@ ngx_http_parse_request_line(ngx_http_req
                     break;
 
                 case 7:
-                    if (m[0] == 'O' && m[1] == 'P'
-                        && m[2] == 'T' && m[3] == 'I'
-                        && m[4] == 'O' && m[5] == 'N' && m[6] == 'S')
+                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
                     {
                         r->method = NGX_HTTP_OPTIONS;
                     }
@@ -182,9 +235,7 @@ ngx_http_parse_request_line(ngx_http_req
                     break;
 
                 case 8:
-                    if (m[0] == 'P' && m[1] == 'R'
-                        && m[2] == 'O' && m[3] == 'P' && m[4] == 'F'
-                        && m[5] == 'I' && m[6] == 'N' && m[7] == 'D')
+                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
                     {
                         r->method = NGX_HTTP_PROPFIND;
                     }
@@ -192,9 +243,8 @@ ngx_http_parse_request_line(ngx_http_req
                     break;
 
                 case 9:
-                    if (m[0] == 'P' && m[1] == 'R' && m[2] == 'O'
-                        && m[3] == 'P' && m[4] == 'P' && m[5] == 'A'
-                        && m[6] == 'T' && m[7] == 'C' && m[8] == 'H')
+                    if (ngx_str9cmp(m,
+                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
                     {
                         r->method = NGX_HTTP_PROPPATCH;
                     }
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -1269,12 +1269,13 @@ ngx_http_variables_init_vars(ngx_conf_t 
 
         for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
 
-            if (v[i].name.len == key[n].key.len
+            av = key[n].value;
+
+            if (av->get_handler
+                && v[i].name.len == key[n].key.len
                 && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
                    == 0)
             {
-                av = key[n].value;
-
                 v[i].get_handler = av->get_handler;
                 v[i].data = av->data;
 
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -47,7 +47,7 @@ ngx_module_t  ngx_http_write_filter_modu
 ngx_int_t
 ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    off_t                      size, sent, to_send;
+    off_t                      size, sent, limit;
     ngx_uint_t                 last, flush;
     ngx_chain_t               *cl, *ln, **ll, *chain;
     ngx_connection_t          *c;
@@ -210,28 +210,31 @@ ngx_http_write_filter(ngx_http_request_t
     }
 
     if (r->limit_rate) {
-        to_send = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent;
+        limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent;
 
-        if (to_send <= 0) {
+        if (limit <= 0) {
             c->write->delayed = 1;
             ngx_add_timer(c->write,
-                          (ngx_msec_t) (- to_send * 1000 / r->limit_rate + 1));
+                          (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));
 
             c->buffered |= NGX_HTTP_WRITE_BUFFERED;
 
             return NGX_AGAIN;
         }
 
+    } else if (clcf->sendfile_max_chunk) {
+        limit = clcf->sendfile_max_chunk;
+
     } else {
-        to_send = 0;
+        limit = 0;
     }
 
     sent = c->sent;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http write filter to send %O", to_send);
+                   "http write filter limit %O", limit);
 
-    chain = c->send_chain(c, r->out, to_send);
+    chain = c->send_chain(c, r->out, limit);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http write filter %p", chain);
@@ -241,10 +244,14 @@ ngx_http_write_filter(ngx_http_request_t
         return NGX_ERROR;
     }
 
-    if (to_send) {
+    if (r->limit_rate) {
         sent = c->sent - sent;
         c->write->delayed = 1;
         ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1));
+
+    } else if (c->write->ready && clcf->sendfile_max_chunk) {
+        c->write->delayed = 1;
+        ngx_add_timer(c->write, 1);
     }
 
     for (cl = r->out; cl && cl != chain; /* void */) {
--- a/src/os/unix/ngx_linux_sendfile_chain.c
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -254,6 +254,10 @@ ngx_linux_sendfile_chain(ngx_connection_
 #else
             offset = (int32_t) file->file_pos;
 #endif
+
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "sendfile: @%O %uz", file->file_pos, file_size);
+
             rc = sendfile(c->fd, file->file->fd, &offset, file_size);
 
             if (rc == -1) {
--- a/src/os/unix/ngx_sunpro_atomic_sparc64.h
+++ b/src/os/unix/ngx_sunpro_atomic_sparc64.h
@@ -24,7 +24,7 @@ static ngx_inline ngx_atomic_uint_t
 ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
     ngx_atomic_uint_t set)
 {
-    NGX_CASA(set, old, lock);
+    set = NGX_CASA(set, old, lock);
 
     return (set == old);
 }
@@ -41,7 +41,7 @@ ngx_atomic_fetch_add(ngx_atomic_t *value
 
         res = old + add;
 
-        NGX_CASA(res, old, value);
+        res = NGX_CASA(res, old, value);
 
         if (res == old) {
             return res;