# Extension points — curl/curl

> Canonical page: https://codevetter.com/unpack/curl/extension-points

# Extension points — curl/curl

Scanned commit c362f034692e05ecb4d33edb740ba64c63a20add (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

curl is extended by adding a record to a table, not by subclassing. New URI schemes are `struct Curl_scheme` records pointing at a `struct Curl_protocol` vtable; new transport behaviour is a `struct Curl_cftype` inserted into the filter chain; new response processing is a `struct Curl_cwtype` registered at a phase; new TLS/SSH/DNS backends are entries in a backend array guarded by a `USE_*` define. On the tool side, a new command-line option means one row in the alphabetical `aliases[]` table, one `C_*` enum value, one case in `getparameter()`, and one curldown page. Several of these tables are additionally mirrored by generated files or documentation that CI verifies.
- Adding a protocol means adding a `Curl_scheme_*` record in lib/protocol.c, externing it in lib/protocol.h, teaching the length-bucketed lookup functions about the new name, and supplying a `struct Curl_protocol` vtable; docs/internals/NEW-PROTOCOL.md documents the social and technical bar for that. — [lib/protocol.h#L240-L288](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/protocol.h#L240-L288) [lib/protocol.c#L560-L628](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/protocol.c#L560-L628) [docs/internals/NEW-PROTOCOL.md#L37-L80](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/internals/NEW-PROTOCOL.md#L37-L80)
- The `struct Curl_protocol` vtable is the per-scheme hook set: setup_connection, connect, connecting, do_it, doing, done, proto_getsock/doing_pollset/domore_pollset/perform_pollset, disconnect, write_resp, write_resp_hd, connection_is_dead, attach, and follow. — [lib/protocol.h#L100-L200](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/protocol.h#L100-L200)
- PROTOPT_* flags are the declarative knobs a new scheme uses to opt into behaviour — SSL, DUAL (two connections), NEEDSPWD, NOURLQUERY, WILDCARD, CREDSPERREQUEST, PROXY_AS_HTTP, CONN_REUSE, SSL_REUSE, NO_TRANSFER, HTTP_PROXY_TUNNEL — rather than special-casing the scheme in shared code. — [lib/protocol.h#L202-L238](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/protocol.h#L202-L238)
- New transport layers plug in as `struct Curl_cftype` instances; `Curl_cf_setup_add()` / `cf_setup_create()` build the base filter and the `cf_setup_add_*` helpers decide insertion order, so a new filter is added by writing one more `cf_setup_add_*` step. — [lib/cf-setup.c#L296-L358](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/cf-setup.c#L296-L358) [lib/cf-setup.c#L414-L480](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/cf-setup.c#L414-L480) [lib/cfilters.h#L217-L232](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/cfilters.h#L217-L232)
- Response-processing extensions register as `struct Curl_cwtype` with a `CURL_CW_*` phase; `cwriter_add()` inserts each writer first within its phase, so ordering is declared rather than coded. — [lib/sendf.c#L331-L365](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.c#L331-L365) [lib/sendf.h#L100-L112](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.h#L100-L112)
- Content and transfer decodings are two explicit registries — `general_unencoders[]` and `transfer_unencoders[]` — resolved by name in `find_unencode_writer()`, which is where a new `Content-Encoding` would be added alongside deflate/gzip/brotli/zstd/identity. — [lib/content_encoding.c#L635-L660](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/content_encoding.c#L635-L660) [lib/content_encoding.c#L724-L780](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/content_encoding.c#L724-L780)
- Upload-side extensions use the mirrored client-reader registry (`struct Curl_crtype` with `CURL_CR_*` phases); HTTP's Expect: 100-continue handling is implemented this way via `http_exp100_add_reader()`. — [lib/sendf.h#L240-L250](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.h#L240-L250) [lib/http.c#L1600-L1650](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/http.c#L1600-L1650) [docs/internals/CLIENT-READERS.md#L44-L50](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/internals/CLIENT-READERS.md#L44-L50)
- A new TLS backend is a `struct Curl_ssl` added to `available_backends[]` behind a `USE_*` define; `curl_global_sslset()` and the `CURL_SSL_BACKEND` lookup both iterate that same array, so no other file needs to change to make it selectable. — [lib/vtls/vtls.c#L706-L726](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vtls/vtls.c#L706-L726) [lib/vtls/vtls.c#L790-L850](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vtls/vtls.c#L790-L850)
- Resolver strategies are swappable at build time behind a common `lib/vdns/asyn.h` interface with three implementations — c-ares (`asyn-ares.c`), threaded (`asyn-thrdd.c`) and the shared base (`asyn-base.c`) — plus `cf-dns.c` exposing resolution as a connection filter. — [lib/vdns/asyn.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vdns/asyn.h) [lib/vdns/asyn-thrdd.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vdns/asyn-thrdd.c) [lib/vdns/cf-dns.c#L412](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vdns/cf-dns.c#L412)
- SSH has two interchangeable backends selected at build time (libssh2 and libssh) behind lib/vssh/vssh.h, following the same one-directory-per-backend-family convention as vtls and vquic. — [lib/vssh/vssh.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vssh/vssh.h) [lib/vssh/libssh2.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vssh/libssh2.c) [lib/vssh/libssh.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/vssh/libssh.c)
- Adding a CLI option touches the alphabetically-sorted `aliases[]` table (the comment above it is explicit: "this array MUST be alphasorted based on the 'lname'"), the `C_*` id enum, a case in `getparameter()`, and the generated help in src/tool_listhelp.c. — [src/tool_getparam.c#L75-L78](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_getparam.c#L75-L78) [src/tool_getparam.c#L2933-L3110](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_getparam.c#L2933-L3110) [src/tool_listhelp.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_listhelp.c) [src/tool_getparam.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_getparam.h)
- New `--write-out` variables are one row in `variables[]` mapping a name to a `VAR_*` id, a `CURLINFO_*` id, and a writer function (`writeString`/`writeLong`/`writeOffset`/`writeTime`), with NULL signalling a special form handled inline. — [src/tool_writeout.c#L427-L470](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_writeout.c#L427-L470)
- New libcurl options plug into three parallel registries that must stay in sync: the `CURLOPT_*` enum in include/curl/curl.h, the typed setters in lib/setopt.c, and one curldown page under docs/libcurl/opts/ (422 of them) plus an entry in docs/libcurl/symbols-in-versions. — [include/curl/curl.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/include/curl/curl.h) [lib/setopt.c#L358-L1030](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/setopt.c#L358-L1030) [docs/libcurl/symbols-in-versions](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/libcurl/symbols-in-versions) [docs/libcurl/opts](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/libcurl/opts)
- Multi-state entry hooks are themselves a small registry: the `state_enter[MSTATE_LAST]` function table in `mstate()` lets a state gain on-entry behaviour without touching `multi_runsingle()`'s switch. — [lib/multi.c#L160-L196](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/multi.c#L160-L196)
- Cross-handle caches are extended through the share interface: `share_setup()` in the tool shows the enumerable set (`CURL_LOCK_DATA_COOKIE`, `DNS`, `SSL_SESSION`, `PSL`, `HSTS`, `CONNECT`), and `CURLSHE_NOT_BUILT_IN` is tolerated so a disabled feature does not break setup. — [src/tool_operate.c#L2393-L2420](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_operate.c#L2393-L2420)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
