# Behavior traces — httpie/cli

> Canonical page: https://codevetter.com/unpack/httpie/behavior-traces

# Behavior traces — httpie/cli

Scanned commit 5b604c37c6c67e18e7c3e9aee6c88a8c22b98345 (2024-12-17). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Three flows carry most of the behaviour. Startup walks __main__ -> core.raw_main -> plugin loading -> parser.parse_args -> check_updates -> core.program. The primary request flow walks program -> collect_messages -> requests.Session.send -> OutputOptions.from_message -> write_message -> stream class -> stdout buffer, message by message, as a generator so headers can print before the body arrives. Persistence walks get_httpie_session -> Session.load -> pre_process_data (with legacy fixups) -> update_headers -> save -> post_process_data. A fourth notable flow is the update daemon, which double-forks out of the main process to fetch a version index without blocking the user.
- Startup, step by step: `__main__.main()` imports `core.main`, which imports `httpie.cli.definition.parser` and calls `raw_main(parser, program, argv, Environment())`; `raw_main` sets `env.program_name`, calls `decode_raw_args`, checks `is_daemon_mode`, calls `plugin_manager.load_installed_plugins`, prepends default options, then `parser.parse_args(args, env)`. — [httpie/__main__.py#L6-13](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/__main__.py#L6-L13) [httpie/core.py#L155-171](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L155-L171) [httpie/core.py#L38-52](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L38-L52)
- `--debug` is handled before parsing: `print_debug_info` writes HTTPie/Requests/Pygments/Python/platform versions plus `repr(env)` and `repr(plugin_manager)` to stderr, and a bare `http --debug` returns SUCCESS without ever building a request. — [httpie/core.py#L73-77](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L73-L77) [httpie/core.py#L262-275](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L262-L275)
- After a successful parse, `check_updates(env)` runs before `main_program`; the `_update_checker` decorator wraps it so that every exception is swallowed by `suppress(BaseException)` unless `developer_mode` is set in config.json. — [httpie/core.py#L95-100](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L95-L100) [httpie/internal/update_warnings.py#L78-104](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/update_warnings.py#L78-L104) [httpie/internal/update_warnings.py#L140-167](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/update_warnings.py#L140-L167)
- Primary request trace: `program()` builds `ProcessingOptions.from_raw_args`, optionally constructs a `Downloader` and calls `downloader.pre_request(args.headers)`, then iterates `collect_messages(env, args, request_body_read_callback)`, calling `OutputOptions.from_message` and `write_message` per message. — [httpie/core.py#L174-230](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L174-L230) [httpie/output/models.py#L34-40](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/output/models.py#L34-L40) [httpie/models.py#L215-233](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/models.py#L215-L233)
- Inside `collect_messages` the order is: resolve session -> `make_request_kwargs` -> `make_send_kwargs` -> `build_requests_session` -> apply session headers/cookies/auth -> `requests.Request(**kwargs)` -> `session.prepare_request` -> `transform_headers` -> `ensure_path_as_is` (if `--path-as-is`) -> `compress_request` (if `--compress`) -> yield/send loop. — [httpie/client.py#L48-134](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L48-L134)
- `max_headers` is a context manager that temporarily monkey-patches `http.client._MAXHEADERS` around the single `session.send()` call so `--max-headers` can exceed the stdlib's default cap, restoring it in a finally block. — [httpie/client.py#L113-118](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L113-L118) [httpie/client.py#L144-153](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L144-L153)
- Write trace: `write_message` -> `build_output_stream_for_message` -> `get_stream_type_and_kwargs` picks RawStream (non-tty, no prettify), EncodedStream (tty, no prettify), PrettyStream (`--stream` or `text/event-stream` auto-detect) or BufferedPrettyStream, then `write_stream` pushes each chunk to `outfile.buffer`, flushing when tty or streaming. — [httpie/output/writer.py#L27-58](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/output/writer.py#L27-L58) [httpie/output/writer.py#L122-203](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/output/writer.py#L122-L203)
- `BaseStream.__iter__` defines the on-wire ordering for one message: headers, then `\r\n\r\n`, then body chunks (each fed to `on_body_chunk_downloaded`), then -- if `--meta` -- a blank line and the metadata block. — [httpie/output/streams.py#L63-85](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/output/streams.py#L63-L85)
- Message separation is stateful: `program()` tracks `prev_with_body` and `force_separator`, emitting `MESSAGE_SEPARATOR_BYTES` between messages when the previous one had a body and either output is redirected or a streamed upload forced it. — [httpie/core.py#L204-222](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L204-L222) [httpie/output/writer.py#L23-24](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/output/writer.py#L23-L24)
- Download trace: after the response loop, `downloader.start(initial_url, final_response)` builds a `RawStream` over the response with `chunk_downloaded` as the progress callback, `write_stream` drains it to the output file, `downloader.finish()` stops the display, and a size mismatch sets `downloader.interrupted` and an error exit status. — [httpie/core.py#L224-240](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L224-L240) [httpie/downloads.py#L202-286](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/downloads.py#L202-L286)
- Session persistence trace: `get_httpie_session` resolves the bound hostname (falling back to `localhost` for hostname-less URLs), builds the path, and calls `Session.load()`, which runs `read_raw_config` then `pre_process_data` -- importing cookies into a `RequestsCookieJar` with `HTTPieCookiePolicy` and headers into an `HTTPHeadersDict`. — [httpie/sessions.py#L92-121](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/sessions.py#L92-L121) [httpie/config.py#L103-108](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/config.py#L103-L108) [httpie/sessions.py#L158-183](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/sessions.py#L158-L183)
- On the way out, the session is saved only when it is new or `--session-read-only` was not used: cookies are replaced with the requests session jar, expired cookies removed, then `save()` writes JSON and stamps `__meta__.httpie` with the current version. — [httpie/client.py#L136-140](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L136-L140) [httpie/config.py#L110-128](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/config.py#L110-L128)
- Update-daemon trace: `maybe_fetch_updates` checks the 2-week FETCH_INTERVAL, calls `spawn_daemon('fetch_updates')`, which on POSIX double-forks, calls `setsid()`, closes stdio, and re-enters `httpie.core.main(['http', 'fetch_updates', '--daemon'])` -- except on Darwin, where it spawns a subprocess instead because double-fork is unreliable there. — [httpie/internal/update_warnings.py#L55-76](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/update_warnings.py#L55-L76) [httpie/internal/daemons.py#L55-120](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/daemons.py#L55-L120) [httpie/internal/daemon_runner.py#L41-49](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/daemon_runner.py#L41-L49)
- Manager trace with a confusing invocation: `manager.__main__.main` calls `raw_main(use_default_options=False)`; if argparse raises, `is_http_command()` silently re-parses the args with the http parser inside `env.as_silent()` and, on success, prints the 'use http/https instead' hint before returning ERROR. — [httpie/manager/__main__.py#L12-48](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/manager/__main__.py#L12-L48) [httpie/manager/core.py#L9-23](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/manager/core.py#L9-L23) [httpie/context.py#L157-168](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/context.py#L157-L168)
- Session upgrade trace: `httpie cli sessions upgrade <host> <name>` loads the session with warnings suppressed, selects fixers whose version is greater than the stored `__meta__.httpie` (3.1.0 cookie-format, 3.2.0 header-format), applies them in order, and calls `save(bump_version=True)`. — [httpie/manager/tasks/sessions.py#L11-60](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/manager/tasks/sessions.py#L11-L60) [httpie/legacy/v3_1_0_session_cookie_format.py#L84-100](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/legacy/v3_1_0_session_cookie_format.py#L84-L100) [httpie/utils.py#L293-309](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/utils.py#L293-L309)

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)
