diff --git a/src/modules/proxy/forward.ts b/src/modules/proxy/forward.ts index c4beb0e..683c9ee 100644 --- a/src/modules/proxy/forward.ts +++ b/src/modules/proxy/forward.ts @@ -1,4 +1,5 @@ import type { Context } from 'hono'; +import { newTraceparent } from '../../shared/trace'; const STRIP_HEADERS = ['x-user-id', 'x-user-roles', 'x-internal-token', 'host', 'content-length']; @@ -21,6 +22,9 @@ export async function forward(c: Context, target: string, stripPrefix: string): url.search = new URL(c.req.url).search; const headers = new Headers(c.req.raw.headers); for (const h of STRIP_HEADERS) headers.delete(h); + // 网关是外部请求的第一跳:调用方没带 traceparent 就在这里补发一条,让下游一路 childOf 续链。 + // 已有的绝不覆盖——那会把上游(浏览器/上级网关)已经开好的链从中间截断。 + if (!headers.has('traceparent')) headers.set('traceparent', newTraceparent()); const method = c.req.method; const init: RequestInit & { duplex?: 'half' } = { method, headers }; if (method !== 'GET' && method !== 'HEAD') { diff --git a/src/shared/trace.ts b/src/shared/trace.ts new file mode 100644 index 0000000..5edc1e2 --- /dev/null +++ b/src/shared/trace.ts @@ -0,0 +1,13 @@ +import { randomBytes } from 'node:crypto'; + +const SHAPE = /^00-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/; + +export function newTraceparent(): string { + return `00-${randomBytes(16).toString('hex')}-${randomBytes(8).toString('hex')}-01`; +} + +export function childOf(parent?: string): string { + const m = parent ? SHAPE.exec(parent) : null; + if (!m) return newTraceparent(); + return `00-${m[1]}-${randomBytes(8).toString('hex')}-01`; +}