UNPKG

@fnlb-project/stanza

Version:

Modern XMPP in the browser, with a JSON API

147 lines (146 loc) 4.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Constants_1 = require("../Constants"); const jxt_1 = require("../jxt"); const platform_1 = require("../platform"); const WS_OPEN = 1; class WSConnection extends platform_1.Duplex { constructor(client, sm, stanzas) { super({ objectMode: true }); this.sm = sm; this.stanzas = stanzas; this.closing = false; this.client = client; this.on('data', (e) => { this.client.emit('stream:data', e.stanza, e.kind); }); this.on('error', () => { this.end(); }); this.on('end', () => { if (this.client.transport === this) { this.client.emit('--transport-disconnected'); } }); } _read() { return; } _write(chunk, encoding, done) { if (!this.socket || this.socket.readyState !== WS_OPEN) { return done(new Error('Socket closed')); } const data = platform_1.Buffer.from(chunk, 'utf8').toString(); this.client.emit('raw', 'outgoing', data); this.socket.send(data); done(); } connect(opts) { this.config = opts; this.hasStream = false; this.closing = false; this.parser = new jxt_1.StreamParser({ acceptLanguages: this.config.acceptLanguages, allowComments: false, lang: this.config.lang, registry: this.stanzas, wrappedStream: false }); this.parser.on('data', (e) => { const name = e.kind; const stanzaObj = e.stanza; if (name === 'stream') { if (stanzaObj.action === 'open') { this.hasStream = true; this.stream = stanzaObj; return this.client.emit('stream:start', stanzaObj); } if (stanzaObj.action === 'close') { this.client.emit('stream:end'); return this.disconnect(); } } this.push({ kind: e.kind, stanza: e.stanza }); }); this.parser.on('error', (err) => { const streamError = { condition: Constants_1.StreamErrorCondition.InvalidXML }; this.client.emit('stream:error', streamError, err); this.write(this.stanzas.export('error', streamError).toString()); return this.disconnect(); }); this.socket = new platform_1.WebSocket(opts.url, 'xmpp'); this.socket.onopen = () => { this.emit('connect'); this.sm.started = false; this.client.emit('connected'); this.write(this.startHeader()); }; this.socket.onmessage = (wsMsg) => { const data = platform_1.Buffer.from(wsMsg.data, 'utf8').toString(); this.client.emit('raw', 'incoming', data); if (this.parser) { this.parser.write(data); } }; this.socket.onclose = () => { this.push(null); }; this.socket.onerror = (err) => { console.error(err); this.push(null); }; } disconnect(clean = true) { if (this.socket && !this.closing && this.hasStream && clean) { this.closing = true; this.write(this.closeHeader()); } else { this.hasStream = false; this.stream = undefined; if (this.socket) { this.end(); this.socket.close(); if (this.client.transport === this) { this.client.emit('--transport-disconnected'); } } this.socket = undefined; } } async send(dataOrName, data) { var _a; let output; if (data) { output = (_a = this.stanzas.export(dataOrName, data)) === null || _a === void 0 ? void 0 : _a.toString(); } if (!output) { return; } return new Promise((resolve, reject) => { this.write(output, 'utf8', (err) => (err ? reject(err) : resolve())); }); } restart() { this.hasStream = false; this.write(this.startHeader()); } startHeader() { const header = this.stanzas.export('stream', { action: 'open', lang: this.config.lang, to: this.config.server, version: '1.0' }); return header.toString(); } closeHeader() { const header = this.stanzas.export('stream', { action: 'close' }); return header.toString(); } } exports.default = WSConnection;