Skip to content

Commit 1ba0630

Browse files
committed
feat(core/client): update trace header conditions
1 parent 49b7c06 commit 1ba0630

3 files changed

Lines changed: 131 additions & 15 deletions

File tree

packages-internal/core/src/submodules/client/middleware-recursion-detection/middleware-recursion-detection.integ.spec.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,91 @@ describe("middleware-recursion-detection", () => {
128128
expect.hasAssertions();
129129
});
130130

131+
it("should NOT overwrite existing traceparent or add tracestate/baggage from InvokeStore when traceparent is already set", async () => {
132+
const existingTraceparent = "00-abcdef1234567890abcdef1234567890-1234567890abcdef-01";
133+
const mockTraceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
134+
const mockTracestate = "congo=t61rcWkgMzE";
135+
const mockBaggage = "userId=alice,serverNode=DF%2028";
136+
vi.spyOn(InvokeStore, "getInstanceAsync").mockResolvedValue({
137+
getXRayTraceId: () => undefined,
138+
getTraceparent: () => mockTraceparent,
139+
getTracestate: () => mockTracestate,
140+
getBaggage: () => mockBaggage,
141+
} as any);
142+
143+
const client = new Lambda({
144+
region: "us-west-2",
145+
});
146+
147+
// Add middleware that sets traceparent before recursion detection runs.
148+
// recursionDetectionMiddleware runs at build step with low priority,
149+
// so we add at build step with high priority to run first.
150+
client.middlewareStack.add(
151+
(next) => async (args: any) => {
152+
args.request.headers["traceparent"] = existingTraceparent;
153+
return next(args);
154+
},
155+
{ step: "build", priority: "high", name: "testSetTraceparent", override: true }
156+
);
157+
158+
requireRequestsFrom(client).toMatch({
159+
headers: {
160+
traceparent: new RegExp(`^${existingTraceparent.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`),
161+
tracestate: (value: any) => expect(value).toBeUndefined(),
162+
baggage: (value: any) => expect(value).toBeUndefined(),
163+
},
164+
});
165+
166+
await client.invoke({
167+
FunctionName: "my-function",
168+
});
169+
170+
expect.hasAssertions();
171+
});
172+
173+
it("should preserve existing traceparent, tracestate, and baggage without overwriting from InvokeStore", async () => {
174+
const existingTraceparent = "00-abcdef1234567890abcdef1234567890-1234567890abcdef-01";
175+
const existingTracestate = "vendor1=opaqueValue1";
176+
const existingBaggage = "key1=value1";
177+
const mockTraceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
178+
const mockTracestate = "congo=t61rcWkgMzE";
179+
const mockBaggage = "userId=alice,serverNode=DF%2028";
180+
vi.spyOn(InvokeStore, "getInstanceAsync").mockResolvedValue({
181+
getXRayTraceId: () => undefined,
182+
getTraceparent: () => mockTraceparent,
183+
getTracestate: () => mockTracestate,
184+
getBaggage: () => mockBaggage,
185+
} as any);
186+
187+
const client = new Lambda({
188+
region: "us-west-2",
189+
});
190+
191+
client.middlewareStack.add(
192+
(next) => async (args: any) => {
193+
args.request.headers["traceparent"] = existingTraceparent;
194+
args.request.headers["tracestate"] = existingTracestate;
195+
args.request.headers["baggage"] = existingBaggage;
196+
return next(args);
197+
},
198+
{ step: "build", priority: "high", name: "testSetTraceHeaders", override: true }
199+
);
200+
201+
requireRequestsFrom(client).toMatch({
202+
headers: {
203+
traceparent: new RegExp(`^${existingTraceparent.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`),
204+
tracestate: new RegExp(`^${existingTracestate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`),
205+
baggage: new RegExp(`^${existingBaggage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`),
206+
},
207+
});
208+
209+
await client.invoke({
210+
FunctionName: "my-function",
211+
});
212+
213+
expect.hasAssertions();
214+
});
215+
131216
it("should NOT add W3C headers when InvokeStore has no traceparent", async () => {
132217
vi.spyOn(InvokeStore, "getInstanceAsync").mockResolvedValue({
133218
getXRayTraceId: () => undefined,

packages-internal/core/src/submodules/client/middleware-recursion-detection/recursionDetectionMiddleware.spec.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ describe(recursionDetectionMiddleware.name, () => {
256256
expect(request.headers["baggage"]).toBeUndefined();
257257
});
258258

259-
it("should preserve existing traceparent header and add tracestate/baggage from InvokeStore", async () => {
259+
it("should preserve existing traceparent header and NOT add tracestate/baggage from InvokeStore", async () => {
260260
const existingTraceparent = "00-abcdef1234567890abcdef1234567890-1234567890abcdef-01";
261261
vi.spyOn(InvokeStore, "getInstanceAsync").mockResolvedValue({
262262
getXRayTraceId: () => undefined,
@@ -277,8 +277,37 @@ describe(recursionDetectionMiddleware.name, () => {
277277

278278
const { request } = mockNextHandler.mock.calls[0][0];
279279
expect(request.headers["traceparent"]).toBe(existingTraceparent);
280-
expect(request.headers["tracestate"]).toBe(mockTracestate);
281-
expect(request.headers["baggage"]).toBe(mockBaggage);
280+
expect(request.headers["tracestate"]).toBeUndefined();
281+
expect(request.headers["baggage"]).toBeUndefined();
282+
});
283+
284+
it("should preserve existing traceparent, tracestate, and baggage headers without overwriting from InvokeStore", async () => {
285+
const existingTraceparent = "00-abcdef1234567890abcdef1234567890-1234567890abcdef-01";
286+
const existingTracestate = "vendor1=opaqueValue1";
287+
const existingBaggage = "key1=value1";
288+
vi.spyOn(InvokeStore, "getInstanceAsync").mockResolvedValue({
289+
getXRayTraceId: () => undefined,
290+
getTraceparent: () => mockTraceparent,
291+
getTracestate: () => mockTracestate,
292+
getBaggage: () => mockBaggage,
293+
} as any);
294+
295+
const handler = recursionDetectionMiddleware()(mockNextHandler, {} as any);
296+
await handler({
297+
input: {},
298+
request: new HttpRequest({
299+
headers: {
300+
traceparent: existingTraceparent,
301+
tracestate: existingTracestate,
302+
baggage: existingBaggage,
303+
},
304+
}),
305+
});
306+
307+
const { request } = mockNextHandler.mock.calls[0][0];
308+
expect(request.headers["traceparent"]).toBe(existingTraceparent);
309+
expect(request.headers["tracestate"]).toBe(existingTracestate);
310+
expect(request.headers["baggage"]).toBe(existingBaggage);
282311
});
283312

284313
it("should not overwrite existing traceparent when InvokeStore also has one", async () => {

packages-internal/core/src/submodules/client/middleware-recursion-detection/recursionDetectionMiddleware.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,21 @@ export const recursionDetectionMiddleware =
6565

6666
sanitizeTraceHeaders(request.headers);
6767

68-
const traceparent =
69-
request.headers[TRACEPARENT] ?? (invokeStore ??= await InvokeStore.getInstanceAsync())?.getTraceparent();
70-
if (traceparent) {
71-
// traceparent is required.
72-
request.headers[TRACEPARENT] = traceparent;
68+
const existingTraceparent = request.headers[TRACEPARENT];
7369

74-
const tracestate = invokeStore?.getTracestate?.();
75-
if (tracestate) {
76-
request.headers[TRACESTATE] = tracestate;
77-
}
78-
const baggage = invokeStore?.getBaggage?.();
79-
if (baggage) {
80-
request.headers[BAGGAGE] = baggage;
70+
if (!existingTraceparent) {
71+
const traceparent = (invokeStore ??= await InvokeStore.getInstanceAsync())?.getTraceparent?.();
72+
if (traceparent) {
73+
request.headers[TRACEPARENT] = traceparent;
74+
75+
const tracestate = invokeStore?.getTracestate?.();
76+
if (tracestate) {
77+
request.headers[TRACESTATE] = tracestate;
78+
}
79+
const baggage = invokeStore?.getBaggage?.();
80+
if (baggage) {
81+
request.headers[BAGGAGE] = baggage;
82+
}
8183
}
8284
}
8385
}

0 commit comments

Comments
 (0)