xmpp-parsers: Implement XEP-0478: Stream Limits Advertisement

Emmanuel Gil Peyrot created

This allows servers to communicate their maximum stanza limit, as well
as idle time limit.

Change summary

parsers/ChangeLog              |  1 
parsers/doap.xml               |  8 +++
parsers/src/lib.rs             |  3 +
parsers/src/ns.rs              |  3 +
parsers/src/stream_features.rs |  9 +++
parsers/src/stream_limits.rs   | 74 ++++++++++++++++++++++++++++++++++++
6 files changed, 96 insertions(+), 2 deletions(-)

Detailed changes

parsers/ChangeLog 🔗

@@ -19,6 +19,7 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
         - Stream Features (RFC 6120) (!400)
         - Extensible SASL Profile (XEP-0388)
         - SASL Channel-Binding Type Capability (XEP-0440)
+        - Stream Limits Advertisement (XEP-0478)
 
 Version 0.21.0:
 2024-07-25 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>

parsers/doap.xml 🔗

@@ -658,6 +658,14 @@
             <xmpp:since>0.20.0</xmpp:since>
         </xmpp:SupportedXep>
     </implements>
+    <implements>
+        <xmpp:SupportedXep>
+            <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0478.html"/>
+            <xmpp:status>complete</xmpp:status>
+            <xmpp:version>0.2.0</xmpp:version>
+            <xmpp:since>NEXT</xmpp:since>
+        </xmpp:SupportedXep>
+    </implements>
     <implements>
         <xmpp:SupportedXep>
             <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0484.html"/>

parsers/src/lib.rs 🔗

@@ -281,5 +281,8 @@ pub mod sasl_cb;
 /// XEP-0444: Message Reactions
 pub mod reactions;
 
+/// XEP-0478: Stream Limits Advertisement
+pub mod stream_limits;
+
 /// XEP-0484: Fast Authentication Streamlining Tokens
 pub mod fast;

parsers/src/ns.rs 🔗

@@ -302,6 +302,9 @@ pub const SASL_CB: &str = "urn:xmpp:sasl-cb:0";
 /// XEP-0444: Message Reactions
 pub const REACTIONS: &str = "urn:xmpp:reactions:0";
 
+/// XEP-0478: Stream Limits Advertisement
+pub const STREAM_LIMITS: &str = "urn:xmpp:stream-limits:0";
+
 /// XEP-0484: Fast Authentication Streamlining Tokens
 pub const FAST: &str = "urn:xmpp:fast:0";
 

parsers/src/stream_features.rs 🔗

@@ -9,6 +9,7 @@ use xso::{AsXml, FromXml};
 
 use crate::bind::BindFeature;
 use crate::ns;
+use crate::stream_limits::Limits;
 
 /// Wraps `<stream:features/>`, usually the very first nonza of a
 /// XMPP stream. Indicates which features are supported.
@@ -27,6 +28,10 @@ pub struct StreamFeatures {
     #[xml(child(default))]
     pub sasl_mechanisms: SaslMechanisms,
 
+    /// Limits advertised by the server.
+    #[xml(child(default))]
+    pub limits: Option<Limits>,
+
     /// Other stream features advertised
     ///
     /// If some features you use end up here, you may want to contribute
@@ -91,7 +96,7 @@ mod tests {
         assert_size!(SaslMechanisms, 12);
         assert_size!(RequiredStartTls, 0);
         assert_size!(StartTls, 1);
-        assert_size!(StreamFeatures, 28);
+        assert_size!(StreamFeatures, 40);
     }
 
     #[cfg(target_pointer_width = "64")]
@@ -101,7 +106,7 @@ mod tests {
         assert_size!(SaslMechanisms, 24);
         assert_size!(RequiredStartTls, 0);
         assert_size!(StartTls, 1);
-        assert_size!(StreamFeatures, 56);
+        assert_size!(StreamFeatures, 64);
     }
 
     #[test]

parsers/src/stream_limits.rs 🔗

@@ -0,0 +1,74 @@
+// Copyright (c) 2024 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+use xso::{AsXml, FromXml};
+
+use crate::ns;
+use std::num::NonZeroU32;
+
+/// Advertises limits on this stream.
+#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::STREAM_LIMITS, name = "limits")]
+pub struct Limits {
+    /// Maximum size of any first-level stream elements (including stanzas), in bytes the
+    /// announcing entity is willing to accept.
+    // TODO: Replace that with a direct u32 once xso supports that.
+    #[xml(child(default))]
+    pub max_bytes: Option<MaxBytes>,
+
+    /// Number of seconds without any traffic from the iniating entity after which the server may
+    /// consider the stream idle, and either perform liveness checks or terminate the stream.
+    // TODO: Replace that with a direct u32 once xso supports that.
+    #[xml(child(default))]
+    pub idle_seconds: Option<IdleSeconds>,
+}
+
+/// Maximum size of any first-level stream elements (including stanzas), in bytes the
+/// announcing entity is willing to accept.
+#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::STREAM_LIMITS, name = "max-bytes")]
+pub struct MaxBytes {
+    /// The number of bytes.
+    #[xml(text)]
+    pub value: NonZeroU32,
+}
+
+/// Number of seconds without any traffic from the iniating entity after which the server may
+/// consider the stream idle, and either perform liveness checks or terminate the stream.
+#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::STREAM_LIMITS, name = "idle-seconds")]
+pub struct IdleSeconds {
+    /// The number of seconds.
+    #[xml(text)]
+    pub value: NonZeroU32,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use minidom::Element;
+
+    #[test]
+    fn test_size() {
+        assert_size!(Limits, 8);
+        assert_size!(MaxBytes, 4);
+        assert_size!(IdleSeconds, 4);
+    }
+
+    #[test]
+    fn test_simple() {
+        let elem: Element =
+            "<limits xmlns='urn:xmpp:stream-limits:0'><max-bytes>262144</max-bytes></limits>"
+                .parse()
+                .unwrap();
+        let limits = Limits::try_from(elem).unwrap();
+        assert_eq!(
+            limits.max_bytes.unwrap().value,
+            NonZeroU32::new(262144).unwrap()
+        );
+        assert!(limits.idle_seconds.is_none());
+    }
+}