Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/browser/mqtt5.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,38 @@ test_utils.conditional_test(test_utils.ClientEnvironmentalConfig.hasValidSuccess
}));
});

test('Connection Failure - mqtt-js error during CONNECT emits connectionFailure', async () => {
/*
* When mqtt-js rejects the password during CONNECT packet serialisation
* (e.g. Uint8Array instead of string|Buffer), the error must surface as a
* connectionFailure event rather than being silently swallowed.
*
* This test does not need a running MQTT broker because the failure
* occurs locally inside mqtt-js writeToStream before any data is sent.
*/
let config: mqtt5.Mqtt5ClientConfig = {
hostName: "localhost",
port: 9999,
connectProperties: {
keepAliveIntervalSeconds: 1200,
username: "testuser",
// @ts-ignore — deliberately passing an invalid type to trigger the bug
password: new Uint8Array([116, 101, 115, 116]), // "test" as Uint8Array
},
websocketOptions: {
urlFactoryOptions: {
urlFactory: mqtt5.Mqtt5WebsocketUrlFactoryType.Ws,
},
},
connectTimeoutMs: 3000,
};

let client = new mqtt5.Mqtt5Client(config);

// @ts-ignore
await test_utils.testFailedConnection(client);
});

function testFailedClientConstruction(config: mqtt5.Mqtt5ClientConfig) {
expect(() => {
new mqtt5.Mqtt5Client(config);
Expand Down
15 changes: 15 additions & 0 deletions lib/browser/mqtt5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,21 @@ export class Mqtt5Client extends BufferedEventEmitter implements mqtt5.IMqtt5Cli
setTimeout(() => {
this.emit(Mqtt5Client.INFO, new CrtError(error));
}, 0);

/*
* If the error occurs while we are still connecting (before the
* mqtt-js 'close' event fires), the underlying stream may never
* close on its own — for example, when mqtt-js rejects the
* password during CONNECT packet serialisation the WebSocket
* stays open but no MQTT frames are ever sent.
*
* Force-close the mqtt-js client so that the normal
* on_browser_close path fires and emits CONNECTION_FAILURE,
* which lets consumers (and the reconnection scheduler) react.
*/
if (this.lifecycleEventState == Mqtt5ClientLifecycleEventState.Connecting) {
this.browserClient?.end(true);
}
}

private on_attempting_connect () {
Expand Down