Open-Source Wikis

/

curl

/

Systems

/

Connection filters

curl/curl

Connection filters

Active contributors: Stefan Eissing, Daniel Stenberg

Purpose

Connection filters ("cfilters") are the layered I/O abstraction beneath every libcurl connection. A filter has connect, cntrl, send, recv, query, and shutdown callbacks, plus a single optional sub-filter underneath. Stacking filters lets curl support HTTPS-over-HTTP-proxy, HTTP/2 multiplexing on top of TLS on top of TCP, HTTP/3 on top of QUIC on top of UDP, and the PROXY protocol — without any of the protocol handlers needing to know how it works.

The framework lives in lib/cfilters.c (34 KB) and lib/cfilters.h (29 KB). Each concrete filter is a cf-*.c file in lib/ or under lib/vtls/ and lib/vquic/.

Key abstractions

Symbol Definition Description
struct Curl_cfilter lib/cfilters.h One filter instance: parent connection, sub-filter, ops vtable, opaque context
struct Curl_cftype lib/cfilters.h The vtable shared by all instances of one filter type
Curl_cf_create() lib/cfilters.c Helper to allocate a filter and initialize it
Curl_conn_cf_add() lib/cfilters.c Push a new filter on top of the chain
Curl_conn_cf_insert_after() lib/cfilters.c Insert a filter between two existing ones
Curl_conn_send/recv lib/cfilters.c Top-of-stack send/recv used by protocol handlers
Curl_conn_connect() lib/cfilters.c Drives connect down the chain
Curl_conn_data_pending() lib/cfilters.c Asks the filter chain whether it has buffered bytes ready

The filter types

Each filter is identified by its Curl_cftype. The major filters in the source tree:

Filter Source Purpose
cf-socket lib/cf-socket.c Bottom-of-stack: a real TCP/UDP/Unix socket
cf-ip-happy lib/cf-ip-happy.c Parallel IPv4/IPv6 connect race
cf-dns lib/cf-dns.c Re-resolves at reconnect time
cf-https-connect lib/cf-https-connect.c HTTPS-over-CONNECT helper
cf-h1-proxy lib/cf-h1-proxy.c HTTP/1 CONNECT tunnel through a proxy
cf-h2-proxy lib/cf-h2-proxy.c HTTP/2 CONNECT tunnel (multiplexed streams)
cf-haproxy lib/cf-haproxy.c PROXY-protocol prelude
socks* lib/socks.c, lib/socks_*.c SOCKS4/4a/5 + SOCKS5 with GSSAPI/SSPI
TLS filters lib/vtls/vtls.c + each backend One filter per supported TLS backend
HTTP/2 filter lib/http2.c Multiplexes streams over a single connection
HTTP/3 filters lib/vquic/curl_ngtcp2.c, lib/vquic/curl_quiche.c QUIC + HTTP/3 backends

How it works

Every connectdata owns a cfilter chain (conn->cfilter[], indexed by socket index for multi-socket protocols like FTP). A protocol handler calls Curl_conn_send/Curl_conn_recv and the call descends the chain:

graph TD
    A[http.c calls Curl_conn_send] --> B[cf_http2: stream framing]
    B --> C[cf_tls=OpenSSL: encrypt]
    C --> D[cf_h1_proxy: forward as is]
    D --> E[cf_socket: write to FD]

recv flows the other way. connect is similar — the top filter calls connect on its sub-filter recursively until the bottom socket dials and the chain settles.

A typical HTTPS-through-HTTPS-proxy connection has this stack from bottom up:

cf_socket                      ← real TCP socket
cf_tls (proxy TLS backend)     ← TLS to the proxy
cf_h1_proxy or cf_h2_proxy     ← CONNECT request through the encrypted proxy
cf_tls (origin TLS backend)    ← TLS to origin, layered through the tunnel
cf_http2 (or none for HTTP/1.1) ← HTTP/2 multiplexer

For HTTP/3 the bottom is cf_socket over UDP, then cf_quic_*, then HTTP/3 framing.

Lifecycle

  1. Build: when a connection is created in lib/url.c, Curl_setup_cfilter_chain (and protocol-specific helpers like Curl_http_proxy_setup, Curl_ssl_cfilter_add, Curl_http2_switch) push filters onto the chain.
  2. Connect: Curl_conn_connect walks the chain calling each connect. Filters return CURLE_AGAIN to yield until ready.
  3. Operate: protocol handlers send/recv at the top of the stack.
  4. Adjust: Curl_conn_adjust_pollset lets each filter declare which FDs it wants polled (e.g. TLS in handshake state vs steady state).
  5. Shutdown: Curl_conn_shutdown walks the chain top-down for graceful close, then bottom-up for socket close.
  6. Free: Curl_conn_close releases all filters and their contexts.

Why this design

Before cfilters existed (they were introduced in 2022 and grew through 2023), each new transport meant another if (data->conn->ssl_config…) branch in lib/transfer.c, lib/sendf.c, lib/connect.c, and the proxy code. The cfilter framework collapses that into "implement a struct Curl_cftype" and the rest works.

It also lets transformations stack cleanly:

  • HAProxy protocol: cf-haproxy writes a single PROXY header, then becomes transparent.
  • HTTPS proxy: just another TLS filter under the proxy CONNECT filter.
  • HTTP/3 with optional 0-RTT: implemented inside vquic filters, invisible to http.c.

Integration points

  • Transfer engine: drives Curl_conn_connect, Curl_conn_send, Curl_conn_recv, Curl_conn_adjust_pollset. See Transfer engine.
  • Protocol handlers: only see the top of the chain. They never know whether they are talking through TLS or a tunnel.
  • TLS, HTTP/2, HTTP/3: each is a filter type; their internals are detailed under Features.
  • Diagnostics: Curl_conn_meta lets each filter expose properties (e.g. cipher suite, ALPN result) for --write-out and getinfo.

Entry points for modification

  • Adding a new transport → write a Curl_cftype in a new lib/cf-*.c, expose a constructor (Curl_cf_<name>_create), and have the connection-setup code in lib/url.c push it onto the chain when appropriate.
  • Changing how an existing filter handshakes → implement the change inside the filter's do_connect; the rest of the codebase does not need to know.
  • Diagnostic queries → add a query operation to Curl_cftype and route it via Curl_conn_get_property.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Connection filters – curl wiki | Factory