@@ -29,9 +29,10 @@ class PhoenixSocket {
29
29
30
30
/// The factory to use to create the WebSocketChannel.
31
31
WebSocketChannel Function (Uri uri)? webSocketChannelFactory,
32
- }) {
33
- _options = socketOptions ?? PhoenixSocketOptions ();
34
-
32
+ }) : _endpoint = endpoint,
33
+ _options = socketOptions ?? const PhoenixSocketOptions (),
34
+ _webSocketChannelFactory =
35
+ webSocketChannelFactory ?? WebSocketChannel .connect {
35
36
_messageStream =
36
37
_receiveStreamController.stream.map (_options.serializer.decode);
37
38
_openStream =
@@ -41,20 +42,6 @@ class PhoenixSocket {
41
42
_errorStream =
42
43
_stateEventStreamController.stream.whereType <PhoenixSocketErrorEvent >();
43
44
44
- _connectionManager = SocketConnectionManager (
45
- factory : () async {
46
- final mountPoint = await _buildMountPoint (endpoint, _options);
47
- return (webSocketChannelFactory ?? WebSocketChannel .connect)
48
- .call (mountPoint);
49
- },
50
- reconnectDelays: _options.reconnectDelays,
51
- onMessage: onSocketDataCallback,
52
- onError: (error, [stackTrace]) => _stateEventStreamController.add (
53
- PhoenixSocketErrorEvent (error: error, stacktrace: stackTrace),
54
- ),
55
- onStateChange: _socketStateStream.add,
56
- );
57
-
58
45
_subscriptions = [
59
46
_messageStream.listen (_onMessage),
60
47
_openStream.listen ((_) => _isOpen = true ),
@@ -64,18 +51,12 @@ class PhoenixSocket {
64
51
];
65
52
}
66
53
67
- static Future <Uri > _buildMountPoint (
68
- String endpoint,
69
- PhoenixSocketOptions options,
70
- ) async {
71
- var decodedUri = Uri .parse (endpoint);
72
- final params = await options.getParams ();
73
- final queryParams = decodedUri.queryParameters.entries.toList ()
74
- ..addAll (params.entries.toList ());
75
- return decodedUri.replace (
76
- queryParameters: Map .fromEntries (queryParams),
77
- );
78
- }
54
+ final String _endpoint;
55
+ final PhoenixSocketOptions _options;
56
+ final WebSocketChannel Function (Uri uri)? _webSocketChannelFactory;
57
+
58
+ /// Default duration for a connection timeout.
59
+ Duration get defaultTimeout => _options.timeout;
79
60
80
61
final Map <String , Completer <Message >> _pendingMessages = {};
81
62
final Map <String , Stream <Message >> _topicStreams = {};
@@ -92,12 +73,7 @@ class PhoenixSocket {
92
73
late Stream <PhoenixSocketCloseEvent > _closeStream;
93
74
late Stream <PhoenixSocketErrorEvent > _errorStream;
94
75
late Stream <Message > _messageStream;
95
- late SocketConnectionManager _connectionManager;
96
-
97
- late PhoenixSocketOptions _options;
98
-
99
- /// Default duration for a connection timeout.
100
- Duration get defaultTimeout => _options.timeout;
76
+ SocketConnectionManager ? _connectionManager;
101
77
102
78
_StreamRouter <Message >? _router;
103
79
@@ -171,9 +147,8 @@ class PhoenixSocket {
171
147
/// To check whether the socket is ready for use, use [isOpen] , or await on an
172
148
/// event from [openStream] .
173
149
///
174
- /// If [immediately] is set to `true` , then if a
175
- /// connection is not established, it will attempt to connect to a socket
176
- /// without delay.
150
+ /// If [immediately] is set to `true` and if a connection is not established,
151
+ /// it will attempt to connect to a socket without delay.
177
152
Future <void > connect ({bool immediately = false }) async {
178
153
if (_disposed) {
179
154
throw StateError ('PhoenixSocket cannot connect after being disposed.' );
@@ -188,13 +163,46 @@ class PhoenixSocket {
188
163
if (isOpen) {
189
164
return ;
190
165
} else if (! _socketStateStream.hasValue || _isConnectingOrConnected) {
191
- return _connectionManager.start (immediately: immediately);
166
+ await (_connectionManager ?? = _createConnectionManager ())
167
+ .start (immediately: immediately);
192
168
} else {
193
- return _reconnect (
169
+ await _reconnect (
194
170
normalClosure, // Any code is a good code.
195
171
immediately: immediately,
196
172
);
197
173
}
174
+
175
+ assert (
176
+ _stateEventStreamController.valueOrNull is ! PhoenixSocketOpenEvent ,
177
+ 'Phoenix socket should not be open at this stage' ,
178
+ );
179
+ await _openStream.first;
180
+ }
181
+
182
+ SocketConnectionManager _createConnectionManager () {
183
+ return SocketConnectionManager (
184
+ factory : () async {
185
+ final mountPoint = await _buildMountPoint ();
186
+ return (_webSocketChannelFactory ?? WebSocketChannel .connect)
187
+ .call (mountPoint);
188
+ },
189
+ reconnectDelays: _options.reconnectDelays,
190
+ onMessage: onSocketDataCallback,
191
+ onError: (error, [stackTrace]) => _stateEventStreamController.add (
192
+ PhoenixSocketErrorEvent (error: error, stacktrace: stackTrace),
193
+ ),
194
+ onStateChange: _socketStateStream.add,
195
+ );
196
+ }
197
+
198
+ Future <Uri > _buildMountPoint () async {
199
+ var decodedUri = Uri .parse (_endpoint);
200
+ final params = await _options.getParams ();
201
+ final queryParams = decodedUri.queryParameters.entries.toList ()
202
+ ..addAll (params.entries.toList ());
203
+ return decodedUri.replace (
204
+ queryParameters: Map .fromEntries (queryParams),
205
+ );
198
206
}
199
207
200
208
/// Close the underlying connection supporting the socket.
@@ -255,19 +263,27 @@ class PhoenixSocket {
255
263
throw StateError ('Cannot reconnect a disposed socket' );
256
264
}
257
265
258
- if (_socketStateStream.hasValue ) {
259
- await _closeConnection (code, reason : reason );
266
+ if (_connectionManager == null ) {
267
+ return connect (immediately : immediately );
260
268
}
261
- _connectionManager.start (immediately: immediately);
269
+
270
+ _connectionManager!
271
+ .reconnect (code, reason: reason, immediately: immediately);
262
272
}
263
273
264
274
Future <void > _closeConnection (int code, {String ? reason}) async {
265
275
if (_disposed) {
266
276
_logger.warning ('Cannot close a disposed socket' );
267
277
}
268
- if (_isConnectingOrConnected) {
269
- _connectionManager.stop (code, reason);
270
- } else if (_socketStateStream.valueOrNull is ! WebSocketDisconnected ) {
278
+ if (_connectionManager != null ) {
279
+ _connectionManager! .dispose (code, reason);
280
+ _connectionManager = null ;
281
+ }
282
+ if (_stateEventStreamController.valueOrNull is ! PhoenixSocketCloseEvent ) {
283
+ _stateEventStreamController
284
+ .add (PhoenixSocketCloseEvent (code: code, reason: reason));
285
+ }
286
+ if (_socketStateStream.valueOrNull is ! WebSocketDisconnected ) {
271
287
await _socketStateStream
272
288
.firstWhere ((state) => state is WebSocketDisconnected );
273
289
}
@@ -295,11 +311,12 @@ class PhoenixSocket {
295
311
return (_pendingMessages[message.ref! ] = Completer <Message >()).future;
296
312
}
297
313
298
- void _addToSink (String data) {
314
+ Future < void > _addToSink (String data) async {
299
315
if (_disposed) {
300
- return ;
316
+ throw StateError ( 'Cannot add messages to a disposed socket' ) ;
301
317
}
302
- _connectionManager.addMessage (data);
318
+
319
+ _connectionManager! .addMessage (data);
303
320
}
304
321
305
322
/// CHANNELS
0 commit comments