@@ -32,65 +32,106 @@ class StreamParser extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse() {
|
parse() {
|
||||||
// Find complete stanzas in buffer
|
if (!this.buffer.length) return;
|
||||||
let startIdx = 0;
|
|
||||||
let inTag = false;
|
|
||||||
let tagDepth = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.buffer.length; i++) {
|
// Handle stream start first (only once)
|
||||||
const char = this.buffer[i];
|
if (!this.streamStarted) {
|
||||||
|
// Look for stream:stream tag anywhere in buffer (skip XML declaration)
|
||||||
|
const streamMatch = this.buffer.match(/<stream:stream[^>]*>/);
|
||||||
|
if (!streamMatch) {
|
||||||
|
// Stream opening tag not complete yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (char === '<') {
|
const streamTag = streamMatch[0];
|
||||||
if (this.buffer.substring(i, i + 2) === '</') {
|
const streamIndex = this.buffer.indexOf(streamTag);
|
||||||
// Closing tag
|
|
||||||
tagDepth--;
|
try {
|
||||||
} else if (this.buffer[i + 1] !== '!' && this.buffer[i + 1] !== '?') {
|
const attrs = this.parseAttributes(streamTag);
|
||||||
// Opening tag
|
this.streamStarted = true;
|
||||||
if (!this.streamStarted && this.buffer.substring(i).match(/^<stream:stream/)) {
|
this.emit('streamStart', attrs);
|
||||||
// Parse stream start
|
// Keep everything after the stream tag
|
||||||
const streamEndIdx = this.buffer.indexOf('>', i);
|
this.buffer = this.buffer.substring(streamIndex + streamTag.length);
|
||||||
if (streamEndIdx !== -1) {
|
// Recursively parse any stanzas that follow
|
||||||
const streamTag = this.buffer.substring(i, streamEndIdx + 1);
|
this.parse();
|
||||||
try {
|
} catch (e) {
|
||||||
const attrs = this.parseAttributes(streamTag);
|
this.emit('error', new Error(`Stream parse error: ${e.message}`));
|
||||||
this.streamStarted = true;
|
}
|
||||||
this.emit('streamStart', attrs);
|
return;
|
||||||
this.buffer = this.buffer.substring(streamEndIdx + 1);
|
}
|
||||||
i = -1;
|
|
||||||
startIdx = 0;
|
// Parse stanzas (after stream is started)
|
||||||
} catch (e) {
|
let index = 0;
|
||||||
this.emit('error', e);
|
|
||||||
}
|
while (index < this.buffer.length) {
|
||||||
|
// Skip whitespace
|
||||||
|
while (index < this.buffer.length && /\s/.test(this.buffer[index])) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= this.buffer.length) break;
|
||||||
|
|
||||||
|
if (this.buffer[index] === '<') {
|
||||||
|
// Check for stream closing
|
||||||
|
if (this.buffer.substring(index).startsWith('</stream:stream>')) {
|
||||||
|
this.emit('streamEnd');
|
||||||
|
this.buffer = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to extract a complete stanza
|
||||||
|
let closeIndex = -1;
|
||||||
|
let depth = 0;
|
||||||
|
let inOpenTag = true;
|
||||||
|
|
||||||
|
for (let i = index; i < this.buffer.length; i++) {
|
||||||
|
const char = this.buffer[i];
|
||||||
|
|
||||||
|
if (char === '<') {
|
||||||
|
// Check if it's a closing tag
|
||||||
|
if (this.buffer[i + 1] === '/') {
|
||||||
|
depth--;
|
||||||
|
} else if (this.buffer[i + 1] !== '!' && this.buffer[i + 1] !== '?') {
|
||||||
|
depth++;
|
||||||
}
|
}
|
||||||
} else {
|
inOpenTag = true;
|
||||||
tagDepth++;
|
} else if (char === '>' && inOpenTag) {
|
||||||
|
// Check if self-closing
|
||||||
|
if (this.buffer[i - 1] === '/') {
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth === 0 && this.buffer[index + 1] !== '/') {
|
||||||
|
// Found end of stanza
|
||||||
|
closeIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inOpenTag = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagDepth > 0) {
|
if (closeIndex === -1) {
|
||||||
inTag = true;
|
// Stanza not complete, wait for more data
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (char === '>' && inTag) {
|
|
||||||
tagDepth--;
|
|
||||||
|
|
||||||
if (tagDepth === 0) {
|
// Extract stanza
|
||||||
// Complete stanza
|
const stanzaStr = this.buffer.substring(index, closeIndex + 1);
|
||||||
const stanzaStr = this.buffer.substring(startIdx, i + 1);
|
try {
|
||||||
if (stanzaStr.trim()) {
|
const stanza = ltx.parse(stanzaStr);
|
||||||
try {
|
this.emit('stanza', stanza);
|
||||||
const stanza = ltx.parse(stanzaStr);
|
index = closeIndex + 1;
|
||||||
this.emit('stanza', stanza);
|
} catch (e) {
|
||||||
} catch (e) {
|
// Invalid stanza, skip this byte
|
||||||
this.emit('error', e);
|
index++;
|
||||||
}
|
|
||||||
}
|
|
||||||
startIdx = i + 1;
|
|
||||||
inTag = false;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buffer = this.buffer.substring(startIdx);
|
// Keep unparsed data in buffer
|
||||||
|
this.buffer = this.buffer.substring(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAttributes(tagStr) {
|
parseAttributes(tagStr) {
|
||||||
@@ -190,7 +231,7 @@ class XMPPStream extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleStreamStart(attrs) {
|
handleStreamStart(attrs) {
|
||||||
this.logger.debug('Stream start received:', attrs);
|
this.logger.info('Stream start received:', attrs);
|
||||||
|
|
||||||
if (this.state === 'wait-for-stream') {
|
if (this.state === 'wait-for-stream') {
|
||||||
// Send stream response
|
// Send stream response
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user