[package]
name = "specters"
version = "4.1.10"
edition = "2021"
license = "MIT"
description = "Rust HTTP client with browser-like Chrome and Firefox fingerprints across TLS, HTTP/1.1, HTTP/2, HTTP/3, and WebSockets"
repository = "https://github.com/jaredboynton/specter"
keywords = ["http-client", "chrome", "tls", "fingerprint", "llm"]
categories = ["network-programming", "web-programming::http-client"]
authors = ["Jared Boynton"]
readme = "README.md"
exclude = [
    "lib/boringssl/",
    "bindings/",
    "scripts/",
    ".github/",
    ".cargo/",
    "examples/",
    "justfile",
    "**/.DS_Store",
]

[lib]
name = "specter"

[features]
default = []
# Replace the system allocator with mimalloc. Opt-in because it raises
# steady-state RSS slightly (mimalloc returns pages to the OS lazily) and
# we don't want to surprise consumers. Cited gain: ~26% RPS on HTTP-shaped
# workloads in filtermail's bench. jemalloc is not offered because it
# bakes the 4 KiB page assumption and segfaults on Apple M-series and
# Raspberry Pi 5 (16 KiB pages).
mimalloc = ["dep:mimalloc"]

[dependencies]
# TLS with BoringSSL
boring = "4"
boring-sys = "4"
tokio-boring = "4"

# Optional global allocator (see [features].mimalloc)
mimalloc = { version = "0.1", optional = true, default-features = false }

# HTTP/1.1 (minimal implementation)
httparse = "1"

# HTTP/2 (custom implementation for full fingerprint control)
http = "1"

# Poll-based body model for the public streaming API
http-body = "1"
futures-core = "0.3"
pin-project-lite = "0.2"
atomic-waker = "1"
parking_lot = "0.12"

# Async runtime
tokio = { version = "1", features = ["net", "time", "sync", "rt-multi-thread", "macros", "fs", "io-util"] }

# Compression
flate2 = "1"
brotli = "8"
zstd = "0.13"

# Platform root certificates (loads from OS cert store at runtime)
rustls-native-certs = "0.8"

# Utilities
bytes = "1"
foreign-types-shared = "0.3"
thiserror = "2"
tracing = "0.1"
chrono = { version = "0.4", features = ["serde"] }
sha2 = "0.10"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
getrandom = "0.3"
psl = "2"
socket2 = { version = "0.6", features = ["all"] }
base64 = "0.22.1"
hex = "0.4"
libc = "0.2"

[dev-dependencies]
tokio-test = "0.4"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
hex = "0.4"
rcgen = "0.14.5"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "stream", "http2"] }
fastwebsockets = "0.10.0"
tokio-tungstenite = { version = "0.24", default-features = false, features = ["connect", "rustls-tls-webpki-roots"] }
futures-util = "0.3"

[[bench]]
name = "streaming_vs_reqwest"
harness = false

[[bench]]
name = "websocket_vs_fastwebsockets"
harness = false

[[bench]]
name = "codex_real_streaming"
harness = false

[[bench]]
name = "codex_ws_streaming"
harness = false

# Inner-loop iteration tuning. These profile sections only affect builds rooted
# at this crate; downstream consumers ignore them, so they are safe for publish.

# Release builds rooted at this crate use thin LTO and a single codegen unit
# so the bench numbers we publish reflect the full inlining the optimiser is
# capable of. Downstream consumers pick their own profile and ignore this.
# We avoid `panic = "abort"` and `lto = "fat"`: empirical measurement
# showed fat LTO + panic=abort regressed the H1 request-body bench on
# this codebase by ~3pp on TTFT and throughput (LLVM's global codegen
# decisions across all crates inlined this hot path less favourably).
# Thin LTO with `codegen-units = 1` captures most of the inlining win
# without the regression.
[profile.release]
lto = "thin"
codegen-units = 1
debug = "line-tables-only"

[profile.bench]
inherits = "release"

[profile.dev]
debug = "line-tables-only"
split-debuginfo = "unpacked"
incremental = true

[profile.dev.package."*"]
debug = 0

[profile.dev.build-override]
opt-level = 3
codegen-units = 16

[profile.test]
debug = "line-tables-only"
split-debuginfo = "unpacked"
incremental = true

[profile.test.package."*"]
debug = 0

[profile.test.build-override]
opt-level = 3
codegen-units = 16

[workspace]
members = [
    "bindings/node",
    "bindings/python",
]
