From 24cb246809c2ab7b12e041c240f810dc7352e675 Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Mon, 8 Jun 2026 22:02:13 +0200 Subject: [PATCH] tokio-xmpp: Send the initial stream:stream in a single packet Prosody would let the stream opening timeout if it receives it in more than one TCP packet, which seems like a bug. The behaviour was highly non-deterministic, a release build would almost always (but not always) succeed to connect to a localhost Prosody, a debug build would almost always (but not always) fail, and RUST_LOG=trace would make any build fail to connect, as would strace. My theory is that successful runs rely on the kernel merging all five TCP packets in a single one, and any tracing would add sufficient delay in-between to let it send every packet on its own. --- tokio-xmpp/ChangeLog | 2 + tokio-xmpp/src/xmlstream/common.rs | 62 +++++++++++++----------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/tokio-xmpp/ChangeLog b/tokio-xmpp/ChangeLog index 9ecf95fa..c1d3fef6 100644 --- a/tokio-xmpp/ChangeLog +++ b/tokio-xmpp/ChangeLog @@ -16,6 +16,8 @@ Version NEXT: `tokio_xmpp::connect::DnsConfig` * Fixed: - Ignore missing "version" stream attribute for 0114 components. + - Always send the stream:stream opening in one TCP packet, to workaround + issue #1995 in Prosody. (!680) - Gate `AsRawFd` behind `ktls` feature to make Windows build work again. - Implicitly use the `Client`'s bound JID on empty `from` and `to` in tokio-xmpp's `IqResponseTracker`. diff --git a/tokio-xmpp/src/xmlstream/common.rs b/tokio-xmpp/src/xmlstream/common.rs index ca3eb358..47a5c10b 100644 --- a/tokio-xmpp/src/xmlstream/common.rs +++ b/tokio-xmpp/src/xmlstream/common.rs @@ -803,48 +803,38 @@ impl StreamHeader<'_> { mut stream: Pin<&mut RawXmlStream>, ) -> io::Result<()> { stream - .send(Item::XmlDeclaration(rxml::XmlVersion::V1_0)) - .await?; - stream - .send(Item::ElementHeadStart( - Namespace::from(XML_STREAM_NS), - Cow::Borrowed(xml_ncname!("stream")), - )) - .await?; + .as_mut() + .start_send(Item::XmlDeclaration(rxml::XmlVersion::V1_0))?; + stream.as_mut().start_send(Item::ElementHeadStart( + Namespace::from(XML_STREAM_NS), + Cow::Borrowed(xml_ncname!("stream")), + ))?; if let Some(from) = self.from { - stream - .send(Item::Attribute( - Namespace::NONE, - Cow::Borrowed(xml_ncname!("from")), - from, - )) - .await?; + stream.as_mut().start_send(Item::Attribute( + Namespace::NONE, + Cow::Borrowed(xml_ncname!("from")), + from, + ))?; } if let Some(to) = self.to { - stream - .send(Item::Attribute( - Namespace::NONE, - Cow::Borrowed(xml_ncname!("to")), - to, - )) - .await?; + stream.as_mut().start_send(Item::Attribute( + Namespace::NONE, + Cow::Borrowed(xml_ncname!("to")), + to, + ))?; } if let Some(id) = self.id { - stream - .send(Item::Attribute( - Namespace::NONE, - Cow::Borrowed(xml_ncname!("id")), - id, - )) - .await?; - } - stream - .send(Item::Attribute( + stream.as_mut().start_send(Item::Attribute( Namespace::NONE, - Cow::Borrowed(xml_ncname!("version")), - Cow::Borrowed("1.0"), - )) - .await?; + Cow::Borrowed(xml_ncname!("id")), + id, + ))?; + } + stream.as_mut().start_send(Item::Attribute( + Namespace::NONE, + Cow::Borrowed(xml_ncname!("version")), + Cow::Borrowed("1.0"), + ))?; stream.send(Item::ElementHeadEnd).await?; Ok(()) }