3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
13 #define NGX_SENDFILE_LIMIT 4096
20 static ngx_inline ngx_int_t
21 ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t
*ctx
, ngx_buf_t
*buf
);
22 static ngx_int_t
ngx_output_chain_add_copy(ngx_pool_t
*pool
,
23 ngx_chain_t
**chain
, ngx_chain_t
*in
);
24 static ngx_int_t
ngx_output_chain_copy_buf(ngx_buf_t
*dst
, ngx_buf_t
*src
,
29 ngx_output_chain(ngx_output_chain_ctx_t
*ctx
, ngx_chain_t
*in
)
35 ngx_chain_t
*cl
, *out
, **last_out
;
37 if (ctx
->in
== NULL
&& ctx
->busy
== NULL
) {
40 * the short path for the case when the ctx->in and ctx->busy chains
41 * are empty, the incoming chain is empty too or has the single buf
42 * that does not require the copy
46 return ctx
->output_filter(ctx
->filter_ctx
, in
);
50 #if (NGX_SENDFILE_LIMIT)
51 && !(in
->buf
->in_file
&& in
->buf
->file_last
> NGX_SENDFILE_LIMIT
)
53 && !ngx_output_chain_need_to_copy(ctx
, in
->buf
))
55 return ctx
->output_filter(ctx
->filter_ctx
, in
);
59 /* add the incoming buf to the chain ctx->in */
62 if (ngx_output_chain_add_copy(ctx
->pool
, &ctx
->in
, in
) == NGX_ERROR
) {
76 * cycle while there are the ctx->in bufs
77 * or there are the free output bufs to copy in
80 bsize
= ngx_buf_size(ctx
->in
->buf
);
82 if (bsize
== 0 && !ngx_buf_special(ctx
->in
->buf
)) {
84 ngx_log_error(NGX_LOG_ALERT
, ctx
->pool
->log
, 0,
85 "zero size buf in output "
86 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
87 ctx
->in
->buf
->temporary
,
88 ctx
->in
->buf
->recycled
,
89 ctx
->in
->buf
->in_file
,
94 ctx
->in
->buf
->file_pos
,
95 ctx
->in
->buf
->file_last
);
99 ctx
->in
= ctx
->in
->next
;
104 if (!ngx_output_chain_need_to_copy(ctx
, ctx
->in
->buf
)) {
106 /* move the chain link to the output chain */
112 last_out
= &cl
->next
;
118 if (ctx
->buf
== NULL
) {
120 /* get the free buf */
125 ctx
->free
= cl
->next
;
126 ngx_free_chain(ctx
->pool
, cl
);
128 } else if (out
|| ctx
->allocated
== ctx
->bufs
.num
) {
134 size
= ctx
->bufs
.size
;
137 if (ctx
->in
->buf
->last_in_chain
) {
139 if (bsize
< (off_t
) ctx
->bufs
.size
) {
142 * allocate small temp buf for the small last buf
143 * or its small last part
146 size
= (size_t) bsize
;
149 } else if (ctx
->bufs
.num
== 1
150 && (bsize
< (off_t
) (ctx
->bufs
.size
151 + (ctx
->bufs
.size
>> 2))))
154 * allocate a temp buf that equals
155 * to the last buf if the last buf size is lesser
156 * than 1.25 of bufs.size and a temp buf is single
159 size
= (size_t) bsize
;
164 ctx
->buf
= ngx_create_temp_buf(ctx
->pool
, size
);
165 if (ctx
->buf
== NULL
) {
169 ctx
->buf
->tag
= ctx
->tag
;
170 ctx
->buf
->recycled
= recycled
;
175 rc
= ngx_output_chain_copy_buf(ctx
->buf
, ctx
->in
->buf
,
178 if (rc
== NGX_ERROR
) {
182 if (rc
== NGX_AGAIN
) {
190 /* delete the completed buf from the ctx->in chain */
192 if (ngx_buf_size(ctx
->in
->buf
) == 0) {
193 ctx
->in
= ctx
->in
->next
;
196 cl
= ngx_alloc_chain_link(ctx
->pool
);
204 last_out
= &cl
->next
;
208 if (out
== NULL
&& last
!= NGX_NONE
) {
217 last
= ctx
->output_filter(ctx
->filter_ctx
, out
);
219 if (last
== NGX_ERROR
|| last
== NGX_DONE
) {
223 ngx_chain_update_chains(&ctx
->free
, &ctx
->busy
, &out
, ctx
->tag
);
229 static ngx_inline ngx_int_t
230 ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t
*ctx
, ngx_buf_t
*buf
)
234 if (ngx_buf_special(buf
)) {
238 sendfile
= ctx
->sendfile
;
240 #if (NGX_SENDFILE_LIMIT)
242 if (buf
->in_file
&& buf
->file_pos
>= NGX_SENDFILE_LIMIT
) {
250 if (!ngx_buf_in_memory(buf
)) {
257 if (ctx
->need_in_memory
&& !ngx_buf_in_memory(buf
)) {
261 if (ctx
->need_in_temp
&& (buf
->memory
|| buf
->mmap
)) {
270 ngx_output_chain_add_copy(ngx_pool_t
*pool
, ngx_chain_t
**chain
,
273 ngx_chain_t
*cl
, **ll
;
274 #if (NGX_SENDFILE_LIMIT)
280 for (cl
= *chain
; cl
; cl
= cl
->next
) {
286 cl
= ngx_alloc_chain_link(pool
);
291 #if (NGX_SENDFILE_LIMIT)
296 && buf
->file_pos
< NGX_SENDFILE_LIMIT
297 && buf
->file_last
> NGX_SENDFILE_LIMIT
)
299 b
= ngx_calloc_buf(pool
);
304 ngx_memcpy(b
, buf
, sizeof(ngx_buf_t
));
306 if (ngx_buf_in_memory(buf
)) {
307 buf
->pos
+= (ssize_t
) (NGX_SENDFILE_LIMIT
- buf
->file_pos
);
311 buf
->file_pos
= NGX_SENDFILE_LIMIT
;
312 b
->file_last
= NGX_SENDFILE_LIMIT
;
338 ngx_output_chain_copy_buf(ngx_buf_t
*dst
, ngx_buf_t
*src
, ngx_uint_t sendfile
)
343 size
= ngx_buf_size(src
);
345 if (size
> dst
->end
- dst
->pos
) {
346 size
= dst
->end
- dst
->pos
;
349 #if (NGX_SENDFILE_LIMIT)
351 if (src
->in_file
&& src
->file_pos
>= NGX_SENDFILE_LIMIT
) {
357 if (ngx_buf_in_memory(src
)) {
358 ngx_memcpy(dst
->pos
, src
->pos
, (size_t) size
);
359 src
->pos
+= (size_t) size
;
360 dst
->last
+= (size_t) size
;
366 dst
->file
= src
->file
;
367 dst
->file_pos
= src
->file_pos
;
368 dst
->file_last
= src
->file_pos
+ size
;
374 src
->file_pos
+= size
;
380 if (src
->last_buf
&& src
->pos
== src
->last
) {
385 n
= ngx_read_file(src
->file
, dst
->pos
, (size_t) size
, src
->file_pos
);
387 if (n
== NGX_ERROR
) {
388 return (ngx_int_t
) n
;
391 #if (NGX_FILE_AIO_READ)
392 if (n
== NGX_AGAIN
) {
393 return (ngx_int_t
) n
;
398 ngx_log_error(NGX_LOG_ALERT
, src
->file
->log
, 0,
399 ngx_read_file_n
" reads only %z of %O from file",
410 dst
->file
= src
->file
;
411 dst
->file_pos
= src
->file_pos
;
412 dst
->file_last
= src
->file_pos
+ n
;
420 if (src
->last_buf
&& src
->file_pos
== src
->file_last
) {
430 ngx_chain_writer(void *data
, ngx_chain_t
*in
)
432 ngx_chain_writer_ctx_t
*ctx
= data
;
437 for (size
= 0; in
; in
= in
->next
) {
440 if (ngx_buf_size(in
->buf
) == 0 && !ngx_buf_special(in
->buf
)) {
445 size
+= ngx_buf_size(in
->buf
);
447 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, ctx
->connection
->log
, 0,
448 "chain writer buf size: %uO", ngx_buf_size(in
->buf
));
450 cl
= ngx_alloc_chain_link(ctx
->pool
);
458 ctx
->last
= &cl
->next
;
461 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, ctx
->connection
->log
, 0,
462 "chain writer in: %p", ctx
->out
);
464 for (cl
= ctx
->out
; cl
; cl
= cl
->next
) {
467 if (ngx_buf_size(cl
->buf
) == 0 && !ngx_buf_special(cl
->buf
)) {
473 size
+= ngx_buf_size(cl
->buf
);
476 if (size
== 0 && !ctx
->connection
->buffered
) {
480 ctx
->out
= ctx
->connection
->send_chain(ctx
->connection
, ctx
->out
,
483 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, ctx
->connection
->log
, 0,
484 "chain writer out: %p", ctx
->out
);
486 if (ctx
->out
== NGX_CHAIN_ERROR
) {
490 if (ctx
->out
== NULL
) {
491 ctx
->last
= &ctx
->out
;
494 if (!ctx
->connection
->buffered
) {