From 0b83cb5fb4d7a65b819d37a1a0291e24eec64a78 Mon Sep 17 00:00:00 2001 From: lovefyf Date: Thu, 13 Aug 2026 12:23:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20W3C=20traceparent=20=E6=9E=81=E7=AE=80?= =?UTF-8?q?=E9=80=8F=E4=BC=A0=E2=80=94=E2=80=94=E7=BD=91=E5=85=B3=E8=A1=A5?= =?UTF-8?q?=E5=8F=91=E3=80=81=E4=BF=A1=E5=B0=81=E7=BB=AD=E9=93=BE=EF=BC=88?= =?UTF-8?q?=E8=A7=82=E6=B5=8B=E5=B0=BD=E5=8A=9B=E8=80=8C=E4=B8=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- src/modules/proxy/forward.ts | 4 ++++ src/shared/trace.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/shared/trace.ts 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`; +}