Skip to content

Commit

Permalink
Simplify status checks
Browse files Browse the repository at this point in the history
  • Loading branch information
sonnyp committed Jan 8, 2025
1 parent 32206c0 commit 8ad2205
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 109 deletions.
21 changes: 16 additions & 5 deletions packages/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,33 +204,44 @@ xmpp.on("offline", () => {
Starts the connection. Attempts to reconnect will automatically happen if it cannot connect or gets disconnected.

```js
xmpp.start().catch(console.error);
xmpp.on("online", (address) => {
console.log("online", address.toString());
});
await xmpp.start();
```

Returns a promise that resolves if the first attempt succeed or rejects if the first attempt fails.

### stop

Stops the connection and prevent any further auto reconnect/retry.
Stops the connection and prevent any further auto reconnect.

```js
xmpp.stop().catch(console.error);
xmpp.on("offline", () => {
console.log("offline");
});
await xmpp.stop();
```

Returns a promise that resolves once the stream closes and the socket disconnects.

### disconnect

Like [`stop`](#stop) but will not prevent auto reconnect.

```js
xmpp.on("disconnect", () => {
console.log("disconnect");
});
await xmpp.disconnect();
```

### send

Sends a stanza.

```js
xmpp.send(xml("presence")).catch(console.error);
await xmpp.send(xml("presence"));
```

Returns a promise that resolves once the stanza is serialized and written to the socket or rejects if any of those fails.
Expand All @@ -247,7 +258,7 @@ const recipients = ["[email protected]", "[email protected]"];
const stanzas = recipients.map((address) =>
xml("message", { to: address, type: "chat" }, xml("body", null, message)),
);
xmpp.sendMany(stanzas).catch(console.error);
await xmpp.sendMany(stanzas);
```

Returns a promise that resolves once all the stanzas have been sent.
Expand Down
21 changes: 16 additions & 5 deletions packages/component/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,23 +170,34 @@ xmpp.on("offline", () => {
Starts the connection. Attempts to reconnect will automatically happen if it cannot connect or gets disconnected.

```js
xmpp.start().catch(console.error);
xmpp.on("online", (address) => {
console.log("online", address.toString());
});
await xmpp.start();
```

Returns a promise that resolves if the first attempt succeed or rejects if the first attempt fails.

### stop

Stops the connection and prevent any further auto reconnect/retry.
Stops the connection and prevent any further auto reconnect.

```js
xmpp.stop().catch(console.error);
xmpp.on("offline", () => {
console.log("offline");
});
await xmpp.stop();
```

### disconnect

Like [`stop`](#stop) but will not prevent auto reconnect.

```js
xmpp.on("disconnect", () => {
console.log("disconnect");
});
await xmpp.disconnect();
```

Returns a promise that resolves once the stream closes and the socket disconnects.
Expand All @@ -196,7 +207,7 @@ Returns a promise that resolves once the stream closes and the socket disconnect
Sends a stanza.

```js
xmpp.send(xml("presence")).catch(console.error);
await xmpp.send(xml("presence"));
```

Returns a promise that resolves once the stanza is serialized and written to the socket or rejects if any of those fails.
Expand All @@ -213,7 +224,7 @@ const recipients = ["[email protected]", "[email protected]"];
const stanzas = recipients.map((address) =>
xml("message", { to: address, type: "chat" }, xml("body", null, message)),
);
xmpp.sendMany(stanzas).catch(console.error);
await xmpp.sendMany(stanzas);
```

Returns a promise that resolves once all the stanzas have been sent.
Expand Down
35 changes: 14 additions & 21 deletions packages/connection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ class Connection extends EventEmitter {
this.root = null;
}

_reset() {
this.status = "offline";
this._detachSocket();
this._detachParser();
this.root = null;
}

async _streamError(condition, children) {
try {
await this.send(
Expand Down Expand Up @@ -57,9 +50,14 @@ class Connection extends EventEmitter {
this.emit("error", error);
}

#onSocketClosed(dirty, event) {
this._reset();
this._status("disconnect", { clean: !dirty, event });
#onSocketClosed(dirty, reason) {
this._detachSocket();
this._status("disconnect", { clean: !dirty, reason });
}

#onStreamClosed(dirty, reason) {
this._detachParser();
this._status("close", { clean: !dirty, reason });
}

_attachSocket(socket) {
Expand Down Expand Up @@ -91,7 +89,6 @@ class Connection extends EventEmitter {
delete socketListeners[k];
}
this.socket = null;
return socket;
}

_onElement(element) {
Expand Down Expand Up @@ -151,10 +148,7 @@ class Connection extends EventEmitter {
listeners.element = this._onElement.bind(this);
listeners.error = this._onParserError.bind(this);

listeners.end = (element) => {
this._detachParser();
this._status("close", element);
};
listeners.end = this.#onStreamClosed.bind(this);

listeners.start = (element) => {
this._status("open", element);
Expand All @@ -173,6 +167,7 @@ class Connection extends EventEmitter {
delete listeners[k];
}
this.parser = null;
this.root = null;
}

_jid(id) {
Expand Down Expand Up @@ -213,16 +208,16 @@ class Connection extends EventEmitter {

async disconnect() {
let el;

try {
el = await this._closeStream();
} catch (err) {
console.log(err);
this.#onStreamClosed(err);
}

try {
await this._closeSocket();
} catch (err) {
console.log(err);
this.#onSocketClosed(true, err);
}

Expand Down Expand Up @@ -266,8 +261,6 @@ class Connection extends EventEmitter {
* https://tools.ietf.org/html/rfc7395#section-3.6
*/
async _closeSocket(timeout = this.timeout) {
if (!this.socket) return;

this._status("disconnecting");
this.socket.end();

Expand Down Expand Up @@ -322,9 +315,9 @@ class Connection extends EventEmitter {
this.write(fragment),
]);

if (this.parser && this.socket) this._status("closing");
this._status("closing");

const [el] = await p;
this.root = null;
return el;
// The 'close' status is set by the parser 'end' listener
}
Expand Down
7 changes: 0 additions & 7 deletions packages/connection/test/_closeSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,3 @@ test("rejects if socket.end throws", (done) => {
done();
});
});

test("resolves if socket is absent", async () => {
const conn = new Connection();
conn.socket = null;

await expect(conn._closeSocket()).toResolve();
});
63 changes: 8 additions & 55 deletions packages/connection/test/_closeStream.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Connection from "../index.js";
import { EventEmitter, promise, timeout, TimeoutError } from "@xmpp/events";
import { EventEmitter, promise, TimeoutError } from "@xmpp/events";
import { xml } from "@xmpp/test";

test("resets properties on socket close event", () => {
Expand All @@ -11,22 +11,11 @@ test("resets properties on socket close event", () => {
expect(conn.status).toBe("disconnect");
});

test("timeout", async () => {
expect.assertions(2);
test("timeout on parser end", async () => {
const conn = new Connection();
conn.parser = new EventEmitter();
conn.footerElement = () => {
return xml("hello");
};

conn.socket = new EventEmitter();
conn.socket.write = (data, cb) => {
return cb();
};

conn.on("output", (el) => {
expect(el).toBe("<hello/>");
});
jest.spyOn(conn, "footerElement").mockImplementation(() => xml("hello"));
jest.spyOn(conn, "write").mockImplementation(async () => {});

await expect(conn._closeStream()).rejects.toThrow(new TimeoutError());
});
Expand All @@ -53,21 +42,11 @@ test("error on status closing", async () => {
});

test("resolves", async () => {
expect.assertions(2);
const conn = new Connection();
conn.parser = new EventEmitter();
conn.footerElement = () => {
return xml("hello");
};

conn.socket = new EventEmitter();
conn.socket.write = (data, cb) => {
return cb();
};

conn.on("output", (el) => {
expect(el).toBe("<hello/>");
});
jest.spyOn(conn, "footerElement").mockImplementation(() => xml("hello"));
jest.spyOn(conn, "write").mockImplementation(async () => {});

const promiseClose = conn._closeStream();
conn.parser.emit("end", xml("goodbye"));
Expand All @@ -80,14 +59,9 @@ test("resolves", async () => {
test("emits closing status", () => {
const conn = new Connection();
conn.parser = new EventEmitter();
conn.footerElement = () => {
return xml("hello");
};

conn.socket = new EventEmitter();
conn.socket.write = (data, cb) => {
return cb();
};
jest.spyOn(conn, "footerElement").mockImplementation(() => xml("hello"));
jest.spyOn(conn, "write").mockImplementation(async () => {});

const p = Promise.all([
promise(conn, "status").then((status) => expect(status).toBe("closing")),
Expand All @@ -97,24 +71,3 @@ test("emits closing status", () => {
conn.parser.emit("end");
return p;
});

test("do not emit closing status if parser property is missing", async () => {
expect.assertions(2);
const conn = new Connection();
conn.parser = null;
conn.footerElement = () => {
return xml("hello");
};

conn.socket = new EventEmitter();
conn.socket.write = (data, cb) => {
return cb();
};

await Promise.all([
expect(timeout(promise(conn, "status"), 500)).rejects.toThrow(
new TimeoutError(),
),
expect(conn._closeStream()).rejects.toThrow(),
]);
});
10 changes: 5 additions & 5 deletions packages/connection/test/socketClose.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import Connection from "../index.js";
import { EventEmitter } from "@xmpp/events";

test("calls _reset and _status", () => {
test("calls _detachSocket and _status", () => {
expect.assertions(3);
const conn = new Connection();
const sock = new EventEmitter();
conn._attachSocket(sock);

const evt = {};
conn._status = (status, { clean, event }) => {
conn._status = (status, { clean, reason }) => {
expect(clean).toBe(false);
expect(event).toBe(evt);
expect(reason).toBe(evt);
};

const spy_reset = jest.spyOn(conn, "_reset");
const spy_detachSocket = jest.spyOn(conn, "_detachSocket");

sock.emit("close", true, evt);

expect(spy_reset).toHaveBeenCalled();
expect(spy_detachSocket).toHaveBeenCalled();
});
2 changes: 1 addition & 1 deletion packages/resolve/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default function resolve({ entity }) {
try {
await fallbackConnect(entity, uris);
} catch (err) {
entity._reset();
await entity.disconnect();
entity._status("disconnect");
throw err;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/tls/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ NebQHyTBqa5P7vjSioiWiSRCNOIL4HywMWtN/nZVk0cl8zwlLtMaGt9Tz7ty2OgL
const error = await promise(conn, "error");
expect(error.message).toBe("certificate has expired");

await conn._closeSocket().catch(() => {});
await conn.disconnect();
server.close();
});

Expand All @@ -111,7 +111,7 @@ test("rejects self signed certificates", async () => {
const error = await promise(conn, "error");
expect(error.code).toBe("DEPTH_ZERO_SELF_SIGNED_CERT");

await conn._closeSocket().catch(() => {});
await conn.disconnect();
server.close();
});

Expand Down Expand Up @@ -213,6 +213,6 @@ yA==
// which is what we want as it delays the sending of the stream header (conn.open)
expect(connect_emitted_on_conn_socket).toBe(true);

await conn._closeSocket().catch(() => {});
await conn.disconnect();
server.close();
});
Loading

0 comments on commit 8ad2205

Please sign in to comment.