Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit 50421a5

Browse files
authored
fix: don't let trace context injection throw (#989)
1 parent fae65bd commit 50421a5

2 files changed

Lines changed: 38 additions & 17 deletions

File tree

src/plugins/plugin-http.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ type HttpModule = typeof httpModule;
2828
type HttpsModule = typeof httpsModule;
2929
type RequestFunction = typeof request;
3030

31+
const ERR_HTTP_HEADERS_SENT = 'ERR_HTTP_HEADERS_SENT';
32+
const ERR_HTTP_HEADERS_SENT_MSG = 'Can\'t set headers after they are sent.';
33+
3134
// tslint:disable:no-any
3235
const isString = is.string as (value: any) => value is string;
3336
// url.URL is used for type checking, but doesn't exist in Node <7.
@@ -44,12 +47,16 @@ function getSpanName(options: ClientRequestArgs|url.URL) {
4447

4548
/**
4649
* Returns whether the Expect header is on the given options object.
50+
* Assumes only that the header key is either capitalized, lowercase, or
51+
* all-caps for simplicity purposes.
4752
* @param options Options for http.request.
4853
*/
4954
function hasExpectHeader(options: ClientRequestArgs|url.URL): boolean {
5055
return !!(
5156
(options as ClientRequestArgs).headers &&
52-
(options as ClientRequestArgs).headers!.Expect);
57+
((options as ClientRequestArgs).headers!.Expect ||
58+
(options as ClientRequestArgs).headers!.expect ||
59+
(options as ClientRequestArgs).headers!.EXPECT));
5360
}
5461

5562
function extractUrl(
@@ -126,6 +133,8 @@ function makeRequestTrace(
126133
// inject the trace context header instead of using ClientRequest#setHeader.
127134
// (We don't do this generally because cloning the options object is an
128135
// expensive operation.)
136+
// See https://github.com/googleapis/cloud-trace-nodejs/pull/766 for a full
137+
// explanation.
129138
let traceHeaderPreinjected = false;
130139
if (hasExpectHeader(options)) {
131140
traceHeaderPreinjected = true;
@@ -179,8 +188,19 @@ function makeRequestTrace(
179188
// Inject the trace context header, but only if it wasn't already injected
180189
// earlier.
181190
if (!traceHeaderPreinjected) {
182-
req.setHeader(
183-
api.constants.TRACE_CONTEXT_HEADER_NAME, span.getTraceContext());
191+
try {
192+
req.setHeader(
193+
api.constants.TRACE_CONTEXT_HEADER_NAME, span.getTraceContext());
194+
} catch (e) {
195+
if (e.code === ERR_HTTP_HEADERS_SENT ||
196+
e.message === ERR_HTTP_HEADERS_SENT_MSG) {
197+
// Swallow the error.
198+
// This would happen in the pathological case where the Expect header
199+
// exists but is not detected by hasExpectHeader.
200+
} else {
201+
throw e;
202+
}
203+
}
184204
}
185205
return req;
186206
};

test/plugins/test-trace-http.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -191,20 +191,6 @@ for (const nodule of Object.keys(servers) as Array<keyof typeof servers>) {
191191
await waitForResponse.done;
192192
}
193193
},
194-
{
195-
description: 'calling http.get with Expect header',
196-
fn: async () => {
197-
const waitForResponse = new WaitForResponse();
198-
const req = http.get(
199-
{
200-
port,
201-
rejectUnauthorized: false,
202-
headers: {Expect: '100-continue'}
203-
},
204-
waitForResponse.handleResponse);
205-
await waitForResponse.done;
206-
}
207-
},
208194
{
209195
description: 'calling http.get, but timing out and emitting an error',
210196
fn: async () => {
@@ -219,6 +205,21 @@ for (const nodule of Object.keys(servers) as Array<keyof typeof servers>) {
219205
await waitForResponse.done;
220206
}
221207
},
208+
...['Expect', 'expect', 'EXPECT', 'eXpEcT'].map(
209+
key => ({
210+
description: `calling http.get with ${key} header`,
211+
fn: async () => {
212+
const waitForResponse = new WaitForResponse();
213+
http.get(
214+
{
215+
port,
216+
rejectUnauthorized: false,
217+
headers: {[key]: '100-continue'}
218+
},
219+
waitForResponse.handleResponse);
220+
await waitForResponse.done;
221+
}
222+
}))
222223
];
223224

224225
beforeEach(async () => {

0 commit comments

Comments
 (0)