Skip to content

Commit

Permalink
feat: do not add W3C TraceContext headers to Amazon Sigv4 outgoing HT…
Browse files Browse the repository at this point in the history
…TP requests (#161)
  • Loading branch information
Michele Mancioppi authored Mar 1, 2023
1 parent 36c7483 commit 4475cc4
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
27 changes: 27 additions & 0 deletions src/propagator/w3cTraceContextPropagator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { diag, Context, TextMapSetter } from '@opentelemetry/api';
import { W3CTraceContextPropagator } from '@opentelemetry/core';

/*
* List of keys in the carrier that signal the need not to inject
* traceparent/tracestate headers, e.g., when the outgoing request
* is signed with a digest, abnd us adding HTTP headers would
* invalidate the signature.
*/
const contextKeysSkipInject = [
'x-amz-content-sha256', // Amazon Sigv4, see https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
];

export class LumigoW3CTraceContextPropagator extends W3CTraceContextPropagator {
override inject(context: Context, carrier: unknown, setter: TextMapSetter): void {
if (typeof carrier === 'object') {
for (const key of contextKeysSkipInject) {
if (key in carrier) {
diag.debug(`Skipping injection of trace context due to key '${key}' in carrier`);
return;
}
}
}

super.inject(context, carrier, setter);
}
}
5 changes: 5 additions & 0 deletions src/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import LumigoMongoDBInstrumentation from './instrumentations/mongodb/MongoDBInst
import { extractEnvVars, getMaxSize } from './utils';
import * as awsResourceDetectors from '@opentelemetry/resource-detector-aws';
import { LumigoDistroDetector, LumigoKubernetesDetector } from './resources/detectors';
import { LumigoW3CTraceContextPropagator } from './propagator/w3cTraceContextPropagator';
import { LUMIGO_DISTRO_VERSION } from './resources/detectors/LumigoDistroDetector';
import { CommonUtils } from '@lumigo/node-core';

Expand Down Expand Up @@ -123,6 +124,10 @@ const trace = async (): Promise<LumigoSdkInitialization> => {
},
});

tracerProvider.register({
propagator: new LumigoW3CTraceContextPropagator(),
});

if (process.env.LUMIGO_DEBUG_SPANDUMP) {
tracerProvider.addSpanProcessor(
new SimpleSpanProcessor(new FileSpanExporter(process.env.LUMIGO_DEBUG_SPANDUMP))
Expand Down
19 changes: 19 additions & 0 deletions test/component/http/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ const requestListener = async function (req, res) {
res.end(JSON.stringify(err));
}
break
case '/amazon-sigv4':
try {
// We expect this to throw due to the timeout + impossibility to connect
await axios.get(`https://httpbin.org/status/201`, {
headers: {
'x-amz-content-sha256': 'abcdefghi',
},
timeout: 5_000, // Milliseconds
});
res.setHeader('Content-Type', 'application/json');
res.writeHead(200);
res.end();
} catch (err) {
res.setHeader('Content-Type', 'application/json');
res.writeHead(500);
res.end(JSON.stringify(err));
}
break

default:
res.writeHead(404);
res.end(JSON.stringify({error: 'Resource not found'}));
Expand Down
85 changes: 84 additions & 1 deletion test/component/http/http.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,5 +373,88 @@ describe(`Component compatibility tests for HTTP`, function () {
)
expect(clientAttributes['http.response.body'].length).toEqual(2048)
}, TEST_TIMEOUT);


test("http test - no trace context set if Amazon Sigv4 header is present", async () => {
const fileExporterName = `${SPANS_DIR}/spans-${COMPONENT_NAME}-default.json`;

// start server
app = startTestApp(EXEC_SERVER_FOLDER, COMPONENT_NAME, fileExporterName);
const port = await getPort(app);

const waited = new Promise((resolve, reject) => {
waitOn(
{
resources: [`http-get://localhost:${port}`],
delay: 5000,
timeout: WAIT_ON_TIMEOUT,
simultaneous: 1,
log: true,
validateStatus: function (status) {
console.debug("server status:", status);
return status >= 200 && status < 300; // default if not provided
},
},
async function (err) {
if (err) {
console.error("inside waitOn", err);
return reject(err)
} else {
console.info('Got a response from server');
await callContainer(port, 'amazon-sigv4', 'get');
let spans = await waitForSpansInFile(fileExporterName, getInstrumentationSpansFromFile);
resolve(spans.map((text) => JSON.parse(text)))
}
}
);
});
try {
spans = await waited
} catch (e) {
console.error(e)
throw e;
}

expect(spans).toHaveLength(2);
const internalSpan = getSpanByKind(spans, 1);
const clientSpan = getSpanByKind(spans, 2);
expect(internalSpan.attributes).toMatchObject(
{
'http.host': `localhost:${port}`,
'net.host.name': "localhost",
'http.method': 'GET',
'http.user_agent': "axios/0.21.4",
'http.flavor': '1.1',
'net.transport': "ip_tcp",
"net.host.ip": expect.any(String),
'net.host.port': expect.any(Number),
"net.peer.ip": expect.any(String),
'net.peer.port': expect.any(Number),
'http.status_code': 200,
'http.status_text': 'OK',
"http.url": `http://localhost:${port}/amazon-sigv4`,
}
)
const clientAttributes = clientSpan.attributes;
expect(clientAttributes).toMatchObject(
{
'http.url': "https://httpbin.org/status/201",
'http.method': 'GET',
'http.target': "/status/201",
'net.peer.name': "httpbin.org",
'http.request.body': '""',
'net.peer.ip': expect.stringMatching(
/\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$))\b/
),
'net.peer.port': 443,
'http.host': "httpbin.org:443",
'http.status_code': 201,
'http.status_text': 'CREATED',
'http.flavor': '1.1',
'http.request.headers': expect.not.stringMatching(/{.*traceparent.*}/),
'http.response.headers': expect.stringMatching(/{.*}/),
'http.response.body': expect.any(String),
}
)
}, TEST_TIMEOUT);

});

0 comments on commit 4475cc4

Please sign in to comment.