Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing headers for fetch #687

Merged
merged 7 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sdk_contrib/fetch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ I could find that this will happen, but throwing it out there "just in case".
## Contributors

- [Jason Terando](https://github.com/jasonterando)
- [Bernd Fuhrmann](https://github.com/berndfuhrmann)
6 changes: 6 additions & 0 deletions sdk_contrib/fetch/lib/fetch_p.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ export function captureFetchModule(
subsegmentCallback?: (subsegment: AWSXRay.Subsegment, req: fetchModule.Request, res: fetchModule.Response | null, error?: Error | undefined) => void):
(url: URL | fetchModule.RequestInfo, init?: fetchModule.RequestInit | undefined) => Promise<fetchModule.Response>;

export function enableCapture<Fetch, Request>(
fetch: Fetch,
request: Request,
downstreamXRayEnabled?: boolean,
subsegmentCallback?: (subsegment: AWSXRay.Subsegment, req: Request, res: Response | null, error?: Error | undefined) => void
): Fetch;
19 changes: 15 additions & 4 deletions sdk_contrib/fetch/lib/fetch_p.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,18 @@ function captureFetchModule(module, downstreamXRayEnabled, subsegmentCallback) {
return module.default;
}

const enableCapture = function enableCapture(baseFetchFunction, requestClass, downstreamXRayEnabled, subsegmentCallback) {
/**
* Return a fetch function that will pass segment information to the target host.
* This does not change any globals
* @param {function} baseFetchFunction fetch function to use as basis
* @param {function} requestClass Request class to use. This should correspond to the supplied fetch function.
* @param {boolean} downstreamXRayEnabled - when true, adds a "traced:true" property to the subsegment
* so the AWS X-Ray service expects a corresponding segment from the downstream service.
* @param {function} subsegmentCallback - a callback that is called with the subsegment, the fetch request,
* the fetch response and any error issued, allowing custom annotations and metadata to be added.
* @returns Response
*/
function enableCapture(baseFetchFunction, requestClass, downstreamXRayEnabled, subsegmentCallback) {

const overridenFetchAsync = async (...args) => {
const thisDownstreamXRayEnabled = !!downstreamXRayEnabled;
Expand Down Expand Up @@ -116,7 +127,7 @@ const enableCapture = function enableCapture(baseFetchFunction, requestClass, do
const requestClone = request.clone();
let response;
try {
response = await baseFetchFunction(...args);
response = await baseFetchFunction(requestClone);

if (thisSubsegmentCallback) {
thisSubsegmentCallback(subsegment, requestClone, response);
Expand Down Expand Up @@ -159,8 +170,8 @@ const enableCapture = function enableCapture(baseFetchFunction, requestClass, do
};

return overridenFetchAsync;
};
}

module.exports.captureFetchGlobal = captureFetchGlobal;
module.exports.captureFetchModule = captureFetchModule;
module.exports._fetchEnableCapture = enableCapture;
module.exports.enableCapture = enableCapture;
2 changes: 1 addition & 1 deletion sdk_contrib/fetch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fetch",
"devDependencies": {
"@types/node-fetch": "^2.6.4",
"chai-as-promised": "^7.1.1",
"chai-as-promised": "=7.1.1",
"node-fetch": "^2.6.11",
"ts-expect": "^1.3.0"
},
Expand Down
51 changes: 49 additions & 2 deletions sdk_contrib/fetch/test/integration/fetch_p.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@

let listener;
let server;
let goodUrl;
let receivedHeaders;


before(() => {
// Create an HTTP server to receive requests.
const http = require('http');
server = http.createServer((req, res) => {
receivedHeaders = { ...req.headers };
// Respond with something
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Example reply\n');
});

listener = server.listen();
const address = server.address();
const host = address.family === 'IPv6' ? `[${address.address}]` : address.address;
goodUrl = `http://${host}:${address.port}/test`;
});

after(() => {
// close http server
listener.close();
});

describe('Integration tests', function () {
const chai = require('chai');
const sinonChai = require('sinon-chai');
Expand All @@ -16,12 +44,11 @@ describe('Integration tests', function () {
const fetchModule = require('node-fetch');
const Segment = AWSXray.Segment;
const Subsegment = AWSXray.Subsegment;
const goodUrl = 'https://example.org';
const badUrl = 'http://localhost:1';

const hasGlobalFetch = globalThis.fetch !== undefined;

describe('captureFetchModule', function () {
describe('captureFetchGlobal', function () {

let saveGlobalFetch;
let mockSegment;
Expand All @@ -43,6 +70,7 @@ describe('Integration tests', function () {
stubAddFetchRequestData = sandbox.stub(mockSubsegment, 'addFetchRequestData');
stubAddErrorFlag = sandbox.stub(mockSubsegment, 'addErrorFlag');
stubClose = sandbox.stub(mockSubsegment, 'close');
receivedHeaders = {};
});

afterEach(function () {
Expand All @@ -65,6 +93,24 @@ describe('Integration tests', function () {
stubClose.should.have.been.calledOnce;
});

it('adds header', async function () {
const spyCallback = sandbox.spy();
const fetch = captureFetchGlobal(true, spyCallback);
const response = await fetch(goodUrl, { headers: {
'foo': 'bar'
}});
response.status.should.equal(200);
receivedHeaders.should.to.have.property('x-amzn-trace-id');
receivedHeaders.should.to.have.property('foo', 'bar');
(await response.text()).should.contain('Example');
stubIsAutomaticMode.should.have.been.called;
stubAddNewSubsegment.should.have.been.calledOnce;
stubResolveSegment.should.have.been.calledOnce;
stubAddFetchRequestData.should.have.been.calledOnce;
stubAddErrorFlag.should.not.have.been.calledOnce;
stubClose.should.have.been.calledOnce;
});

it('sets error flag on failed fetch when global fetch exists', async function () {
const spyCallback = sandbox.spy();
const fetch = captureFetchGlobal(true, spyCallback);
Expand Down Expand Up @@ -116,6 +162,7 @@ describe('Integration tests', function () {
const fetch = captureFetchModule(fetchModule, true, spyCallback);
const response = await fetch(goodUrl);
response.status.should.equal(200);
receivedHeaders.should.to.have.property('x-amzn-trace-id');
(await response.text()).should.contain('Example');
stubIsAutomaticMode.should.have.been.called;
stubAddNewSubsegment.should.have.been.calledOnce;
Expand Down
11 changes: 0 additions & 11 deletions sdk_contrib/fetch/test/unit/fetch_p.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,17 +339,6 @@ describe('Unit tests', function () {
response.should.equal(stubValidResponse);
});

it('resolves to response through proxy when fetch options are supplied', async function() {
const activeFetch = captureFetch(true);
const proxyStub = sinon.stub();
const request = new FetchRequest('https://www.foo.com/test');
const response = await activeFetch(request, {
dispatcher: proxyStub
});
stubFetch.should.have.been.calledOnceWith(request, {dispatcher: proxyStub});
response.should.equal(stubValidResponse);
});

it('calls subsegmentCallback with error upon fetch throwing', async function () {
const spyCallback = sandbox.spy();
const activeFetch = captureFetch(true, spyCallback);
Expand Down
Loading