@@ -3,6 +3,8 @@ package peer
3
3
import (
4
4
"bytes"
5
5
"context"
6
+ "fmt"
7
+
6
8
"crypto/rand"
7
9
"crypto/sha256"
8
10
"io"
@@ -69,6 +71,8 @@ func newWithClientOrServer(servers []webrtc.ICEServer, client bool, opts *ConnOp
69
71
rtc : rtc ,
70
72
offerrer : client ,
71
73
closed : make (chan struct {}),
74
+ closedRTC : make (chan struct {}),
75
+ closedICE : make (chan struct {}),
72
76
dcOpenChannel : make (chan * webrtc.DataChannel ),
73
77
dcDisconnectChannel : make (chan struct {}),
74
78
dcFailedChannel : make (chan struct {}),
@@ -109,6 +113,8 @@ type Conn struct {
109
113
offerrer bool
110
114
111
115
closed chan struct {}
116
+ closedRTC chan struct {}
117
+ closedICE chan struct {}
112
118
closeMutex sync.Mutex
113
119
closeError error
114
120
@@ -142,26 +148,22 @@ type Conn struct {
142
148
func (c * Conn ) init () error {
143
149
c .rtc .OnNegotiationNeeded (c .negotiate )
144
150
c .rtc .OnICEConnectionStateChange (func (iceConnectionState webrtc.ICEConnectionState ) {
145
- // Close must be locked here otherwise log output can appear
146
- // after the connection has been closed.
147
- c .closeMutex .Lock ()
148
- defer c .closeMutex .Unlock ()
149
- if c .isClosed () {
150
- return
151
- }
152
-
153
151
c .opts .Logger .Debug (context .Background (), "ice connection state updated" ,
154
152
slog .F ("state" , iceConnectionState ))
153
+
154
+ if iceConnectionState == webrtc .ICEConnectionStateClosed {
155
+ select {
156
+ case <- c .closedICE :
157
+ default :
158
+ close (c .closedICE )
159
+ }
160
+ }
161
+ })
162
+ c .rtc .OnSignalingStateChange (func (signalState webrtc.SignalingState ) {
163
+ c .opts .Logger .Debug (context .Background (), "signal state updated" ,
164
+ slog .F ("state" , signalState ))
155
165
})
156
166
c .rtc .OnICEGatheringStateChange (func (iceGatherState webrtc.ICEGathererState ) {
157
- // Close can't be locked here, because this is triggered
158
- // when close is called. It doesn't appear this get's
159
- // executed after close though, so it shouldn't cause
160
- // problems.
161
- if c .isClosed () {
162
- return
163
- }
164
-
165
167
c .opts .Logger .Debug (context .Background (), "ice gathering state updated" ,
166
168
slog .F ("state" , iceGatherState ))
167
169
})
@@ -171,7 +173,7 @@ func (c *Conn) init() error {
171
173
}
172
174
json := iceCandidate .ToJSON ()
173
175
c .opts .Logger .Debug (context .Background (), "writing candidate to channel" ,
174
- slog .F ("hash" , sha256 . Sum224 ([] byte ( json . Candidate ) )),
176
+ slog .F ("hash" , c . hashCandidate ( json )),
175
177
slog .F ("length" , len (json .Candidate )),
176
178
)
177
179
select {
@@ -188,19 +190,11 @@ func (c *Conn) init() error {
188
190
default :
189
191
}
190
192
})
191
- c .rtc .OnConnectionStateChange (func (pcs webrtc.PeerConnectionState ) {
192
- // Close must be locked here otherwise log output can appear
193
- // after the connection has been closed.
194
- c .closeMutex .Lock ()
195
- defer c .closeMutex .Unlock ()
196
- if c .isClosed () {
197
- return
198
- }
199
-
193
+ c .rtc .OnConnectionStateChange (func (peerConnectionState webrtc.PeerConnectionState ) {
200
194
c .opts .Logger .Debug (context .Background (), "rtc connection updated" ,
201
- slog .F ("state" , pcs ))
195
+ slog .F ("state" , peerConnectionState ))
202
196
203
- switch pcs {
197
+ switch peerConnectionState {
204
198
case webrtc .PeerConnectionStateDisconnected :
205
199
for i := 0 ; i < int (c .dcDisconnectListeners .Load ()); i ++ {
206
200
select {
@@ -216,6 +210,20 @@ func (c *Conn) init() error {
216
210
}
217
211
}
218
212
}
213
+
214
+ if peerConnectionState == webrtc .PeerConnectionStateClosed {
215
+ // Pion executes event handlers after close is called
216
+ // on the RTC connection. This ensures our Close()
217
+ // handler properly cleans up before returning.
218
+ //
219
+ // Pion can execute this multiple times, so we check
220
+ // if it's open before closing.
221
+ select {
222
+ case <- c .closedRTC :
223
+ default :
224
+ close (c .closedRTC )
225
+ }
226
+ }
219
227
})
220
228
_ , err := c .pingChannel ()
221
229
if err != nil {
@@ -275,17 +283,30 @@ func (c *Conn) pingEchoChannel() (*Channel, error) {
275
283
return c .pingEchoChan , c .pingEchoError
276
284
}
277
285
286
+ // Computes a hash of the ICE candidate. Used for debug logging.
287
+ func (* Conn ) hashCandidate (iceCandidate webrtc.ICECandidateInit ) string {
288
+ hash := sha256 .Sum224 ([]byte (iceCandidate .Candidate ))
289
+ return fmt .Sprintf ("%x" , hash [:8 ])
290
+ }
291
+
278
292
func (c * Conn ) negotiate () {
279
293
c .opts .Logger .Debug (context .Background (), "negotiating" )
280
294
281
295
if c .offerrer {
296
+ c .closeMutex .Lock ()
282
297
offer , err := c .rtc .CreateOffer (& webrtc.OfferOptions {})
298
+ c .closeMutex .Unlock ()
283
299
if err != nil {
284
300
_ = c .CloseWithError (xerrors .Errorf ("create offer: %w" , err ))
285
301
return
286
302
}
287
- c .opts .Logger .Debug (context .Background (), "setting local description" )
303
+ c .opts .Logger .Debug (context .Background (), "setting local description" , slog .F ("closed" , c .isClosed ()))
304
+ if c .isClosed () {
305
+ return
306
+ }
307
+ c .closeMutex .Lock ()
288
308
err = c .rtc .SetLocalDescription (offer )
309
+ c .closeMutex .Unlock ()
289
310
if err != nil {
290
311
_ = c .CloseWithError (xerrors .Errorf ("set local description: %w" , err ))
291
312
return
@@ -305,7 +326,9 @@ func (c *Conn) negotiate() {
305
326
}
306
327
307
328
c .opts .Logger .Debug (context .Background (), "setting remote description" )
329
+ c .closeMutex .Lock ()
308
330
err := c .rtc .SetRemoteDescription (remoteDescription )
331
+ c .closeMutex .Unlock ()
309
332
if err != nil {
310
333
_ = c .CloseWithError (xerrors .Errorf ("set remote description (closed %v): %w" , c .isClosed (), err ))
311
334
return
@@ -317,15 +340,17 @@ func (c *Conn) negotiate() {
317
340
_ = c .CloseWithError (xerrors .Errorf ("create answer: %w" , err ))
318
341
return
319
342
}
320
- c .opts .Logger .Debug (context .Background (), "setting local description" )
343
+ c .opts .Logger .Debug (context .Background (), "setting local description" , slog .F ("closed" , c .isClosed ()))
344
+ if c .isClosed () {
345
+ return
346
+ }
347
+ c .closeMutex .Lock ()
321
348
err = c .rtc .SetLocalDescription (answer )
349
+ c .closeMutex .Unlock ()
322
350
if err != nil {
323
351
_ = c .CloseWithError (xerrors .Errorf ("set local description: %w" , err ))
324
352
return
325
353
}
326
- if c .isClosed () {
327
- return
328
- }
329
354
select {
330
355
case <- c .closed :
331
356
return
@@ -339,9 +364,9 @@ func (c *Conn) negotiate() {
339
364
c .pendingCandidatesMutex .Lock ()
340
365
defer c .pendingCandidatesMutex .Unlock ()
341
366
for _ , pendingCandidate := range c .pendingRemoteCandidates {
342
- hash := sha256 . Sum224 ([] byte ( pendingCandidate . Candidate ))
367
+ //nolint:gosec
343
368
c .opts .Logger .Debug (context .Background (), "flushing buffered remote candidate" ,
344
- slog .F ("hash" , hash ),
369
+ slog .F ("hash" , c . hashCandidate ( pendingCandidate ) ),
345
370
slog .F ("length" , len (pendingCandidate .Candidate )),
346
371
)
347
372
err := c .rtc .AddICECandidate (pendingCandidate )
@@ -368,7 +393,7 @@ func (c *Conn) AddRemoteCandidate(i webrtc.ICECandidateInit) error {
368
393
c .pendingCandidatesMutex .Lock ()
369
394
defer c .pendingCandidatesMutex .Unlock ()
370
395
fields := []slog.Field {
371
- slog .F ("hash" , sha256 . Sum224 ([] byte ( i . Candidate ) )),
396
+ slog .F ("hash" , c . hashCandidate ( i )),
372
397
slog .F ("length" , len (i .Candidate )),
373
398
}
374
399
if ! c .pendingCandidatesFlushed {
@@ -542,5 +567,15 @@ func (c *Conn) CloseWithError(err error) error {
542
567
// All logging, goroutines, and async functionality is cleaned up after this.
543
568
c .dcClosedWaitGroup .Wait ()
544
569
570
+ if c .rtc .ConnectionState () != webrtc .PeerConnectionStateNew {
571
+ c .opts .Logger .Debug (context .Background (), "waiting for rtc connection close..." )
572
+ <- c .closedRTC
573
+ }
574
+
575
+ if c .rtc .ICEConnectionState () != webrtc .ICEConnectionStateNew {
576
+ c .opts .Logger .Debug (context .Background (), "waiting for ice connection close..." )
577
+ <- c .closedICE
578
+ }
579
+
545
580
return err
546
581
}
0 commit comments