nginx/nginx
Connection (ngx_connection_t)
Active contributors: Sergey Kandaurov, Maxim Dounin
What it is
ngx_connection_t is the per-connection state struct: the file descriptor, the read and write events, the connection's pool, the listener it came from, the peer's address, optional TLS state, optional QUIC stream pointer, optional UDP state, and a handful of flags. It's also re-used as the per-stream state inside QUIC — every HTTP/3 stream gets its own ngx_connection_t even though there's no separate fd. This is the layer that lets the higher protocols pretend everything is a TCP connection.
Definition
src/core/ngx_connection.h:
struct ngx_connection_s {
void *data; /* per-protocol pointer */
ngx_event_t *read;
ngx_event_t *write;
ngx_socket_t fd;
ngx_recv_pt recv;
ngx_send_pt send;
ngx_recv_chain_pt recv_chain;
ngx_send_chain_pt send_chain;
ngx_listening_t *listening;
off_t sent;
ngx_log_t *log;
ngx_pool_t *pool;
int type;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t addr_text;
ngx_proxy_protocol_t *proxy_protocol;
ngx_quic_stream_t *quic; /* set if this is a QUIC stream */
ngx_ssl_connection_t *ssl; /* set if this is a TLS connection */
ngx_udp_connection_t *udp;
struct sockaddr *local_sockaddr;
socklen_t local_socklen;
ngx_buf_t *buffer;
ngx_queue_t queue; /* link in the per-cycle reusable list */
ngx_atomic_uint_t number; /* the *N counter you see in logs */
ngx_msec_t start_time;
ngx_uint_t requests;
unsigned buffered:8;
unsigned log_error:3;
unsigned timedout:1;
unsigned error:1;
unsigned destroyed:1;
unsigned pipeline:1;
unsigned idle:1;
unsigned reusable:1;
unsigned close:1;
unsigned shared:1;
unsigned sendfile:1;
unsigned sndlowat:1;
unsigned tcp_nodelay:2;
unsigned tcp_nopush:2;
unsigned need_last_buf:1;
unsigned need_flush_buf:1;
#if (NGX_THREADS || NGX_COMPAT)
ngx_thread_task_t *sendfile_task;
#endif
};Lifecycle
stateDiagram-v2
[*] --> Pool: ngx_get_connection (pop from free list)
Pool --> Reading: ngx_http_init_connection / ngx_mail_init_connection / ngx_stream_init_connection
Reading --> Working: read handler fires
Working --> Reading: keep-alive cycle
Working --> Closing: ngx_close_connection
Closing --> [*]: pool destroyed, slot pushed backngx_get_connection(fd, log) (src/core/ngx_connection.c) pops a slot from cycle->free_connections, attaches the fd, the log, and a freshly-allocated pool, and returns. ngx_close_connection(c) (same file) cancels timers, removes events from the kernel poller, destroys the pool, and pushes the slot back.
The connection table is pre-allocated to worker_connections entries at cycle init. There's no per-connection malloc on the hot path.
The data pointer
c->data is the protocol-specific state. For HTTP/1.1, it's the ngx_http_connection_t (which itself holds a pointer to the current ngx_http_request_t). For HTTP/2, it's the ngx_http_v2_connection_t. For HTTP/3, it's the QUIC connection or stream-specific state. For Mail / Stream, it's their session structs.
This is the indirection that lets generic event-loop code call c->read->handler(c->read) without knowing what protocol is in play.
I/O function pointers
c->recv, c->send, c->recv_chain, c->send_chain are set per-connection:
- For plain TCP — set by
ngx_io(the per-OS function table) at connection init. - For TLS — replaced by
ngx_ssl_recv/ngx_ssl_sendafter handshake. - For QUIC streams — replaced by
ngx_quic_*functions.
Higher-level code calls c->send_chain(c, chain, limit) and lets the right implementation handle the details.
Connection numbering
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1) — a globally unique id for the connection. This is the *N you see in error logs. Useful for grepping out one connection's lifecycle from error_log debug.
Reusable connections
The c->reusable bit means "this connection is idle and can be closed if we run out of slots." Workers keep a cycle->reusable_connections_queue (LRU). When a worker hits the connection limit, it walks this queue and closes the oldest reusable connections to free slots for new accepts.
ngx_reusable_connection(c, 1) adds; (c, 0) removes. HTTP keep-alive idle connections are typically reusable.
QUIC streams as connections
In QUIC, each stream gets a fresh ngx_connection_t with c->quic = stream_ptr and c->fd = -1. c->recv / c->send are wired to QUIC stream-buffer functions. The HTTP/3 layer creates one such fake connection per stream so the request engine can reuse all the existing code.
This is one of the more elegant pieces of design — the "connection" abstraction is general enough to cover both TCP sockets and QUIC streams without conditionals scattered throughout the request engine.
Listeners
ngx_listening_t (in the same header) is the configured listening socket. Each carries:
- The bound
sockaddr - The accept
handler(the per-protocolinit_connection) - Reuseport / IPv6-only / TCP-fastopen / deferred-accept flags
- A backpointer to the
connectionthat's been allocated for the listen fd
Listeners survive across reloads — ngx_init_cycle matches new listening blocks against old_cycle->listening and reuses fds where possible. That's what keeps in-flight connections alive across nginx -s reload.
Cross-references
- systems/event-loop — the event machinery the connection plugs into
- systems/openssl —
c->ssland the TLS handshake handler - systems/quic —
c->quicand how stream connections work - systems/http/request-lifecycle — what happens on
c->readfor HTTP - primitives/cycle — the connection table lives in the cycle
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.