XEP-0198 Stream Resumption issue in a case of off/on Internet connection

Hi there,

We use Smack 4.1.4 and found an issue with XEP-0198, stream resumption feature

We enabled stream management and stream resumption

XMPPTCPConnection connection = new XMPPTCPConnection(configuration);

connection.setUseStreamManagement(true);

connection.setUseStreamManagementResumption(true);

Next, User is connected and online.
Then we switch OFF the internet connection on our Android device and after that we send a message to server.
We don’t receive any exception, so looks like our message goes to the PacketWriter internal queue and stays there.

Next, in 15 seconds, we switch ON the Internet connection, then got ‘Stream resumed’ and our message goes from the PacketWriter internal queue to server.

But, now the size of XMPPTCPConnection.unacknowledgedStanzas queue is always 1, not zero.
And it depends of how many messages we sent without active internet connection.
If we sent 3 then the queue size will be 3 always. So our messages doubled in **XMPPTCPConnection.unacknowledgedStanzas **and next the weird things happen.

I did a quick investigation of **XMPPTCPConnection **class and found a potential place of the issue:

private void drainWriterQueueToUnacknowledgedStanzas() {

  • List elements = new ArrayList(queue.size());*

  • queue.drainTo(elements);*

  • for (Element element : elements) {*

  •    if (element instanceof Stanza) {*
    
  •       unacknowledgedStanzas.add((Stanza) element);*
    
  •    }*
    
  • }*

}

Inside this method you add all packets from PacketWriter internal queue into ‘unacknowledgedStanzas’.
And then you add them again(!) inside

packetWriter.sendStreamElement(stanza);

which is called by **Resumed.ELEMENT in ‘parsePackets’ **method

So a single packet goes 2 times into ‘unacknowledgedStanzas’ queue after Stream Resumption

Looks like you have to cleanup the ‘drainWriterQueueToUnacknowledgedStanzas’ method

I did a fix which works

it’s not so optimal, but you should got the idea

this is inside XMPPTCPConnection.PacketReader.parsePackets

case Resumed.ELEMENT:

Resumed resumed = ParseStreamManagement.resumed(parser);

if (!smSessionId.equals(resumed.getPrevId())) {

throw new StreamIdDoesNotMatchException(smSessionId, resumed.getPrevId());

}

// First, drop the stanzas already handled by the server

processHandledCount(resumed.getHandledCount());

// // Then re-send what is left in the unacknowledged queue

// List stanzasToResend = new LinkedList();

// stanzasToResend.addAll(unacknowledgedStanzas);

// for (Stanza stanza : stanzasToResend) {

// packetWriter.sendStreamElement(stanza);

// }

long unacknowledgedSize = unacknowledgedStanzas.size();

for (long i = 0; i < unacknowledgedSize; i++) {

Stanza unackedStanza = unacknowledgedStanzas.poll();

packetWriter.sendStreamElement(unackedStanza);

}

smResumedSyncPoint.reportSuccess();

smEnabledSyncPoint.reportSuccess();

// If there where stanzas resent, then request a SM ack for them.

// Writer’s sendStreamElement() won’t do it automatically based on

// predicates.

// if (!stanzasToResend.isEmpty()) {

// requestSmAcknowledgementInternal();

// }

if(unacknowledgedSize > 0){

requestSmAcknowledgementInternal();

}

LOGGER.fine(“Stream Management (XEP-198): Stream resumed”);

break;

Duplicate stanzas in unacknowledgedStanzas queue when stream is resumed.

it’s already there, so closing