Skip to content

Commit e4eadf8

Browse files
authored
feat: add connector factory method (#1540)
This adds a new `connector` config option that may be used to define a custom socket factory method, providing much more flexible control of the socket connection creation.
1 parent d075b9c commit e4eadf8

3 files changed

Lines changed: 113 additions & 4 deletions

File tree

examples/custom-connector.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
var net = require('net');
2+
var Connection = require('../lib/tedious').Connection;
3+
4+
var config = {
5+
server: '192.168.1.212',
6+
authentication: {
7+
type: 'default',
8+
options: {
9+
userName: 'test',
10+
password: 'test'
11+
}
12+
},
13+
options: {
14+
connector: async () => net.connect({
15+
host: '192.168.1.212',
16+
port: 1433,
17+
})
18+
}
19+
};
20+
21+
const connection = new Connection(config);
22+
23+
connection.connect((err) => {
24+
if (err) {
25+
console.log('Connection Failed');
26+
throw err;
27+
}
28+
29+
console.log('Custom connection Succeeded');
30+
});

src/connection.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export interface InternalConnectionOptions {
343343
columnEncryptionSetting: boolean;
344344
columnNameReplacer: undefined | ((colName: string, index: number, metadata: Metadata) => string);
345345
connectionRetryInterval: number;
346+
connector: undefined | (() => Promise<Socket>);
346347
connectTimeout: number;
347348
connectionIsolationLevel: typeof ISOLATION_LEVEL[keyof typeof ISOLATION_LEVEL];
348349
cryptoCredentialsDetails: SecureContextOptions;
@@ -535,6 +536,13 @@ export interface ConnectionOptions {
535536
*/
536537
connectionRetryInterval?: number;
537538

539+
/**
540+
* Custom connector factory method.
541+
*
542+
* (default: `undefined`)
543+
*/
544+
connector?: () => Promise<Socket>;
545+
538546
/**
539547
* The number of milliseconds before the attempt to connect is considered failed
540548
*
@@ -1222,6 +1230,7 @@ class Connection extends EventEmitter {
12221230
columnNameReplacer: undefined,
12231231
connectionRetryInterval: DEFAULT_CONNECT_RETRY_INTERVAL,
12241232
connectTimeout: DEFAULT_CONNECT_TIMEOUT,
1233+
connector: undefined,
12251234
connectionIsolationLevel: ISOLATION_LEVEL.READ_COMMITTED,
12261235
cryptoCredentialsDetails: {},
12271236
database: undefined,
@@ -1330,6 +1339,14 @@ class Connection extends EventEmitter {
13301339
this.config.options.connectTimeout = config.options.connectTimeout;
13311340
}
13321341

1342+
if (config.options.connector !== undefined) {
1343+
if (typeof config.options.connector !== 'function') {
1344+
throw new TypeError('The "config.options.connector" property must be a function.');
1345+
}
1346+
1347+
this.config.options.connector = config.options.connector;
1348+
}
1349+
13331350
if (config.options.cryptoCredentialsDetails !== undefined) {
13341351
if (typeof config.options.cryptoCredentialsDetails !== 'object' || config.options.cryptoCredentialsDetails === null) {
13351352
throw new TypeError('The "config.options.cryptoCredentialsDetails" property must be of type Object.');
@@ -1884,7 +1901,7 @@ class Connection extends EventEmitter {
18841901
const signal = this.createConnectTimer();
18851902

18861903
if (this.config.options.port) {
1887-
return this.connectOnPort(this.config.options.port, this.config.options.multiSubnetFailover, signal);
1904+
return this.connectOnPort(this.config.options.port, this.config.options.multiSubnetFailover, signal, this.config.options.connector);
18881905
} else {
18891906
return instanceLookup({
18901907
server: this.config.server,
@@ -1893,7 +1910,7 @@ class Connection extends EventEmitter {
18931910
signal: signal
18941911
}).then((port) => {
18951912
process.nextTick(() => {
1896-
this.connectOnPort(port, this.config.options.multiSubnetFailover, signal);
1913+
this.connectOnPort(port, this.config.options.multiSubnetFailover, signal, this.config.options.connector);
18971914
});
18981915
}, (err) => {
18991916
this.clearConnectTimer();
@@ -1956,14 +1973,14 @@ class Connection extends EventEmitter {
19561973
return new TokenStreamParser(message, this.debug, handler, this.config.options);
19571974
}
19581975

1959-
connectOnPort(port: number, multiSubnetFailover: boolean, signal: AbortSignal) {
1976+
connectOnPort(port: number, multiSubnetFailover: boolean, signal: AbortSignal, customConnector?: () => Promise<Socket>) {
19601977
const connectOpts = {
19611978
host: this.routingData ? this.routingData.server : this.config.server,
19621979
port: this.routingData ? this.routingData.port : port,
19631980
localAddress: this.config.options.localAddress
19641981
};
19651982

1966-
const connect = multiSubnetFailover ? connectInParallel : connectInSequence;
1983+
const connect = customConnector || (multiSubnetFailover ? connectInParallel : connectInSequence);
19671984

19681985
connect(connectOpts, dns.lookup, signal).then((socket) => {
19691986
process.nextTick(() => {

test/unit/custom-connector.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const net = require('net');
2+
const assert = require('chai').assert;
3+
4+
const { Connection } = require('../../src/tedious');
5+
6+
describe('custom connector', function() {
7+
let server;
8+
9+
beforeEach(function(done) {
10+
server = net.createServer();
11+
server.listen(0, '127.0.0.1', done);
12+
});
13+
14+
afterEach(() => {
15+
server.close();
16+
});
17+
18+
it('connection using a custom connector', function(done) {
19+
let attemptedConnection = false;
20+
let customConnectorCalled = false;
21+
22+
server.on('connection', async (connection) => {
23+
attemptedConnection = true;
24+
// no need to test auth/login, just end the connection sooner
25+
connection.end();
26+
});
27+
28+
const host = server.address().address;
29+
const port = server.address().port;
30+
const connection = new Connection({
31+
server: host,
32+
options: {
33+
connector: async () => {
34+
customConnectorCalled = true;
35+
return net.connect({
36+
host,
37+
port,
38+
});
39+
},
40+
port
41+
},
42+
});
43+
44+
connection.on('end', (err) => {
45+
// validates the connection was stablished using the custom connector
46+
assert.isOk(attemptedConnection);
47+
assert.isOk(customConnectorCalled);
48+
49+
connection.close();
50+
done();
51+
});
52+
53+
connection.on('error', (err) => {
54+
// Connection lost errors are expected due to ending connection sooner
55+
if (!/Connection lost/.test(err)) {
56+
throw err;
57+
}
58+
});
59+
60+
connection.connect();
61+
});
62+
});

0 commit comments

Comments
 (0)