Skip to content

Commit

Permalink
fix(NODE-4208): add aws http request timeout handler (#3225)
Browse files Browse the repository at this point in the history
  • Loading branch information
dariakp committed May 2, 2022
1 parent 35eeba3 commit 829d7be
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions .evergreen/config.in.yml
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ functions:
working_dir: "src"
script: |
${PREPARE_SHELL}
export IS_EC2=true
${PROJECT_DIRECTORY}/.evergreen/run-mongodb-aws-test.sh
"run aws auth test with aws credentials as environment variables":
Expand Down
1 change: 1 addition & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ functions:
working_dir: src
script: |
${PREPARE_SHELL}
export IS_EC2=true
${PROJECT_DIRECTORY}/.evergreen/run-mongodb-aws-test.sh
run aws auth test with aws credentials as environment variables:
- command: shell.exec
Expand Down
5 changes: 5 additions & 0 deletions src/cmap/auth/mongodb_aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Binary, BSONSerializeOptions } from '../../bson';
import * as BSON from '../../bson';
import { aws4 } from '../../deps';
import {
MongoAWSError,
MongoCompatibilityError,
MongoMissingCredentialsError,
MongoRuntimeError
Expand Down Expand Up @@ -283,6 +284,10 @@ function request(uri: string, _options: RequestOptions | undefined, callback: Ca
});
});

req.on('timeout', () => {
req.destroy(new MongoAWSError(`AWS request to ${uri} timed out after ${options.timeout} ms`));
});

req.on('error', err => callback(err));
req.end();
}
17 changes: 17 additions & 0 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,23 @@ export class MongoKerberosError extends MongoRuntimeError {
}
}

/**
* A error generated when the user attempts to authenticate
* via AWS, but fails
*
* @public
* @category Error
*/
export class MongoAWSError extends MongoRuntimeError {
constructor(message: string) {
super(message);
}

override get name(): string {
return 'MongoAWSError';
}
}

/**
* An error generated when a ChangeStream operation fails to execute.
*
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const ObjectID = ObjectId;
export { AnyBulkWriteOperation, BulkWriteOptions, MongoBulkWriteError } from './bulk/common';
export {
MongoAPIError,
MongoAWSError,
MongoBatchReExecutionError,
MongoChangeStreamError,
MongoCompatibilityError,
Expand Down
46 changes: 46 additions & 0 deletions test/integration/auth/mongodb_aws.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
'use strict';
const { expect } = require('chai');
const { removeAuthFromConnectionString } = require('../../tools/utils');
const sinon = require('sinon');
const http = require('http');
const { performance } = require('perf_hooks');
const { MongoAWSError } = require('../../../src');

describe('MONGODB-AWS', function () {
beforeEach(function () {
Expand Down Expand Up @@ -52,4 +56,46 @@ describe('MONGODB-AWS', function () {
.to.have.nested.property('options.credentials.mechanismProperties.AWS_SESSION_TOKEN')
.that.equals('');
});

describe('EC2 with missing credentials', () => {
let client;

beforeEach(function () {
if (!process.env.IS_EC2) {
this.currentTest.skipReason = 'requires an AWS EC2 environment';
this.skip();
}
sinon.stub(http, 'request').callsFake(function () {
arguments[0].hostname = 'www.example.com';
arguments[0].port = 81;
return http.request.wrappedMethod.apply(this, arguments);
});
});

afterEach(async () => {
sinon.restore();
if (client) {
await client.close();
}
});

it('should respect the default timeout of 10000ms', async function () {
const config = this.configuration;
client = config.newClient(process.env.MONGODB_URI, { authMechanism: 'MONGODB-AWS' }); // use the URI built by the test environment
const startTime = performance.now();

let caughtError = null;
await client.connect().catch(err => {
caughtError = err;
});

const endTime = performance.now();
const timeTaken = endTime - startTime;
expect(caughtError).to.be.instanceOf(MongoAWSError);
expect(caughtError)
.property('message')
.match(/timed out after/);
expect(timeTaken).to.be.within(10000, 12000);
});
});
});

0 comments on commit 829d7be

Please sign in to comment.