feat(xep): Allow inlining CSI

This commit is contained in:
PapaTutuWawa 2023-04-01 23:16:37 +02:00
parent 4a6aa79e56
commit fbb495dc2f
2 changed files with 138 additions and 10 deletions

View File

@ -5,6 +5,7 @@ import 'package:moxxmpp/src/negotiators/namespaces.dart';
import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart';
import 'package:moxxmpp/src/stringxml.dart'; import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/types/result.dart';
import 'package:moxxmpp/src/xeps/xep_0386.dart';
class CSIActiveNonza extends XMLNode { class CSIActiveNonza extends XMLNode {
CSIActiveNonza() CSIActiveNonza()
@ -23,7 +24,8 @@ class CSIInactiveNonza extends XMLNode {
} }
/// A Stub negotiator that is just for "intercepting" the stream feature. /// A Stub negotiator that is just for "intercepting" the stream feature.
class CSINegotiator extends XmppFeatureNegotiatorBase { class CSINegotiator extends XmppFeatureNegotiatorBase
implements Bind2FeatureNegotiator {
CSINegotiator() : super(11, false, csiXmlns, csiNegotiator); CSINegotiator() : super(11, false, csiXmlns, csiNegotiator);
/// True if CSI is supported. False otherwise. /// True if CSI is supported. False otherwise.
@ -40,19 +42,43 @@ class CSINegotiator extends XmppFeatureNegotiatorBase {
return const Result(NegotiatorState.done); return const Result(NegotiatorState.done);
} }
@override
Future<List<XMLNode>> onBind2FeaturesReceived(
List<String> bind2Features,
) async {
if (!bind2Features.contains(csiXmlns)) {
return [];
}
final active = attributes.getManagerById<CSIManager>(csiManager)!.isActive;
return [
if (active) CSIActiveNonza() else CSIInactiveNonza(),
];
}
@override @override
void reset() { void reset() {
_supported = false; _supported = false;
super.reset(); super.reset();
} }
@override
Future<void> postRegisterCallback() async {
attributes
.getNegotiatorById<Bind2Negotiator>(bind2Negotiator)
?.registerNegotiator(this);
}
} }
/// The manager requires a CSINegotiator to be registered as a feature negotiator. /// The manager requires a CSINegotiator to be registered as a feature negotiator.
class CSIManager extends XmppManagerBase { class CSIManager extends XmppManagerBase {
CSIManager() : super(csiManager); CSIManager() : super(csiManager);
/// Flag indicating whether the application is currently active and the CSI
/// traffic optimisation should be disabled (true).
bool _isActive = true; bool _isActive = true;
bool get isActive => _isActive;
@override @override
Future<bool> isSupported() async { Future<bool> isSupported() async {
@ -71,23 +97,31 @@ class CSIManager extends XmppManagerBase {
} }
} }
/// Tells the server to top optimizing traffic /// Tells the server to stop optimizing traffic.
Future<void> setActive() async { /// If [sendNonza] is false, then no nonza is sent. This is useful
/// for setting up the CSI manager for Bind2.
Future<void> setActive({bool sendNonza = true}) async {
_isActive = true; _isActive = true;
final attrs = getAttributes(); if (sendNonza) {
if (await isSupported()) { final attrs = getAttributes();
attrs.sendNonza(CSIActiveNonza()); if (await isSupported()) {
attrs.sendNonza(CSIActiveNonza());
}
} }
} }
/// Tells the server to optimize traffic following XEP-0352 /// Tells the server to optimize traffic following XEP-0352
Future<void> setInactive() async { /// If [sendNonza] is false, then no nonza is sent. This is useful
/// for setting up the CSI manager for Bind2.
Future<void> setInactive({bool sendNonza = true}) async {
_isActive = false; _isActive = false;
final attrs = getAttributes(); if (sendNonza) {
if (await isSupported()) { final attrs = getAttributes();
attrs.sendNonza(CSIInactiveNonza()); if (await isSupported()) {
attrs.sendNonza(CSIInactiveNonza());
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp/moxxmpp.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../helpers/logging.dart';
import '../helpers/xmpp.dart'; import '../helpers/xmpp.dart';
class MockedCSINegotiator extends CSINegotiator { class MockedCSINegotiator extends CSINegotiator {
@ -28,6 +29,8 @@ T? getUnsupportedCSINegotiator<T extends XmppFeatureNegotiatorBase>(String id) {
} }
void main() { void main() {
initLogger();
group('Test the XEP-0352 implementation', () { group('Test the XEP-0352 implementation', () {
test('Test setting the CSI state when CSI is unsupported', () { test('Test setting the CSI state when CSI is unsupported', () {
var nonzaSent = false; var nonzaSent = false;
@ -111,4 +114,95 @@ void main() {
..setInactive(); ..setInactive();
}); });
}); });
test('Test CSI with Bind2', () async {
final fakeSocket = StubTCPSocket([
StringExpectation(
"<stream:stream xmlns='jabber:client' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' to='test.server' xml:lang='en'>",
'''
<stream:stream
xmlns="jabber:client"
version="1.0"
xmlns:stream="http://etherx.jabber.org/streams"
from="test.server"
xml:lang="en">
<stream:features xmlns="http://etherx.jabber.org/streams">
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
</mechanisms>
<authentication xmlns='urn:xmpp:sasl:2'>
<mechanism>PLAIN</mechanism>
<inline>
<bind xmlns="urn:xmpp:bind:0">
<inline>
<feature var="urn:xmpp:csi:0" />
</inline>
</bind>
</inline>
</authentication>
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<required/>
</bind>
</stream:features>''',
),
StanzaExpectation(
'''
<authenticate xmlns='urn:xmpp:sasl:2' mechanism='PLAIN'>
<user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
<software>moxxmpp</software>
<device>PapaTutuWawa's awesome device</device>
</user-agent>
<initial-response>AHBvbHlub21kaXZpc2lvbgBhYWFh</initial-response>
<bind xmlns='urn:xmpp:bind:0'>
<inactive xmlns='urn:xmpp:csi:0' />
</bind>
</authenticate>''',
'''
<success xmlns='urn:xmpp:sasl:2'>
<authorization-identifier>polynomdivision@test.server/test-resource</authorization-identifier>
</success>
''',
),
]);
final conn = XmppConnection(
TestingReconnectionPolicy(),
AlwaysConnectedConnectivityManager(),
fakeSocket,
)..setConnectionSettings(
ConnectionSettings(
jid: JID.fromString('polynomdivision@test.server'),
password: 'aaaa',
useDirectTLS: true,
),
);
final csi = CSIManager();
await csi.setInactive(sendNonza: false);
await conn.registerManagers([
RosterManager(TestingRosterStateManager('', [])),
DiscoManager([]),
csi,
]);
await conn.registerFeatureNegotiators([
SaslPlainNegotiator(),
ResourceBindingNegotiator(),
FASTSaslNegotiator(),
Bind2Negotiator(),
CSINegotiator(),
Sasl2Negotiator(
userAgent: const UserAgent(
id: 'd4565fa7-4d72-4749-b3d3-740edbf87770',
software: 'moxxmpp',
device: "PapaTutuWawa's awesome device",
),
),
]);
final result = await conn.connect(
waitUntilLogin: true,
shouldReconnect: false,
enableReconnectOnSuccess: false,
);
expect(result.isType<NegotiatorError>(), false);
expect(fakeSocket.getState(), 2);
});
} }