nginx-0.0.1-2003-10-22-11:05:29 import

This commit is contained in:
Igor Sysoev 2003-10-22 07:05:29 +00:00
parent 419f9aceb4
commit 5bf3d25d69
15 changed files with 154 additions and 83 deletions

View File

@ -125,7 +125,7 @@ int ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **ch, ngx_chain_t *in)
void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out)
ngx_chain_t **out, ngx_hunk_tag_t tag)
{
ngx_chain_t *te;
@ -154,9 +154,7 @@ void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
}
#endif
/* TODO: change to hunk->tag */
if (!((*busy)->hunk->type & NGX_HUNK_TEMP)) {
if ((*busy)->hunk->tag != tag) {
*busy = (*busy)->next;
continue;
}

View File

@ -34,25 +34,28 @@
/* last hunk */
#define NGX_HUNK_LAST 0x2000
#define NGX_HUNK_LAST_SHADOW 0x4000
#define NGX_HUNK_TEMP_FILE 0x8000
typedef void * ngx_hunk_tag_t;
typedef struct ngx_hunk_s ngx_hunk_t;
struct ngx_hunk_s {
char *pos;
char *last;
off_t file_pos;
off_t file_last;
char *pos;
char *last;
off_t file_pos;
off_t file_last;
int type;
char *start; /* start of hunk */
char *end; /* end of hunk */
char *pre_start; /* start of pre-allocated hunk */
char *post_end; /* end of post-allocated hunk */
int tag;
ngx_file_t *file;
ngx_hunk_t *shadow;
int type;
char *start; /* start of hunk */
char *end; /* end of hunk */
char *pre_start; /* start of pre-allocated hunk */
char *post_end; /* end of post-allocated hunk */
ngx_hunk_tag_t tag;
ngx_file_t *file;
ngx_hunk_t *shadow;
/* STUB */ int num;
};
@ -119,7 +122,7 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_pool_t *pool, int size,
int ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **ch, ngx_chain_t *in);
void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out);
ngx_chain_t **out, ngx_hunk_tag_t tag);

View File

@ -27,12 +27,13 @@ int ngx_event_pipe(ngx_event_pipe_t *p, int do_write)
}
p->read = 0;
p->upstream_blocked = 0;
if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
return NGX_ABORT;
}
if (!p->read) {
if (!p->read && !p->upstream_blocked) {
break;
}
@ -140,6 +141,8 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
* a downstream is ready then write the hunks to a downstream
*/
p->upstream_blocked = 1;
ngx_log_debug(p->log, "downstream ready");
break;
@ -184,6 +187,8 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
break;
}
ngx_log_debug(p->log, "HUNK_FREE: %d" _ chain->hunk->num);
n = ngx_recv_chain(p->upstream, chain);
ngx_log_debug(p->log, "recv_chain: %d" _ n);
@ -218,6 +223,8 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
if (n >= size) {
ce->hunk->last = ce->hunk->end;
/* STUB */ ce->hunk->num = p->num++;
if (p->input_filter(p, ce->hunk) == NGX_ERROR) {
return NGX_ABORT;
}
@ -235,6 +242,7 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
}
if ((p->upstream_eof || p->upstream_error) && p->free_raw_hunks) {
/* STUB */ p->free_raw_hunks->hunk->num = p->num++;
if (p->input_filter(p, p->free_raw_hunks->hunk) == NGX_ERROR) {
return NGX_ABORT;
}
@ -302,6 +310,8 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
p->out = p->out->next;
ngx_remove_shadow_free_raw_hunk(&p->free_raw_hunks, ce->hunk);
ngx_log_debug(p->log, "HUNK OUT: %d %x" _ ce->hunk->num _ ce->hunk->type);
} else if (!p->cachable && p->in) {
ce = p->in;
@ -313,6 +323,8 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
p->in = p->in->next;
ngx_log_debug(p->log, "HUNK IN: %d" _ ce->hunk->num);
} else {
break;
}
@ -323,22 +335,32 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
}
if (out == NULL) {
break;
ngx_log_debug(p->log, "no hunks to write BUSY: %d" _ busy_len);
if (!p->upstream_blocked || busy_len == 0) {
break;
}
/* if the upstream is blocked then write the busy hunks */
}
if (p->output_filter(p->output_ctx, out) == NGX_ERROR) {
p->downstream_error = 1;
/* handle the downstream error at the begin of the cycle. */
continue;
}
ngx_chain_update_chains(&p->free, &p->busy, &out);
/* add the free shadow raw hunks to p->free_raw_hunks */
ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag);
for (ce = p->free; ce; ce = ce->next) {
/* add the free shadow raw hunk to p->free_raw_hunks */
if (ce->hunk->type & NGX_HUNK_LAST_SHADOW) {
h = ce->hunk->shadow;
/* THINK NEEDED ??? */ h->pos = h->last = h->start;
h->pos = h->last = h->start;
h->shadow = NULL;
ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
ngx_add_after_partially_filled_hunk(&p->free_raw_hunks, te);
@ -346,6 +368,15 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
ce->hunk->type &= ~NGX_HUNK_LAST_SHADOW;
}
ce->hunk->shadow = NULL;
if (p->cyclic_temp_file && (ce->hunk->type & NGX_HUNK_TEMP_FILE)) {
/* reset p->temp_offset if all hunks had been sent */
if (ce->hunk->file_last == p->temp_offset) {
p->temp_offset = 0;
}
}
}
}
@ -355,7 +386,7 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
{
int rc, size, hunk_size;
int rc, size, hsize;
ngx_hunk_t *h;
ngx_chain_t *ce, *te, *next, *out, **le, **last_free;
@ -389,17 +420,17 @@ static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
ngx_log_debug(p->log, "offset: %d" _ p->temp_offset);
do {
hunk_size = ce->hunk->last - ce->hunk->pos;
hsize = ce->hunk->last - ce->hunk->pos;
ngx_log_debug(p->log, "hunk size: %d" _ hunk_size);
ngx_log_debug(p->log, "hunk size: %d" _ hsize);
if ((size + hunk_size > p->temp_file_write_size)
|| (p->temp_offset + hunk_size > p->max_temp_file_size))
if ((size + hsize > p->temp_file_write_size)
|| (p->temp_offset + size + hsize > p->max_temp_file_size))
{
break;
}
size += hunk_size;
size += hsize;
le = &ce->next;
ce = ce->next;
@ -438,12 +469,17 @@ ngx_log_debug(p->log, "size: %d" _ size);
ce->next = NULL;
h = ce->hunk;
h->type |= NGX_HUNK_FILE;
h->file = p->temp_file;
h->file_pos = p->temp_offset;
p->temp_offset += h->last - h->pos;
h->file_last = p->temp_offset;
if (p->cachable) {
h->type |= NGX_HUNK_FILE;
} else {
h->type |= NGX_HUNK_FILE|NGX_HUNK_TEMP_FILE;
}
ngx_chain_add_ce(p->out, p->last_out, ce);
if (h->type & NGX_HUNK_LAST_SHADOW) {
@ -479,10 +515,12 @@ int ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_hunk_t *hunk)
ngx_memcpy(h, hunk, sizeof(ngx_hunk_t));
h->shadow = hunk;
h->tag = p->tag;
h->type |= NGX_HUNK_LAST_SHADOW|NGX_HUNK_RECYCLED;
hunk->shadow = h;
ngx_alloc_ce_and_set_hunk(ce, h, p->pool, NGX_ERROR);
ngx_log_debug(p->log, "HUNK %d" _ h->num);
ngx_chain_add_ce(p->in, p->last_in, ce);
return NGX_OK;

View File

@ -39,13 +39,16 @@ struct ngx_event_pipe_s {
unsigned read:1;
unsigned cachable:1;
unsigned upstream_done:1;
unsigned upstream_eof:1;
unsigned upstream_error:1;
unsigned upstream_eof:1;
unsigned upstream_blocked:1;
unsigned downstream_done:1;
unsigned downstream_error:1;
unsigned cyclic_temp_file:1;
int hunks;
ngx_bufs_t bufs;
ngx_hunk_tag_t tag;
size_t max_busy_len;
@ -65,6 +68,7 @@ struct ngx_event_pipe_s {
ngx_file_t *temp_file;
ngx_path_t *temp_path;
char *temp_file_warn;
/* STUB */ int num;
};

View File

@ -287,6 +287,8 @@ static int ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_create_temp_hunk(r->pool, conf->bufs.size,
0, 0),
ngx_http_gzip_error(ctx));
ctx->out_hunk->tag = (ngx_hunk_tag_t)
&ngx_http_gzip_filter_module;
ctx->out_hunk->type |= NGX_HUNK_RECYCLED;
ctx->hunks++;
@ -417,7 +419,8 @@ ngx_log_debug(r->connection->log, "DEFLATE(): %08x %08x %d %d %d" _
return ngx_http_gzip_error(ctx);
}
ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out);
ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
(ngx_hunk_tag_t) &ngx_http_gzip_filter_module);
ctx->last_out = &ctx->out;
}
}

View File

@ -410,6 +410,7 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev)
ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
}
n = ngx_http_proxy_read_upstream_header(p);
@ -712,6 +713,7 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p)
ep->output_filter = (ngx_event_pipe_output_filter_pt)
ngx_http_output_filter;
ep->output_ctx = r;
ep->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
ep->bufs = p->lcf->bufs;
ep->max_busy_len = p->lcf->max_busy_len;
ep->upstream = p->upstream.connection;
@ -720,7 +722,7 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p)
ep->log = r->connection->log;
ep->temp_path = p->lcf->temp_path;
ep->temp_file = ngx_palloc(r->pool, sizeof(ngx_file_t));
ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
if (ep->temp_file == NULL) {
ngx_http_proxy_finalize_request(p, 0);
return;
@ -750,10 +752,23 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p)
*/
p->header_in->last = p->header_in->pos;
/* STUB */ ep->cachable = 1;
#if 0
ep->max_temp_file_size = 1000000000;
#endif
/* STUB */ ep->cachable = 0;
if (p->lcf->cyclic_temp_file) {
/*
* we need to disable the use of sendfile() if we use cyclic temp file
* because the writing a new data can interfere with sendfile
* that uses the same kernel file pages
*/
ep->cyclic_temp_file = 1;
r->sendfile = 0;
} else {
ep->cyclic_temp_file = 0;
r->sendfile = 1;
}
p->event_pipe = ep;
@ -1162,16 +1177,17 @@ static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->header_size = 4096;
conf->read_timeout = 30000;
conf->bufs.num = 10;
conf->bufs.num = 5;
conf->bufs.size = 4096;
conf->max_busy_len = 8192;
/* CHECK in _init conf->max_temp_size >= conf->bufs.size !!! */
conf->max_temp_file_size = 4096 * 6;
conf->max_temp_file_size = 4096 * 3;
conf->temp_file_write_size = 4096 * 1;
conf->temp_file_write_size = 4096 * 2;
conf->cyclic_temp_file= 1;
ngx_test_null(conf->temp_path, ngx_pcalloc(cf->pool, sizeof(ngx_path_t)),
NULL);

View File

@ -26,10 +26,11 @@ typedef struct {
ngx_bufs_t bufs;
/* STUB */
/* STUB names */
int max_busy_len;
int max_temp_file_size;
int temp_file_write_size;
int cyclic_temp_file;
/* */
ngx_path_t *temp_path;

View File

@ -130,6 +130,13 @@ static ngx_command_t ngx_http_core_commands[] = {
offsetof(ngx_http_core_loc_conf_t, doc_root),
NULL},
{ngx_string("sendfile"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_core_loc_conf_t, sendfile),
NULL},
{ngx_string("send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
@ -334,7 +341,6 @@ int ngx_http_find_location_config(ngx_http_request_t *r)
int i, rc;
ngx_http_core_loc_conf_t *clcf, **clcfp;
ngx_http_core_srv_conf_t *cscf;
ngx_http_write_filter_conf_t *wcf;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
@ -364,14 +370,15 @@ ngx_log_debug(r->connection->log, "rc: %d" _ rc);
}
}
wcf = ngx_http_get_module_loc_conf(r, ngx_http_write_filter_module);
if (!(ngx_io.flags & NGX_IO_SENDFILE) || !wcf->sendfile) {
r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!(ngx_io.flags & NGX_IO_SENDFILE) || !clcf->sendfile) {
r->sendfile = 0;
} else {
r->sendfile = 1;
}
if (clcf->handler) {
/*
* if the location already has content handler then skip
@ -828,6 +835,7 @@ static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf)
*/
lcf->sendfile = NGX_CONF_UNSET;
lcf->send_timeout = NGX_CONF_UNSET;
lcf->discarded_buffer_size = NGX_CONF_UNSET;
lcf->keepalive_timeout = NGX_CONF_UNSET;
@ -896,6 +904,7 @@ static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf,
ngx_conf_merge_str_value(conf->default_type,
prev->default_type, "text/plain");
ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 10000);
ngx_conf_merge_size_value(conf->discarded_buffer_size,
prev->discarded_buffer_size, 1500);

View File

@ -110,6 +110,7 @@ typedef struct {
ngx_array_t *types;
ngx_str_t default_type;
int sendfile; /* sendfile */
ngx_msec_t send_timeout; /* send_timeout */
ssize_t send_lowat; /* send_lowat */
ssize_t discarded_buffer_size; /* discarded_buffer_size */

View File

@ -7,12 +7,6 @@
#define NGX_HTTP_FILTER_NEED_TEMP 4
typedef struct {
ssize_t buffer_output;
int sendfile;
} ngx_http_write_filter_conf_t;
int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in);
int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
@ -20,7 +14,5 @@ int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
extern int (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
extern ngx_module_t ngx_http_write_filter_module;
#endif /* _NGX_HTTP_FILTER_H_INCLUDED_ */

View File

@ -32,7 +32,8 @@ typedef struct {
} ngx_http_output_filter_ctx_t;
static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src);
static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
int sendfile);
static void *ngx_http_output_filter_create_conf(ngx_conf_t *cf);
static char *ngx_http_output_filter_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
@ -77,7 +78,8 @@ ngx_module_t ngx_http_output_filter_module = {
#define need_to_copy(r, hunk) \
(!ngx_hunk_special(hunk) \
&& (((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY) \
&& (!r->sendfile \
|| ((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY) \
&& (hunk->type & NGX_HUNK_IN_MEMORY) == 0) \
|| ((r->filter & NGX_HTTP_FILTER_NEED_TEMP) \
&& (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP)))))
@ -162,6 +164,8 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_create_temp_hunk(r->pool, conf->bufs.size,
0, 0),
NGX_ERROR);
ctx->hunk->tag = (ngx_hunk_tag_t)
&ngx_http_output_filter_module;
ctx->hunk->type |= NGX_HUNK_RECYCLED;
ctx->hunks++;
@ -170,7 +174,8 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
}
rc = ngx_http_output_filter_copy_hunk(ctx->hunk, ctx->in->hunk);
rc = ngx_http_output_filter_copy_hunk(ctx->hunk, ctx->in->hunk,
r->sendfile);
if (rc == NGX_ERROR) {
return rc;
@ -215,13 +220,15 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
last = ngx_next_filter(r, ctx->out);
ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out);
ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
(ngx_hunk_tag_t) &ngx_http_output_filter_module);
ctx->last_out = &ctx->out;
}
}
static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src)
static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
int sendfile)
{
ssize_t n, size;
@ -279,6 +286,10 @@ ngx_log_debug(src->file->log, "READ: %qd:%qd %X:%X %X:%X" _
src->file_pos += n;
dst->last += n;
if (!sendfile) {
dst->type &= ~NGX_HUNK_FILE;
}
if ((src->type & NGX_HUNK_LAST) && src->file_pos == src->file_last) {
dst->type |= NGX_HUNK_LAST;
}

View File

@ -190,6 +190,9 @@ struct ngx_http_request_s {
#endif
unsigned pipeline:1;
/* can we use sendfile ? */
unsigned sendfile:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned keepalive:1;

View File

@ -5,6 +5,11 @@
#include <ngx_http.h>
typedef struct {
ssize_t buffer_output;
} ngx_http_write_filter_conf_t;
typedef struct {
ngx_chain_t *out;
} ngx_http_write_filter_ctx_t;
@ -18,13 +23,6 @@ static int ngx_http_write_filter_init(ngx_cycle_t *cycle);
static ngx_command_t ngx_http_write_filter_commands[] = {
{ngx_string("sendfile"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_write_filter_conf_t, sendfile),
NULL},
{ngx_string("buffer_output"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
@ -94,9 +92,6 @@ int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
}
conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
ngx_http_write_filter_module);
/* add the new chain to the existent one */
for (/* void */; in; in = in->next) {
@ -107,10 +102,6 @@ int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
*le = ce;
le = &ce->next;
if (!(ngx_io.flags & NGX_IO_SENDFILE) || !conf->sendfile) {
ce->hunk->type &= ~NGX_HUNK_FILE;
}
size += ngx_hunk_size(ce->hunk);
if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
@ -128,6 +119,9 @@ int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
last _ flush _ size);
#endif
conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
ngx_http_write_filter_module);
/*
* avoid the output if there is no last hunk, no flush point and
* size of the hunks is smaller then "buffer_output"
@ -174,7 +168,6 @@ static void *ngx_http_write_filter_create_conf(ngx_conf_t *cf)
NULL);
conf->buffer_output = NGX_CONF_UNSET;
conf->sendfile = NGX_CONF_UNSET;
return conf;
}
@ -187,7 +180,6 @@ static char *ngx_http_write_filter_merge_conf(ngx_conf_t *cf,
ngx_http_write_filter_conf_t *conf = child;
ngx_conf_merge_size_value(conf->buffer_output, prev->buffer_output, 1460);
ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
return NULL;
}

View File

@ -77,15 +77,15 @@ ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,
ce = ce->next;
}
if (lseek(file->fd, offset, SEEK_SET) == -1) {
ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed");
return NGX_ERROR;
if (file->offset != offset) {
if (lseek(file->fd, offset, SEEK_SET) == -1) {
ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed");
return NGX_ERROR;
}
}
n = writev(file->fd, (struct iovec *) io.elts, io.nelts);
ngx_destroy_array(&io);
if (n == -1) {
ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "writev() failed");
return NGX_ERROR;

View File

@ -83,9 +83,9 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in)
if (ce && (ce->hunk->type & NGX_HUNK_FILE)) {
file = ce->hunk;
ce = ce->next;
fsize = (size_t) (file->file_last - file->file_pos);
fprev = file->file_last;
ce = ce->next;
/* coalesce the neighbouring file hunks */