@@ -2,6 +2,7 @@ import 'dart:async';
2
2
import 'dart:io' ;
3
3
import 'dart:typed_data' ;
4
4
import 'package:chalkdart/chalk.dart' ;
5
+ import 'package:mutex/mutex.dart' ;
5
6
import 'package:socket_connector/src/types.dart' ;
6
7
7
8
/// Typical usage is via the [serverToServer] , [serverToSocket] ,
@@ -97,6 +98,9 @@ class SocketConnector {
97
98
if (closed) {
98
99
throw StateError ('Connector is closed' );
99
100
}
101
+ unawaited (thisSide.socket.done
102
+ .then ((v) => _closeSide (thisSide))
103
+ .catchError ((err) => _closeSide (thisSide)));
100
104
if (thisSide.socketAuthVerifier == null ) {
101
105
thisSide.authenticated = true ;
102
106
} else {
@@ -119,7 +123,7 @@ class SocketConnector {
119
123
}
120
124
if (! thisSide.authenticated) {
121
125
_log ('Authentication failed on side ${thisSide .name }' , force: true );
122
- _destroySide (thisSide);
126
+ _closeSide (thisSide);
123
127
return ;
124
128
}
125
129
@@ -136,31 +140,34 @@ class SocketConnector {
136
140
'Added connection. There are now ${connections .length } connections.' ));
137
141
138
142
for (final side in [thisSide, thisSide.farSide! ]) {
139
- unawaited (side.socket.done
140
- .then ((v) => _destroySide (side))
141
- .catchError ((err) => _destroySide (side)));
142
143
if (side.transformer != null ) {
143
144
// transformer is there to transform data originating FROM its side
145
+ // transformer's output will write to the SOCKET on the far side
144
146
StreamController <Uint8List > sc = StreamController <Uint8List >();
145
147
side.farSide! .sink = sc;
146
148
Stream <List <int >> transformed = side.transformer !(sc.stream);
147
- transformed.listen ((data) {
148
- try {
149
- if (side.farSide ! .state == SideState .open) {
149
+ transformed.listen (
150
+ (data) {
151
+ try {
150
152
side.farSide! .socket.add (data);
151
- } else {
152
- throw StateError (
153
- 'Will not write to side ${side .farSide !.name } as its state is ${side .farSide !.state }' );
153
+ side.farSide! .sent += data.length;
154
+ if (side.state == SideState .closed &&
155
+ side.rcvd == side.farSide! .sent) {
156
+ _closeSide (side.farSide! );
157
+ }
158
+ } catch (e, st) {
159
+ _log ('Failed to write to side ${side .farSide !.name } - closing' ,
160
+ force: true );
161
+ _log ('(Error was $e ; Stack trace follows\n $st ' , force: true );
162
+ _closeSide (side.farSide! );
154
163
}
155
- } catch (e, st) {
156
- _log ('Failed to write to side ${side .farSide !.name } - closing' ,
157
- force: true );
158
- _log ('(Error was $e ; Stack trace follows\n $st ' );
159
- _destroySide (side.farSide! );
160
- }
161
- });
164
+ },
165
+ onDone: () => _closeSide (side),
166
+ onError: (error) => _closeSide (side),
167
+ );
162
168
}
163
169
side.stream.listen ((Uint8List data) {
170
+ side.rcvd += data.length;
164
171
if (logTraffic) {
165
172
final message = String .fromCharCodes (data);
166
173
if (side.isSideA) {
@@ -172,34 +179,41 @@ class SocketConnector {
172
179
}
173
180
}
174
181
try {
175
- if (side.farSide! .state == SideState .open) {
176
- side.farSide! .sink.add (data);
177
- } else {
178
- throw StateError (
179
- 'Will not write to side ${side .farSide !.name } as its state is ${side .farSide !.state }' );
182
+ side.farSide! .sink.add (data);
183
+ if (side.farSide! .sink is Socket ) {
184
+ side.farSide! .sent += data.length;
185
+ if (side.state == SideState .closed &&
186
+ side.rcvd == side.farSide! .sent) {
187
+ _closeSide (side.farSide! );
188
+ }
180
189
}
181
190
} catch (e, st) {
182
191
_log ('Failed to write to side ${side .farSide !.name } - closing' ,
183
192
force: true );
184
- _log ('(Error was $e ; Stack trace follows\n $st ' );
185
- _destroySide (side.farSide! );
193
+ _log ('(Error was $e ; Stack trace follows\n $st ' , force : true );
194
+ _closeSide (side.farSide! );
186
195
}
187
- }, onDone: () {
188
- _log ('stream.onDone on side ${side .name }' );
189
- _destroySide (side);
196
+ }, onDone: () async {
197
+ _log ('${ side . stream . runtimeType } .onDone on side ${side .name }' );
198
+ _closeSide (side);
190
199
}, onError: (error) {
191
- _log ('stream.onError on side ${side .name }: $error ' , force: true );
192
- _destroySide (side);
200
+ _log (
201
+ '${side .stream .runtimeType }.onError on side ${side .name }: $error ' ,
202
+ force: true );
203
+ _closeSide (side);
193
204
});
194
205
}
195
206
}
196
207
}
197
208
198
- _destroySide (final Side side) {
209
+ _closeSide (final Side side) async {
199
210
if (side.state != SideState .open) {
200
211
return ;
201
212
}
202
- side.state = SideState .closing;
213
+ side.state = SideState .closed;
214
+
215
+ _log (chalk.brightBlue ('_closeSide ${side .name }: RCVD: ${side .rcvd } bytes; SENT: ${side .sent } bytes' ));
216
+
203
217
Connection ? connectionToRemove;
204
218
for (final c in connections) {
205
219
if (c.sideA == side || c.sideB == side) {
@@ -219,16 +233,24 @@ class SocketConnector {
219
233
close ();
220
234
}
221
235
}
222
- side.state = SideState .closed;
236
+
223
237
try {
224
238
_log (chalk.brightBlue ('Destroying socket on side ${side .name }' ));
239
+ await side.socket.flush ();
225
240
side.socket.destroy ();
226
- if (side.farSide != null ) {
227
- _log (chalk.brightBlue (
228
- 'Destroying socket on far side (${side .farSide ?.name })' ));
229
- _destroySide (side.farSide! );
241
+ if (side.farSide != null && side.farSide! .state != SideState .closed) {
242
+ if (side.rcvd == side.farSide! .sent) {
243
+ _log (chalk.brightBlue (
244
+ 'Far side (${side .farSide ?.name }) has received all data - will close it' ));
245
+ _closeSide (side.farSide! );
246
+ } else {
247
+ _log (chalk.brightBlue (
248
+ 'Far side (${side .farSide ?.name }) has NOT YET received all data' ));
249
+ }
230
250
}
231
- } catch (_) {}
251
+ } catch (err) {
252
+ _log ('_closeSide encountered error $err ' );
253
+ }
232
254
}
233
255
234
256
void close () {
@@ -243,11 +265,11 @@ class SocketConnector {
243
265
_log ('closed' );
244
266
}
245
267
for (final s in pendingA) {
246
- _destroySide (s);
268
+ _closeSide (s);
247
269
}
248
270
pendingA.clear ();
249
271
for (final s in pendingB) {
250
- _destroySide (s);
272
+ _closeSide (s);
251
273
}
252
274
pendingB.clear ();
253
275
}
@@ -504,32 +526,40 @@ class SocketConnector {
504
526
);
505
527
506
528
StreamController <Socket > ssc = StreamController ();
529
+ Mutex m = Mutex ();
507
530
ssc.stream.listen ((sideASocket) async {
508
- Side sideA = Side (sideASocket, true , transformer: transformAtoB);
509
- unawaited (connector.handleSingleConnection (sideA).catchError ((err) {
510
- logSink
511
- .writeln ('ERROR $err from handleSingleConnection on sideA $sideA ' );
512
- }));
513
-
514
- if (verbose) {
515
- logSink.writeln ('Creating socket #${++connections } to the "B" side' );
516
- }
517
- // connect to the side 'B' address and port
518
- Socket sideBSocket = await Socket .connect (addressB, portB);
519
- if (verbose) {
520
- logSink.writeln ('"B" side socket #$connections created' );
521
- }
522
- Side sideB = Side (sideBSocket, false , transformer: transformBtoA);
523
- if (verbose) {
524
- logSink.writeln ('Calling the beforeJoining callback' );
531
+ try {
532
+ // It's important we handle these in sequence with no chance for race
533
+ // So we're going to use a mutex
534
+ await m.acquire ();
535
+ Side sideA = Side (sideASocket, true , transformer: transformAtoB);
536
+ unawaited (connector.handleSingleConnection (sideA).catchError ((err) {
537
+ logSink.writeln (
538
+ 'ERROR $err from handleSingleConnection on sideA $sideA ' );
539
+ }));
540
+
541
+ if (verbose) {
542
+ logSink.writeln ('Creating socket #${++connections } to the "B" side' );
543
+ }
544
+ // connect to the side 'B' address and port
545
+ Socket sideBSocket = await Socket .connect (addressB, portB);
546
+ if (verbose) {
547
+ logSink.writeln ('"B" side socket #$connections created' );
548
+ }
549
+ Side sideB = Side (sideBSocket, false , transformer: transformBtoA);
550
+ if (verbose) {
551
+ logSink.writeln ('Calling the beforeJoining callback' );
552
+ }
553
+ await beforeJoining? .call (sideA, sideB);
554
+ unawaited (connector.handleSingleConnection (sideB).catchError ((err) {
555
+ logSink.writeln (
556
+ 'ERROR $err from handleSingleConnection on sideB $sideB ' );
557
+ }));
558
+
559
+ onConnect? .call (sideASocket, sideBSocket);
560
+ } finally {
561
+ m.release ();
525
562
}
526
- await beforeJoining? .call (sideA, sideB);
527
- unawaited (connector.handleSingleConnection (sideB).catchError ((err) {
528
- logSink
529
- .writeln ('ERROR $err from handleSingleConnection on sideB $sideB ' );
530
- }));
531
-
532
- onConnect? .call (sideASocket, sideBSocket);
533
563
});
534
564
535
565
// listen on the local port and connect the inbound socket
0 commit comments