diff --git a/packages/connection/index.js b/packages/connection/index.js
index 20318d66..5c1cfcbb 100644
--- a/packages/connection/index.js
+++ b/packages/connection/index.js
@@ -245,21 +245,6 @@ class Connection extends EventEmitter {
await promise(this.socket, "close", "error", timeout);
}
- /**
- * Forcibly disconnects the socket
- * https://xmpp.org/rfcs/rfc6120.html#streams-close
- * https://tools.ietf.org/html/rfc7395#section-3.6
- */
- async forceDisconnect(timeout = this.timeout) {
- if (!this.socket) return;
-
- this._status("disconnecting");
- this.socket.destroy();
-
- // The 'disconnect' status is set by the socket 'close' listener
- await promise(this.socket, "close", "error", timeout);
- }
-
/**
* Opens the stream
*/
diff --git a/packages/stream-management/index.js b/packages/stream-management/index.js
index 26eb9f37..78d76f23 100644
--- a/packages/stream-management/index.js
+++ b/packages/stream-management/index.js
@@ -154,7 +154,7 @@ export default function streamManagement({
clearTimeout(timeoutTimeout);
if (sm.timeout) {
timeoutTimeout = setTimeout(
- () => entity.forceDisconnect().catch(),
+ () => entity.disconnect().catch(() => {}),
sm.timeout,
);
}
diff --git a/packages/tls/lib/Socket.js b/packages/tls/lib/Socket.js
index 93eef98d..f96ac449 100644
--- a/packages/tls/lib/Socket.js
+++ b/packages/tls/lib/Socket.js
@@ -50,10 +50,6 @@ class Socket extends EventEmitter {
this.socket.end();
}
- destroy() {
- this.socket.destroy();
- }
-
write(data, fn) {
this.socket.write(data, fn);
}
diff --git a/packages/websocket/lib/Socket.js b/packages/websocket/lib/Socket.js
index 63b0f8d9..84a8ba04 100644
--- a/packages/websocket/lib/Socket.js
+++ b/packages/websocket/lib/Socket.js
@@ -62,10 +62,6 @@ export default class Socket extends EventEmitter {
this.socket.close();
}
- destroy() {
- this.socket.close();
- }
-
write(data, fn) {
function done(err) {
if (!fn) return;
diff --git a/test/stream-management.js b/test/stream-management.js
index 5d8aacca..ef66faed 100644
--- a/test/stream-management.js
+++ b/test/stream-management.js
@@ -46,10 +46,12 @@ test("client fail stanzas", async () => {
await xmpp.start();
// Expect send but don't actually send to server, so it will fail
await xmpp.streamManagement.outbound_q.push({
- stanza:
-
- ,
- stamp: datetime()
+ stanza: (
+
+
+
+ ),
+ stamp: datetime(),
});
await xmpp.stop();
@@ -68,10 +70,12 @@ test("client retry stanzas", async () => {
await xmpp.start();
// Add to queue but don't actually send so it can retry after disconnect
await xmpp.streamManagement.outbound_q.push({
- stanza:
-
- ,
- stamp: datetime()
+ stanza: (
+
+
+
+ ),
+ stamp: datetime(),
});
await xmpp.disconnect();
@@ -79,7 +83,7 @@ test("client retry stanzas", async () => {
expect(el.attrs.id).toEqual("ping");
});
-test("client reconnect automatically", async () => {
+test("client reconnects when server fails to ack", async () => {
await server.enableModules(["smacks"]);
await server.restart();
diff --git a/test/stream-management.test.js b/test/stream-management.test.js
new file mode 100644
index 00000000..fa5e055a
--- /dev/null
+++ b/test/stream-management.test.js
@@ -0,0 +1,112 @@
+import { client } from "../packages/client/index.js";
+import { promise } from "../packages/events/index.js";
+import { datetime } from "../packages/time/index.js";
+import debug from "../packages/debug/index.js";
+import server from "../server/index.js";
+
+const username = "client";
+const password = "foobar";
+const credentials = { username, password };
+const domain = "localhost";
+
+let xmpp;
+
+afterEach(async () => {
+ await xmpp?.stop();
+ await server.reset();
+});
+
+test("client ack stanzas", async () => {
+ await server.enableModules(["smacks"]);
+ await server.restart();
+
+ xmpp = client({ credentials, service: domain });
+ debug(xmpp);
+
+ const promise_ack = promise(xmpp.streamManagement, "ack");
+ await xmpp.start();
+ await xmpp.send(
+
+
+ ,
+ );
+
+ const el = await promise_ack;
+ expect(el.attrs.id).toEqual("ping");
+});
+
+test("client fail stanzas", async () => {
+ await server.enableModules(["smacks"]);
+ await server.restart();
+
+ xmpp = client({ credentials, service: domain });
+ debug(xmpp);
+
+ const promise_fail = promise(xmpp.streamManagement, "fail");
+ await xmpp.start();
+ // Expect send but don't actually send to server, so it will fail
+ await xmpp.streamManagement.outbound_q.push({
+ stanza: (
+
+
+
+ ),
+ stamp: datetime(),
+ });
+ await xmpp.stop();
+
+ const el = await promise_fail;
+ expect(el.attrs.id).toEqual("ping");
+});
+
+test("client retry stanzas", async () => {
+ await server.enableModules(["smacks"]);
+ await server.restart();
+
+ xmpp = client({ credentials, service: domain });
+ debug(xmpp);
+
+ const promise_ack = promise(xmpp.streamManagement, "ack");
+ await xmpp.start();
+ // Add to queue but don't actually send so it can retry after disconnect
+ await xmpp.streamManagement.outbound_q.push({
+ stanza: (
+
+
+
+ ),
+ stamp: datetime(),
+ });
+ await xmpp.disconnect();
+
+ const el = await promise_ack;
+ expect(el.attrs.id).toEqual("ping");
+});
+
+test(
+ "client reconnects when server fails to ack stanza",
+ async () => {
+ await server.enableModules(["smacks"]);
+ await server.restart();
+
+ xmpp = client({ credentials, service: domain });
+ xmpp.streamManagement.timeout = 10;
+ xmpp.streamManagement.debounceAckRequest = 1;
+ debug(xmpp, true);
+
+ const promise_resumed = promise(xmpp.streamManagement, "resumed");
+ await xmpp.start();
+ xmpp.send(
+
+
+ ,
+ );
+ // Pretend we don't receive the ack by removing event listeners
+ // on the socket
+ xmpp._detachSocket();
+
+ await promise_resumed;
+ expect().pass();
+ },
+ 1000 * 10,
+);