Skip to content

Commit b78df14

Browse files
authored
feat: add redirect info to connection errors (#1527)
Co-authored with @arthurschreiber Co-authored with @mShan0
1 parent c02e30f commit b78df14

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

src/connection.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,7 +2129,14 @@ class Connection extends EventEmitter {
21292129
* @private
21302130
*/
21312131
connectTimeout() {
2132-
const message = `Failed to connect to ${this.config.server}${this.config.options.port ? `:${this.config.options.port}` : `\\${this.config.options.instanceName}`} in ${this.config.options.connectTimeout}ms`;
2132+
const hostPostfix = this.config.options.port ? `:${this.config.options.port}` : `\\${this.config.options.instanceName}`;
2133+
// If we have routing data stored, this connection has been redirected
2134+
const server = this.routingData ? this.routingData.server : this.config.server;
2135+
const port = this.routingData ? `:${this.routingData.port}` : hostPostfix;
2136+
// Grab the target host from the connection configration, and from a redirect message
2137+
// otherwise, leave the message empty.
2138+
const routingMessage = this.routingData ? ` (redirected from ${this.config.server}${hostPostfix})` : '';
2139+
const message = `Failed to connect to ${server}${port}${routingMessage} in ${this.config.options.connectTimeout}ms`;
21332140
this.debug.log(message);
21342141
this.emit('connect', new ConnectionError(message, 'ETIMEOUT'));
21352142
this.connectTimer = undefined;
@@ -2258,7 +2265,14 @@ class Connection extends EventEmitter {
22582265
*/
22592266
socketError(error: Error) {
22602267
if (this.state === this.STATE.CONNECTING || this.state === this.STATE.SENT_TLSSSLNEGOTIATION) {
2261-
const message = `Failed to connect to ${this.config.server}:${this.config.options.port} - ${error.message}`;
2268+
const hostPostfix = this.config.options.port ? `:${this.config.options.port}` : `\\${this.config.options.instanceName}`;
2269+
// If we have routing data stored, this connection has been redirected
2270+
const server = this.routingData ? this.routingData.server : this.config.server;
2271+
const port = this.routingData ? `:${this.routingData.port}` : hostPostfix;
2272+
// Grab the target host from the connection configration, and from a redirect message
2273+
// otherwise, leave the message empty.
2274+
const routingMessage = this.routingData ? ` (redirected from ${this.config.server}${hostPostfix})` : '';
2275+
const message = `Failed to connect to ${server}${port}${routingMessage} - ${error.message}`;
22622276
this.debug.log(message);
22632277
this.emit('connect', new ConnectionError(message, 'ESOCKET'));
22642278
} else {

test/unit/rerouting-test.js

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { assert } = require('chai');
22
const net = require('net');
33

4-
const { Connection } = require('../../src/tedious');
4+
const { Connection, ConnectionError } = require('../../src/tedious');
55
const IncomingMessageStream = require('../../src/incoming-message-stream');
66
const OutgoingMessageStream = require('../../src/outgoing-message-stream');
77
const Debug = require('../../src/debug');
@@ -381,4 +381,85 @@ describe('Connecting to a server that sends a re-routing information', function(
381381
connection.close();
382382
}
383383
});
384+
385+
it('it should throw an error with redirect information when targetserver connection failed', async function() {
386+
routingServer.on('connection', async (connection) => {
387+
const debug = new Debug();
388+
const incomingMessageStream = new IncomingMessageStream(debug);
389+
const outgoingMessageStream = new OutgoingMessageStream(debug, { packetSize: 4 * 1024 });
390+
391+
connection.pipe(incomingMessageStream);
392+
outgoingMessageStream.pipe(connection);
393+
394+
try {
395+
const messageIterator = incomingMessageStream[Symbol.asyncIterator]();
396+
397+
// PRELOGIN
398+
{
399+
const { value: message } = await messageIterator.next();
400+
assert.strictEqual(message.type, 0x12);
401+
402+
const chunks = [];
403+
for await (const data of message) {
404+
chunks.push(data);
405+
}
406+
407+
const responsePayload = new PreloginPayload({ encrypt: false, version: { major: 0, minor: 0, build: 0, subbuild: 0 } });
408+
const responseMessage = new Message({ type: 0x12 });
409+
responseMessage.end(responsePayload.data);
410+
outgoingMessageStream.write(responseMessage);
411+
}
412+
413+
// LOGIN7
414+
{
415+
const { value: message } = await messageIterator.next();
416+
assert.strictEqual(message.type, 0x10);
417+
418+
const chunks = [];
419+
for await (const data of message) {
420+
chunks.push(data);
421+
}
422+
423+
const responseMessage = new Message({ type: 0x04 });
424+
responseMessage.write(buildLoginAckToken());
425+
responseMessage.end(buildRoutingEnvChangeToken('test.invalid', targetServer.address().port));
426+
outgoingMessageStream.write(responseMessage);
427+
}
428+
429+
// No further messages, connection closed on remote
430+
{
431+
const { done } = await messageIterator.next();
432+
assert.isTrue(done);
433+
}
434+
} catch (err) {
435+
process.nextTick(() => {
436+
throw err;
437+
});
438+
} finally {
439+
connection.end();
440+
}
441+
});
442+
443+
const connection = new Connection({
444+
server: routingServer.address().address,
445+
options: {
446+
port: routingServer.address().port,
447+
encrypt: false
448+
}
449+
});
450+
451+
try {
452+
await new Promise((resolve, reject) => {
453+
connection.connect((err) => {
454+
err ? reject(err) : resolve(err);
455+
});
456+
});
457+
} catch (err) {
458+
assert.instanceOf(err, ConnectionError);
459+
const message = `Failed to connect to test.invalid:${targetServer.address().port} (redirected from ${routingServer.address().address}:${routingServer.address().port})`;
460+
assert.include(err.message, message);
461+
} finally {
462+
connection.close();
463+
}
464+
});
384465
});

0 commit comments

Comments
 (0)