Skip to content

Commit fde3256

Browse files
committed
[core] Parse XEP-0478 Stream <limits> Advertisement feature
1 parent e545fc5 commit fde3256

4 files changed

Lines changed: 161 additions & 5 deletions

File tree

smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import org.jivesoftware.smack.packet.ErrorIQ;
8383
import org.jivesoftware.smack.packet.ExtensionElement;
8484
import org.jivesoftware.smack.packet.IQ;
85+
import org.jivesoftware.smack.packet.Limits;
8586
import org.jivesoftware.smack.packet.Mechanisms;
8687
import org.jivesoftware.smack.packet.Message;
8788
import org.jivesoftware.smack.packet.MessageBuilder;
@@ -1902,6 +1903,9 @@ protected final void parseFeatures(XmlPullParser parser) throws XmlPullParserExc
19021903
case Compress.Feature.ELEMENT:
19031904
streamFeature = PacketParserUtils.parseCompressionFeature(parser);
19041905
break;
1906+
case Limits.ELEMENT:
1907+
streamFeature = PacketParserUtils.parseLimitsFeature(parser);
1908+
break;
19051909
default:
19061910
ExtensionElementProvider<ExtensionElement> provider = ProviderManager.getStreamFeatureProvider(name, namespace);
19071911
if (provider != null) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
*
3+
* Copyright © 2014-2026 Florian Schmaus
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.jivesoftware.smack.packet;
18+
19+
import javax.xml.namespace.QName;
20+
21+
import org.jivesoftware.smack.util.XmlStringBuilder;
22+
23+
/**
24+
* Limits feature.
25+
* For any XMPP stream, there is an "initiating entity" (a client or server) and a "responding entity" that they are connecting to.
26+
* The responding entity advertises its limits in the <code>&lt;stream:features/&gt;</code> element that it sends at the start of the stream.
27+
* <a href="https://xmpp.org/extensions/xep-0478.html">XEP-0478: Stream Limits Advertisement</a>
28+
*/
29+
public class Limits implements ExtensionElement {
30+
public static final String ELEMENT = "limits";
31+
public static final String NAMESPACE = "urn:xmpp:stream-limits:0";
32+
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
33+
34+
/**
35+
* The maximum size of any first-level stream elements (including stanzas),
36+
* in bytes the announcing entity is willing to accept.
37+
* Guidance on acceptable limits is provided in RFC 6120 section 13.12.
38+
* If the responding entity is unable to determine its limits, this child can be absent.
39+
* Element: <code>&lt;max-bytes/&gt;</code>. Type UnsignedInt.
40+
*/
41+
public final int maxBytes;
42+
43+
/**
44+
* The number of seconds without any traffic from the initiating entity after which
45+
* the server may consider the stream idle, and either perform liveness checks
46+
* (using e.g. Stream Management (XEP-0198) or XMPP Ping (XEP-0199)) or terminate the stream.
47+
* Guidance on handling idle connections is provided in RFC 6120 section 4.6.
48+
* If the responding entity is unable to determine its limits, this child can be absent.
49+
* Element: <code>&lt;idle-seconds/&gt;</code>. Type UnsignedInt.
50+
*/
51+
public final int idleSeconds;
52+
53+
public Limits(int maxBytes, int idleSeconds) {
54+
this.maxBytes = maxBytes;
55+
this.idleSeconds = idleSeconds;
56+
}
57+
58+
@Override
59+
public String getElementName() {
60+
return ELEMENT;
61+
}
62+
63+
@Override
64+
public String getNamespace() {
65+
return NAMESPACE;
66+
}
67+
68+
public int getMaxBytes() {
69+
return maxBytes;
70+
}
71+
72+
public int getIdleSeconds() {
73+
return idleSeconds;
74+
}
75+
76+
@Override
77+
public XmlStringBuilder toXML(XmlEnvironment enclosingNamespace) {
78+
XmlStringBuilder xml = new XmlStringBuilder(this);
79+
xml.rightAngleBracket();
80+
if (maxBytes != 0) {
81+
xml.element("max-bytes", String.valueOf(maxBytes));
82+
}
83+
if (idleSeconds != 0) {
84+
xml.element("idle-seconds", String.valueOf(idleSeconds));
85+
}
86+
xml.closeElement(this);
87+
return xml;
88+
}
89+
90+
}

smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.jivesoftware.smack.packet.ExtensionElement;
3737
import org.jivesoftware.smack.packet.IQ;
3838
import org.jivesoftware.smack.packet.IqData;
39+
import org.jivesoftware.smack.packet.Limits;
3940
import org.jivesoftware.smack.packet.Message;
4041
import org.jivesoftware.smack.packet.MessageBuilder;
4142
import org.jivesoftware.smack.packet.Presence;
@@ -894,6 +895,57 @@ public static Session.Feature parseSessionFeature(XmlPullParser parser) throws X
894895
return new Session.Feature(optional);
895896
}
896897

898+
899+
/**
900+
* Parse the stream limits from XEP-0478: Stream Limits Advertisement.
901+
*
902+
* @param parser the XML parser, positioned at the start of the "limits" stanza.
903+
* @return a collection of Stings with the mechanisms included in the mechanisms stanza.
904+
* @throws IOException if an I/O error occurred.
905+
* @throws XmlPullParserException if an error in the XML parser occurred.
906+
*/
907+
public static Limits parseLimitsFeature(XmlPullParser parser)
908+
throws XmlPullParserException, IOException {
909+
ParserUtils.assertAtStartTag(parser);
910+
final int initialDepth = parser.getDepth();
911+
int maxBytes = 0;
912+
int idleSeconds = 0;
913+
outerloop: while (true) {
914+
XmlPullParser.Event event = parser.next();
915+
switch (event) {
916+
case START_ELEMENT:
917+
String name = parser.getName();
918+
switch (name) {
919+
case "max-bytes":
920+
try {
921+
maxBytes = Integer.parseUnsignedInt(parser.nextText());
922+
} catch (NumberFormatException ignored) {
923+
// ignore
924+
}
925+
break;
926+
case "idle-seconds":
927+
try {
928+
idleSeconds = Integer.parseUnsignedInt(parser.nextText());
929+
} catch (NumberFormatException ignored) {
930+
// ignore
931+
}
932+
break;
933+
}
934+
break;
935+
case END_ELEMENT:
936+
if (parser.getDepth() == initialDepth) {
937+
break outerloop;
938+
}
939+
break;
940+
default:
941+
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
942+
break;
943+
}
944+
}
945+
946+
return new Limits(maxBytes, idleSeconds);
947+
}
948+
897949
public static void addExtensionElement(StanzaBuilder<?> stanzaBuilder, XmlPullParser parser, XmlEnvironment outerXmlEnvironment, JxmppContext jxmppContext)
898950
throws XmlPullParserException, IOException, SmackParsingException {
899951
ParserUtils.assertAtStartTag(parser);

smack-core/src/test/java/org/jivesoftware/smack/AbstractXMPPConnectionTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
import static java.util.Arrays.asList;
66

7+
import java.io.ByteArrayInputStream;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.nio.charset.StandardCharsets;
11+
712
import org.jivesoftware.smack.packet.Bind;
13+
import org.jivesoftware.smack.packet.Limits;
814
import org.jivesoftware.smack.packet.Mechanisms;
915
import org.jivesoftware.smack.packet.Session;
1016
import org.jivesoftware.smack.parsing.SmackParsingException;
@@ -15,11 +21,6 @@
1521
import org.junit.jupiter.api.BeforeEach;
1622
import org.junit.jupiter.api.Test;
1723

18-
import java.io.ByteArrayInputStream;
19-
import java.io.IOException;
20-
import java.io.InputStream;
21-
import java.nio.charset.StandardCharsets;
22-
2324
class AbstractXMPPConnectionTest extends SmackTestSuite {
2425
private DummyConnection connection;
2526

@@ -46,6 +47,10 @@ void parseFeatures() throws XmlPullParserException, IOException, SmackParsingExc
4647
"<session xmlns='urn:ietf:params:xml:ns:xmpp-session'>\n" +
4748
" <optional />\n" +
4849
"</session>\n" +
50+
"<limits xmlns='urn:xmpp:stream-limits:0'>\n" +
51+
" <max-bytes>262144</max-bytes>\n" +
52+
" <idle-seconds>840</idle-seconds>\n" +
53+
"</limits>\n" +
4954
"<register xmlns='urn:xmpp:invite' />\n" +
5055
"<register xmlns='urn:xmpp:ibr-token:0' />\n" +
5156
"<register xmlns='http://jabber.org/features/iq-register' />\n" +
@@ -70,5 +75,10 @@ void parseFeatures() throws XmlPullParserException, IOException, SmackParsingExc
7075
assertTrue(connection.hasFeature("session", "urn:ietf:params:xml:ns:xmpp-session"));
7176
Session.Feature featSession = connection.getFeature(Session.Feature.class);
7277
assertTrue(featSession.isOptional());
78+
79+
assertTrue(connection.hasFeature("limits", "urn:xmpp:stream-limits:0"));
80+
Limits featLimits = connection.getFeature(Limits.class);
81+
assertEquals(262144, featLimits.getMaxBytes());
82+
assertEquals(840, featLimits.getIdleSeconds());
7383
}
7484
}

0 commit comments

Comments
 (0)