This repository has been archived by the owner on Oct 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 96
HTTP Propagation: set Correlation-Context
header
#567
Open
mayurkale22
wants to merge
3
commits into
census-instrumentation:master
Choose a base branch
from
mayurkale22:Correlation-Contex
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+185
−5
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,6 @@ import * as http from 'http'; | |
import * as nock from 'nock'; | ||
import * as shimmer from 'shimmer'; | ||
import * as url from 'url'; | ||
|
||
import { HttpPlugin, plugin } from '../src/'; | ||
import * as stats from '../src/http-stats'; | ||
|
||
|
@@ -183,11 +182,19 @@ function assertCustomAttribute( | |
function assertClientStats( | ||
testExporter: TestExporter, | ||
httpStatusCode: number, | ||
httpMethod: string | ||
httpMethod: string, | ||
tagCtx?: TagMap | ||
) { | ||
const tags = new TagMap(); | ||
tags.set(stats.HTTP_CLIENT_METHOD, { value: httpMethod }); | ||
tags.set(stats.HTTP_CLIENT_STATUS, { value: `${httpStatusCode}` }); | ||
|
||
if (tagCtx) { | ||
tagCtx.tags.forEach((tagValue: TagValue, tagKey: TagKey) => { | ||
tags.set(tagKey, tagValue); | ||
}); | ||
} | ||
|
||
assert.strictEqual(testExporter.registeredViews.length, 8); | ||
assert.strictEqual(testExporter.recordedMeasurements.length, 1); | ||
assert.strictEqual( | ||
|
@@ -202,12 +209,20 @@ function assertServerStats( | |
testExporter: TestExporter, | ||
httpStatusCode: number, | ||
httpMethod: string, | ||
path: string | ||
path: string, | ||
tagCtx?: TagMap | ||
) { | ||
const tags = new TagMap(); | ||
tags.set(stats.HTTP_SERVER_METHOD, { value: httpMethod }); | ||
tags.set(stats.HTTP_SERVER_STATUS, { value: `${httpStatusCode}` }); | ||
tags.set(stats.HTTP_SERVER_ROUTE, { value: path }); | ||
|
||
if (tagCtx) { | ||
tagCtx.tags.forEach((tagValue: TagValue, tagKey: TagKey) => { | ||
tags.set(tagKey, tagValue); | ||
}); | ||
} | ||
|
||
assert.strictEqual(testExporter.registeredViews.length, 8); | ||
assert.strictEqual(testExporter.recordedMeasurements.length, 1); | ||
assert.strictEqual( | ||
|
@@ -357,6 +372,60 @@ describe('HttpPlugin', () => { | |
}); | ||
}); | ||
|
||
it('should create a child span for GET requests with tag context', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if I understand these tests - why test spans? Same for other tests. |
||
const testPath = '/outgoing/rootSpan/childs/1'; | ||
doNock(urlHost, testPath, 200, 'Ok'); | ||
const tags = new TagMap(); | ||
tags.set({ name: 'testKey1' }, { value: 'value1' }); | ||
tags.set({ name: 'testKey2' }, { value: 'value2' }); | ||
return globalStats.withTagContext(tags, async () => { | ||
return tracer.startRootSpan( | ||
{ name: 'TestRootSpan' }, | ||
async (root: Span) => { | ||
await httpRequest.get(`${urlHost}${testPath}`).then(result => { | ||
assert.ok(root.name.indexOf('TestRootSpan') >= 0); | ||
assert.strictEqual(root.spans.length, 1); | ||
const [span] = root.spans; | ||
assert.ok(span.name.indexOf(testPath) >= 0); | ||
assert.strictEqual(root.traceId, span.traceId); | ||
assertSpanAttributes(span, 200, 'GET', hostName, testPath); | ||
assert.strictEqual(span.messageEvents.length, 1); | ||
const [messageEvent] = span.messageEvents; | ||
assert.strictEqual(messageEvent.type, MessageEventType.SENT); | ||
assert.strictEqual(messageEvent.id, 1); | ||
assertClientStats(testExporter, 200, 'GET', tags); | ||
}); | ||
} | ||
); | ||
}); | ||
}); | ||
|
||
it('should create a child span for GET requests with empty tag context', () => { | ||
const testPath = '/outgoing/rootSpan/childs/1'; | ||
doNock(urlHost, testPath, 200, 'Ok'); | ||
const tags = new TagMap(); | ||
return globalStats.withTagContext(tags, async () => { | ||
return tracer.startRootSpan( | ||
{ name: 'TestRootSpan' }, | ||
async (root: Span) => { | ||
await httpRequest.get(`${urlHost}${testPath}`).then(result => { | ||
assert.ok(root.name.indexOf('TestRootSpan') >= 0); | ||
assert.strictEqual(root.spans.length, 1); | ||
const [span] = root.spans; | ||
assert.ok(span.name.indexOf(testPath) >= 0); | ||
assert.strictEqual(root.traceId, span.traceId); | ||
assertSpanAttributes(span, 200, 'GET', hostName, testPath); | ||
assert.strictEqual(span.messageEvents.length, 1); | ||
const [messageEvent] = span.messageEvents; | ||
assert.strictEqual(messageEvent.type, MessageEventType.SENT); | ||
assert.strictEqual(messageEvent.id, 1); | ||
assertClientStats(testExporter, 200, 'GET'); | ||
}); | ||
} | ||
); | ||
}); | ||
}); | ||
|
||
for (let i = 0; i < httpErrorCodes.length; i++) { | ||
it(`should test a child spans for GET requests with http error ${ | ||
httpErrorCodes[i] | ||
|
@@ -529,6 +598,84 @@ describe('HttpPlugin', () => { | |
}); | ||
}); | ||
|
||
it('should handle incoming requests with long request url path', async () => { | ||
const testPath = '/test&code=' + 'a'.repeat(300); | ||
const options = { | ||
host: 'localhost', | ||
path: testPath, | ||
port: serverPort, | ||
headers: { 'User-Agent': 'Android' }, | ||
}; | ||
shimmer.unwrap(http, 'get'); | ||
shimmer.unwrap(http, 'request'); | ||
nock.enableNetConnect(); | ||
|
||
assert.strictEqual(spanVerifier.endedSpans.length, 0); | ||
|
||
await httpRequest.get(options).then(result => { | ||
assert.strictEqual(spanVerifier.endedSpans.length, 1); | ||
assert.ok(spanVerifier.endedSpans[0].name.indexOf(testPath) >= 0); | ||
const [span] = spanVerifier.endedSpans; | ||
assertSpanAttributes( | ||
span, | ||
200, | ||
'GET', | ||
'localhost', | ||
testPath, | ||
'Android' | ||
); | ||
assertServerStats( | ||
testExporter, | ||
200, | ||
'GET', | ||
'/test&code=' + 'a'.repeat(244) | ||
); | ||
}); | ||
}); | ||
it('should create a root span for incoming requests with Correlation Context header', async () => { | ||
const testPath = '/incoming/rootSpan/'; | ||
const options = { | ||
host: 'localhost', | ||
path: testPath, | ||
port: serverPort, | ||
headers: { | ||
'User-Agent': 'Android', | ||
'Correlation-Context': 'k1=v1,k2=v2', | ||
}, | ||
}; | ||
|
||
const expectedTagsFromHeaders = new TagMap(); | ||
expectedTagsFromHeaders.set({ name: 'k1' }, { value: 'v1' }); | ||
expectedTagsFromHeaders.set({ name: 'k2' }, { value: 'v2' }); | ||
|
||
shimmer.unwrap(http, 'get'); | ||
shimmer.unwrap(http, 'request'); | ||
nock.enableNetConnect(); | ||
|
||
assert.strictEqual(spanVerifier.endedSpans.length, 0); | ||
|
||
await httpRequest.get(options).then(result => { | ||
assert.ok(spanVerifier.endedSpans[0].name.indexOf(testPath) >= 0); | ||
assert.strictEqual(spanVerifier.endedSpans.length, 1); | ||
const [span] = spanVerifier.endedSpans; | ||
assertSpanAttributes( | ||
span, | ||
200, | ||
'GET', | ||
'localhost', | ||
testPath, | ||
'Android' | ||
); | ||
assertServerStats( | ||
testExporter, | ||
200, | ||
'GET', | ||
testPath, | ||
expectedTagsFromHeaders | ||
); | ||
}); | ||
}); | ||
|
||
it('should handle incoming requests with long request url path', async () => { | ||
const testPath = '/test&code=' + 'a'.repeat(300); | ||
const options = { | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
toLocaleLowerCase
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK Node core automatically converts all HTTP headers to lowercase. Without much frills, I easily found the location where outgoing HTTP headers are converted to lowercase.
@draffensperger correct me if this statement is wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you think about just making the constant itself be in lower case so you don't need to convert it here along with a comment that Node converts headers to lower case?
I'm not super familiar with how headers in Node are handled. Could this be related? nodejs/node-v0.x-archive#1954
How are headers for trace context handled? Do we assume those are lowercase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TraceContext headers are defined in lower case only. As per official documentation -> In order to increase interoperability across multiple protocols and encourage successful integration by default it is recommended to keep the header name lower case. Platforms and libraries MUST expect header name in any casing and SHOULD send header name in lower case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I guess I'm a little confused by the code then, to me it looks like you are looking for the header key in lower case rather than converting all the incoming headers to lower case to compare them?
Or am I missing something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's for
TraceContext
. I don't think it's the case forCorrelation-Context
, per https://github.com/w3c/correlation-context/blob/master/correlation_context/HTTP_HEADER_FORMAT.md#examples-of-http-headers.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phrased a different way, how do you know that
headers: IncomingHttpHeaders
here will always have its values in lower case? Could they ever be in upper-case, and if so, should you just convert them to lower case to make sure before comparing them to a lower-cased header?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure on this, perhaps that's why we use both the variant of
user-agent
key. I think it is safe bet to compare both the variant until we don;t know the complete picture.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Maybe you can just making both the incoming header and the constant be lower case and compare those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to approve this, and if this comes up as a bug in the future, it should be fixed.