1. ngx_http_top_body_filter
该链表用于构造响应消息的响应正文.
大致有以下模块在该链表中插入了自己的函数:
- ngx_http_range_filter_module: ngx_http_range_body_filter
- ngx_http_copy_filter_module: ngx_http_copy_filter
- ngx_http_headers_filter_module: ngx_http_trailers_filter
- ngx_http_charset_filter_module: ngx_http_charset_body_filter
- ngx_http_ssi_filter_module: ngx_http_ssi_body_filter
- ngx_http_postpone_filter_module: ngx_http_postpone_filter
- ngx_http_gzip_filter_module:ngx_http_gzip_body_filter
- ngx_http_chunked_filter_module:ngx_http_chunked_body_filter
- ngx_http_write_filter_module:ngx_http_write_filter
1.1 ngx_http_range_body_filter
static ngx_int_t
ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_range_filter_ctx_t *ctx;
if (in == NULL) {
return ngx_http_next_body_filter(r, in);
}
/* 获取 ngx_http_range_body_filter_module 模块的上下文结构体 */
ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
/* 若为 NULL 则执行链表的下一个函数 */
if (ctx == NULL) {
return ngx_http_next_body_filter(r, in);
}
if (ctx->ranges.nelts == 1) {
return ngx_http_range_singlepart_body(r, ctx, in);
}
/*
* multipart ranges are supported only if whole body is in a single buffer
*/
if (ngx_buf_special(in->buf)) {
return ngx_http_next_body_filter(r, in);
}
if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_range_multipart_body(r, ctx, in);
}
1.2 ngx_http_copy_filter
static ngx_int_t
ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_connection_t *c;
ngx_output_chain_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
ngx_http_copy_filter_conf_t *conf;
c = r->connection;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http copy filter: "%V?%V"", &r->uri, &r->args);
/* 获取 ngx_http_copy_filter_module 模块的上下文结构体 */
ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
/* 若为 NULL,则分配 */
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
/* 将该 ngx_http_copy_filter_module 模块的上下文结构体
* ngx_output_chain_ctx_t 存放到 ngx_http_request_t 结构体
* 中的 ctx 数组中,下标为单前模块的 ctx_index */
ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/* 标志位,为 1 表示使用 sendfile 系统调用发送文件 */
ctx->sendfile = c->sendfile;
ctx->need_in_memory = r->main_filter_need_in_memory
|| r->filter_need_in_memory;
ctx->need_in_temp = r->filter_need_temporary;
ctx->alignment = clcf->directio_alignment;
ctx->pool = r->pool;
ctx->bufs = conf->bufs;
ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
ctx->output_filter = (ngx_output_chain_filter_pt)
ngx_http_next_body_filter;
ctx->filter_ctx = r;
#if (NGX_HAVE_FILE_AIO)
if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
ctx->aio_handler = ngx_http_copy_aio_handler;
#if (NGX_HAVE_AIO_SENDFILE)
ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
#endif
}
#endif
#if (NGX_THREADS)
if (clcf->aio == NGX_HTTP_AIO_THREADS) {
ctx->thread_handler = ngx_http_copy_thread_handler;
}
#endif
if (in && in->buf && ngx_buf_size(in->buf)) {
r->request_output = 1;
}
}
#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
ctx->aio = r->aio;
#endif
rc = ngx_output_chain(ctx, in);
if (ctx->in == NULL) {
r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
} else {
r->buffered |= NGX_HTTP_COPY_BUFFERED;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http copy filter: %i "%V?%V"", rc, &r->uri, &r->args);
return rc;
}
1.2.1 ngx_output_chain
ngx_int_t
ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
{
off_t bsize;
ngx_int_t rc, last;
ngx_chain_t *cl, *out, **last_out;
if (ctx->in == NULL && ctx->busy == NULL
#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
&& !ctx->aio
#endif
)
{
/*
* the short path for the case when the ctx->in and ctx->busy chains
* are empty, the incoming chain is empty too or has the single buf
* that does not require the copy
*/
if (in == NULL) {
return ctx->output_filter(ctx->filter_ctx, in);
}
if (in->next == NULL
#if (NGX_SENDFILE_LIMIT)
&& !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
#endif
&& ngx_output_chain_as_is(ctx, in->buf))
{
return ctx->output_filter(ctx->filter_ctx, in);
}
}
/* add the incoming buf to the chain ctx->in */
if (in) {
if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
return NGX_ERROR;
}
}
out = NULL;
last_out = &out;
last = NGX_NONE;
for ( ;; ) {
#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
if (ctx->aio) {
return NGX_AGAIN;
}
#endif
while (ctx->in) {
/*
* cycle while there are the ctx->in bufs
* and there are the free output bufs to copy in
*/
bsize = ngx_buf_size(ctx->in->buf);
if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
"zero size buf in output "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
ctx->in->buf->temporary,
ctx->in->buf->recycled,
ctx->in->buf->in_file,
ctx->in->buf->start,
ctx->in->buf->pos,
ctx->in->buf->last,
ctx->in->buf->file,
ctx->in->buf->file_pos,
ctx->in->buf->file_last);
ngx_debug_point();
ctx->in = ctx->in->next;
continue;
}
if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
/* move the chain link to the output chain */
cl = ctx->in;
ctx->in = cl->next;
*last_out = cl;
last_out = &cl->next;
cl->next = NULL;
continue;
}
if (ctx->buf == NULL) {
rc = ngx_output_chain_align_file_buf(ctx, bsize);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc != NGX_OK) {
if (ctx->free) {
/* get the free buf */
cl = ctx->free;
ctx->buf = cl->buf;
ctx->free = cl->next;
ngx_free_chain(ctx->pool, cl);
} else if (out || ctx->allocated == ctx->bufs.num) {
break;
} else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
return NGX_ERROR;
}
}
}
rc = ngx_output_chain_copy_buf(ctx);
if (rc == NGX_ERROR) {
return rc;
}
if (rc == NGX_AGAIN) {
if (out) {
break;
}
return rc;
}
/* delete the completed buf from the ctx->in chain */
if (ngx_buf_size(ctx->in->buf) == 0) {
ctx->in = ctx->in->next;
}
cl = ngx_alloc_chain_link(ctx->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ctx->buf;
cl->next = NULL;
*last_out = cl;
last_out = &cl->next;
ctx->buf = NULL;
}
if (out == NULL && last != NGX_NONE) {
if (ctx->in) {
return NGX_AGAIN;
}
return last;
}
last = ctx->output_filter(ctx->filter_ctx, out);
if (last == NGX_ERROR || last == NGX_DONE) {
return last;
}
ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
ctx->tag);
last_out = &out;
}
}
1.3 ngx_http_trailers_filter
static ngx_int_t
ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_str_t value;
ngx_uint_t i, safe_status;
ngx_chain_t *cl;
ngx_table_elt_t *t;
ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
/* 若 trailers 为 NULL 或其他,则下一个 */
if (in == NULL
|| conf->trailers == NULL
|| !r->expect_trailers
|| r->header_only)
{
return ngx_http_next_body_filter(r, in);
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
break;
}
}
if (cl == NULL) {
return ngx_http_next_body_filter(r, in);
}
switch (r->headers_out.status) {
case NGX_HTTP_OK:
case NGX_HTTP_CREATED:
case NGX_HTTP_NO_CONTENT:
case NGX_HTTP_PARTIAL_CONTENT:
case NGX_HTTP_MOVED_PERMANENTLY:
case NGX_HTTP_MOVED_TEMPORARILY:
case NGX_HTTP_SEE_OTHER:
case NGX_HTTP_NOT_MODIFIED:
case NGX_HTTP_TEMPORARY_REDIRECT:
case NGX_HTTP_PERMANENT_REDIRECT:
safe_status = 1;
break;
default:
safe_status = 0;
break;
}
h = conf->trailers->elts;
for (i = 0; i < conf->trailers->nelts; i++) {
if (!safe_status && !h[i].always) {
continue;
}
if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
return NGX_ERROR;
}
if (value.len) {
t = ngx_list_push(&r->headers_out.trailers);
if (t == NULL) {
return NGX_ERROR;
}
t->key = h[i].key;
t->value = value;
t->hash = 1;
}
}
return ngx_http_next_body_filter(r, in);
}
1.4 ngx_http_charset_body_filter
暂略
1.5 ngx_http_ssi_body_filter
暂略
1.6 ngx_http_postpone_filter
暂略
1.7 ngx_http_gzip_body_filter
暂略
1.8 ngx_http_chunked_body_filter
暂略
1.9 ngx_http_write_filter
ngx_int_t
ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
off_t size, sent, nsent, limit;
ngx_uint_t last, flush, sync;
ngx_msec_t delay;
ngx_chain_t *cl, *ln, **ll, *chain;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
if (c->error) {
return NGX_ERROR;
}
size = 0;
flush = 0;
sync = 0;
last = 0;
/* 指向 r->out 链表的第一个元素 */
ll = &r->out;
/* find the size, the flush point and the last link of the saved chain */
/* 若此时缓存的是 body,则此时 r->out 链表中已经缓存响应消息的消息报头 */
for (cl = r->out; cl; cl = cl->next) {
ll = &cl->next;
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
"write old buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
#if 1
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"zero size buf in writer "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
cl->buf->temporary,
cl->buf->recycled,
cl->buf->in_file,
cl->buf->start,
cl->buf->pos,
cl->buf->last,
cl->buf->file,
cl->buf->file_pos,
cl->buf->file_last);
ngx_debug_point();
return NGX_ERROR;
}
#endif
size += ngx_buf_size(cl->buf);
if (cl->buf->flush || cl->buf->recycled) {
flush = 1;
}
if (cl->buf->sync) {
sync = 1;
}
if (cl->buf->last_buf) {
last = 1;
}
}
/* add the new chain to the existent one */
for (ln = in; ln; ln = ln->next) {
/* 生成一个 ngx_chain_t */
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ln->buf;
/* 将该新生成的 ngx_chain_t 添加到 r->out 链表尾部 */
*ll = cl;
ll = &cl->next;
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
"write new buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
#if 1
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"zero size buf in writer "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
cl->buf->temporary,
cl->buf->recycled,
cl->buf->in_file,
cl->buf->start,
cl->buf->pos,
cl->buf->last,
cl->buf->file,
cl->buf->file_pos,
cl->buf->file_last);
ngx_debug_point();
return NGX_ERROR;
}
#endif
size += ngx_buf_size(cl->buf);
if (cl->buf->flush || cl->buf->recycled) {
flush = 1;
}
if (cl->buf->sync) {
sync = 1;
}
/* 若 last_buf 为 1,表明这个最后一个 buf 了 */
if (cl->buf->last_buf) {
last = 1;
}
}
*ll = NULL;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter: l:%ui f:%ui s:%O", last, flush, size);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/*
* avoid the output if there are no last buf, no flush point,
* there are the incoming bufs and the size of all bufs
* is smaller than "postpone_output" directive
*/
if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
return NGX_OK;
}
if (c->write->delayed) {
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
if (size == 0
&& !(c->buffered & NGX_LOWLEVEL_BUFFERED)
&& !(last && c->need_last_buf))
{
if (last || flush || sync) {
for (cl = r->out; cl; /* void */) {
ln = cl;
cl = cl->next;
ngx_free_chain(r->pool, ln);
}
r->out = NULL;
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"the http output chain is empty");
ngx_debug_point();
return NGX_ERROR;
}
if (r->limit_rate) {
if (r->limit_rate_after == 0) {
r->limit_rate_after = clcf->limit_rate_after;
}
limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
- (c->sent - r->limit_rate_after);
if (limit <= 0) {
c->write->delayed = 1;
delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
ngx_add_timer(c->write, delay);
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
if (clcf->sendfile_max_chunk
&& (off_t) clcf->sendfile_max_chunk < limit)
{
limit = clcf->sendfile_max_chunk;
}
} else {
limit = clcf->sendfile_max_chunk;
}
/* 当前已经发送的字节数 */
sent = c->sent;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter limit %O", limit);
/* 指向 ngx_linux_sendfile_chain 函数, 将响应消息发送给客户端 */
chain = c->send_chain(c, r->out, limit);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter %p", chain);
if (chain == NGX_CHAIN_ERROR) {
c->error = 1;
return NGX_ERROR;
}
if (r->limit_rate) {
nsent = c->sent;
if (r->limit_rate_after) {
sent -= r->limit_rate_after;
if (sent < 0) {
sent = 0;
}
nsent -= r->limit_rate_after;
if (nsent < 0) {
nsent = 0;
}
}
delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
if (delay > 0) {
limit = 0;
c->write->delayed = 1;
ngx_add_timer(c->write, delay);
}
}
if (limit
&& c->write->ready
&& c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
{
c->write->delayed = 1;
ngx_add_timer(c->write, 1);
}
/* 将 r->out 链表中的元素(即 ngx_chain_t)插入到 r->pool->chain 链表中 */
for (cl = r->out; cl && cl != chain; /* void */) {
ln = cl;
cl = cl->next;
ngx_free_chain(r->pool, ln);
}
r->out = chain;
if (chain) {
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
return NGX_AGAIN;
}
return NGX_OK;
}
1.9.1 c->send_chain: ngx_linux_sendfile_chain
ngx_chain_t *
ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
{
int tcp_nodelay;
off_t send, prev_send;
size_t file_size, sent;
ssize_t n;
ngx_err_t err;
ngx_buf_t *file;
ngx_event_t *wev;
ngx_chain_t *cl;
ngx_iovec_t header;
struct iovec headers[NGX_IOVS_PREALLOCATE];
/* 获取该连接的写事件 */
wev = c->write;
if (!wev->ready) {
return in;
}
/* the maximum limit size is 2G-1 - the page size */
if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {
limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;
}
send = 0;
header.iovs = headers;
header.nalloc = NGX_IOVS_PREALLOCATE;
for ( ;; ) {
prev_send = send;
/* create the iovec and coalesce the neighbouring bufs */
/* 将 in 中的数据(除在文件中的数据外)构造成 iovec */
cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);
if (cl == NGX_CHAIN_ERROR) {
return NGX_CHAIN_ERROR;
}
send += header.size;
/* set TCP_CORK if there is a header before a file */
if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
&& header.count != 0
&& cl
&& cl->buf->in_file)
{
/* the TCP_CORK and TCP_NODELAY are mutually exclusive */
if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
tcp_nodelay = 0;
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
(const void *) &tcp_nodelay, sizeof(int)) == -1)
{
err = ngx_socket_errno;
/*
* there is a tiny chance to be interrupted, however,
* we continue a processing with the TCP_NODELAY
* and without the TCP_CORK
*/
if (err != NGX_EINTR) {
wev->error = 1;
ngx_connection_error(c, err,
"setsockopt(TCP_NODELAY) failed");
return NGX_CHAIN_ERROR;
}
} else {
c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"no tcp_nodelay");
}
}
if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
err = ngx_socket_errno;
/*
* there is a tiny chance to be interrupted, however,
* we continue a processing without the TCP_CORK
*/
if (err != NGX_EINTR) {
wev->error = 1;
ngx_connection_error(c, err,
ngx_tcp_nopush_n " failed");
return NGX_CHAIN_ERROR;
}
} else {
c->tcp_nopush = NGX_TCP_NOPUSH_SET;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"tcp_nopush");
}
}
}
/* get the file buf */
/* 这里开始获取文件中的数据 */
if (header.count == 0 && cl && cl->buf->in_file && send < limit) {
file = cl->buf;
/* coalesce the neighbouring file bufs */
/* 统计该文件的大小 */
file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);
send += file_size;
#if 1
if (file_size == 0) {
ngx_debug_point();
return NGX_CHAIN_ERROR;
}
#endif
/* 将该文件中的数据发送出去 */
n = ngx_linux_sendfile(c, file, file_size);
if (n == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
if (n == NGX_DONE) {
/* thread task posted */
return in;
}
sent = (n == NGX_AGAIN) ? 0 : n;
} else {
n = ngx_writev(c, &header);
if (n == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
sent = (n == NGX_AGAIN) ? 0 : n;
}
/* 更新已经发送的字节数 */
c->sent += sent;
in = ngx_chain_update_sent(in, sent);
if (n == NGX_AGAIN) {
wev->ready = 0;
return in;
}
if ((size_t) (send - prev_send) != sent) {
/*
* sendfile() on Linux 4.3+ might be interrupted at any time,
* and provides no indication if it was interrupted or not,
* so we have to retry till an explicit EAGAIN
*
* sendfile() in threads can also report less bytes written
* than we are prepared to send now, since it was started in
* some point in the past, so we again have to retry
*/
send = prev_send + sent;
continue;
}
if (send >= limit || in == NULL) {
return in;
}
}
}
1.9.2 ngx_output_chain_to_iovec
ngx_chain_t *
ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, size_t limit,
ngx_log_t *log)
{
size_t total, size;
u_char *prev;
ngx_uint_t n;
struct iovec *iov;
iov = NULL;
prev = NULL;
total = 0;
n = 0;
for ( /* void */ ; in && total < limit; in = in->next) {
if (ngx_buf_special(in->buf)) {
continue;
}
/* 若该buf中的数据是在文件中,则跳出该循环 */
if (in->buf->in_file) {
break;
}
if (!ngx_buf_in_memory(in->buf)) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"bad buf in output chain "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
in->buf->temporary,
in->buf->recycled,
in->buf->in_file,
in->buf->start,
in->buf->pos,
in->buf->last,
in->buf->file,
in->buf->file_pos,
in->buf->file_last);
ngx_debug_point();
return NGX_CHAIN_ERROR;
}
size = in->buf->last - in->buf->pos;
if (size > limit - total) {
size = limit - total;
}
if (prev == in->buf->pos) {
iov->iov_len += size;
} else {
if (n == vec->nalloc) {
break;
}
iov = &vec->iovs[n++];
iov->iov_base = (void *) in->buf->pos;
iov->iov_len = size;
}
prev = in->buf->pos + size;
total += size;
}
vec->count = n;
vec->size = total;
return in;
}
1.9.3 ngx_writev
ssize_t
ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec)
{
ssize_t n;
ngx_err_t err;
eintr:
n = writev(c->fd, vec->iovs, vec->count);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"writev: %z of %uz", n, vec->size);
if (n == -1) {
err = ngx_errno;
switch (err) {
case NGX_EAGAIN:
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"writev() not ready");
return NGX_AGAIN;
case NGX_EINTR:
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"writev() was interrupted");
goto eintr;
default:
c->write->error = 1;
ngx_connection_error(c, err, "writev() failed");
return NGX_ERROR;
}
}
return n;
}
1.9.4 ngx_chain_update_sent
ngx_chain_t *
ngx_chain_update_sent(ngx_chain_t *in, off_t sent)
{
off_t size;
for ( /* void */ ; in; in = in->next) {
if (ngx_buf_special(in->buf)) {
continue;
}
if (sent == 0) {
break;
}
size = ngx_buf_size(in->buf);
/* 若已将 buf 中的数据都发送出去 */
if (sent >= size) {
sent -= size;
if (ngx_buf_in_memory(in->buf)) {
/* 则更新 pos 指针的位置 */
in->buf->pos = in->buf->last;
}
if (in->buf->in_file) {
in->buf->file_pos = in->buf->file_last;
}
continue;
}
if (ngx_buf_in_memory(in->buf)) {
in->buf->pos += (size_t) sent;
}
if (in->buf->in_file) {
in->buf->file_pos += sent;
}
break;
}
return in;
}
1.9.5 ngx_chain_coalesce_file
off_t
ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit)
{
off_t total, size, aligned, fprev;
ngx_fd_t fd;
ngx_chain_t *cl;
total = 0;
cl = *in;
fd = cl->buf->file->fd;
do {
/* 计算当前需要发送的字节数 */
size = cl->buf->file_last - cl->buf->file_pos;
if (size > limit - total) {
size = limit - total;
aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
& ~((off_t) ngx_pagesize - 1);
if (aligned <= cl->buf->file_last) {
size = aligned - cl->buf->file_pos;
}
total += size;
break;
}
total += size;
fprev = cl->buf->file_pos + size;
cl = cl->next;
} while (cl
&& cl->buf->in_file
&& total < limit
&& fd == cl->buf->file->fd
&& fprev == cl->buf->file_pos);
*in = cl;
return total;
}
1.9.6 ngx_linux_sendfile
static ssize_t
ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
{
#if (NGX_HAVE_SENDFILE64)
off_t offset;
#else
int32_t offset;
#endif
ssize_t n;
ngx_err_t err;
#if (NGX_THREADS)
if (file->file->thread_handler) {
return ngx_linux_sendfile_thread(c, file, size);
}
#endif
#if (NGX_HAVE_SENDFILE64)
offset = file->file_pos;
#else
offset = (int32_t) file->file_pos;
#endif
eintr:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"sendfile: @%O %uz", file->file_pos, size);
n = sendfile(c->fd, file->file->fd, &offset, size);
if (n == -1) {
err = ngx_errno;
switch (err) {
case NGX_EAGAIN:
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"sendfile() is not ready");
return NGX_AGAIN;
case NGX_EINTR:
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"sendfile() was interrupted");
goto eintr;
default:
c->write->error = 1;
ngx_connection_error(c, err, "sendfile() failed");
return NGX_ERROR;
}
}
if (n == 0) {
/*
* if sendfile returns zero, then someone has truncated the file,
* so the offset became beyond the end of the file
*/
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"sendfile() reported that "%s" was truncated at %O",
file->file->name.data, file->file_pos);
return NGX_ERROR;
}
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O",
n, size, file->file_pos);
return n;
}