github.com/pion/webrtc/v4@v4.0.1/peerconnection_go_test.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 //go:build !js 5 // +build !js 6 7 package webrtc 8 9 import ( 10 "bufio" 11 "context" 12 "crypto/ecdsa" 13 "crypto/elliptic" 14 "crypto/rand" 15 "crypto/x509" 16 "fmt" 17 "math/big" 18 "reflect" 19 "regexp" 20 "strings" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/pion/ice/v4" 26 "github.com/pion/rtp" 27 "github.com/pion/transport/v3/test" 28 "github.com/pion/transport/v3/vnet" 29 "github.com/pion/webrtc/v4/internal/util" 30 "github.com/pion/webrtc/v4/pkg/rtcerr" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 // newPair creates two new peer connections (an offerer and an answerer) using 35 // the api. 36 func (api *API) newPair(cfg Configuration) (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) { 37 pca, err := api.NewPeerConnection(cfg) 38 if err != nil { 39 return nil, nil, err 40 } 41 42 pcb, err := api.NewPeerConnection(cfg) 43 if err != nil { 44 return nil, nil, err 45 } 46 47 return pca, pcb, nil 48 } 49 50 func TestNew_Go(t *testing.T) { 51 report := test.CheckRoutines(t) 52 defer report() 53 54 api := NewAPI() 55 t.Run("Success", func(t *testing.T) { 56 secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 57 assert.Nil(t, err) 58 59 certificate, err := GenerateCertificate(secretKey) 60 assert.Nil(t, err) 61 62 pc, err := api.NewPeerConnection(Configuration{ 63 ICEServers: []ICEServer{ 64 { 65 URLs: []string{ 66 "stun:stun.l.google.com:19302", 67 "turns:google.de?transport=tcp", 68 }, 69 Username: "unittest", 70 Credential: OAuthCredential{ 71 MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=", 72 AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==", 73 }, 74 CredentialType: ICECredentialTypeOauth, 75 }, 76 }, 77 ICETransportPolicy: ICETransportPolicyRelay, 78 BundlePolicy: BundlePolicyMaxCompat, 79 RTCPMuxPolicy: RTCPMuxPolicyNegotiate, 80 PeerIdentity: "unittest", 81 Certificates: []Certificate{*certificate}, 82 ICECandidatePoolSize: 5, 83 }) 84 assert.Nil(t, err) 85 assert.NotNil(t, pc) 86 assert.NoError(t, pc.Close()) 87 }) 88 t.Run("Failure", func(t *testing.T) { 89 testCases := []struct { 90 initialize func() (*PeerConnection, error) 91 expectedErr error 92 }{ 93 {func() (*PeerConnection, error) { 94 secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 95 assert.Nil(t, err) 96 97 certificate, err := NewCertificate(secretKey, x509.Certificate{ 98 Version: 2, 99 SerialNumber: big.NewInt(1653), 100 NotBefore: time.Now().AddDate(0, -2, 0), 101 NotAfter: time.Now().AddDate(0, -1, 0), 102 }) 103 assert.Nil(t, err) 104 105 return api.NewPeerConnection(Configuration{ 106 Certificates: []Certificate{*certificate}, 107 }) 108 }, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}}, 109 {func() (*PeerConnection, error) { 110 return api.NewPeerConnection(Configuration{ 111 ICEServers: []ICEServer{ 112 { 113 URLs: []string{ 114 "stun:stun.l.google.com:19302", 115 "turns:google.de?transport=tcp", 116 }, 117 Username: "unittest", 118 }, 119 }, 120 }) 121 }, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}}, 122 } 123 124 for i, testCase := range testCases { 125 pc, err := testCase.initialize() 126 assert.EqualError(t, err, testCase.expectedErr.Error(), 127 "testCase: %d %v", i, testCase, 128 ) 129 if pc != nil { 130 assert.NoError(t, pc.Close()) 131 } 132 } 133 }) 134 t.Run("ICEServers_Copy", func(t *testing.T) { 135 const expectedURL = "stun:stun.l.google.com:19302?foo=bar" 136 const expectedUsername = "username" 137 const expectedPassword = "password" 138 139 cfg := Configuration{ 140 ICEServers: []ICEServer{ 141 { 142 URLs: []string{expectedURL}, 143 Username: expectedUsername, 144 Credential: expectedPassword, 145 }, 146 }, 147 } 148 pc, err := api.NewPeerConnection(cfg) 149 assert.NoError(t, err) 150 assert.NotNil(t, pc) 151 152 pc.configuration.ICEServers[0].Username = util.MathRandAlpha(15) // Tests doesn't need crypto random 153 pc.configuration.ICEServers[0].Credential = util.MathRandAlpha(15) 154 pc.configuration.ICEServers[0].URLs[0] = util.MathRandAlpha(15) 155 156 assert.Equal(t, expectedUsername, cfg.ICEServers[0].Username) 157 assert.Equal(t, expectedPassword, cfg.ICEServers[0].Credential) 158 assert.Equal(t, expectedURL, cfg.ICEServers[0].URLs[0]) 159 160 assert.NoError(t, pc.Close()) 161 }) 162 } 163 164 func TestPeerConnection_SetConfiguration_Go(t *testing.T) { 165 // Note: this test includes all SetConfiguration features that are supported 166 // by Go but not the WASM bindings, namely: ICEServer.Credential, 167 // ICEServer.CredentialType, and Certificates. 168 report := test.CheckRoutines(t) 169 defer report() 170 171 api := NewAPI() 172 173 secretKey1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 174 assert.Nil(t, err) 175 176 certificate1, err := GenerateCertificate(secretKey1) 177 assert.Nil(t, err) 178 179 secretKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 180 assert.Nil(t, err) 181 182 certificate2, err := GenerateCertificate(secretKey2) 183 assert.Nil(t, err) 184 185 for _, test := range []struct { 186 name string 187 init func() (*PeerConnection, error) 188 config Configuration 189 wantErr error 190 }{ 191 { 192 name: "valid", 193 init: func() (*PeerConnection, error) { 194 pc, err := api.NewPeerConnection(Configuration{ 195 PeerIdentity: "unittest", 196 Certificates: []Certificate{*certificate1}, 197 ICECandidatePoolSize: 5, 198 }) 199 if err != nil { 200 return pc, err 201 } 202 203 err = pc.SetConfiguration(Configuration{ 204 ICEServers: []ICEServer{ 205 { 206 URLs: []string{ 207 "stun:stun.l.google.com:19302", 208 "turns:google.de?transport=tcp", 209 }, 210 Username: "unittest", 211 Credential: OAuthCredential{ 212 MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=", 213 AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==", 214 }, 215 CredentialType: ICECredentialTypeOauth, 216 }, 217 }, 218 ICETransportPolicy: ICETransportPolicyAll, 219 BundlePolicy: BundlePolicyBalanced, 220 RTCPMuxPolicy: RTCPMuxPolicyRequire, 221 PeerIdentity: "unittest", 222 Certificates: []Certificate{*certificate1}, 223 ICECandidatePoolSize: 5, 224 }) 225 if err != nil { 226 return pc, err 227 } 228 229 return pc, nil 230 }, 231 config: Configuration{}, 232 wantErr: nil, 233 }, 234 { 235 name: "update multiple certificates", 236 init: func() (*PeerConnection, error) { 237 return api.NewPeerConnection(Configuration{}) 238 }, 239 config: Configuration{ 240 Certificates: []Certificate{*certificate1, *certificate2}, 241 }, 242 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}, 243 }, 244 { 245 name: "update certificate", 246 init: func() (*PeerConnection, error) { 247 return api.NewPeerConnection(Configuration{}) 248 }, 249 config: Configuration{ 250 Certificates: []Certificate{*certificate1}, 251 }, 252 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}, 253 }, 254 { 255 name: "update ICEServers, no TURN credentials", 256 init: func() (*PeerConnection, error) { 257 return NewPeerConnection(Configuration{}) 258 }, 259 config: Configuration{ 260 ICEServers: []ICEServer{ 261 { 262 URLs: []string{ 263 "stun:stun.l.google.com:19302", 264 "turns:google.de?transport=tcp", 265 }, 266 Username: "unittest", 267 }, 268 }, 269 }, 270 wantErr: &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}, 271 }, 272 } { 273 pc, err := test.init() 274 if err != nil { 275 t.Errorf("SetConfiguration %q: init failed: %v", test.name, err) 276 } 277 278 err = pc.SetConfiguration(test.config) 279 if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) { 280 t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want) 281 } 282 283 assert.NoError(t, pc.Close()) 284 } 285 } 286 287 func TestPeerConnection_EventHandlers_Go(t *testing.T) { 288 lim := test.TimeOut(time.Second * 5) 289 defer lim.Stop() 290 291 report := test.CheckRoutines(t) 292 defer report() 293 294 // Note: When testing the Go event handlers we peer into the state a bit more 295 // than what is possible for the environment agnostic (Go or WASM/JavaScript) 296 // EventHandlers test. 297 api := NewAPI() 298 pc, err := api.NewPeerConnection(Configuration{}) 299 assert.Nil(t, err) 300 301 onTrackCalled := make(chan struct{}) 302 onICEConnectionStateChangeCalled := make(chan struct{}) 303 onDataChannelCalled := make(chan struct{}) 304 305 // Verify that the noop case works 306 assert.NotPanics(t, func() { pc.onTrack(nil, nil) }) 307 assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ICEConnectionStateNew) }) 308 309 pc.OnTrack(func(*TrackRemote, *RTPReceiver) { 310 close(onTrackCalled) 311 }) 312 313 pc.OnICEConnectionStateChange(func(ICEConnectionState) { 314 close(onICEConnectionStateChangeCalled) 315 }) 316 317 pc.OnDataChannel(func(dc *DataChannel) { 318 // Questions: 319 // (1) How come this callback is made with dc being nil? 320 // (2) How come this callback is made without CreateDataChannel? 321 if dc != nil { 322 close(onDataChannelCalled) 323 } 324 }) 325 326 // Verify that the handlers deal with nil inputs 327 assert.NotPanics(t, func() { pc.onTrack(nil, nil) }) 328 assert.NotPanics(t, func() { go pc.onDataChannelHandler(nil) }) 329 330 // Verify that the set handlers are called 331 assert.NotPanics(t, func() { pc.onTrack(&TrackRemote{}, &RTPReceiver{}) }) 332 assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ICEConnectionStateNew) }) 333 assert.NotPanics(t, func() { go pc.onDataChannelHandler(&DataChannel{api: api}) }) 334 335 <-onTrackCalled 336 <-onICEConnectionStateChangeCalled 337 <-onDataChannelCalled 338 assert.NoError(t, pc.Close()) 339 } 340 341 // This test asserts that nothing deadlocks we try to shutdown when DTLS is in flight 342 // We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost 343 func TestPeerConnection_ShutdownNoDTLS(t *testing.T) { 344 lim := test.TimeOut(time.Second * 10) 345 defer lim.Stop() 346 347 report := test.CheckRoutines(t) 348 defer report() 349 350 api := NewAPI() 351 offerPC, answerPC, err := api.newPair(Configuration{}) 352 if err != nil { 353 t.Fatal(err) 354 } 355 356 // Drop all incoming DTLS traffic 357 dropAllDTLS := func([]byte) bool { 358 return false 359 } 360 offerPC.dtlsTransport.dtlsMatcher = dropAllDTLS 361 answerPC.dtlsTransport.dtlsMatcher = dropAllDTLS 362 363 if err = signalPair(offerPC, answerPC); err != nil { 364 t.Fatal(err) 365 } 366 367 iceComplete := make(chan interface{}) 368 answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) { 369 if iceState == ICEConnectionStateConnected { 370 time.Sleep(time.Second) // Give time for DTLS to start 371 372 select { 373 case <-iceComplete: 374 default: 375 close(iceComplete) 376 } 377 } 378 }) 379 380 <-iceComplete 381 closePairNow(t, offerPC, answerPC) 382 } 383 384 func TestPeerConnection_PropertyGetters(t *testing.T) { 385 pc := &PeerConnection{ 386 currentLocalDescription: &SessionDescription{}, 387 pendingLocalDescription: &SessionDescription{}, 388 currentRemoteDescription: &SessionDescription{}, 389 pendingRemoteDescription: &SessionDescription{}, 390 signalingState: SignalingStateHaveLocalOffer, 391 } 392 pc.iceConnectionState.Store(ICEConnectionStateChecking) 393 pc.connectionState.Store(PeerConnectionStateConnecting) 394 395 assert.Equal(t, pc.currentLocalDescription, pc.CurrentLocalDescription(), "should match") 396 assert.Equal(t, pc.pendingLocalDescription, pc.PendingLocalDescription(), "should match") 397 assert.Equal(t, pc.currentRemoteDescription, pc.CurrentRemoteDescription(), "should match") 398 assert.Equal(t, pc.pendingRemoteDescription, pc.PendingRemoteDescription(), "should match") 399 assert.Equal(t, pc.signalingState, pc.SignalingState(), "should match") 400 assert.Equal(t, pc.iceConnectionState.Load(), pc.ICEConnectionState(), "should match") 401 assert.Equal(t, pc.connectionState.Load(), pc.ConnectionState(), "should match") 402 } 403 404 func TestPeerConnection_AnswerWithoutOffer(t *testing.T) { 405 report := test.CheckRoutines(t) 406 defer report() 407 408 pc, err := NewPeerConnection(Configuration{}) 409 if err != nil { 410 t.Errorf("New PeerConnection: got error: %v", err) 411 } 412 _, err = pc.CreateAnswer(nil) 413 if !reflect.DeepEqual(&rtcerr.InvalidStateError{Err: ErrNoRemoteDescription}, err) { 414 t.Errorf("CreateAnswer without RemoteDescription: got error: %v", err) 415 } 416 417 assert.NoError(t, pc.Close()) 418 } 419 420 func TestPeerConnection_AnswerWithClosedConnection(t *testing.T) { 421 report := test.CheckRoutines(t) 422 defer report() 423 424 offerPeerConn, answerPeerConn, err := newPair() 425 assert.NoError(t, err) 426 427 inChecking, inCheckingCancel := context.WithCancel(context.Background()) 428 answerPeerConn.OnICEConnectionStateChange(func(i ICEConnectionState) { 429 if i == ICEConnectionStateChecking { 430 inCheckingCancel() 431 } 432 }) 433 434 _, err = offerPeerConn.CreateDataChannel("test-channel", nil) 435 assert.NoError(t, err) 436 437 offer, err := offerPeerConn.CreateOffer(nil) 438 assert.NoError(t, err) 439 assert.NoError(t, offerPeerConn.SetLocalDescription(offer)) 440 441 assert.NoError(t, offerPeerConn.Close()) 442 443 assert.NoError(t, answerPeerConn.SetRemoteDescription(offer)) 444 445 <-inChecking.Done() 446 assert.NoError(t, answerPeerConn.Close()) 447 448 _, err = answerPeerConn.CreateAnswer(nil) 449 assert.Equal(t, err, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}) 450 } 451 452 func TestPeerConnection_satisfyTypeAndDirection(t *testing.T) { 453 createTransceiver := func(kind RTPCodecType, direction RTPTransceiverDirection) *RTPTransceiver { 454 r := &RTPTransceiver{kind: kind} 455 r.setDirection(direction) 456 457 return r 458 } 459 460 for _, test := range []struct { 461 name string 462 463 kinds []RTPCodecType 464 directions []RTPTransceiverDirection 465 466 localTransceivers []*RTPTransceiver 467 want []*RTPTransceiver 468 }{ 469 { 470 "Audio and Video Transceivers can not satisfy each other", 471 []RTPCodecType{RTPCodecTypeVideo}, 472 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 473 []*RTPTransceiver{createTransceiver(RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv)}, 474 []*RTPTransceiver{nil}, 475 }, 476 { 477 "No local Transceivers, every remote should get nil", 478 []RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeAudio, RTPCodecTypeVideo, RTPCodecTypeVideo}, 479 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly, RTPTransceiverDirectionInactive}, 480 481 []*RTPTransceiver{}, 482 483 []*RTPTransceiver{ 484 nil, 485 nil, 486 nil, 487 nil, 488 }, 489 }, 490 { 491 "Local Recv can satisfy remote SendRecv", 492 []RTPCodecType{RTPCodecTypeVideo}, 493 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 494 495 []*RTPTransceiver{createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly)}, 496 497 []*RTPTransceiver{createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly)}, 498 }, 499 { 500 "Don't satisfy a Sendonly with a SendRecv, later SendRecv will be marked as Inactive", 501 []RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeVideo}, 502 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv}, 503 504 []*RTPTransceiver{ 505 createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionSendrecv), 506 createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly), 507 }, 508 509 []*RTPTransceiver{ 510 createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly), 511 createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionSendrecv), 512 }, 513 }, 514 } { 515 if len(test.kinds) != len(test.directions) { 516 t.Fatal("Kinds and Directions must be the same length") 517 } 518 519 got := []*RTPTransceiver{} 520 for i := range test.kinds { 521 res, filteredLocalTransceivers := satisfyTypeAndDirection(test.kinds[i], test.directions[i], test.localTransceivers) 522 523 got = append(got, res) 524 test.localTransceivers = filteredLocalTransceivers 525 } 526 527 if !reflect.DeepEqual(got, test.want) { 528 gotStr := "" 529 for _, t := range got { 530 gotStr += fmt.Sprintf("%+v\n", t) 531 } 532 533 wantStr := "" 534 for _, t := range test.want { 535 wantStr += fmt.Sprintf("%+v\n", t) 536 } 537 t.Errorf("satisfyTypeAndDirection %q: \ngot\n%s \nwant\n%s", test.name, gotStr, wantStr) 538 } 539 } 540 } 541 542 func TestOneAttrKeyConnectionSetupPerMediaDescriptionInSDP(t *testing.T) { 543 pc, err := NewPeerConnection(Configuration{}) 544 assert.NoError(t, err) 545 546 _, err = pc.AddTransceiverFromKind(RTPCodecTypeVideo) 547 assert.NoError(t, err) 548 549 _, err = pc.AddTransceiverFromKind(RTPCodecTypeAudio) 550 assert.NoError(t, err) 551 552 _, err = pc.AddTransceiverFromKind(RTPCodecTypeAudio) 553 assert.NoError(t, err) 554 555 _, err = pc.AddTransceiverFromKind(RTPCodecTypeVideo) 556 assert.NoError(t, err) 557 558 sdp, err := pc.CreateOffer(nil) 559 assert.NoError(t, err) 560 561 re := regexp.MustCompile(`a=setup:[[:alpha:]]+`) 562 563 matches := re.FindAllStringIndex(sdp.SDP, -1) 564 565 assert.Len(t, matches, 4) 566 assert.NoError(t, pc.Close()) 567 } 568 569 func TestPeerConnection_IceLite(t *testing.T) { 570 report := test.CheckRoutines(t) 571 defer report() 572 573 lim := test.TimeOut(time.Second * 10) 574 defer lim.Stop() 575 576 connectTwoAgents := func(offerIsLite, answerisLite bool) { 577 offerSettingEngine := SettingEngine{} 578 offerSettingEngine.SetLite(offerIsLite) 579 offerPC, err := NewAPI(WithSettingEngine(offerSettingEngine)).NewPeerConnection(Configuration{}) 580 if err != nil { 581 t.Fatal(err) 582 } 583 584 answerSettingEngine := SettingEngine{} 585 answerSettingEngine.SetLite(answerisLite) 586 answerPC, err := NewAPI(WithSettingEngine(answerSettingEngine)).NewPeerConnection(Configuration{}) 587 if err != nil { 588 t.Fatal(err) 589 } 590 591 if err = signalPair(offerPC, answerPC); err != nil { 592 t.Fatal(err) 593 } 594 595 dataChannelOpen := make(chan interface{}) 596 answerPC.OnDataChannel(func(_ *DataChannel) { 597 close(dataChannelOpen) 598 }) 599 600 <-dataChannelOpen 601 closePairNow(t, offerPC, answerPC) 602 } 603 604 t.Run("Offerer", func(*testing.T) { 605 connectTwoAgents(true, false) 606 }) 607 608 t.Run("Answerer", func(*testing.T) { 609 connectTwoAgents(false, true) 610 }) 611 612 t.Run("Both", func(*testing.T) { 613 connectTwoAgents(true, true) 614 }) 615 } 616 617 func TestOnICEGatheringStateChange(t *testing.T) { 618 seenGathering := &atomicBool{} 619 seenComplete := &atomicBool{} 620 621 seenGatheringAndComplete := make(chan interface{}) 622 623 peerConn, err := NewPeerConnection(Configuration{}) 624 assert.NoError(t, err) 625 626 var onStateChange func(s ICEGatheringState) 627 onStateChange = func(s ICEGatheringState) { 628 // Access to ICEGatherer in the callback must not cause dead lock. 629 peerConn.OnICEGatheringStateChange(onStateChange) 630 631 switch s { // nolint:exhaustive 632 case ICEGatheringStateGathering: 633 if seenComplete.get() { 634 t.Error("Completed before gathering") 635 } 636 seenGathering.set(true) 637 case ICEGatheringStateComplete: 638 seenComplete.set(true) 639 } 640 641 if seenGathering.get() && seenComplete.get() { 642 close(seenGatheringAndComplete) 643 } 644 } 645 peerConn.OnICEGatheringStateChange(onStateChange) 646 647 offer, err := peerConn.CreateOffer(nil) 648 assert.NoError(t, err) 649 assert.NoError(t, peerConn.SetLocalDescription(offer)) 650 651 select { 652 case <-time.After(time.Second * 10): 653 t.Fatal("Gathering and Complete were never seen") 654 case <-seenGatheringAndComplete: 655 } 656 657 assert.NoError(t, peerConn.Close()) 658 } 659 660 // Assert Trickle ICE behaviors 661 func TestPeerConnectionTrickle(t *testing.T) { 662 offerPC, answerPC, err := newPair() 663 assert.NoError(t, err) 664 665 _, err = offerPC.CreateDataChannel("test-channel", nil) 666 assert.NoError(t, err) 667 668 addOrCacheCandidate := func(pc *PeerConnection, c *ICECandidate, candidateCache []ICECandidateInit) []ICECandidateInit { 669 if c == nil { 670 return candidateCache 671 } 672 673 if pc.RemoteDescription() == nil { 674 return append(candidateCache, c.ToJSON()) 675 } 676 677 assert.NoError(t, pc.AddICECandidate(c.ToJSON())) 678 return candidateCache 679 } 680 681 candidateLock := sync.RWMutex{} 682 var offerCandidateDone, answerCandidateDone bool 683 684 cachedOfferCandidates := []ICECandidateInit{} 685 offerPC.OnICECandidate(func(c *ICECandidate) { 686 if offerCandidateDone { 687 t.Error("Received OnICECandidate after finishing gathering") 688 } 689 if c == nil { 690 offerCandidateDone = true 691 } 692 693 candidateLock.Lock() 694 defer candidateLock.Unlock() 695 696 cachedOfferCandidates = addOrCacheCandidate(answerPC, c, cachedOfferCandidates) 697 }) 698 699 cachedAnswerCandidates := []ICECandidateInit{} 700 answerPC.OnICECandidate(func(c *ICECandidate) { 701 if answerCandidateDone { 702 t.Error("Received OnICECandidate after finishing gathering") 703 } 704 if c == nil { 705 answerCandidateDone = true 706 } 707 708 candidateLock.Lock() 709 defer candidateLock.Unlock() 710 711 cachedAnswerCandidates = addOrCacheCandidate(offerPC, c, cachedAnswerCandidates) 712 }) 713 714 offerPCConnected, offerPCConnectedCancel := context.WithCancel(context.Background()) 715 offerPC.OnICEConnectionStateChange(func(i ICEConnectionState) { 716 if i == ICEConnectionStateConnected { 717 offerPCConnectedCancel() 718 } 719 }) 720 721 answerPCConnected, answerPCConnectedCancel := context.WithCancel(context.Background()) 722 answerPC.OnICEConnectionStateChange(func(i ICEConnectionState) { 723 if i == ICEConnectionStateConnected { 724 answerPCConnectedCancel() 725 } 726 }) 727 728 offer, err := offerPC.CreateOffer(nil) 729 assert.NoError(t, err) 730 731 assert.NoError(t, offerPC.SetLocalDescription(offer)) 732 assert.NoError(t, answerPC.SetRemoteDescription(offer)) 733 734 answer, err := answerPC.CreateAnswer(nil) 735 assert.NoError(t, err) 736 737 assert.NoError(t, answerPC.SetLocalDescription(answer)) 738 assert.NoError(t, offerPC.SetRemoteDescription(answer)) 739 740 candidateLock.Lock() 741 for _, c := range cachedAnswerCandidates { 742 assert.NoError(t, offerPC.AddICECandidate(c)) 743 } 744 for _, c := range cachedOfferCandidates { 745 assert.NoError(t, answerPC.AddICECandidate(c)) 746 } 747 candidateLock.Unlock() 748 749 <-answerPCConnected.Done() 750 <-offerPCConnected.Done() 751 closePairNow(t, offerPC, answerPC) 752 } 753 754 // Issue #1121, assert populateLocalCandidates doesn't mutate 755 func TestPopulateLocalCandidates(t *testing.T) { 756 t.Run("PendingLocalDescription shouldn't add extra mutations", func(t *testing.T) { 757 pc, err := NewPeerConnection(Configuration{}) 758 assert.NoError(t, err) 759 760 offer, err := pc.CreateOffer(nil) 761 assert.NoError(t, err) 762 763 offerGatheringComplete := GatheringCompletePromise(pc) 764 assert.NoError(t, pc.SetLocalDescription(offer)) 765 <-offerGatheringComplete 766 767 assert.Equal(t, pc.PendingLocalDescription(), pc.PendingLocalDescription()) 768 assert.NoError(t, pc.Close()) 769 }) 770 771 t.Run("end-of-candidates only when gathering is complete", func(t *testing.T) { 772 pc, err := NewAPI().NewPeerConnection(Configuration{}) 773 assert.NoError(t, err) 774 775 _, err = pc.CreateDataChannel("test-channel", nil) 776 assert.NoError(t, err) 777 778 offer, err := pc.CreateOffer(nil) 779 assert.NoError(t, err) 780 assert.NotContains(t, offer.SDP, "a=candidate") 781 assert.NotContains(t, offer.SDP, "a=end-of-candidates") 782 783 offerGatheringComplete := GatheringCompletePromise(pc) 784 assert.NoError(t, pc.SetLocalDescription(offer)) 785 <-offerGatheringComplete 786 787 assert.Contains(t, pc.PendingLocalDescription().SDP, "a=candidate") 788 assert.Contains(t, pc.PendingLocalDescription().SDP, "a=end-of-candidates") 789 790 assert.NoError(t, pc.Close()) 791 }) 792 } 793 794 // Assert that two agents that only generate mDNS candidates can connect 795 func TestMulticastDNSCandidates(t *testing.T) { 796 lim := test.TimeOut(time.Second * 30) 797 defer lim.Stop() 798 799 report := test.CheckRoutines(t) 800 defer report() 801 802 s := SettingEngine{} 803 s.SetICEMulticastDNSMode(ice.MulticastDNSModeQueryAndGather) 804 805 pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{}) 806 assert.NoError(t, err) 807 808 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 809 810 onDataChannel, onDataChannelCancel := context.WithCancel(context.Background()) 811 pcAnswer.OnDataChannel(func(*DataChannel) { 812 onDataChannelCancel() 813 }) 814 <-onDataChannel.Done() 815 816 closePairNow(t, pcOffer, pcAnswer) 817 } 818 819 func TestICERestart(t *testing.T) { 820 extractCandidates := func(sdp string) (candidates []string) { 821 sc := bufio.NewScanner(strings.NewReader(sdp)) 822 for sc.Scan() { 823 if strings.HasPrefix(sc.Text(), "a=candidate:") { 824 candidates = append(candidates, sc.Text()) 825 } 826 } 827 828 return 829 } 830 831 lim := test.TimeOut(time.Second * 30) 832 defer lim.Stop() 833 834 report := test.CheckRoutines(t) 835 defer report() 836 837 offerPC, answerPC, err := newPair() 838 assert.NoError(t, err) 839 840 var connectedWaitGroup sync.WaitGroup 841 connectedWaitGroup.Add(2) 842 843 offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) { 844 if state == ICEConnectionStateConnected { 845 connectedWaitGroup.Done() 846 } 847 }) 848 answerPC.OnICEConnectionStateChange(func(state ICEConnectionState) { 849 if state == ICEConnectionStateConnected { 850 connectedWaitGroup.Done() 851 } 852 }) 853 854 // Connect two PeerConnections and block until ICEConnectionStateConnected 855 assert.NoError(t, signalPair(offerPC, answerPC)) 856 connectedWaitGroup.Wait() 857 858 // Store candidates from first Offer/Answer, compare later to make sure we re-gathered 859 firstOfferCandidates := extractCandidates(offerPC.LocalDescription().SDP) 860 firstAnswerCandidates := extractCandidates(answerPC.LocalDescription().SDP) 861 862 // Use Trickle ICE for ICE Restart 863 offerPC.OnICECandidate(func(c *ICECandidate) { 864 if c != nil { 865 assert.NoError(t, answerPC.AddICECandidate(c.ToJSON())) 866 } 867 }) 868 869 answerPC.OnICECandidate(func(c *ICECandidate) { 870 if c != nil { 871 assert.NoError(t, offerPC.AddICECandidate(c.ToJSON())) 872 } 873 }) 874 875 // Re-signal with ICE Restart, block until ICEConnectionStateConnected 876 connectedWaitGroup.Add(2) 877 offer, err := offerPC.CreateOffer(&OfferOptions{ICERestart: true}) 878 assert.NoError(t, err) 879 880 assert.NoError(t, offerPC.SetLocalDescription(offer)) 881 assert.NoError(t, answerPC.SetRemoteDescription(offer)) 882 883 answer, err := answerPC.CreateAnswer(nil) 884 assert.NoError(t, err) 885 886 assert.NoError(t, answerPC.SetLocalDescription(answer)) 887 assert.NoError(t, offerPC.SetRemoteDescription(answer)) 888 889 // Block until we have connected again 890 connectedWaitGroup.Wait() 891 892 // Compare ICE Candidates across each run, fail if they haven't changed 893 assert.NotEqual(t, firstOfferCandidates, extractCandidates(offerPC.LocalDescription().SDP)) 894 assert.NotEqual(t, firstAnswerCandidates, extractCandidates(answerPC.LocalDescription().SDP)) 895 closePairNow(t, offerPC, answerPC) 896 } 897 898 // Assert error handling when an Agent is restart 899 func TestICERestart_Error_Handling(t *testing.T) { 900 iceStates := make(chan ICEConnectionState, 100) 901 blockUntilICEState := func(wantedState ICEConnectionState) { 902 stateCount := 0 903 for i := range iceStates { 904 if i == wantedState { 905 stateCount++ 906 } 907 908 if stateCount == 2 { 909 return 910 } 911 } 912 } 913 914 connectWithICERestart := func(offerPeerConnection, answerPeerConnection *PeerConnection) { 915 offer, err := offerPeerConnection.CreateOffer(&OfferOptions{ICERestart: true}) 916 assert.NoError(t, err) 917 918 assert.NoError(t, offerPeerConnection.SetLocalDescription(offer)) 919 assert.NoError(t, answerPeerConnection.SetRemoteDescription(*offerPeerConnection.LocalDescription())) 920 921 answer, err := answerPeerConnection.CreateAnswer(nil) 922 assert.NoError(t, err) 923 924 assert.NoError(t, answerPeerConnection.SetLocalDescription(answer)) 925 assert.NoError(t, offerPeerConnection.SetRemoteDescription(*answerPeerConnection.LocalDescription())) 926 } 927 928 lim := test.TimeOut(time.Second * 30) 929 defer lim.Stop() 930 931 report := test.CheckRoutines(t) 932 defer report() 933 934 offerPeerConnection, answerPeerConnection, wan := createVNetPair(t, nil) 935 936 pushICEState := func(i ICEConnectionState) { iceStates <- i } 937 offerPeerConnection.OnICEConnectionStateChange(pushICEState) 938 answerPeerConnection.OnICEConnectionStateChange(pushICEState) 939 940 keepPackets := &atomicBool{} 941 keepPackets.set(true) 942 943 // Add a filter that monitors the traffic on the router 944 wan.AddChunkFilter(func(vnet.Chunk) bool { 945 return keepPackets.get() 946 }) 947 948 const testMessage = "testMessage" 949 950 d, err := answerPeerConnection.CreateDataChannel("foo", nil) 951 assert.NoError(t, err) 952 953 dataChannelMessages := make(chan string, 100) 954 d.OnMessage(func(m DataChannelMessage) { 955 dataChannelMessages <- string(m.Data) 956 }) 957 958 dataChannelAnswerer := make(chan *DataChannel) 959 offerPeerConnection.OnDataChannel(func(d *DataChannel) { 960 d.OnOpen(func() { 961 dataChannelAnswerer <- d 962 }) 963 }) 964 965 // Connect and Assert we have connected 966 assert.NoError(t, signalPair(offerPeerConnection, answerPeerConnection)) 967 blockUntilICEState(ICEConnectionStateConnected) 968 969 offerPeerConnection.OnICECandidate(func(c *ICECandidate) { 970 if c != nil { 971 assert.NoError(t, answerPeerConnection.AddICECandidate(c.ToJSON())) 972 } 973 }) 974 975 answerPeerConnection.OnICECandidate(func(c *ICECandidate) { 976 if c != nil { 977 assert.NoError(t, offerPeerConnection.AddICECandidate(c.ToJSON())) 978 } 979 }) 980 981 dataChannel := <-dataChannelAnswerer 982 assert.NoError(t, dataChannel.SendText(testMessage)) 983 assert.Equal(t, testMessage, <-dataChannelMessages) 984 985 // Drop all packets, assert we have disconnected 986 // and send a DataChannel message when disconnected 987 keepPackets.set(false) 988 blockUntilICEState(ICEConnectionStateFailed) 989 assert.NoError(t, dataChannel.SendText(testMessage)) 990 991 // ICE Restart and assert we have reconnected 992 // block until our DataChannel message is delivered 993 keepPackets.set(true) 994 connectWithICERestart(offerPeerConnection, answerPeerConnection) 995 blockUntilICEState(ICEConnectionStateConnected) 996 assert.Equal(t, testMessage, <-dataChannelMessages) 997 998 assert.NoError(t, wan.Stop()) 999 closePairNow(t, offerPeerConnection, answerPeerConnection) 1000 } 1001 1002 type trackRecords struct { 1003 mu sync.Mutex 1004 trackIDs map[string]struct{} 1005 receivedTrackIDs map[string]struct{} 1006 } 1007 1008 func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) { 1009 trackID := fmt.Sprintf("pion-track-%d", len(r.trackIDs)) 1010 track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, "pion") 1011 r.trackIDs[trackID] = struct{}{} 1012 return track, err 1013 } 1014 1015 func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) { 1016 r.mu.Lock() 1017 defer r.mu.Unlock() 1018 tID := t.ID() 1019 if _, exist := r.trackIDs[tID]; exist { 1020 r.receivedTrackIDs[tID] = struct{}{} 1021 } 1022 } 1023 1024 func (r *trackRecords) remains() int { 1025 r.mu.Lock() 1026 defer r.mu.Unlock() 1027 return len(r.trackIDs) - len(r.receivedTrackIDs) 1028 } 1029 1030 // This test assure that all track events emits. 1031 func TestPeerConnection_MassiveTracks(t *testing.T) { 1032 var ( 1033 tRecs = &trackRecords{ 1034 trackIDs: make(map[string]struct{}), 1035 receivedTrackIDs: make(map[string]struct{}), 1036 } 1037 tracks = []*TrackLocalStaticRTP{} 1038 trackCount = 256 1039 pingInterval = 1 * time.Second 1040 noiseInterval = 100 * time.Microsecond 1041 timeoutDuration = 20 * time.Second 1042 rawPkt = []byte{ 1043 0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64, 1044 0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x88, 0x9e, 1045 } 1046 samplePkt = &rtp.Packet{ 1047 Header: rtp.Header{ 1048 Marker: true, 1049 Extension: false, 1050 ExtensionProfile: 1, 1051 Version: 2, 1052 SequenceNumber: 27023, 1053 Timestamp: 3653407706, 1054 CSRC: []uint32{}, 1055 }, 1056 Payload: rawPkt[20:], 1057 } 1058 connected = make(chan struct{}) 1059 stopped = make(chan struct{}) 1060 ) 1061 offerPC, answerPC, err := newPair() 1062 assert.NoError(t, err) 1063 // Create massive tracks. 1064 for range make([]struct{}, trackCount) { 1065 track, err := tRecs.newTrack() 1066 assert.NoError(t, err) 1067 _, err = offerPC.AddTrack(track) 1068 assert.NoError(t, err) 1069 tracks = append(tracks, track) 1070 } 1071 answerPC.OnTrack(tRecs.handleTrack) 1072 offerPC.OnICEConnectionStateChange(func(s ICEConnectionState) { 1073 if s == ICEConnectionStateConnected { 1074 close(connected) 1075 } 1076 }) 1077 // A routine to periodically call GetTransceivers. This action might cause 1078 // the deadlock and prevent track event to emit. 1079 go func() { 1080 for { 1081 answerPC.GetTransceivers() 1082 time.Sleep(noiseInterval) 1083 select { 1084 case <-stopped: 1085 return 1086 default: 1087 } 1088 } 1089 }() 1090 assert.NoError(t, signalPair(offerPC, answerPC)) 1091 // Send a RTP packets to each track to trigger track event after connected. 1092 <-connected 1093 time.Sleep(1 * time.Second) 1094 for _, track := range tracks { 1095 assert.NoError(t, track.WriteRTP(samplePkt)) 1096 } 1097 // Ping trackRecords to see if any track event not received yet. 1098 tooLong := time.After(timeoutDuration) 1099 for { 1100 remains := tRecs.remains() 1101 if remains == 0 { 1102 break 1103 } 1104 t.Log("remain tracks", remains) 1105 time.Sleep(pingInterval) 1106 select { 1107 case <-tooLong: 1108 t.Error("unable to receive all track events in time") 1109 default: 1110 } 1111 } 1112 close(stopped) 1113 closePairNow(t, offerPC, answerPC) 1114 } 1115 1116 func TestEmptyCandidate(t *testing.T) { 1117 testCases := []struct { 1118 ICECandidate ICECandidateInit 1119 expectError bool 1120 }{ 1121 {ICECandidateInit{"", nil, nil, nil}, false}, 1122 {ICECandidateInit{ 1123 "211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0", 1124 nil, nil, nil, 1125 }, false}, 1126 {ICECandidateInit{ 1127 "1234567", 1128 nil, nil, nil, 1129 }, true}, 1130 } 1131 1132 for i, testCase := range testCases { 1133 peerConn, err := NewPeerConnection(Configuration{}) 1134 if err != nil { 1135 t.Errorf("Case %d: got error: %v", i, err) 1136 } 1137 1138 err = peerConn.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer}) 1139 if err != nil { 1140 t.Errorf("Case %d: got error: %v", i, err) 1141 } 1142 1143 if testCase.expectError { 1144 assert.Error(t, peerConn.AddICECandidate(testCase.ICECandidate)) 1145 } else { 1146 assert.NoError(t, peerConn.AddICECandidate(testCase.ICECandidate)) 1147 } 1148 1149 assert.NoError(t, peerConn.Close()) 1150 } 1151 } 1152 1153 const liteOffer = `v=0 1154 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1155 s=- 1156 t=0 0 1157 a=msid-semantic: WMS 1158 a=ice-lite 1159 m=application 47299 DTLS/SCTP 5000 1160 c=IN IP4 192.168.20.129 1161 a=ice-ufrag:1/MvHwjAyVf27aLu 1162 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1163 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1164 a=mid:data 1165 ` 1166 1167 // this test asserts that if an ice-lite offer is received, 1168 // pion will take the ICE-CONTROLLING role 1169 func TestICELite(t *testing.T) { 1170 peerConnection, err := NewPeerConnection(Configuration{}) 1171 assert.NoError(t, err) 1172 1173 assert.NoError(t, peerConnection.SetRemoteDescription( 1174 SessionDescription{SDP: liteOffer, Type: SDPTypeOffer}, 1175 )) 1176 1177 SDPAnswer, err := peerConnection.CreateAnswer(nil) 1178 assert.NoError(t, err) 1179 1180 assert.NoError(t, peerConnection.SetLocalDescription(SDPAnswer)) 1181 1182 assert.Equal(t, ICERoleControlling, peerConnection.iceTransport.Role(), 1183 "pion did not set state to ICE-CONTROLLED against ice-light offer") 1184 1185 assert.NoError(t, peerConnection.Close()) 1186 } 1187 1188 func TestPeerConnection_TransceiverDirection(t *testing.T) { 1189 lim := test.TimeOut(time.Second * 30) 1190 defer lim.Stop() 1191 1192 report := test.CheckRoutines(t) 1193 defer report() 1194 1195 createTransceiver := func(pc *PeerConnection, dir RTPTransceiverDirection) error { 1196 // AddTransceiverFromKind() can't be used with sendonly 1197 if dir == RTPTransceiverDirectionSendonly { 1198 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1199 1200 track, err := NewTrackLocalStaticSample(codecs[0].RTPCodecCapability, util.MathRandAlpha(16), util.MathRandAlpha(16)) 1201 if err != nil { 1202 return err 1203 } 1204 1205 _, err = pc.AddTransceiverFromTrack(track, []RTPTransceiverInit{ 1206 {Direction: dir}, 1207 }...) 1208 return err 1209 } 1210 1211 _, err := pc.AddTransceiverFromKind( 1212 RTPCodecTypeVideo, 1213 RTPTransceiverInit{Direction: dir}, 1214 ) 1215 return err 1216 } 1217 1218 for _, test := range []struct { 1219 name string 1220 offerDirection RTPTransceiverDirection 1221 answerStartDirection RTPTransceiverDirection 1222 answerFinalDirections []RTPTransceiverDirection 1223 }{ 1224 { 1225 "offer sendrecv answer sendrecv", 1226 RTPTransceiverDirectionSendrecv, 1227 RTPTransceiverDirectionSendrecv, 1228 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1229 }, 1230 { 1231 "offer sendonly answer sendrecv", 1232 RTPTransceiverDirectionSendonly, 1233 RTPTransceiverDirectionSendrecv, 1234 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1235 }, 1236 { 1237 "offer recvonly answer sendrecv", 1238 RTPTransceiverDirectionRecvonly, 1239 RTPTransceiverDirectionSendrecv, 1240 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, 1241 }, 1242 { 1243 "offer sendrecv answer sendonly", 1244 RTPTransceiverDirectionSendrecv, 1245 RTPTransceiverDirectionSendonly, 1246 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1247 }, 1248 { 1249 "offer sendonly answer sendonly", 1250 RTPTransceiverDirectionSendonly, 1251 RTPTransceiverDirectionSendonly, 1252 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionRecvonly}, 1253 }, 1254 { 1255 "offer recvonly answer sendonly", 1256 RTPTransceiverDirectionRecvonly, 1257 RTPTransceiverDirectionSendonly, 1258 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, 1259 }, 1260 { 1261 "offer sendrecv answer recvonly", 1262 RTPTransceiverDirectionSendrecv, 1263 RTPTransceiverDirectionRecvonly, 1264 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, 1265 }, 1266 { 1267 "offer sendonly answer recvonly", 1268 RTPTransceiverDirectionSendonly, 1269 RTPTransceiverDirectionRecvonly, 1270 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, 1271 }, 1272 { 1273 "offer recvonly answer recvonly", 1274 RTPTransceiverDirectionRecvonly, 1275 RTPTransceiverDirectionRecvonly, 1276 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly}, 1277 }, 1278 } { 1279 offerDirection := test.offerDirection 1280 answerStartDirection := test.answerStartDirection 1281 answerFinalDirections := test.answerFinalDirections 1282 1283 t.Run(test.name, func(t *testing.T) { 1284 pcOffer, pcAnswer, err := newPair() 1285 assert.NoError(t, err) 1286 1287 err = createTransceiver(pcOffer, offerDirection) 1288 assert.NoError(t, err) 1289 1290 offer, err := pcOffer.CreateOffer(nil) 1291 assert.NoError(t, err) 1292 1293 err = createTransceiver(pcAnswer, answerStartDirection) 1294 assert.NoError(t, err) 1295 1296 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 1297 1298 assert.Equal(t, len(answerFinalDirections), len(pcAnswer.GetTransceivers())) 1299 1300 for i, tr := range pcAnswer.GetTransceivers() { 1301 assert.Equal(t, answerFinalDirections[i], tr.Direction()) 1302 } 1303 1304 assert.NoError(t, pcOffer.Close()) 1305 assert.NoError(t, pcAnswer.Close()) 1306 }) 1307 } 1308 } 1309 1310 func TestPeerConnection_SessionID(t *testing.T) { 1311 defer test.TimeOut(time.Second * 10).Stop() 1312 defer test.CheckRoutines(t)() 1313 1314 pcOffer, pcAnswer, err := newPair() 1315 assert.NoError(t, err) 1316 var offerSessionID uint64 1317 var offerSessionVersion uint64 1318 var answerSessionID uint64 1319 var answerSessionVersion uint64 1320 for i := 0; i < 10; i++ { 1321 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1322 offer := pcOffer.LocalDescription().parsed 1323 sessionID := offer.Origin.SessionID 1324 sessionVersion := offer.Origin.SessionVersion 1325 if offerSessionID == 0 { 1326 offerSessionID = sessionID 1327 offerSessionVersion = sessionVersion 1328 } else { 1329 if offerSessionID != sessionID { 1330 t.Errorf("offer[%v] session id mismatch: expected=%v, got=%v", i, offerSessionID, sessionID) 1331 } 1332 if offerSessionVersion+1 != sessionVersion { 1333 t.Errorf("offer[%v] session version mismatch: expected=%v, got=%v", i, offerSessionVersion+1, sessionVersion) 1334 } 1335 offerSessionVersion++ 1336 } 1337 1338 answer := pcAnswer.LocalDescription().parsed 1339 sessionID = answer.Origin.SessionID 1340 sessionVersion = answer.Origin.SessionVersion 1341 if answerSessionID == 0 { 1342 answerSessionID = sessionID 1343 answerSessionVersion = sessionVersion 1344 } else { 1345 if answerSessionID != sessionID { 1346 t.Errorf("answer[%v] session id mismatch: expected=%v, got=%v", i, answerSessionID, sessionID) 1347 } 1348 if answerSessionVersion+1 != sessionVersion { 1349 t.Errorf("answer[%v] session version mismatch: expected=%v, got=%v", i, answerSessionVersion+1, sessionVersion) 1350 } 1351 answerSessionVersion++ 1352 } 1353 } 1354 closePairNow(t, pcOffer, pcAnswer) 1355 } 1356 1357 func TestPeerConnectionNilCallback(t *testing.T) { 1358 pc, err := NewPeerConnection(Configuration{}) 1359 assert.NoError(t, err) 1360 1361 pc.onSignalingStateChange(SignalingStateStable) 1362 pc.OnSignalingStateChange(func(SignalingState) { 1363 t.Error("OnSignalingStateChange called") 1364 }) 1365 pc.OnSignalingStateChange(nil) 1366 pc.onSignalingStateChange(SignalingStateStable) 1367 1368 pc.onConnectionStateChange(PeerConnectionStateNew) 1369 pc.OnConnectionStateChange(func(PeerConnectionState) { 1370 t.Error("OnConnectionStateChange called") 1371 }) 1372 pc.OnConnectionStateChange(nil) 1373 pc.onConnectionStateChange(PeerConnectionStateNew) 1374 1375 pc.onICEConnectionStateChange(ICEConnectionStateNew) 1376 pc.OnICEConnectionStateChange(func(ICEConnectionState) { 1377 t.Error("OnConnectionStateChange called") 1378 }) 1379 pc.OnICEConnectionStateChange(nil) 1380 pc.onICEConnectionStateChange(ICEConnectionStateNew) 1381 1382 pc.onNegotiationNeeded() 1383 pc.negotiationNeededOp() 1384 pc.OnNegotiationNeeded(func() { 1385 t.Error("OnNegotiationNeeded called") 1386 }) 1387 pc.OnNegotiationNeeded(nil) 1388 pc.onNegotiationNeeded() 1389 pc.negotiationNeededOp() 1390 1391 assert.NoError(t, pc.Close()) 1392 } 1393 1394 func TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote(t *testing.T) { 1395 t.Run("Codec MatchExact", func(t *testing.T) { //nolint:dupl 1396 const remoteSdp = `v=0 1397 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1398 s=- 1399 t=0 0 1400 m=video 60323 UDP/TLS/RTP/SAVPF 98 94 106 1401 a=ice-ufrag:1/MvHwjAyVf27aLu 1402 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1403 a=ice-options:google-ice 1404 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1405 a=mid:0 1406 a=rtpmap:98 H264/90000 1407 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f 1408 a=rtpmap:94 VP8/90000 1409 a=rtpmap:106 H264/90000 1410 a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1411 a=sendonly 1412 m=video 60323 UDP/TLS/RTP/SAVPF 108 98 125 1413 a=ice-ufrag:1/MvHwjAyVf27aLu 1414 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1415 a=ice-options:google-ice 1416 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1417 a=mid:1 1418 a=rtpmap:98 H264/90000 1419 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f 1420 a=rtpmap:108 VP8/90000 1421 a=sendonly 1422 a=rtpmap:125 H264/90000 1423 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1424 ` 1425 m := MediaEngine{} 1426 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1427 RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, 1428 PayloadType: 94, 1429 }, RTPCodecTypeVideo)) 1430 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1431 RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, 1432 PayloadType: 98, 1433 }, RTPCodecTypeVideo)) 1434 1435 api := NewAPI(WithMediaEngine(&m)) 1436 pc, err := api.NewPeerConnection(Configuration{}) 1437 assert.NoError(t, err) 1438 assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ 1439 Type: SDPTypeOffer, 1440 SDP: remoteSdp, 1441 })) 1442 ans, _ := pc.CreateAnswer(nil) 1443 assert.NoError(t, pc.SetLocalDescription(ans)) 1444 codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0] 1445 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1446 _, matchType := codecParametersFuzzySearch(codecOfTr1, codecs) 1447 assert.Equal(t, codecMatchExact, matchType) 1448 codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0] 1449 _, matchType = codecParametersFuzzySearch(codecOfTr2, codecs) 1450 assert.Equal(t, codecMatchExact, matchType) 1451 assert.EqualValues(t, 94, codecOfTr2.PayloadType) 1452 assert.NoError(t, pc.Close()) 1453 }) 1454 1455 t.Run("Codec PartialExact Only", func(t *testing.T) { //nolint:dupl 1456 const remoteSdp = `v=0 1457 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1458 s=- 1459 t=0 0 1460 m=video 60323 UDP/TLS/RTP/SAVPF 98 106 1461 a=ice-ufrag:1/MvHwjAyVf27aLu 1462 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1463 a=ice-options:google-ice 1464 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1465 a=mid:0 1466 a=rtpmap:98 H264/90000 1467 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1468 a=rtpmap:106 H264/90000 1469 a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 1470 a=sendonly 1471 m=video 60323 UDP/TLS/RTP/SAVPF 125 98 1472 a=ice-ufrag:1/MvHwjAyVf27aLu 1473 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1474 a=ice-options:google-ice 1475 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1476 a=mid:1 1477 a=rtpmap:125 H264/90000 1478 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 1479 a=rtpmap:98 H264/90000 1480 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1481 a=sendonly 1482 ` 1483 m := MediaEngine{} 1484 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1485 RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, 1486 PayloadType: 94, 1487 }, RTPCodecTypeVideo)) 1488 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1489 RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, 1490 PayloadType: 98, 1491 }, RTPCodecTypeVideo)) 1492 1493 api := NewAPI(WithMediaEngine(&m)) 1494 pc, err := api.NewPeerConnection(Configuration{}) 1495 assert.NoError(t, err) 1496 assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ 1497 Type: SDPTypeOffer, 1498 SDP: remoteSdp, 1499 })) 1500 ans, _ := pc.CreateAnswer(nil) 1501 assert.NoError(t, pc.SetLocalDescription(ans)) 1502 codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0] 1503 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1504 _, matchType := codecParametersFuzzySearch(codecOfTr1, codecs) 1505 assert.Equal(t, codecMatchExact, matchType) 1506 codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0] 1507 _, matchType = codecParametersFuzzySearch(codecOfTr2, codecs) 1508 assert.Equal(t, codecMatchExact, matchType) 1509 // h.264/profile-id=640032 should be remap to 106 as same as transceiver 1 1510 assert.EqualValues(t, 106, codecOfTr2.PayloadType) 1511 assert.NoError(t, pc.Close()) 1512 }) 1513 } 1514 1515 // Assert that remote candidates with an unknown transport are ignored and logged. 1516 // This allows us to accept SessionDescriptions with proprietary candidates 1517 // like `ssltcp`. 1518 func TestInvalidCandidateTransport(t *testing.T) { 1519 const ( 1520 sslTCPCandidate = `candidate:1 1 ssltcp 1 127.0.0.1 443 typ host generation 0` 1521 sslTCPOffer = `v=0 1522 o=- 0 2 IN IP4 127.0.0.1 1523 s=- 1524 t=0 0 1525 a=msid-semantic: WMS 1526 m=application 9 DTLS/SCTP 5000 1527 c=IN IP4 0.0.0.0 1528 a=ice-ufrag:1/MvHwjAyVf27aLu 1529 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1530 a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24 1531 a=mid:0 1532 a=` + sslTCPCandidate + "\n" 1533 ) 1534 1535 peerConnection, err := NewPeerConnection(Configuration{}) 1536 assert.NoError(t, err) 1537 1538 assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: sslTCPOffer})) 1539 assert.NoError(t, peerConnection.AddICECandidate(ICECandidateInit{Candidate: sslTCPCandidate})) 1540 1541 assert.NoError(t, peerConnection.Close()) 1542 } 1543 1544 func TestOfferWithInactiveDirection(t *testing.T) { 1545 const remoteSDP = `v=0 1546 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1547 s=- 1548 t=0 0 1549 a=fingerprint:sha-256 F7:BF:B4:42:5B:44:C0:B9:49:70:6D:26:D7:3E:E6:08:B1:5B:25:2E:32:88:50:B6:3C:BE:4E:18:A7:2C:85:7C 1550 a=group:BUNDLE 0 1 1551 a=msid-semantic:WMS * 1552 m=video 9 UDP/TLS/RTP/SAVPF 97 1553 c=IN IP4 0.0.0.0 1554 a=inactive 1555 a=ice-pwd:05d682b2902af03db90d9a9a5f2f8d7f 1556 a=ice-ufrag:93cc7e4d 1557 a=mid:0 1558 a=rtpmap:97 H264/90000 1559 a=setup:actpass 1560 a=ssrc:1455629982 cname:{61fd3093-0326-4b12-8258-86bdc1fe677a} 1561 ` 1562 1563 peerConnection, err := NewPeerConnection(Configuration{}) 1564 assert.NoError(t, err) 1565 1566 assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: remoteSDP})) 1567 assert.Equal(t, RTPTransceiverDirectionInactive, peerConnection.rtpTransceivers[0].direction.Load().(RTPTransceiverDirection)) //nolint:forcetypeassert 1568 1569 assert.NoError(t, peerConnection.Close()) 1570 } 1571 1572 func TestPeerConnectionState(t *testing.T) { 1573 pc, err := NewPeerConnection(Configuration{}) 1574 assert.NoError(t, err) 1575 assert.Equal(t, PeerConnectionStateNew, pc.ConnectionState()) 1576 1577 pc.updateConnectionState(ICEConnectionStateChecking, DTLSTransportStateNew) 1578 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1579 1580 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateNew) 1581 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1582 1583 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnecting) 1584 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1585 1586 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnected) 1587 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1588 1589 pc.updateConnectionState(ICEConnectionStateCompleted, DTLSTransportStateConnected) 1590 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1591 1592 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateClosed) 1593 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1594 1595 pc.updateConnectionState(ICEConnectionStateDisconnected, DTLSTransportStateConnected) 1596 assert.Equal(t, PeerConnectionStateDisconnected, pc.ConnectionState()) 1597 1598 pc.updateConnectionState(ICEConnectionStateFailed, DTLSTransportStateConnected) 1599 assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState()) 1600 1601 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateFailed) 1602 assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState()) 1603 1604 assert.NoError(t, pc.Close()) 1605 assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState()) 1606 } 1607 1608 func TestPeerConnectionDeadlock(t *testing.T) { 1609 lim := test.TimeOut(time.Second * 5) 1610 defer lim.Stop() 1611 1612 report := test.CheckRoutines(t) 1613 defer report() 1614 1615 closeHdlr := func(peerConnection *PeerConnection) { 1616 peerConnection.OnICEConnectionStateChange(func(i ICEConnectionState) { 1617 if i == ICEConnectionStateFailed || i == ICEConnectionStateClosed { 1618 if err := peerConnection.Close(); err != nil { 1619 assert.NoError(t, err) 1620 } 1621 } 1622 }) 1623 } 1624 1625 pcOffer, pcAnswer, err := NewAPI().newPair(Configuration{}) 1626 assert.NoError(t, err) 1627 1628 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1629 1630 onDataChannel, onDataChannelCancel := context.WithCancel(context.Background()) 1631 pcAnswer.OnDataChannel(func(*DataChannel) { 1632 onDataChannelCancel() 1633 }) 1634 <-onDataChannel.Done() 1635 1636 closeHdlr(pcOffer) 1637 closeHdlr(pcAnswer) 1638 1639 closePairNow(t, pcOffer, pcAnswer) 1640 }