nginx/nginx
Patterns and conventions
The nginx codebase has a strong, explicit style. Most of it is captured at nginx.org's development guide; this page summarizes the rules that come up most often in PR review.
Naming
| Item | Convention | Example |
|---|---|---|
| Public symbol | ngx_<area>_<thing> |
ngx_palloc, ngx_http_init_request |
| Type | ngx_<thing>_t |
ngx_pool_t, ngx_str_t |
| Struct definition | struct ngx_<thing>_s + typedef ... ngx_<thing>_t |
ngx_module_s / ngx_module_t |
| Module | ngx_<area>_<name>_module |
ngx_http_proxy_module |
| Static helper | static ngx_<area>_<verb>_<noun>(...) |
static ngx_http_get_indexed_variable |
| Macro | NGX_<UPPER>_<UPPER> |
NGX_HTTP_OK, NGX_OK |
| Local variable | short, lowercase | c, r, cf, cln, pool |
Short locals are idiomatic and reviewers won't ask for longer names. The standard short names:
| Local | Type |
|---|---|
c |
ngx_connection_t * |
r |
ngx_http_request_t * |
cf |
ngx_conf_t * |
cscf |
ngx_http_core_srv_conf_t * |
clcf |
ngx_http_core_loc_conf_t * |
cln |
ngx_pool_cleanup_t * |
pool |
ngx_pool_t * |
b |
ngx_buf_t * |
cl, chain |
ngx_chain_t * |
e |
ngx_http_script_engine_t * |
u |
ngx_http_upstream_t * |
Formatting
- Indent: 4 spaces, no tabs
- Line length: ~80 columns; the whitespace check enforces no trailing whitespace
- Braces: K&R for functions (opening brace on its own line); for blocks (
if,for,while,switch) the opening brace stays on the same line
static ngx_int_t
ngx_http_my_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
if (r->method != NGX_HTTP_GET) {
return NGX_HTTP_NOT_ALLOWED;
}
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
return NGX_OK;
}- Blank lines between local declarations and code — and locals are usually grouped/aligned by type.
- Two blank lines between functions.
- Comments:
/* ... */only. C++//comments are not used in the source tree.
Strings
ngx_str_t is { size_t len; u_char *data; } — not null-terminated.
ngx_str_t name = ngx_string("hello");
ngx_str_t empty = ngx_null_string;
ngx_str_t buf;
ngx_str_set(&buf, "world"); /* sets len + data, data is a literal */Beware:
dataisu_char *, notchar *. Cast at the boundary, or usengx_strlow,ngx_strncasecmp, etc.printf("%V", &str)is the formatter — not%s. TheVspecifier consumes angx_str_t *.ngx_pnalloc(pool, len)allocateslenbytes;ngx_pcalloczeroes them.
Error handling
Most functions return one of:
#define NGX_OK 0
#define NGX_ERROR -1
#define NGX_AGAIN -2
#define NGX_BUSY -3
#define NGX_DONE -4
#define NGX_DECLINED -5
#define NGX_ABORT -6Patterns:
NGX_OK— success.NGX_ERROR— fatal; finalize the request/connection.NGX_AGAIN— would block, schedule yourself again.NGX_DECLINED— "I don't handle this case, try the next module."NGX_DONE— "the request is fully finalized, do nothing further."
HTTP handlers can also return one of the NGX_HTTP_* status macros (NGX_HTTP_OK, NGX_HTTP_FORBIDDEN, NGX_HTTP_INTERNAL_SERVER_ERROR, ...) which the request engine maps to either the response status or to the error_page machinery.
Errors are logged through ngx_log_error(level, log, errno, fmt, ...). Don't printf.
Memory ownership
Almost nothing is malloc'd. Allocate from a pool whose lifetime matches what you're producing:
| Need | Allocate from |
|---|---|
| Per-request data | r->pool (HTTP) / s->connection->pool (Stream) |
| Per-connection data | c->pool |
| Per-cycle (config) data | cf->pool / cycle->pool |
| Cross-worker shared data | A shared zone, allocated via ngx_slab_alloc |
If you need to free something before the pool is destroyed (e.g., a file you opened, a regex you compiled), register a cleanup:
ngx_pool_cleanup_t *cln;
cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_my_cleanup_t));
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_my_cleanup_handler;
cln->data = my_data;Concurrency
Workers never share heap memory through threads inside a single worker — there's one event-loop thread per worker. Cross-worker state goes through:
- Shared memory zones — set up at config time via
ngx_shared_memory_add(), attached lazily per worker. - The slab allocator (
ngx_slab.c) inside a shared zone, withngx_shmtx_tlocks for atomicity. - Atomics (
ngx_atomic_*) for counters and simple flags. ngx_rwlock_tfor short-held reader/writer sections inside shared zones.
Modules that use the thread pool (aio threads, --with-threads) hand work off via ngx_thread_task_t and get a callback in the event loop when the worker thread finishes. There's no other shared-mutable-memory threading inside a single worker.
Modules: the standard skeleton
A new HTTP module typically defines:
static ngx_command_t ngx_http_my_commands[] = { /* directive table */ };
static ngx_http_module_t ngx_http_my_module_ctx = {
NULL, /* preconfiguration */
ngx_http_my_init, /* postconfiguration: register handlers in phases */
NULL, NULL, /* create main / init main conf */
NULL, NULL, /* create srv / merge srv conf */
ngx_http_my_create_loc_conf, /* create location conf */
ngx_http_my_merge_loc_conf, /* merge location conf */
};
ngx_module_t ngx_http_my_module = {
NGX_MODULE_V1,
&ngx_http_my_module_ctx,
ngx_http_my_commands,
NGX_HTTP_MODULE,
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};Read primitives/module for the full layout and systems/configuration for how directives become C state.
What gets pushed back at review
- Magic numbers without
#define - Missing error handling on a function that can fail
- A
malloc()orfree()— should almost always be a pool allocation - A
printforfprintf— usengx_log_error - A
strncpy/strncat— usengx_cpymem,ngx_copy, or pool-allocated buffers - A
for (int i = ...)— declare the loop var separately (C89 habit; the project targets old compilers too) - Unbounded growth in a hot path — the event loop must remain bounded per iteration
- A long subject line in the commit message — the linter will catch it but reviewers will too
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.