@@ -20,6 +20,8 @@ open class NWWebSocket: WebSocketConnection {
20
20
return options
21
21
}
22
22
23
+ private let errorWhileWaitingLimit = 20
24
+
23
25
// MARK: - Private properties
24
26
25
27
private var connection : NWConnection ?
@@ -28,6 +30,8 @@ open class NWWebSocket: WebSocketConnection {
28
30
private let connectionQueue : DispatchQueue
29
31
private var pingTimer : Timer ?
30
32
private var disconnectionWorkItem : DispatchWorkItem ?
33
+ private var isMigratingConnection = false
34
+ private var errorWhileWaitingCount = 0
31
35
32
36
// MARK: - Initialization
33
37
@@ -77,6 +81,11 @@ open class NWWebSocket: WebSocketConnection {
77
81
}
78
82
}
79
83
84
+ deinit {
85
+ connection? . intentionalDisconnection = true
86
+ connection? . cancel ( )
87
+ }
88
+
80
89
// MARK: - WebSocketConnection conformance
81
90
82
91
/// Connect to the WebSocket.
@@ -94,6 +103,8 @@ open class NWWebSocket: WebSocketConnection {
94
103
}
95
104
listen ( )
96
105
connection? . start ( queue: connectionQueue)
106
+ } else if connection? . state != . ready && !isMigratingConnection {
107
+ connection? . start ( queue: connectionQueue)
97
108
}
98
109
}
99
110
@@ -189,8 +200,12 @@ open class NWWebSocket: WebSocketConnection {
189
200
let context = NWConnection . ContentContext ( identifier: " closeContext " ,
190
201
metadata: [ metadata] )
191
202
192
- // See implementation of `send(data:context:)` for `scheduleDisconnection(closeCode:, reason:)`
193
- send ( data: nil , context: context)
203
+ if connection? . state == . ready {
204
+ // See implementation of `send(data:context:)` for `scheduleDisconnection(closeCode:, reason:)`
205
+ send ( data: nil , context: context)
206
+ } else {
207
+ scheduleDisconnectionReporting ( closeCode: closeCode, reason: nil )
208
+ }
194
209
}
195
210
}
196
211
@@ -203,14 +218,26 @@ open class NWWebSocket: WebSocketConnection {
203
218
private func stateDidChange( to state: NWConnection . State ) {
204
219
switch state {
205
220
case . ready:
221
+ isMigratingConnection = false
206
222
delegate? . webSocketDidConnect ( connection: self )
207
223
case . waiting( let error) :
224
+ isMigratingConnection = false
208
225
reportErrorOrDisconnection ( error)
226
+
227
+ /// Workaround to prevent loop while reconnecting
228
+ errorWhileWaitingCount += 1
229
+ if errorWhileWaitingCount >= errorWhileWaitingLimit {
230
+ tearDownConnection ( error: error)
231
+ errorWhileWaitingCount = 0
232
+ }
209
233
case . failed( let error) :
234
+ errorWhileWaitingCount = 0
235
+ isMigratingConnection = false
210
236
tearDownConnection ( error: error)
211
237
case . setup, . preparing:
212
238
break
213
239
case . cancelled:
240
+ errorWhileWaitingCount = 0
214
241
tearDownConnection ( error: nil )
215
242
@unknown default :
216
243
fatalError ( )
@@ -243,35 +270,22 @@ open class NWWebSocket: WebSocketConnection {
243
270
/// - Parameter completionHandler: Returns a `Result`with the new connection if the migration was successful
244
271
/// or a `NWError` if the migration failed for some reason.
245
272
private func migrateConnection( completionHandler: @escaping ( Result < WebSocketConnection , NWError > ) -> Void ) {
246
-
247
- let migratedConnection = NWConnection ( to: endpoint, using: parameters)
248
- migratedConnection. stateUpdateHandler = { [ weak self] state in
249
- guard let self = self else {
250
- return
251
- }
252
-
253
- switch state {
254
- case . ready:
255
- self . connection = nil
256
- migratedConnection. stateUpdateHandler = self . stateDidChange ( to: )
257
- migratedConnection. betterPathUpdateHandler = self . betterPath ( isAvailable: )
258
- migratedConnection. viabilityUpdateHandler = self . viabilityDidChange ( isViable: )
259
- self . connection = migratedConnection
260
- self . listen ( )
261
- completionHandler ( . success( self ) )
262
- case . waiting( let error) :
263
- completionHandler ( . failure( error) )
264
- case . failed( let error) :
265
- completionHandler ( . failure( error) )
266
- case . setup, . preparing:
267
- break
268
- case . cancelled:
269
- completionHandler ( . failure( . posix( . ECANCELED) ) )
270
- @unknown default :
271
- fatalError ( )
272
- }
273
+ guard !isMigratingConnection else { return }
274
+ connection? . intentionalDisconnection = true
275
+ connection? . cancel ( )
276
+ isMigratingConnection = true
277
+ connection = NWConnection ( to: endpoint, using: parameters)
278
+ connection? . stateUpdateHandler = { [ weak self] state in
279
+ self ? . stateDidChange ( to: state)
280
+ }
281
+ connection? . betterPathUpdateHandler = { [ weak self] isAvailable in
282
+ self ? . betterPath ( isAvailable: isAvailable)
273
283
}
274
- migratedConnection. start ( queue: connectionQueue)
284
+ connection? . viabilityUpdateHandler = { [ weak self] isViable in
285
+ self ? . viabilityDidChange ( isViable: isViable)
286
+ }
287
+ listen ( )
288
+ connection? . start ( queue: connectionQueue)
275
289
}
276
290
277
291
// MARK: Connection data transfer
@@ -321,21 +335,21 @@ open class NWWebSocket: WebSocketConnection {
321
335
contentContext: context,
322
336
isComplete: true ,
323
337
completion: . contentProcessed( { [ weak self] error in
324
- guard let self = self else {
325
- return
326
- }
327
-
328
- // If a connection closure was sent, inform delegate on completion
329
- if let socketMetadata = context. protocolMetadata. first as? NWProtocolWebSocket . Metadata ,
330
- socketMetadata. opcode == . close {
331
- self . scheduleDisconnectionReporting ( closeCode: socketMetadata. closeCode,
332
- reason: data)
333
- }
334
-
335
- if let error = error {
336
- self . reportErrorOrDisconnection ( error)
337
- }
338
- } ) )
338
+ guard let self = self else {
339
+ return
340
+ }
341
+
342
+ // If a connection closure was sent, inform delegate on completion
343
+ if let socketMetadata = context. protocolMetadata. first as? NWProtocolWebSocket . Metadata ,
344
+ socketMetadata. opcode == . close {
345
+ self . scheduleDisconnectionReporting ( closeCode: socketMetadata. closeCode,
346
+ reason: data)
347
+ }
348
+
349
+ if let error = error {
350
+ self . reportErrorOrDisconnection ( error)
351
+ }
352
+ } ) )
339
353
}
340
354
341
355
// MARK: Connection cleanup
@@ -369,6 +383,7 @@ open class NWWebSocket: WebSocketConnection {
369
383
delegate? . webSocketDidReceiveError ( connection: self , error: error)
370
384
}
371
385
pingTimer? . invalidate ( )
386
+ connection? . cancel ( )
372
387
connection = nil
373
388
374
389
if let disconnectionWorkItem = disconnectionWorkItem {
0 commit comments