nginx/nginx
Buffer and chain (ngx_buf_t, ngx_chain_t)
Active contributors: Maxim Dounin, Sergey Kandaurov
What they are
A buffer is a span of bytes plus metadata about where those bytes live, what they mean, and what should happen after they're sent. A chain is a linked-list link of buffers. Output flowing through nginx travels as a ngx_chain_t * — the filter chain takes a chain in, transforms it, and passes a chain out. This is the universal data-movement primitive.
Definition
src/core/ngx_buf.h:
struct ngx_buf_s {
u_char *pos; /* current read position */
u_char *last; /* one past the last valid byte */
off_t file_pos;
off_t file_last;
u_char *start; /* allocation start */
u_char *end; /* allocation end */
ngx_buf_tag_t tag; /* who allocated me (for buffer reuse) */
ngx_file_t *file;
ngx_buf_t *shadow;
/* the buffer can be in memory */
unsigned temporary:1; /* writable scratch */
unsigned memory:1; /* read-only memory (e.g., a literal) */
unsigned mmap:1; /* mmap'd region */
unsigned recycled:1; /* buffer can be reused after sending */
unsigned in_file:1; /* read from file at file_pos..file_last */
unsigned flush:1; /* flush after this buffer */
unsigned sync:1; /* sync buffer */
unsigned last_buf:1; /* last buffer in this stream */
unsigned last_in_chain:1;
unsigned last_shadow:1;
unsigned temp_file:1;
int num;
};
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};Buffer flavors
A single ngx_buf_t can represent any of:
- In-memory, read-only —
memory=1(a config-time string literal, an HTTP status line) - In-memory, scratch —
temporary=1(allocated for header serialization) - mmap'd file region —
mmap=1(rare; used by some cache paths) - File-backed —
in_file=1withfile,file_pos,file_last(a static file being served) - In-memory + in-file — both bits set; the in-memory part is the file's currently-buffered chunk
The send-chain machinery handles each correctly: in-memory buffers get writev'd; file buffers get sendfile'd; mmap'd is treated like memory.
What the flags mean
| Flag | Semantics |
|---|---|
last_buf |
The terminal buffer of the response. After sending this, finalize. |
last_in_chain |
The terminal buffer of the current chain (subrequest output, partial flush). Not necessarily the last buffer of the response. |
flush |
After sending, flush the socket. Used for SSE / progressive responses. |
sync |
Empty buffer used purely as a marker — no bytes to send, but signals state to the next filter. |
recycled |
After being sent, the buffer's storage is reusable (return to a free chain pool). |
temp_file |
The file is a temp file owned by nginx (created by client_body_temp_path etc.). Delete on cleanup. |
Reading buffers
A buffer with content has bytes at pos..last. After consuming n bytes, the consumer advances b->pos += n. When b->pos == b->last, the buffer is exhausted.
Filters that consume a chain typically loop:
for (cl = in; cl; cl = cl->next) {
n = ngx_buf_size(cl->buf);
/* ... do stuff with cl->buf->pos[0..n] ... */
}ngx_buf_size(b) is (b->in_file ? (b->file_last - b->file_pos) : (b->last - b->pos)) — works whichever the buffer is.
Writing buffers
To produce output:
Allocate a buffer (typically from
r->poolorc->pool):ngx_buf_t *b = ngx_create_temp_buf(pool, size);This sets
b->start = b->pos = b->last = pool_alloc; b->end = ... + size; b->temporary = 1.Write bytes:
ngx_memcpy(b->last, src, n); b->last += n;Wrap in a chain:
ngx_chain_t *cl = ngx_alloc_chain_link(pool); cl->buf = b; cl->next = NULL;Pass it down:
return ngx_http_output_filter(r, cl);.
For static file responses: build a buffer with in_file = 1, file = ..., file_pos = 0, file_last = stat.st_size, last_buf = 1. The send-chain machinery will sendfile() it.
Chain pools
Allocating a fresh chain link per buffer is wasteful in tight loops. Modules use a per-pool free list:
pool->chainis a linked list of unusedngx_chain_tlinks.ngx_alloc_chain_link(pool)pops from the free list or allocates fresh.ngx_free_chain(pool, cl)pushes back to the free list.
This is a small optimization but matters for filters that build many short chains per request.
The output filter chain
When a content handler produces a chain, it calls ngx_http_output_filter(r, in). That kicks off the body filter chain (see systems/http/index). Each filter takes a chain, possibly transforms it (ngx_http_gzip_body_filter compresses, ngx_http_chunked_body_filter adds chunked framing), and passes the result downward. The chain links may be rewritten — a single input buffer might fan out into multiple output buffers.
The bottom of the chain is ngx_http_write_filter, which packs as many bytes as possible into a writev/sendfile call and stores leftover in r->out for the next pass.
Buffer "tags"
b->tag is a void pointer used to identify "who owns this buffer." When a filter wants to recycle buffers, it walks the output r->out chain and reuses any buffer with its own tag. This avoids cross-module buffer corruption — if module A's buffer is sitting in module B's pending queue, B knows not to recycle it.
Convention: use the module's address as the tag, e.g., b->tag = (ngx_buf_tag_t) &ngx_http_my_module;.
Subrequest interaction
The postpone filter is the trickiest user of chains. A subrequest's output chain gets queued onto the parent's r->postponed list rather than going directly to the wire. When the subrequest finishes, the parent walks postponed in order and emits the correct interleaved output. See systems/http/request-lifecycle.
Pitfalls
- Mutating
b->posadvances the read cursor, not the buffer's identity. Don't changeb->startafter creation. - Setting
last_bufon a non-final buffer terminates the response. A misplaced flag will silently truncate a body. - Don't share temporary buffers across requests — they live in the request pool and die with the request.
b->mmap = 1is rare; most static-file paths go throughin_file+ sendfile, not mmap.
Cross-references
- systems/core —
ngx_create_temp_buf,ngx_alloc_chain_link - systems/http/index — filter chains
- systems/http/request-lifecycle — output flow
- primitives/pool — where buffers come from
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.