Skip to content

Commit

Permalink
Add proxy support for global fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
Kruspe committed Nov 17, 2024
1 parent 3b89008 commit 60c56b3
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
24 changes: 24 additions & 0 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"sinon-chai": "^3.5.0",
"tsd": "^0.25.0",
"typescript": "^4.4.4",
"undici": "^6.21.0",
"upath": "^1.2.0"
},
"engines": {
Expand Down
3 changes: 2 additions & 1 deletion sdk_contrib/fetch/lib/fetch_p.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ function enableCapture(baseFetchFunction, requestClass, downstreamXRayEnabled, s

// Facilitate the addition of Segment information via the request arguments
const params = args.length > 1 ? args[1] : {};
const fetchOptions = 'dispatcher' in params ? {dispatcher: params.dispatcher} : undefined;

// Short circuit if the HTTP is already being captured
if (request.headers.has('X-Amzn-Trace-Id')) {
Expand Down Expand Up @@ -127,7 +128,7 @@ function enableCapture(baseFetchFunction, requestClass, downstreamXRayEnabled, s
const requestClone = request.clone();
let response;
try {
response = await baseFetchFunction(requestClone);
response = await baseFetchFunction(requestClone, fetchOptions);

if (thisSubsegmentCallback) {
thisSubsegmentCallback(subsegment, requestClone, response);
Expand Down
46 changes: 46 additions & 0 deletions sdk_contrib/fetch/test/integration/fetch_p.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ let listener;
let server;
let goodUrl;
let receivedHeaders;
let proxyServer;
let proxyUrl;
let proxyListener;


before(() => {
Expand All @@ -19,11 +22,37 @@ before(() => {
const address = server.address();
const host = address.family === 'IPv6' ? `[${address.address}]` : address.address;
goodUrl = `http://${host}:${address.port}/test`;

proxyServer = http.createServer();
proxyServer.on('connect', (req, socket) => {
const res = new http.ServerResponse(req);
res.assignSocket(socket);

const lastColon = req.url.lastIndexOf(':');
const host = req.url.substring(0, lastColon);
const port = parseInt(req.url.substring(lastColon + 1), 10);
const opts = {host: host.replace(/^\[|\]$/g, ''), port};

const net = require('net');
const target = net.connect(opts, () => {
res.writeHead(200);
res.flushHeaders();
res.detachSocket(socket);

socket.pipe(target);
target.pipe(socket);
});
});
proxyListener = proxyServer.listen();
const proxyAddress = proxyServer.address();
const proxyHost = proxyAddress.family === 'IPv6' ? `[${proxyAddress.address}]` : proxyAddress.address;
proxyUrl = `http://${proxyHost}:${proxyAddress.port}`;
});

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

describe('Integration tests', function () {
Expand Down Expand Up @@ -111,6 +140,23 @@ describe('Integration tests', function () {
stubClose.should.have.been.calledOnce;
});

it('adds headers when called with fetchOptions', async function () {
const spyCallback = sandbox.spy();
const fetch = captureFetchGlobal(true, spyCallback);
const undici = require('undici');
const response = await fetch(goodUrl, {dispatcher: new undici.ProxyAgent(proxyUrl), 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
14 changes: 14 additions & 0 deletions sdk_contrib/fetch/test/unit/fetch_p.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,20 @@ 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.calledOnce;
const callArgs = stubFetch.firstCall.args;
callArgs[0].should.contain(request);
callArgs[1].dispatcher.should.equal(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

0 comments on commit 60c56b3

Please sign in to comment.