Function Definition
Ngx_int_t ngx_output_chain (ngx_output_chain_ctx_tCTX, ngx_chain_tIn)
The purpose of the function is to send data in. CTX is used to save the sending context, because sending usually cannot be completed at one time. Nginx uses the et mode to easily manage network programming events. However, it is complicated to process events in programming and requires constant loop processing. The function of the event is returned, the number of times is not determined. Therefore, you need to use the context object to save the link sent.
The HTTP Proxy module depends on the HTTP upstream module. The ngx_http_upstream_send_request function calls the ngx_output_chain function to send client request data to the backend server. The call is as follows:
Rc = ngx_output_chain (& U-> output, U-> request_sent? Null: U-> request_bufs );
U-> request_bufs is the data requested by the client
The following describes the functions and implementation mechanisms in ngx_output_chain.
Ngx_int_t
Ngx_output_chain (ngx_output_chain_ctx_tCTX, ngx_chain_tIn)
{
Off_t bsize; ngx_int_t RC, last; ngx_chain_t * Cl, * Out, ** last_out; If (CTX-> In = NULL & CTX-> busy = NULL) {If (in = NULL) {return CTX-> output_filter (CTX-> filter_ctx, In);} // only one Buf is to be sent, 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 );}} // append the output in to the CTX-> in Chain List. The chain object is newly created, the Buf object is also reused in the 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; (;;){
If (ngx_have_file_aio)
if (ctx->aio) { return NGX_AGAIN; }
Endif
While (CTX-> In) {// traverse the CTX-> in Chain List and process in, only once. If no data is sent, enter the function next time, CTX-> In is null/** 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 % 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) {// After the Buf does not need to be copied, out points to the chain to which CTX-> in points // CTX-> in points to the next 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); // copy Buf 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); // send data, filter_ctx is an ngx_chain_writer_ctx_t object. Various context objects in nginx are very important because they need to record many States. 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 ;}
}
Detailed analysis of the ngx_output_chain_add_copy Function
Function call: ngx_output_chain_add_copy (CTX-> pool, & CTX-> In, in)
The CTX-> pool parameter is the pool in the request. The CTX-> in parameter is the data to be processed, that is, request_bufs in upstream.
Final effect of this function: Create a new chain object and append it to the CTX-> in list. These objects point to the Buf object in the input in.
Static ngx_int_t
Ngx_output_chain_add_copy (ngx_pool_tPool, ngx_chain_t* Chain,
ngx_chain_t *in)
{
ngx_chain_t *cl, **ll;
If (ngx_sendfile_limit)
ngx_buf_t *b, *buf;
Endif
LL = chain; // chain points to in CTX. In is initially null // After receiving cyclically, ll points to the next variable of the last chain in the in chain in CTX for (CL = * chain; CL = Cl-> next) {ll = & CL-> next ;} while (in) {// traverse the chain Cl = ngx_alloc_chain_link (pool) to be processed; // obtain a chain object from the pool if (CL = NULL) {return ngx_error ;}
If (ngx_sendfile_limit)
Buf = In-> Buf; If (BUF-> in_file & Buf-> file_pos <ngx_sendfile_limit & Buf-> file_last> ngx_sendfile_limit) {// Buf split, do not read this part first/* Split a file BUF on two bufs by the sendfile limit */B = ngx_calloc_buf (pool); If (B = NULL) {return ngx_error ;} ngx_memcpy (B, Buf, sizeof (ngx_buf_t); If (then (BUF) {Buf-> POS + = (ssize_t) (ngx_sendfile_limit-Buf-> file_pos ); b-> last = Buf-> Pos;} Buf-> file_pos = ngx_sendfile_limit; B-> file_last = ngx_sendfile_limit; CL-> Buf = B ;} else {Cl-> Buf = Buf; In = In-> next ;}
Else
CL-> Buf = In-> Buf; // The Buf object is still original, and the chain object is newly created in = In-> next;
Endif
CL-> next = NULL; * LL = Cl; // connect to ll. ll is the next pointer of CTX-> In the last chain. LL = & CL-> next ;} return ngx_ OK;
}
The ngx_alloc_chain_link function gets a chain from the pool. This function will reuse the chain in the pool.
Ngx_chain_t *
Ngx_alloc_chain_link (ngx_pool_t * Pool)
{
Ngx_chain_t * Cl; CL = pool-> chain; If (CL) {// There is an idle chain pool in the pool-> chain = Cl-> next; return Cl ;} CL = ngx_palloc (pool, sizeof (ngx_chain_t); If (CL = NULL) {return NULL;} return Cl;
}
This function determines that the chain does not need to be copied, the conditions are complex, and analysis is performed when problems occur. If 1 is returned, no replication is required. If 0 is returned, the chain needs to be copied.
Static ngx_inline ngx_int_t
Ngx_output_chain_as_is (ngx_output_chain_ctx_tCTX, ngx_buf_tBuf)
{
Ngx_uint_t sendfile; If (ngx_buf_special (BUF) {// special Buf does not need to copy return 1;} If (BUF-> in_file & Buf-> file-> directio) {return 0;} sendfile = CTX-> sendfile;
If (ngx_sendfile_limit)
if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0;}
Endif
If (! Sendfile) {If (! Ngx_buf_in_memory (BUF) {return 0;} Buf-> in_file = 0;} If (CTX-> need_in_memory &&! Ngx_buf_in_memory (BUF) {// it must be in the memory, but not in the memory. Copy return 0 ;} if (CTX-> need_in_temp & (BUF-> memory | Buf-> MMAP) {return 0;} return 1;
}
Create temporary Buf
Static ngx_int_t
Ngx_output_chain_align_file_buf (ngx_output_chain_ctx_t * CTX, off_t bsize)
{
Size_t size; ngx_buf_t * In; In = CTX-> In-> Buf; If (in-> file = NULL |! In-> file-> directio ){//? This flag will be used later, and it will feel like nginx is compatible with various situations. Return ngx_declined;} CTX-> directio = 1; size = (size_t) (In-> file_pos-(In-> file_pos &~ (CTX-> alignment-1); If (size = 0) {If (bsize> = (off_t) CTX-> bufs. size) {return ngx_declined;} size = (size_t) bsize;} else {size = (size_t) CTX-> alignment-size; If (off_t) size> bsize) {size = (size_t) bsize ;}} CTX-> Buf = ngx_create_temp_buf (CTX-> pool, size); If (CTX-> Buf = NULL) {return ngx_error ;} /** we do not set CTX-> Buf-> tag, because we do not want * to reuse the Buf via CTX-> free list */
If (ngx_have_aligned_directio)
ctx->unaligned = 1;
Endif
return NGX_OK;
}
Buf replication, from CTX-> in to CTX-> Buf
Possible memory replication, possibly reading data from files
Static ngx_int_t
Ngx_output_chain_copy_buf (ngx_output_chain_ctx_t * CTX)
{
off_t size;ssize_t n;ngx_buf_t *src, *dst;ngx_uint_t sendfile;src = ctx->in->buf;dst = ctx->buf;size = ngx_buf_size(src);size = ngx_min(size, dst->end - dst->pos);sendfile = ctx->sendfile & !ctx->directio;
If (ngx_sendfile_limit)
if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0;}
Endif
if (ngx_buf_in_memory(src)) { ngx_memcpy(dst->pos, src->pos, (size_t) size); src->pos += (size_t) size; dst->last += (size_t) size; if (src->in_file) { if (sendfile) { dst->in_file = 1; dst->file = src->file; dst->file_pos = src->file_pos; dst->file_last = src->file_pos + size; } else { dst->in_file = 0; } src->file_pos += size; } else { dst->in_file = 0; } if (src->pos == src->last) { dst->flush = src->flush; dst->last_buf = src->last_buf; dst->last_in_chain = src->last_in_chain; }} else {
If (ngx_have_aligned_directio)
if (ctx->unaligned) { if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_off_n " \"%s\" failed", src->file->name.data); } }
Endif if (ngx_have_file_aio)
if (ctx->aio_handler) { n = ngx_file_aio_read(src->file, dst->pos, (size_t) size, src->file_pos, ctx->pool); if (n == NGX_AGAIN) { ctx->aio_handler(ctx, src->file); return NGX_AGAIN; } } else { n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos); }
Else
n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
Endif if (ngx_have_aligned_directio)
if (ctx->unaligned) { ngx_err_t err; err = ngx_errno; if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_on_n " \"%s\" failed", src->file->name.data); } ngx_set_errno(err); ctx->unaligned = 0; }
Endif
if (n == NGX_ERROR) { return (ngx_int_t) n; } if (n != size) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, ngx_read_file_n " read only %z of %O from \"%s\"", n, size, src->file->name.data); return NGX_ERROR; } dst->last += n; if (sendfile) { dst->in_file = 1; dst->file = src->file; dst->file_pos = src->file_pos; dst->file_last = src->file_pos + n; } else { dst->in_file = 0; } src->file_pos += n; if (src->file_pos == src->file_last) { dst->flush = src->flush; dst->last_buf = src->last_buf; dst->last_in_chain = src->last_in_chain; }}return NGX_OK;
}
Ngx_output_chain Function Analysis