diff --git a/src/lib/client.ts b/src/lib/client.ts index 8d11e8e2d..da953c4c9 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -102,10 +102,16 @@ export interface ISecureClientOptions { caPaths?: string | string[] rejectUnauthorized?: boolean + /** - * optional alpn's + * optional alpn's (Application Layer Protocol Negotiation) */ ALPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array + + /** + * optional alpn's (Application Layer Protocol Negotiation) + */ + alpn?: string } export type AckHandler = ( diff --git a/src/lib/connect/index.ts b/src/lib/connect/index.ts index a25863f78..c067d05d2 100644 --- a/src/lib/connect/index.ts +++ b/src/lib/connect/index.ts @@ -109,7 +109,11 @@ function connect( if (opts.cert && opts.key) { if (opts.protocol) { - if (['mqtts', 'wss', 'wxs', 'alis'].indexOf(opts.protocol) === -1) { + if ( + ['mqtts', 'wss', 'wxs', 'alis', 'quic'].indexOf( + opts.protocol, + ) === -1 + ) { switch (opts.protocol) { case 'mqtt': opts.protocol = 'mqtts' @@ -147,6 +151,8 @@ function connect( protocols.ssl = require('./tls').default protocols.tls = protocols.ssl protocols.mqtts = require('./tls').default + + protocols.quic = require('./quic').default } else { protocols.ws = require('./ws').browserStreamBuilder protocols.wss = require('./ws').browserStreamBuilder diff --git a/src/lib/connect/quic.ts b/src/lib/connect/quic.ts new file mode 100644 index 000000000..a95733029 --- /dev/null +++ b/src/lib/connect/quic.ts @@ -0,0 +1,61 @@ +import { StreamBuilder } from '../shared' +import _debug from 'debug' +import { createSocket } from 'node:quic' + +const debug = _debug('mqttjs:quic') + +const buildStream: StreamBuilder = (client, opts) => { + opts.port = opts.port || 8885 + opts.host = opts.hostname || opts.host || 'localhost' + opts.servername = opts.host + + opts.rejectUnauthorized = opts.rejectUnauthorized !== false + + delete opts.path + + debug( + 'port %d host %s rejectUnauthorized %b', + opts.port, + opts.host, + opts.rejectUnauthorized, + ) + + const socket = createSocket({ + client: { + alpn: opts.alpn || 'mqtt', + }, + }) + + const req = socket.connect({ + address: opts.host, + port: opts.port || 8885, + cert: opts.cert, + key: opts.key, + idleTimeout: 0, // disable timeout + }) + + const stream = req.openStream() + + req.on('secure', (servername) => { + // servername is checked for any string for authorisation acceptance + if (opts.rejectUnauthorized && !servername) { + req.emit('error', new Error('TLS not authorized')) + } else { + req.removeListener('error', handleTLSErrors) + } + }) + + function handleTLSErrors(err) { + if (opts.rejectUnauthorized) { + client.emit('error', err) + } + + stream.end() + socket.close() + } + + req.on('error', handleTLSErrors) + return stream +} + +export default buildStream