Commit Graph
21 Commits
Author SHA1 Message Date
txgo b518d0033b fix(relay): bound the wait for upstream response headers (fixes unbounded heap growth → OOM) (#6949)
* fix(relay): bound the wait for upstream response headers (fixes unbounded heap growth)

The relay transport sets a dial timeout, a TLS handshake timeout and an expect-continue
timeout, but nothing bounds how long it waits for the upstream *response headers* after
the request has been written. An upstream that accepts the connection and then never
answers -- without sending FIN/RST, which is what happens when a NAT/firewall silently
drops the flow or the provider hangs -- parks the goroutine in
net/http.(*persistConn).roundTrip forever.

That goroutine keeps the whole request alive, which in practice means three copies of the
request body stay reachable for the lifetime of the process: the raw bytes from
io.ReadAll in CreateBodyStorageFromReader, the decoded messages held as json.RawMessage,
and the re-marshalled upstream body from common.Marshal. BodyStorageCleanup cannot help
here: it runs after c.Next() returns, and for these requests c.Next() never returns.

Measured on v1.0.0-rc.23 in production (see #6947 for the full evidence):

  - 23 goroutines stuck in persistConn.roundTrip on a single 40h-old instance,
    blocked between 353 and 1894 minutes (5.9h to 31.5h)
  - 96.9% of the live heap, sampled after a forced GC, attributable to those three
    body copies (HeapAlloc 892 MiB surviving three GC cycles; HeapObjects dropping
    30x while bytes dropped only 25%)
  - the live floor grows with uptime: 33.7 MiB at 0.1h, 89.2 at 13.8h, 510.0 at 40.1h,
    955.2 at 146.8h, OOMKilled at 172.9h -- same image, same config, same load

Doubling the memory limit and adding GOMEMLIMIT only moved the OOM from 132h to 172.9h.

RELAY_TIMEOUT (http.Client.Timeout) cannot be used for this: it covers the whole response
read and would cut legitimate long streaming calls, which is why it defaults to 0.
ResponseHeaderTimeout only bounds the wait for the headers; streaming after they arrive is
unaffected.

The default is deliberately generous. Non-streaming upstreams usually send the response
headers only once generation has finished, so the value has to leave room for a long
completion. 1800s is 12x shorter than the shortest hang observed here while leaving
several times the headroom a normal non-streaming request needs; 0 restores the previous
unbounded behaviour.

The assignment goes next to the other transport.* lines rather than inside the else
branch: newRelayHTTPTransport() normally takes the http.DefaultTransport.Clone() path,
and DefaultTransport does not set ResponseHeaderTimeout either.

This repo already sets ResponseHeaderTimeout on its other outbound transports
(controller/model_sync.go, controller/ratio_sync.go); the relay path appears to have
been missed.

Refs #6947. Likely also the root cause of #6731, which reported the same symptom
(production OOM on /v1/responses after ~64h) but was closed for template reasons.

* review: clamp overflowing timeout values and switch the test to testify

Addresses the two CodeRabbit findings on this PR.

Overflow (common/init.go:113): a RELAY_RESPONSE_HEADER_TIMEOUT beyond ~9.2e9 seconds
overflows time.Duration and can wrap into a *tiny positive* timeout, which would cut
every relay request instead of only the stuck ones. The value is now clamped before the
conversion, with regression tests for both the negative and the overflowing input.

I did not add fail-on-startup validation for negative values, for two reasons: the
existing `if seconds > 0` guard already treats them as "disabled", and the neighbouring
env-driven timeouts in this file are less strict still -- RelayIdleConnTimeout is
converted with no guard at all. Failing startup on a bad value would be a behaviour
change out of step with the rest of the file; happy to add it if you'd prefer that
direction repo-wide.

Test style: switched to testify (require.Equal / require.Zero / require.Positive), which
is what every other test under service/ uses.

go build, go vet and go test ./common/... ./service/... pass.
(`go build ./...` fails on the `web/dist` embed both with and without this change -- the
frontend bundle is not checked in.)
2026-08-30 21:18:41 +08:00
CaIon e99a9bd86f feat: add per-channel HTTP transport controls 2026-07-27 21:41:13 +08:00
yyhhyyyyyy e13d4033e5 fix(channel): improve proxy client compatibility and cache lifecycle (#6157)
* fix(channel): improve proxy client compatibility and cache lifecycle

* test(controller): use non-fatal assertions for channel tests
2026-07-20 18:11:22 +08:00
CaIon df087b022d feat(ssrf): implement SSRF protection in HTTP clients and validation functions 2026-07-06 14:52:01 +08:00
Seefs 933ea0cddc fix: add relay idle connection timeout config (#5309) 2026-06-05 11:30:08 +08:00
Seefs 980af061fd feat: TLS_INSECURE_SKIP_VERIFY env 2026-01-15 14:43:53 +08:00
Seefs 0ef2804757 fix: fix the proxyURL is empty, not using the default HTTP client configuration && the AWS calling side did not apply the relay timeout. 2026-01-05 17:56:24 +08:00
wwalt1a e683a3ae00 feat: support HTTP_PROXY environment variable for default HTTP client
- Add Proxy: http.ProxyFromEnvironment to default transport
- Allow users to set global proxy via Docker environment variables
- Per-channel proxy settings still override global proxy
- Fully backward compatible
2025-12-30 03:55:06 +08:00
Seefs 920e005048 fix: Use channel proxy settings for task query scenarios 2025-12-09 11:15:27 +08:00
Papersnake bf1a31df80 fix: add ForceAttemptHTTP2 2025-12-02 10:08:58 +08:00
Papersnake 14622aede6 fix: set MaxIdleConnsPerHost to 100 2025-12-02 09:55:03 +08:00
Seefs 5010f2d004 format: package name -> github.com/QuantumNous/new-api (#2017) 2025-10-11 15:30:09 +08:00
CaIon bdb522021a feat: enhance HTTP client with custom redirect handling and SSRF protection 2025-10-10 15:13:41 +08:00
CaIon f03f4187e0 fix(http_client): improve error message for unsupported proxy schemes 2025-09-29 14:02:15 +08:00
RedwindA 4f3ec81b3f fix: add missing timeout 2025-09-27 22:29:27 +08:00
RedwindA fcc5c73bc0 feat: 添加代理客户端缓存和重置功能,优化 HTTP 客户端管理 2025-09-27 22:18:39 +08:00
CaIon 2f04ab0daf feat: enhance environment configuration and resource initialization 2025-07-01 13:13:30 +08:00
creamlike1024 5c53f48a86 feat: add support for socks5h 2025-05-11 17:00:33 +08:00
1808837298@qq.com 906516fb90 feat: add SOCKS5 proxy authentication support
- Enhance `NewProxyHttpClient` to handle SOCKS5 proxy authentication
- Extract username and password from proxy URL for SOCKS5 proxy configuration
- Provide optional authentication for SOCKS5 proxy connections
2025-02-04 18:10:25 +08:00
1808837298@qq.com 5c8af189eb feat: support channel request proxy 2025-02-02 22:15:06 +08:00
1808837298@qq.com 1735981217 feat: 初步重构 2024-02-29 01:08:18 +08:00