# Data flow — curl/curl

> Canonical page: https://codevetter.com/unpack/curl/data-flow

# Data flow — curl/curl

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

Input boundaries are argv, `.curlrc`/`-K` config files, environment variables, `.netrc`, stdin uploads, and the network. argv is normalised into a linked list of `struct OperationConfig` (one per `--next`), which `config2setopts.c` translates wholesale into `curl_easy_setopt()` calls; from there the state owner is `struct Curl_easy` (`data->set` for user intent, `data->state`/`data->req` for per-request state). The URL becomes a `CURLU` via lib/urlapi.c, resolves to a `struct Curl_scheme`, and is matched against the connection pool before a filter chain is built. Bytes in from the network flow up through `Curl_xfer_write_resp()` → `Curl_client_write()` → an ordered writer stack → `CURLOPT_WRITEFUNCTION`; bytes out flow down through the mirror-image client reader chain. Output boundaries are the write callbacks (which the tool points at files or stdout), the header callback, the progress callback, and the file-backed caches flushed at cleanup.
- argv is consumed by `parse_args()`, which loops over argv, calls `getparameter()` per flag, treats bare arguments as `--url`, and on `PARAM_NEXT_OPERATION` allocates a fresh `OperationConfig` and links it onto `global->last` — so `--next` literally forks the config list. — [src/tool_getparam.c#L3116-L3212](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_getparam.c#L3116-L3212)
- Config files are a second input boundary parsed by the same code path: `operate()` calls `parseconfig(NULL, CONFIG_MAX_LEVELS, &curlrc_path)` for `.curlrc` before argv, and `-K` re-enters it, with recursion bounded by `CONFIG_MAX_LEVELS`. — [src/tool_operate.c#L2437-L2448](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_operate.c#L2437-L2448) [src/tool_parsecfg.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_parsecfg.c)
- The config→library translation is centralised in src/config2setopts.c, split into `ssh_setopts()`, `ssl_ca_setopts()`, `ssl_setopts()`, `cookie_setopts()`, `httpsig_setopts()`, `http_setopts()` and friends, all using `my_setopt_*` wrappers so the same call sites feed both libcurl and the `--libcurl` code generator. — [src/config2setopts.c#L205-L710](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/config2setopts.c#L205-L710) [src/tool_setopt.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_setopt.h)
- Library-side, options land in `struct Curl_easy` guarded by `magic == CURLEASY_MAGIC_NUMBER`; `lib/setopt.c` fans out into typed setters (`setopt_long_bool`, `setopt_long_net`, `setopt_long_ssl`, `setopt_long_proxy`, `setopt_long_http`) rather than one giant switch. — [lib/setopt.c#L358-L1030](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/setopt.c#L358-L1030) [lib/urldata.h#L1170-L1200](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/urldata.h#L1170-L1200) [lib/api.h#L3-L16](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/api.h#L3-L16)
- URLs are parsed by `parseurl()` in lib/urlapi.c into a `CURLU`, with behaviour gated by flags such as `CURLU_GUESS_SCHEME`, `CURLU_DEFAULT_SCHEME`, `CURLU_NO_AUTHORITY`, `CURLU_DISALLOW_USER`, `CURLU_URLENCODE` and `CURLU_PATH_AS_IS`; the same parser backs the public `curl_url_*` API. — [lib/urlapi.c#L1224-L1320](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/urlapi.c#L1224-L1320) [lib/urlapi.c#L1412-L1440](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/urlapi.c#L1412-L1440) [include/curl/urlapi.h](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/include/curl/urlapi.h)
- The parsed scheme is only accepted for a connection if it passes `url_set_conn_scheme()`, which requires a non-NULL `run` vtable and membership in `data->set.allowed_protocols` (and `redir_protocols` when following a redirect) — this is the choke point that makes `--proto`/`--proto-redir` effective. — [lib/url.c#L1170-L1195](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/url.c#L1170-L1195)
- Before dialling out, libcurl asks the connection pool for a reusable `connectdata` through `Curl_cpool_find()` with a match callback; reuse eligibility consults PROTOPT_SSL/PROTOPT_SSL_REUSE/PROTOPT_CONN_REUSE and compares origins and protocol families. — [lib/conncache.h#L105-L135](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/conncache.h#L105-L135) [lib/url.c#L700-L910](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/url.c#L700-L910)
- Received bytes enter the client-writer chain via `Curl_client_write()`, which lazily builds the stack (`Curl_cwt_out` at CURL_CW_CLIENT, `cw_download` at CURL_CW_PROTOCOL, `cw_raw` at CURL_CW_RAW) and inserts each new writer first within its phase. — [lib/sendf.c#L331-L395](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.c#L331-L395) [lib/sendf.c#L297-L328](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.c#L297-L328)
- Writer ordering is a fixed phase enum — RAW → TRANSFER_DECODE → PROTOCOL → BEFORE_DECODE → CONTENT_DECODE → CLIENT — so chunked decoding, size accounting, and gzip/brotli/zstd decompression always run in that order before the application callback. — [lib/sendf.h#L100-L112](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.h#L100-L112) [lib/content_encoding.c#L635-L660](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/content_encoding.c#L635-L660) [docs/internals/CLIENT-WRITERS.md#L28-L52](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/internals/CLIENT-WRITERS.md#L28-L52)
- `cw_download_write()` is where `--max-filesize` is enforced and where excess body bytes beyond `req.maxdownload` cause `connclose()`; exceeding the limit returns `CURLE_FILESIZE_EXCEEDED` from inside the writer chain. — [lib/sendf.c#L270-L296](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.c#L270-L296)
- Upload data flows through the mirror-image client-reader chain with phases CLIENT → CONTENT_ENCODE → PROTOCOL → TRANSFER_ENCODE → NET, entered via `Curl_client_read()`; HTTP's Expect: 100-continue is itself implemented as a reader (`cr_exp100_read`). — [lib/sendf.h#L240-L250](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/sendf.h#L240-L250) [docs/internals/CLIENT-READERS.md#L26-L45](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/docs/internals/CLIENT-READERS.md#L26-L45) [lib/http.c#L1526-L1620](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/http.c#L1526-L1620)
- On the tool side the terminal output boundary is `tool_cb_wrt.c`'s write callback writing into a `struct OutStruct` (stream, filename, `fopened`, `init` offset), with `setup_outfile()` deciding stdout vs. file vs. `--out-null` and opening in append mode when resuming. — [src/tool_cb_wrt.c](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_cb_wrt.c) [src/tool_operate.c#L1032-L1166](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_operate.c#L1032-L1166)
- Response headers reach the tool through `tool_cb_hdr.c`, where `parse_filename()` derives a local name from `Content-Disposition:` (or `Location:`) by trimming quotes, query/fragment, CR/LF, and any path prefix before `/` or `\`. — [src/tool_cb_hdr.c#L160-L229](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_cb_hdr.c#L160-L229)
- Cross-transfer state is shared deliberately: `share_setup()` enables `CURL_LOCK_DATA_COOKIE`, `DNS`, `SSL_SESSION`, `PSL`, `HSTS` on the `CURLSH`, and adds `CURL_LOCK_DATA_CONNECT` only in serial mode because parallel mode uses the multi handle's own pool. — [src/tool_operate.c#L2401-L2420](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/src/tool_operate.c#L2401-L2420)
- Persistence is flushed at teardown rather than continuously: `Curl_flush_cookies()` writes the jar through `cookie_output()`, and `Curl_fopen()` performs the durable write as O_EXCL temp file + `fchmod` clone of the original mode + rename. — [lib/cookie.c#L1674-L1695](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/cookie.c#L1674-L1695) [lib/curl_fopen.c#L78-L150](https://github.com/curl/curl/blob/c362f034692e05ecb4d33edb740ba64c63a20add/lib/curl_fopen.c#L78-L150)

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)
