node-firebird 2.16.1 → 2.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/lib/wire/eventConnection.d.ts +3 -2
- package/lib/wire/eventConnection.js +17 -2
- package/package.json +1 -1
- package/src/wire/eventConnection.ts +18 -6
package/README.md
CHANGED
|
@@ -1434,6 +1434,49 @@ Firebird.attach(options, function (err, db) {
|
|
|
1434
1434
|
});
|
|
1435
1435
|
```
|
|
1436
1436
|
|
|
1437
|
+
#### The auxiliary connection, and when `attachEvent` fails
|
|
1438
|
+
|
|
1439
|
+
Events do **not** travel over the connection you attached with. `db.attachEvent()` sends
|
|
1440
|
+
`op_connect_request`; the server opens a fresh listening socket, replies with its address and
|
|
1441
|
+
port, and the driver dials that address as a **second, independent TCP connection**. Only once
|
|
1442
|
+
that aux socket is up is the `FbEventManager` created and handed to your callback.
|
|
1443
|
+
|
|
1444
|
+
The aux port is chosen by the server and is *not* the port you connected to, so it has to be
|
|
1445
|
+
reachable from the client in its own right. Two deployments commonly get this wrong:
|
|
1446
|
+
|
|
1447
|
+
- **Containers.** A Firebird container that publishes only `3050` answers `op_connect_request`
|
|
1448
|
+
with an aux port nothing outside the container can reach. Pin it with `RemoteAuxPort` in
|
|
1449
|
+
`firebird.conf` and publish that port too, or run the client on the same network.
|
|
1450
|
+
- **Firewalls.** A rule that DROPs the aux port (rather than REJECTing it) leaves the dial
|
|
1451
|
+
waiting out the operating system's connect timeout — around two minutes on Linux — before
|
|
1452
|
+
the failure is reported.
|
|
1453
|
+
|
|
1454
|
+
If the server reports `0.0.0.0` or `::` as the aux address — usual when it listens on all
|
|
1455
|
+
interfaces — the driver dials the host from your connection options instead, so that host must
|
|
1456
|
+
be the one reaching the aux port.
|
|
1457
|
+
|
|
1458
|
+
Since **2.16.2** a failed dial is reported to the attachment callback as an `Error`, exactly
|
|
1459
|
+
once, carrying Node's socket `code`. Earlier versions recorded it internally and never called
|
|
1460
|
+
back, so `attachEvent()` and `attachEventAsync()` hung indefinitely:
|
|
1461
|
+
|
|
1462
|
+
```js
|
|
1463
|
+
db.attachEvent(function (err, evtmgr) {
|
|
1464
|
+
if (err) {
|
|
1465
|
+
// ECONNREFUSED → aux port not reachable (unpublished container port, nothing listening)
|
|
1466
|
+
// ETIMEDOUT → aux port filtered by a firewall
|
|
1467
|
+
// EHOSTUNREACH → wrong host: the address the server reported, or your connection host
|
|
1468
|
+
// when the server reported 0.0.0.0/::
|
|
1469
|
+
console.error('event attachment failed:', err.code || err.message);
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
// ...
|
|
1473
|
+
});
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
Errors on the aux socket *after* it connects are not delivered to this callback. Poll
|
|
1477
|
+
`evtmgr.getState().isEventConnectionOpen` if you need to detect an aux connection that dies
|
|
1478
|
+
mid-subscription.
|
|
1479
|
+
|
|
1437
1480
|
### Escaping Query values
|
|
1438
1481
|
|
|
1439
1482
|
```js
|
|
@@ -2429,6 +2472,21 @@ If the failure is intermittent — the same code with the same credentials succe
|
|
|
2429
2472
|
|
|
2430
2473
|
If the failure is consistent, the credentials really don't match an account for the authentication plugin in use: check `AuthServer`/`UserManager` in `firebird.conf` and remember that SRP and Legacy user managers keep separate password stores — a user created under one plugin does not automatically exist for the other.
|
|
2431
2474
|
|
|
2475
|
+
#### `db.attachEvent()` never calls back, or hangs, in Docker or behind a firewall
|
|
2476
|
+
|
|
2477
|
+
Upgrade to **2.16.2 or later**: before that release the driver recorded a failure to open the
|
|
2478
|
+
auxiliary event connection internally and never invoked the attachment callback, so
|
|
2479
|
+
`attachEvent()` silently stalled and `attachEventAsync()` returned a promise that never settled.
|
|
2480
|
+
From 2.16.2 the failure reaches your callback as an `Error` carrying Node's socket `code`.
|
|
2481
|
+
|
|
2482
|
+
The failure itself is a reachability problem, not a driver bug, and upgrading only makes it
|
|
2483
|
+
visible. Firebird events use a **second TCP connection** to a port the *server* chooses, which
|
|
2484
|
+
is not the port you connected to, so publishing `3050` alone is not enough: pin `RemoteAuxPort`
|
|
2485
|
+
in `firebird.conf` and publish that port too. See
|
|
2486
|
+
[§ The auxiliary connection](#the-auxiliary-connection-and-when-attachevent-fails) for the full
|
|
2487
|
+
picture, including the `0.0.0.0`/`::` case and why a DROP firewall rule delays the error by
|
|
2488
|
+
roughly two minutes.
|
|
2489
|
+
|
|
2432
2490
|
## Contributing
|
|
2433
2491
|
|
|
2434
2492
|
Contributions are welcome — code, documentation, and bug reports alike.
|
|
@@ -9,8 +9,9 @@ declare class EventConnection {
|
|
|
9
9
|
_xdr?: XdrReader;
|
|
10
10
|
error: any;
|
|
11
11
|
eventcallback: ((err: any, ret?: any) => void) | null;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
_connectSettled: boolean;
|
|
13
|
+
constructor(host: string, port: number, callback: ((err?: Error) => void) | undefined, db: any);
|
|
14
|
+
_bind_events(host: string, port: number, callback?: (err?: Error) => void): void;
|
|
14
15
|
throwClosed(callback?: (err: any) => void): this;
|
|
15
16
|
}
|
|
16
17
|
export = EventConnection;
|
|
@@ -13,6 +13,7 @@ class EventConnection {
|
|
|
13
13
|
this.emgr = null;
|
|
14
14
|
this._isClosed = false;
|
|
15
15
|
this._isOpened = false;
|
|
16
|
+
this._connectSettled = false;
|
|
16
17
|
this._socket = net_1.default.createConnection(port, host);
|
|
17
18
|
this._bind_events(host, port, callback);
|
|
18
19
|
this.error = null;
|
|
@@ -20,17 +21,31 @@ class EventConnection {
|
|
|
20
21
|
}
|
|
21
22
|
_bind_events(host, port, callback) {
|
|
22
23
|
var self = this;
|
|
24
|
+
function finishConnect(err) {
|
|
25
|
+
if (self._connectSettled)
|
|
26
|
+
return;
|
|
27
|
+
self._connectSettled = true;
|
|
28
|
+
if (callback)
|
|
29
|
+
callback(err);
|
|
30
|
+
}
|
|
23
31
|
self._socket.on('close', function () {
|
|
24
32
|
self._isClosed = true;
|
|
33
|
+
if (!self._isOpened) {
|
|
34
|
+
finishConnect(self.error || new Error(`Event connection to ${host}:${port} closed before connecting.`));
|
|
35
|
+
}
|
|
25
36
|
});
|
|
26
37
|
self._socket.on('error', function (e) {
|
|
27
38
|
self.error = e;
|
|
39
|
+
if (!self._isOpened) {
|
|
40
|
+
if (!self._socket.destroyed)
|
|
41
|
+
self._socket.destroy();
|
|
42
|
+
finishConnect(e);
|
|
43
|
+
}
|
|
28
44
|
});
|
|
29
45
|
self._socket.on('connect', function () {
|
|
30
46
|
self._isClosed = false;
|
|
31
47
|
self._isOpened = true;
|
|
32
|
-
|
|
33
|
-
callback();
|
|
48
|
+
finishConnect();
|
|
34
49
|
});
|
|
35
50
|
self._socket.on('data', function (data) {
|
|
36
51
|
var xdr, buf;
|
package/package.json
CHANGED
|
@@ -13,37 +13,49 @@ class EventConnection {
|
|
|
13
13
|
_xdr?: XdrReader;
|
|
14
14
|
error: any;
|
|
15
15
|
eventcallback: ((err: any, ret?: any) => void) | null;
|
|
16
|
+
_connectSettled: boolean;
|
|
16
17
|
|
|
17
|
-
constructor(host: string, port: number, callback: (() => void) | undefined, db: any) {
|
|
18
|
+
constructor(host: string, port: number, callback: ((err?: Error) => void) | undefined, db: any) {
|
|
18
19
|
var self = this;
|
|
19
20
|
this.db = db;
|
|
20
21
|
this.emgr = null;
|
|
21
22
|
this._isClosed = false;
|
|
22
23
|
this._isOpened = false;
|
|
24
|
+
this._connectSettled = false;
|
|
23
25
|
this._socket = net.createConnection(port, host);
|
|
24
26
|
this._bind_events(host, port, callback);
|
|
25
27
|
this.error = null;
|
|
26
28
|
this.eventcallback = null;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
_bind_events(host: string, port: number, callback?: () => void): void {
|
|
31
|
+
_bind_events(host: string, port: number, callback?: (err?: Error) => void): void {
|
|
30
32
|
var self = this;
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
function finishConnect(err?: Error) {
|
|
35
|
+
if (self._connectSettled) return;
|
|
36
|
+
self._connectSettled = true;
|
|
37
|
+
if (callback) callback(err);
|
|
38
|
+
}
|
|
33
39
|
|
|
40
|
+
self._socket.on('close', function () {
|
|
34
41
|
self._isClosed = true;
|
|
42
|
+
if (!self._isOpened) {
|
|
43
|
+
finishConnect(self.error || new Error(`Event connection to ${host}:${port} closed before connecting.`));
|
|
44
|
+
}
|
|
35
45
|
})
|
|
36
46
|
|
|
37
47
|
self._socket.on('error', function (e) {
|
|
38
|
-
|
|
39
48
|
self.error = e;
|
|
49
|
+
if (!self._isOpened) {
|
|
50
|
+
if (!self._socket.destroyed) self._socket.destroy();
|
|
51
|
+
finishConnect(e);
|
|
52
|
+
}
|
|
40
53
|
})
|
|
41
54
|
|
|
42
55
|
self._socket.on('connect', function () {
|
|
43
56
|
self._isClosed = false;
|
|
44
57
|
self._isOpened = true;
|
|
45
|
-
|
|
46
|
-
callback();
|
|
58
|
+
finishConnect();
|
|
47
59
|
});
|
|
48
60
|
|
|
49
61
|
self._socket.on('data', function (data: Buffer) {
|