mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-22 14:48:41 +08:00
fix(relay): add request timeout in HTTP relay
This commit is contained in:
@@ -22,6 +22,10 @@ export interface RelayResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MAX_BODY_BYTES = 8 * 1024 * 1024
|
const MAX_BODY_BYTES = 8 * 1024 * 1024
|
||||||
|
// Relay callers are all lightweight JSON endpoints (login, codes, balances,
|
||||||
|
// model lists); a 10s cap is generous while still preventing a stalled request
|
||||||
|
// from hanging forever (and, for pollers, piling up across ticks).
|
||||||
|
const REQUEST_TIMEOUT_MS = 10 * 1000
|
||||||
|
|
||||||
function relay(req: RelayRequest): Promise<RelayResponse> {
|
function relay(req: RelayRequest): Promise<RelayResponse> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -45,16 +49,33 @@ function relay(req: RelayRequest): Promise<RelayResponse> {
|
|||||||
for (const [k, v] of Object.entries(req.headers)) request.setHeader(k, v)
|
for (const [k, v] of Object.entries(req.headers)) request.setHeader(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cap the whole request (connect + response) so a stalled endpoint can't
|
||||||
|
// hang forever. On timeout we abort, which surfaces as an 'error' event.
|
||||||
|
// `done` guards against settling twice once the timer has fired.
|
||||||
|
let done = false
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
if (done) return
|
||||||
|
request.abort()
|
||||||
|
reject(new Error('request timeout'))
|
||||||
|
}, REQUEST_TIMEOUT_MS)
|
||||||
|
const settle = (fn: () => void) => {
|
||||||
|
if (done) return
|
||||||
|
done = true
|
||||||
|
clearTimeout(timer)
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
|
||||||
request.on('response', (response) => {
|
request.on('response', (response) => {
|
||||||
const chunks: Buffer[] = []
|
const chunks: Buffer[] = []
|
||||||
let size = 0
|
let size = 0
|
||||||
let aborted = false
|
let aborted = false
|
||||||
response.on('data', (chunk: Buffer) => {
|
response.on('data', (chunk: Buffer) => {
|
||||||
if (aborted) return
|
if (aborted || done) return
|
||||||
size += chunk.length
|
size += chunk.length
|
||||||
if (size > MAX_BODY_BYTES) {
|
if (size > MAX_BODY_BYTES) {
|
||||||
aborted = true
|
aborted = true
|
||||||
reject(new Error('response too large'))
|
request.abort()
|
||||||
|
settle(() => reject(new Error('response too large')))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chunks.push(chunk)
|
chunks.push(chunk)
|
||||||
@@ -66,15 +87,17 @@ function relay(req: RelayRequest): Promise<RelayResponse> {
|
|||||||
headers[k] = Array.isArray(v) ? v.join(', ') : String(v)
|
headers[k] = Array.isArray(v) ? v.join(', ') : String(v)
|
||||||
}
|
}
|
||||||
const status = response.statusCode || 0
|
const status = response.statusCode || 0
|
||||||
|
settle(() =>
|
||||||
resolve({
|
resolve({
|
||||||
ok: status >= 200 && status < 300,
|
ok: status >= 200 && status < 300,
|
||||||
status,
|
status,
|
||||||
headers,
|
headers,
|
||||||
body: Buffer.concat(chunks).toString('utf8'),
|
body: Buffer.concat(chunks).toString('utf8'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
request.on('error', (err) => settle(() => reject(err)))
|
||||||
request.on('error', (err) => reject(err))
|
|
||||||
|
|
||||||
if (req.body != null) request.write(req.body)
|
if (req.body != null) request.write(req.body)
|
||||||
request.end()
|
request.end()
|
||||||
|
|||||||
Reference in New Issue
Block a user