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.
This commit is contained in:
Link Mauve 2026-06-08 22:02:13 +02:00
commit 24cb246809
2 changed files with 28 additions and 36 deletions

View file

@ -16,6 +16,8 @@ Version NEXT:
`tokio_xmpp::connect::DnsConfig` `tokio_xmpp::connect::DnsConfig`
* Fixed: * Fixed:
- Ignore missing "version" stream attribute for 0114 components. - 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. - Gate `AsRawFd` behind `ktls` feature to make Windows build work again.
- Implicitly use the `Client`'s bound JID on empty `from` and `to` in - Implicitly use the `Client`'s bound JID on empty `from` and `to` in
tokio-xmpp's `IqResponseTracker`. tokio-xmpp's `IqResponseTracker`.

View file

@ -803,48 +803,38 @@ impl StreamHeader<'_> {
mut stream: Pin<&mut RawXmlStream<Io>>, mut stream: Pin<&mut RawXmlStream<Io>>,
) -> io::Result<()> { ) -> io::Result<()> {
stream stream
.send(Item::XmlDeclaration(rxml::XmlVersion::V1_0)) .as_mut()
.await?; .start_send(Item::XmlDeclaration(rxml::XmlVersion::V1_0))?;
stream stream.as_mut().start_send(Item::ElementHeadStart(
.send(Item::ElementHeadStart( Namespace::from(XML_STREAM_NS),
Namespace::from(XML_STREAM_NS), Cow::Borrowed(xml_ncname!("stream")),
Cow::Borrowed(xml_ncname!("stream")), ))?;
))
.await?;
if let Some(from) = self.from { if let Some(from) = self.from {
stream stream.as_mut().start_send(Item::Attribute(
.send(Item::Attribute( Namespace::NONE,
Namespace::NONE, Cow::Borrowed(xml_ncname!("from")),
Cow::Borrowed(xml_ncname!("from")), from,
from, ))?;
))
.await?;
} }
if let Some(to) = self.to { if let Some(to) = self.to {
stream stream.as_mut().start_send(Item::Attribute(
.send(Item::Attribute( Namespace::NONE,
Namespace::NONE, Cow::Borrowed(xml_ncname!("to")),
Cow::Borrowed(xml_ncname!("to")), to,
to, ))?;
))
.await?;
} }
if let Some(id) = self.id { if let Some(id) = self.id {
stream stream.as_mut().start_send(Item::Attribute(
.send(Item::Attribute(
Namespace::NONE,
Cow::Borrowed(xml_ncname!("id")),
id,
))
.await?;
}
stream
.send(Item::Attribute(
Namespace::NONE, Namespace::NONE,
Cow::Borrowed(xml_ncname!("version")), Cow::Borrowed(xml_ncname!("id")),
Cow::Borrowed("1.0"), id,
)) ))?;
.await?; }
stream.as_mut().start_send(Item::Attribute(
Namespace::NONE,
Cow::Borrowed(xml_ncname!("version")),
Cow::Borrowed("1.0"),
))?;
stream.send(Item::ElementHeadEnd).await?; stream.send(Item::ElementHeadEnd).await?;
Ok(()) Ok(())
} }