fix(core): An empty iq is okay with roster versioning
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
ddf781daff
commit
d4166d087e
@ -182,8 +182,18 @@ class RosterManager extends XmppManagerBase {
|
|||||||
|
|
||||||
/// Shared code between requesting rosters without and with roster versioning, if
|
/// Shared code between requesting rosters without and with roster versioning, if
|
||||||
/// the server deems a regular roster response more efficient than n roster pushes.
|
/// the server deems a regular roster response more efficient than n roster pushes.
|
||||||
|
///
|
||||||
|
/// [query] is the <query /> child of the iq, if available.
|
||||||
|
///
|
||||||
|
/// If roster versioning was used, then [requestedRosterVersion] is the version
|
||||||
|
/// we requested the roster with.
|
||||||
|
///
|
||||||
|
/// Note that if roster versioning is used and the server returns us an empty iq,
|
||||||
|
/// it means that the roster did not change since the last version. In that case,
|
||||||
|
/// we do nothing and just return. The roster state manager will not be notified.
|
||||||
Future<Result<RosterRequestResult, RosterError>> _handleRosterResponse(
|
Future<Result<RosterRequestResult, RosterError>> _handleRosterResponse(
|
||||||
XMLNode? query,
|
XMLNode? query,
|
||||||
|
String? requestedRosterVersion,
|
||||||
) async {
|
) async {
|
||||||
final List<XmppRosterItem> items;
|
final List<XmppRosterItem> items;
|
||||||
String? rosterVersion;
|
String? rosterVersion;
|
||||||
@ -204,6 +214,14 @@ class RosterManager extends XmppManagerBase {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
rosterVersion = query.attributes['ver'] as String?;
|
rosterVersion = query.attributes['ver'] as String?;
|
||||||
|
} else if (requestedRosterVersion != null) {
|
||||||
|
// Skip the handleRosterFetch call since nothing changed.
|
||||||
|
return Result(
|
||||||
|
RosterRequestResult(
|
||||||
|
[],
|
||||||
|
requestedRosterVersion,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Server response to roster request without roster versioning does not contain a <query /> element, while the type is not error. This violates RFC6121',
|
'Server response to roster request without roster versioning does not contain a <query /> element, while the type is not error. This violates RFC6121',
|
||||||
@ -258,7 +276,7 @@ class RosterManager extends XmppManagerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final responseQuery = response.firstTag('query', xmlns: rosterXmlns);
|
final responseQuery = response.firstTag('query', xmlns: rosterXmlns);
|
||||||
return _handleRosterResponse(responseQuery);
|
return _handleRosterResponse(responseQuery, rosterVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests a series of roster pushes according to RFC6121. Requires that the server
|
/// Requests a series of roster pushes according to RFC6121. Requires that the server
|
||||||
@ -266,6 +284,7 @@ class RosterManager extends XmppManagerBase {
|
|||||||
Future<Result<RosterRequestResult?, RosterError>>
|
Future<Result<RosterRequestResult?, RosterError>>
|
||||||
requestRosterPushes() async {
|
requestRosterPushes() async {
|
||||||
final attrs = getAttributes();
|
final attrs = getAttributes();
|
||||||
|
final rosterVersion = await _stateManager.getRosterVersion();
|
||||||
final result = (await attrs.sendStanza(
|
final result = (await attrs.sendStanza(
|
||||||
StanzaDetails(
|
StanzaDetails(
|
||||||
Stanza.iq(
|
Stanza.iq(
|
||||||
@ -275,7 +294,7 @@ class RosterManager extends XmppManagerBase {
|
|||||||
tag: 'query',
|
tag: 'query',
|
||||||
xmlns: rosterXmlns,
|
xmlns: rosterXmlns,
|
||||||
attributes: {
|
attributes: {
|
||||||
'ver': await _stateManager.getRosterVersion() ?? '',
|
'ver': rosterVersion ?? '',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -289,7 +308,7 @@ class RosterManager extends XmppManagerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final query = result.firstTag('query', xmlns: rosterXmlns);
|
final query = result.firstTag('query', xmlns: rosterXmlns);
|
||||||
return _handleRosterResponse(query);
|
return _handleRosterResponse(query, rosterVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rosterVersioningAvailable() {
|
bool rosterVersioningAvailable() {
|
||||||
|
@ -89,6 +89,7 @@ List<ExpectationBase> buildAuthenticatedPlay(ConnectionSettings settings) {
|
|||||||
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
||||||
<required/>
|
<required/>
|
||||||
</bind>
|
</bind>
|
||||||
|
<ver xmlns='urn:xmpp:features:rosterver'/>
|
||||||
</stream:features>
|
</stream:features>
|
||||||
''',
|
''',
|
||||||
),
|
),
|
||||||
|
53
packages/moxxmpp/test/roster_test.dart
Normal file
53
packages/moxxmpp/test/roster_test.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'helpers/logging.dart';
|
||||||
|
import 'helpers/xmpp.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
initLogger();
|
||||||
|
|
||||||
|
test('Test a versioned roster fetch returning an empty iq', () async {
|
||||||
|
final sm = TestingRosterStateManager('ver14', []);
|
||||||
|
final rm = RosterManager(sm);
|
||||||
|
final cs = ConnectionSettings(
|
||||||
|
jid: JID.fromString('user@example.org'),
|
||||||
|
password: 'abc123',
|
||||||
|
);
|
||||||
|
final socket = StubTCPSocket([
|
||||||
|
...buildAuthenticatedPlay(cs),
|
||||||
|
StanzaExpectation(
|
||||||
|
'<iq xmlns="jabber:client" id="r1h3vzp7" type="get"><query xmlns="jabber:iq:roster" ver="ver14"/></iq>',
|
||||||
|
'<iq xmlns="jabber:client" id="r1h3vzp7" type="result" />',
|
||||||
|
ignoreId: true,
|
||||||
|
adjustId: true,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
final conn = XmppConnection(
|
||||||
|
TestingReconnectionPolicy(),
|
||||||
|
AlwaysConnectedConnectivityManager(),
|
||||||
|
ClientToServerNegotiator(),
|
||||||
|
socket,
|
||||||
|
)..connectionSettings = cs;
|
||||||
|
await conn.registerManagers([
|
||||||
|
rm,
|
||||||
|
PresenceManager(),
|
||||||
|
]);
|
||||||
|
await conn.registerFeatureNegotiators([
|
||||||
|
SaslPlainNegotiator(),
|
||||||
|
ResourceBindingNegotiator(),
|
||||||
|
RosterFeatureNegotiator(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
await conn.connect(
|
||||||
|
shouldReconnect: false,
|
||||||
|
waitUntilLogin: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Request the roster
|
||||||
|
final rawResult = await rm.requestRoster();
|
||||||
|
expect(rawResult.isType<RosterRequestResult>(), true);
|
||||||
|
final result = rawResult.get<RosterRequestResult>();
|
||||||
|
expect(result.items.isEmpty, true);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user