Single-binary self-hosted market watcher for stocks, ETFs, indexes, and futures: live charts, key stats, fundamentals, SEC filings, and SSE streaming.
axumdockerfinancerustself-hostedsqlitestocksvite
1use std::time::Duration;
2
3use crate::Config;
4
5/// One shared reqwest client for all outbound data requests.
6///
7/// rustls negotiates HTTP/2 over ALPN, and gzip/brotli are decompressed
8/// transparently. A reusable client is correct here: unlike a latency
9/// probe, these calls are plain data fetches with no per-request handshake
10/// measurement to preserve.
11pub fn build_client(config: &Config) -> reqwest::Client {
12 reqwest::Client::builder()
13 .user_agent(config.user_agent.clone())
14 .timeout(Duration::from_secs(25))
15 // Yahoo's v10 `quoteSummary` endpoint is crumb-gated and the crumb
16 // round-trip requires session cookies — the GET to fc.yahoo.com sets
17 // one, which the subsequent /v1/test/getcrumb call must echo. With
18 // cookies enabled here, [`YahooProvider::ensure_crumb`] does the
19 // dance and the cookies replay automatically on later requests.
20 .cookie_store(true)
21 .build()
22 .expect("reqwest client builds")
23}
24
25/// A client for SEC EDGAR requests.
26///
27/// SEC's fair-access policy asks every consumer to identify itself, so the
28/// configured contact email is appended to the User-Agent on these requests
29/// only (the public market endpoints get the plain browser string from
30/// `build_client`). A `companyfacts` payload can run to several MB, so the
31/// timeout is more generous than the default client's.
32pub fn build_sec_client(config: &Config) -> reqwest::Client {
33 let user_agent = if config.sec_contact_email.is_empty() {
34 config.user_agent.clone()
35 } else {
36 format!("{} {}", config.user_agent, config.sec_contact_email)
37 };
38 reqwest::Client::builder()
39 .user_agent(user_agent)
40 .timeout(Duration::from_secs(40))
41 .build()
42 .expect("reqwest client builds")
43}