github.com/pion/webrtc/v3@v3.2.24/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/v2" 26 "github.com/pion/rtp" 27 "github.com/pion/transport/v2/test" 28 "github.com/pion/transport/v2/vnet" 29 "github.com/pion/webrtc/v3/internal/util" 30 "github.com/pion/webrtc/v3/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(t *TrackRemote, r *RTPReceiver) { 310 close(onTrackCalled) 311 }) 312 313 pc.OnICEConnectionStateChange(func(cs 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(t *testing.T) { 605 connectTwoAgents(true, false) 606 }) 607 608 t.Run("Answerer", func(t *testing.T) { 609 connectTwoAgents(false, true) 610 }) 611 612 t.Run("Both", func(t *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 seenClosed := make(chan interface{}) 623 624 peerConn, err := NewPeerConnection(Configuration{}) 625 assert.NoError(t, err) 626 627 var onStateChange func(s ICEGathererState) 628 onStateChange = func(s ICEGathererState) { 629 // Access to ICEGatherer in the callback must not cause dead lock. 630 peerConn.OnICEGatheringStateChange(onStateChange) 631 if state := peerConn.iceGatherer.State(); state != s { 632 t.Errorf("State change callback argument (%s) and State() (%s) result differs", 633 s, state, 634 ) 635 } 636 637 switch s { // nolint:exhaustive 638 case ICEGathererStateClosed: 639 close(seenClosed) 640 return 641 case ICEGathererStateGathering: 642 if seenComplete.get() { 643 t.Error("Completed before gathering") 644 } 645 seenGathering.set(true) 646 case ICEGathererStateComplete: 647 seenComplete.set(true) 648 } 649 650 if seenGathering.get() && seenComplete.get() { 651 close(seenGatheringAndComplete) 652 } 653 } 654 peerConn.OnICEGatheringStateChange(onStateChange) 655 656 offer, err := peerConn.CreateOffer(nil) 657 assert.NoError(t, err) 658 assert.NoError(t, peerConn.SetLocalDescription(offer)) 659 660 select { 661 case <-time.After(time.Second * 10): 662 t.Fatal("Gathering and Complete were never seen") 663 case <-seenClosed: 664 t.Fatal("Closed before PeerConnection Close") 665 case <-seenGatheringAndComplete: 666 } 667 668 assert.NoError(t, peerConn.Close()) 669 670 select { 671 case <-time.After(time.Second * 10): 672 t.Fatal("Closed was never seen") 673 case <-seenClosed: 674 } 675 } 676 677 // Assert Trickle ICE behaviors 678 func TestPeerConnectionTrickle(t *testing.T) { 679 offerPC, answerPC, err := newPair() 680 assert.NoError(t, err) 681 682 _, err = offerPC.CreateDataChannel("test-channel", nil) 683 assert.NoError(t, err) 684 685 addOrCacheCandidate := func(pc *PeerConnection, c *ICECandidate, candidateCache []ICECandidateInit) []ICECandidateInit { 686 if c == nil { 687 return candidateCache 688 } 689 690 if pc.RemoteDescription() == nil { 691 return append(candidateCache, c.ToJSON()) 692 } 693 694 assert.NoError(t, pc.AddICECandidate(c.ToJSON())) 695 return candidateCache 696 } 697 698 candidateLock := sync.RWMutex{} 699 var offerCandidateDone, answerCandidateDone bool 700 701 cachedOfferCandidates := []ICECandidateInit{} 702 offerPC.OnICECandidate(func(c *ICECandidate) { 703 if offerCandidateDone { 704 t.Error("Received OnICECandidate after finishing gathering") 705 } 706 if c == nil { 707 offerCandidateDone = true 708 } 709 710 candidateLock.Lock() 711 defer candidateLock.Unlock() 712 713 cachedOfferCandidates = addOrCacheCandidate(answerPC, c, cachedOfferCandidates) 714 }) 715 716 cachedAnswerCandidates := []ICECandidateInit{} 717 answerPC.OnICECandidate(func(c *ICECandidate) { 718 if answerCandidateDone { 719 t.Error("Received OnICECandidate after finishing gathering") 720 } 721 if c == nil { 722 answerCandidateDone = true 723 } 724 725 candidateLock.Lock() 726 defer candidateLock.Unlock() 727 728 cachedAnswerCandidates = addOrCacheCandidate(offerPC, c, cachedAnswerCandidates) 729 }) 730 731 offerPCConnected, offerPCConnectedCancel := context.WithCancel(context.Background()) 732 offerPC.OnICEConnectionStateChange(func(i ICEConnectionState) { 733 if i == ICEConnectionStateConnected { 734 offerPCConnectedCancel() 735 } 736 }) 737 738 answerPCConnected, answerPCConnectedCancel := context.WithCancel(context.Background()) 739 answerPC.OnICEConnectionStateChange(func(i ICEConnectionState) { 740 if i == ICEConnectionStateConnected { 741 answerPCConnectedCancel() 742 } 743 }) 744 745 offer, err := offerPC.CreateOffer(nil) 746 assert.NoError(t, err) 747 748 assert.NoError(t, offerPC.SetLocalDescription(offer)) 749 assert.NoError(t, answerPC.SetRemoteDescription(offer)) 750 751 answer, err := answerPC.CreateAnswer(nil) 752 assert.NoError(t, err) 753 754 assert.NoError(t, answerPC.SetLocalDescription(answer)) 755 assert.NoError(t, offerPC.SetRemoteDescription(answer)) 756 757 candidateLock.Lock() 758 for _, c := range cachedAnswerCandidates { 759 assert.NoError(t, offerPC.AddICECandidate(c)) 760 } 761 for _, c := range cachedOfferCandidates { 762 assert.NoError(t, answerPC.AddICECandidate(c)) 763 } 764 candidateLock.Unlock() 765 766 <-answerPCConnected.Done() 767 <-offerPCConnected.Done() 768 closePairNow(t, offerPC, answerPC) 769 } 770 771 // Issue #1121, assert populateLocalCandidates doesn't mutate 772 func TestPopulateLocalCandidates(t *testing.T) { 773 t.Run("PendingLocalDescription shouldn't add extra mutations", func(t *testing.T) { 774 pc, err := NewPeerConnection(Configuration{}) 775 assert.NoError(t, err) 776 777 offer, err := pc.CreateOffer(nil) 778 assert.NoError(t, err) 779 780 offerGatheringComplete := GatheringCompletePromise(pc) 781 assert.NoError(t, pc.SetLocalDescription(offer)) 782 <-offerGatheringComplete 783 784 assert.Equal(t, pc.PendingLocalDescription(), pc.PendingLocalDescription()) 785 assert.NoError(t, pc.Close()) 786 }) 787 788 t.Run("end-of-candidates only when gathering is complete", func(t *testing.T) { 789 pc, err := NewAPI().NewPeerConnection(Configuration{}) 790 assert.NoError(t, err) 791 792 _, err = pc.CreateDataChannel("test-channel", nil) 793 assert.NoError(t, err) 794 795 offer, err := pc.CreateOffer(nil) 796 assert.NoError(t, err) 797 assert.NotContains(t, offer.SDP, "a=candidate") 798 assert.NotContains(t, offer.SDP, "a=end-of-candidates") 799 800 offerGatheringComplete := GatheringCompletePromise(pc) 801 assert.NoError(t, pc.SetLocalDescription(offer)) 802 <-offerGatheringComplete 803 804 assert.Contains(t, pc.PendingLocalDescription().SDP, "a=candidate") 805 assert.Contains(t, pc.PendingLocalDescription().SDP, "a=end-of-candidates") 806 807 assert.NoError(t, pc.Close()) 808 }) 809 } 810 811 // Assert that two agents that only generate mDNS candidates can connect 812 func TestMulticastDNSCandidates(t *testing.T) { 813 lim := test.TimeOut(time.Second * 30) 814 defer lim.Stop() 815 816 report := test.CheckRoutines(t) 817 defer report() 818 819 s := SettingEngine{} 820 s.SetICEMulticastDNSMode(ice.MulticastDNSModeQueryAndGather) 821 822 pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{}) 823 assert.NoError(t, err) 824 825 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 826 827 onDataChannel, onDataChannelCancel := context.WithCancel(context.Background()) 828 pcAnswer.OnDataChannel(func(d *DataChannel) { 829 onDataChannelCancel() 830 }) 831 <-onDataChannel.Done() 832 833 closePairNow(t, pcOffer, pcAnswer) 834 } 835 836 func TestICERestart(t *testing.T) { 837 extractCandidates := func(sdp string) (candidates []string) { 838 sc := bufio.NewScanner(strings.NewReader(sdp)) 839 for sc.Scan() { 840 if strings.HasPrefix(sc.Text(), "a=candidate:") { 841 candidates = append(candidates, sc.Text()) 842 } 843 } 844 845 return 846 } 847 848 lim := test.TimeOut(time.Second * 30) 849 defer lim.Stop() 850 851 report := test.CheckRoutines(t) 852 defer report() 853 854 offerPC, answerPC, err := newPair() 855 assert.NoError(t, err) 856 857 var connectedWaitGroup sync.WaitGroup 858 connectedWaitGroup.Add(2) 859 860 offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) { 861 if state == ICEConnectionStateConnected { 862 connectedWaitGroup.Done() 863 } 864 }) 865 answerPC.OnICEConnectionStateChange(func(state ICEConnectionState) { 866 if state == ICEConnectionStateConnected { 867 connectedWaitGroup.Done() 868 } 869 }) 870 871 // Connect two PeerConnections and block until ICEConnectionStateConnected 872 assert.NoError(t, signalPair(offerPC, answerPC)) 873 connectedWaitGroup.Wait() 874 875 // Store candidates from first Offer/Answer, compare later to make sure we re-gathered 876 firstOfferCandidates := extractCandidates(offerPC.LocalDescription().SDP) 877 firstAnswerCandidates := extractCandidates(answerPC.LocalDescription().SDP) 878 879 // Use Trickle ICE for ICE Restart 880 offerPC.OnICECandidate(func(c *ICECandidate) { 881 if c != nil { 882 assert.NoError(t, answerPC.AddICECandidate(c.ToJSON())) 883 } 884 }) 885 886 answerPC.OnICECandidate(func(c *ICECandidate) { 887 if c != nil { 888 assert.NoError(t, offerPC.AddICECandidate(c.ToJSON())) 889 } 890 }) 891 892 // Re-signal with ICE Restart, block until ICEConnectionStateConnected 893 connectedWaitGroup.Add(2) 894 offer, err := offerPC.CreateOffer(&OfferOptions{ICERestart: true}) 895 assert.NoError(t, err) 896 897 assert.NoError(t, offerPC.SetLocalDescription(offer)) 898 assert.NoError(t, answerPC.SetRemoteDescription(offer)) 899 900 answer, err := answerPC.CreateAnswer(nil) 901 assert.NoError(t, err) 902 903 assert.NoError(t, answerPC.SetLocalDescription(answer)) 904 assert.NoError(t, offerPC.SetRemoteDescription(answer)) 905 906 // Block until we have connected again 907 connectedWaitGroup.Wait() 908 909 // Compare ICE Candidates across each run, fail if they haven't changed 910 assert.NotEqual(t, firstOfferCandidates, extractCandidates(offerPC.LocalDescription().SDP)) 911 assert.NotEqual(t, firstAnswerCandidates, extractCandidates(answerPC.LocalDescription().SDP)) 912 closePairNow(t, offerPC, answerPC) 913 } 914 915 // Assert error handling when an Agent is restart 916 func TestICERestart_Error_Handling(t *testing.T) { 917 iceStates := make(chan ICEConnectionState, 100) 918 blockUntilICEState := func(wantedState ICEConnectionState) { 919 stateCount := 0 920 for i := range iceStates { 921 if i == wantedState { 922 stateCount++ 923 } 924 925 if stateCount == 2 { 926 return 927 } 928 } 929 } 930 931 connectWithICERestart := func(offerPeerConnection, answerPeerConnection *PeerConnection) { 932 offer, err := offerPeerConnection.CreateOffer(&OfferOptions{ICERestart: true}) 933 assert.NoError(t, err) 934 935 assert.NoError(t, offerPeerConnection.SetLocalDescription(offer)) 936 assert.NoError(t, answerPeerConnection.SetRemoteDescription(*offerPeerConnection.LocalDescription())) 937 938 answer, err := answerPeerConnection.CreateAnswer(nil) 939 assert.NoError(t, err) 940 941 assert.NoError(t, answerPeerConnection.SetLocalDescription(answer)) 942 assert.NoError(t, offerPeerConnection.SetRemoteDescription(*answerPeerConnection.LocalDescription())) 943 } 944 945 lim := test.TimeOut(time.Second * 30) 946 defer lim.Stop() 947 948 report := test.CheckRoutines(t) 949 defer report() 950 951 offerPeerConnection, answerPeerConnection, wan := createVNetPair(t) 952 953 pushICEState := func(i ICEConnectionState) { iceStates <- i } 954 offerPeerConnection.OnICEConnectionStateChange(pushICEState) 955 answerPeerConnection.OnICEConnectionStateChange(pushICEState) 956 957 keepPackets := &atomicBool{} 958 keepPackets.set(true) 959 960 // Add a filter that monitors the traffic on the router 961 wan.AddChunkFilter(func(c vnet.Chunk) bool { 962 return keepPackets.get() 963 }) 964 965 const testMessage = "testMessage" 966 967 d, err := answerPeerConnection.CreateDataChannel("foo", nil) 968 assert.NoError(t, err) 969 970 dataChannelMessages := make(chan string, 100) 971 d.OnMessage(func(m DataChannelMessage) { 972 dataChannelMessages <- string(m.Data) 973 }) 974 975 dataChannelAnswerer := make(chan *DataChannel) 976 offerPeerConnection.OnDataChannel(func(d *DataChannel) { 977 d.OnOpen(func() { 978 dataChannelAnswerer <- d 979 }) 980 }) 981 982 // Connect and Assert we have connected 983 assert.NoError(t, signalPair(offerPeerConnection, answerPeerConnection)) 984 blockUntilICEState(ICEConnectionStateConnected) 985 986 offerPeerConnection.OnICECandidate(func(c *ICECandidate) { 987 if c != nil { 988 assert.NoError(t, answerPeerConnection.AddICECandidate(c.ToJSON())) 989 } 990 }) 991 992 answerPeerConnection.OnICECandidate(func(c *ICECandidate) { 993 if c != nil { 994 assert.NoError(t, offerPeerConnection.AddICECandidate(c.ToJSON())) 995 } 996 }) 997 998 dataChannel := <-dataChannelAnswerer 999 assert.NoError(t, dataChannel.SendText(testMessage)) 1000 assert.Equal(t, testMessage, <-dataChannelMessages) 1001 1002 // Drop all packets, assert we have disconnected 1003 // and send a DataChannel message when disconnected 1004 keepPackets.set(false) 1005 blockUntilICEState(ICEConnectionStateFailed) 1006 assert.NoError(t, dataChannel.SendText(testMessage)) 1007 1008 // ICE Restart and assert we have reconnected 1009 // block until our DataChannel message is delivered 1010 keepPackets.set(true) 1011 connectWithICERestart(offerPeerConnection, answerPeerConnection) 1012 blockUntilICEState(ICEConnectionStateConnected) 1013 assert.Equal(t, testMessage, <-dataChannelMessages) 1014 1015 assert.NoError(t, wan.Stop()) 1016 closePairNow(t, offerPeerConnection, answerPeerConnection) 1017 } 1018 1019 type trackRecords struct { 1020 mu sync.Mutex 1021 trackIDs map[string]struct{} 1022 receivedTrackIDs map[string]struct{} 1023 } 1024 1025 func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) { 1026 trackID := fmt.Sprintf("pion-track-%d", len(r.trackIDs)) 1027 track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, "pion") 1028 r.trackIDs[trackID] = struct{}{} 1029 return track, err 1030 } 1031 1032 func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) { 1033 r.mu.Lock() 1034 defer r.mu.Unlock() 1035 tID := t.ID() 1036 if _, exist := r.trackIDs[tID]; exist { 1037 r.receivedTrackIDs[tID] = struct{}{} 1038 } 1039 } 1040 1041 func (r *trackRecords) remains() int { 1042 r.mu.Lock() 1043 defer r.mu.Unlock() 1044 return len(r.trackIDs) - len(r.receivedTrackIDs) 1045 } 1046 1047 // This test assure that all track events emits. 1048 func TestPeerConnection_MassiveTracks(t *testing.T) { 1049 var ( 1050 api = NewAPI() 1051 tRecs = &trackRecords{ 1052 trackIDs: make(map[string]struct{}), 1053 receivedTrackIDs: make(map[string]struct{}), 1054 } 1055 tracks = []*TrackLocalStaticRTP{} 1056 trackCount = 256 1057 pingInterval = 1 * time.Second 1058 noiseInterval = 100 * time.Microsecond 1059 timeoutDuration = 20 * time.Second 1060 rawPkt = []byte{ 1061 0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64, 1062 0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x88, 0x9e, 1063 } 1064 samplePkt = &rtp.Packet{ 1065 Header: rtp.Header{ 1066 Marker: true, 1067 Extension: false, 1068 ExtensionProfile: 1, 1069 Version: 2, 1070 SequenceNumber: 27023, 1071 Timestamp: 3653407706, 1072 CSRC: []uint32{}, 1073 }, 1074 Payload: rawPkt[20:], 1075 } 1076 connected = make(chan struct{}) 1077 stopped = make(chan struct{}) 1078 ) 1079 assert.NoError(t, api.mediaEngine.RegisterDefaultCodecs()) 1080 offerPC, answerPC, err := api.newPair(Configuration{}) 1081 assert.NoError(t, err) 1082 // Create massive tracks. 1083 for range make([]struct{}, trackCount) { 1084 track, err := tRecs.newTrack() 1085 assert.NoError(t, err) 1086 _, err = offerPC.AddTrack(track) 1087 assert.NoError(t, err) 1088 tracks = append(tracks, track) 1089 } 1090 answerPC.OnTrack(tRecs.handleTrack) 1091 offerPC.OnICEConnectionStateChange(func(s ICEConnectionState) { 1092 if s == ICEConnectionStateConnected { 1093 close(connected) 1094 } 1095 }) 1096 // A routine to periodically call GetTransceivers. This action might cause 1097 // the deadlock and prevent track event to emit. 1098 go func() { 1099 for { 1100 answerPC.GetTransceivers() 1101 time.Sleep(noiseInterval) 1102 select { 1103 case <-stopped: 1104 return 1105 default: 1106 } 1107 } 1108 }() 1109 assert.NoError(t, signalPair(offerPC, answerPC)) 1110 // Send a RTP packets to each track to trigger track event after connected. 1111 <-connected 1112 time.Sleep(1 * time.Second) 1113 for _, track := range tracks { 1114 assert.NoError(t, track.WriteRTP(samplePkt)) 1115 } 1116 // Ping trackRecords to see if any track event not received yet. 1117 tooLong := time.After(timeoutDuration) 1118 for { 1119 remains := tRecs.remains() 1120 if remains == 0 { 1121 break 1122 } 1123 t.Log("remain tracks", remains) 1124 time.Sleep(pingInterval) 1125 select { 1126 case <-tooLong: 1127 t.Error("unable to receive all track events in time") 1128 default: 1129 } 1130 } 1131 close(stopped) 1132 closePairNow(t, offerPC, answerPC) 1133 } 1134 1135 func TestEmptyCandidate(t *testing.T) { 1136 testCases := []struct { 1137 ICECandidate ICECandidateInit 1138 expectError bool 1139 }{ 1140 {ICECandidateInit{"", nil, nil, nil}, false}, 1141 {ICECandidateInit{ 1142 "211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0", 1143 nil, nil, nil, 1144 }, false}, 1145 {ICECandidateInit{ 1146 "1234567", 1147 nil, nil, nil, 1148 }, true}, 1149 } 1150 1151 for i, testCase := range testCases { 1152 peerConn, err := NewPeerConnection(Configuration{}) 1153 if err != nil { 1154 t.Errorf("Case %d: got error: %v", i, err) 1155 } 1156 1157 err = peerConn.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer}) 1158 if err != nil { 1159 t.Errorf("Case %d: got error: %v", i, err) 1160 } 1161 1162 if testCase.expectError { 1163 assert.Error(t, peerConn.AddICECandidate(testCase.ICECandidate)) 1164 } else { 1165 assert.NoError(t, peerConn.AddICECandidate(testCase.ICECandidate)) 1166 } 1167 1168 assert.NoError(t, peerConn.Close()) 1169 } 1170 } 1171 1172 const liteOffer = `v=0 1173 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1174 s=- 1175 t=0 0 1176 a=msid-semantic: WMS 1177 a=ice-lite 1178 m=application 47299 DTLS/SCTP 5000 1179 c=IN IP4 192.168.20.129 1180 a=ice-ufrag:1/MvHwjAyVf27aLu 1181 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1182 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 1183 a=mid:data 1184 ` 1185 1186 // this test asserts that if an ice-lite offer is received, 1187 // pion will take the ICE-CONTROLLING role 1188 func TestICELite(t *testing.T) { 1189 peerConnection, err := NewPeerConnection(Configuration{}) 1190 assert.NoError(t, err) 1191 1192 assert.NoError(t, peerConnection.SetRemoteDescription( 1193 SessionDescription{SDP: liteOffer, Type: SDPTypeOffer}, 1194 )) 1195 1196 SDPAnswer, err := peerConnection.CreateAnswer(nil) 1197 assert.NoError(t, err) 1198 1199 assert.NoError(t, peerConnection.SetLocalDescription(SDPAnswer)) 1200 1201 assert.Equal(t, ICERoleControlling, peerConnection.iceTransport.Role(), 1202 "pion did not set state to ICE-CONTROLLED against ice-light offer") 1203 1204 assert.NoError(t, peerConnection.Close()) 1205 } 1206 1207 func TestPeerConnection_TransceiverDirection(t *testing.T) { 1208 lim := test.TimeOut(time.Second * 30) 1209 defer lim.Stop() 1210 1211 report := test.CheckRoutines(t) 1212 defer report() 1213 1214 createTransceiver := func(pc *PeerConnection, dir RTPTransceiverDirection) error { 1215 // AddTransceiverFromKind() can't be used with sendonly 1216 if dir == RTPTransceiverDirectionSendonly { 1217 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1218 1219 track, err := NewTrackLocalStaticSample(codecs[0].RTPCodecCapability, util.MathRandAlpha(16), util.MathRandAlpha(16)) 1220 if err != nil { 1221 return err 1222 } 1223 1224 _, err = pc.AddTransceiverFromTrack(track, []RTPTransceiverInit{ 1225 {Direction: dir}, 1226 }...) 1227 return err 1228 } 1229 1230 _, err := pc.AddTransceiverFromKind( 1231 RTPCodecTypeVideo, 1232 RTPTransceiverInit{Direction: dir}, 1233 ) 1234 return err 1235 } 1236 1237 for _, test := range []struct { 1238 name string 1239 offerDirection RTPTransceiverDirection 1240 answerStartDirection RTPTransceiverDirection 1241 answerFinalDirections []RTPTransceiverDirection 1242 }{ 1243 { 1244 "offer sendrecv answer sendrecv", 1245 RTPTransceiverDirectionSendrecv, 1246 RTPTransceiverDirectionSendrecv, 1247 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1248 }, 1249 { 1250 "offer sendonly answer sendrecv", 1251 RTPTransceiverDirectionSendonly, 1252 RTPTransceiverDirectionSendrecv, 1253 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1254 }, 1255 { 1256 "offer recvonly answer sendrecv", 1257 RTPTransceiverDirectionRecvonly, 1258 RTPTransceiverDirectionSendrecv, 1259 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, 1260 }, 1261 { 1262 "offer sendrecv answer sendonly", 1263 RTPTransceiverDirectionSendrecv, 1264 RTPTransceiverDirectionSendonly, 1265 []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv}, 1266 }, 1267 { 1268 "offer sendonly answer sendonly", 1269 RTPTransceiverDirectionSendonly, 1270 RTPTransceiverDirectionSendonly, 1271 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionRecvonly}, 1272 }, 1273 { 1274 "offer recvonly answer sendonly", 1275 RTPTransceiverDirectionRecvonly, 1276 RTPTransceiverDirectionSendonly, 1277 []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, 1278 }, 1279 { 1280 "offer sendrecv answer recvonly", 1281 RTPTransceiverDirectionSendrecv, 1282 RTPTransceiverDirectionRecvonly, 1283 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, 1284 }, 1285 { 1286 "offer sendonly answer recvonly", 1287 RTPTransceiverDirectionSendonly, 1288 RTPTransceiverDirectionRecvonly, 1289 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, 1290 }, 1291 { 1292 "offer recvonly answer recvonly", 1293 RTPTransceiverDirectionRecvonly, 1294 RTPTransceiverDirectionRecvonly, 1295 []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly}, 1296 }, 1297 } { 1298 offerDirection := test.offerDirection 1299 answerStartDirection := test.answerStartDirection 1300 answerFinalDirections := test.answerFinalDirections 1301 1302 t.Run(test.name, func(t *testing.T) { 1303 pcOffer, pcAnswer, err := newPair() 1304 assert.NoError(t, err) 1305 1306 err = createTransceiver(pcOffer, offerDirection) 1307 assert.NoError(t, err) 1308 1309 offer, err := pcOffer.CreateOffer(nil) 1310 assert.NoError(t, err) 1311 1312 err = createTransceiver(pcAnswer, answerStartDirection) 1313 assert.NoError(t, err) 1314 1315 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 1316 1317 assert.Equal(t, len(answerFinalDirections), len(pcAnswer.GetTransceivers())) 1318 1319 for i, tr := range pcAnswer.GetTransceivers() { 1320 assert.Equal(t, answerFinalDirections[i], tr.Direction()) 1321 } 1322 1323 assert.NoError(t, pcOffer.Close()) 1324 assert.NoError(t, pcAnswer.Close()) 1325 }) 1326 } 1327 } 1328 1329 func TestPeerConnection_SessionID(t *testing.T) { 1330 defer test.TimeOut(time.Second * 10).Stop() 1331 defer test.CheckRoutines(t)() 1332 1333 pcOffer, pcAnswer, err := newPair() 1334 assert.NoError(t, err) 1335 var offerSessionID uint64 1336 var offerSessionVersion uint64 1337 var answerSessionID uint64 1338 var answerSessionVersion uint64 1339 for i := 0; i < 10; i++ { 1340 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1341 offer := pcOffer.LocalDescription().parsed 1342 sessionID := offer.Origin.SessionID 1343 sessionVersion := offer.Origin.SessionVersion 1344 if offerSessionID == 0 { 1345 offerSessionID = sessionID 1346 offerSessionVersion = sessionVersion 1347 } else { 1348 if offerSessionID != sessionID { 1349 t.Errorf("offer[%v] session id mismatch: expected=%v, got=%v", i, offerSessionID, sessionID) 1350 } 1351 if offerSessionVersion+1 != sessionVersion { 1352 t.Errorf("offer[%v] session version mismatch: expected=%v, got=%v", i, offerSessionVersion+1, sessionVersion) 1353 } 1354 offerSessionVersion++ 1355 } 1356 1357 answer := pcAnswer.LocalDescription().parsed 1358 sessionID = answer.Origin.SessionID 1359 sessionVersion = answer.Origin.SessionVersion 1360 if answerSessionID == 0 { 1361 answerSessionID = sessionID 1362 answerSessionVersion = sessionVersion 1363 } else { 1364 if answerSessionID != sessionID { 1365 t.Errorf("answer[%v] session id mismatch: expected=%v, got=%v", i, answerSessionID, sessionID) 1366 } 1367 if answerSessionVersion+1 != sessionVersion { 1368 t.Errorf("answer[%v] session version mismatch: expected=%v, got=%v", i, answerSessionVersion+1, sessionVersion) 1369 } 1370 answerSessionVersion++ 1371 } 1372 } 1373 closePairNow(t, pcOffer, pcAnswer) 1374 } 1375 1376 func TestPeerConnectionNilCallback(t *testing.T) { 1377 pc, err := NewPeerConnection(Configuration{}) 1378 assert.NoError(t, err) 1379 1380 pc.onSignalingStateChange(SignalingStateStable) 1381 pc.OnSignalingStateChange(func(ss SignalingState) { 1382 t.Error("OnSignalingStateChange called") 1383 }) 1384 pc.OnSignalingStateChange(nil) 1385 pc.onSignalingStateChange(SignalingStateStable) 1386 1387 pc.onConnectionStateChange(PeerConnectionStateNew) 1388 pc.OnConnectionStateChange(func(pcs PeerConnectionState) { 1389 t.Error("OnConnectionStateChange called") 1390 }) 1391 pc.OnConnectionStateChange(nil) 1392 pc.onConnectionStateChange(PeerConnectionStateNew) 1393 1394 pc.onICEConnectionStateChange(ICEConnectionStateNew) 1395 pc.OnICEConnectionStateChange(func(ics ICEConnectionState) { 1396 t.Error("OnConnectionStateChange called") 1397 }) 1398 pc.OnICEConnectionStateChange(nil) 1399 pc.onICEConnectionStateChange(ICEConnectionStateNew) 1400 1401 pc.onNegotiationNeeded() 1402 pc.negotiationNeededOp() 1403 pc.OnNegotiationNeeded(func() { 1404 t.Error("OnNegotiationNeeded called") 1405 }) 1406 pc.OnNegotiationNeeded(nil) 1407 pc.onNegotiationNeeded() 1408 pc.negotiationNeededOp() 1409 1410 assert.NoError(t, pc.Close()) 1411 } 1412 1413 func TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote(t *testing.T) { 1414 t.Run("Codec MatchExact", func(t *testing.T) { //nolint:dupl 1415 const remoteSdp = `v=0 1416 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1417 s=- 1418 t=0 0 1419 m=video 60323 UDP/TLS/RTP/SAVPF 98 94 106 1420 a=ice-ufrag:1/MvHwjAyVf27aLu 1421 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1422 a=ice-options:google-ice 1423 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 1424 a=mid:0 1425 a=rtpmap:98 H264/90000 1426 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f 1427 a=rtpmap:94 VP8/90000 1428 a=rtpmap:106 H264/90000 1429 a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1430 a=sendonly 1431 m=video 60323 UDP/TLS/RTP/SAVPF 108 98 125 1432 a=ice-ufrag:1/MvHwjAyVf27aLu 1433 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1434 a=ice-options:google-ice 1435 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 1436 a=mid:1 1437 a=rtpmap:98 H264/90000 1438 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f 1439 a=rtpmap:108 VP8/90000 1440 a=sendonly 1441 a=rtpmap:125 H264/90000 1442 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1443 ` 1444 m := MediaEngine{} 1445 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1446 RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, 1447 PayloadType: 94, 1448 }, RTPCodecTypeVideo)) 1449 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1450 RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, 1451 PayloadType: 98, 1452 }, RTPCodecTypeVideo)) 1453 1454 api := NewAPI(WithMediaEngine(&m)) 1455 pc, err := api.NewPeerConnection(Configuration{}) 1456 assert.NoError(t, err) 1457 assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ 1458 Type: SDPTypeOffer, 1459 SDP: remoteSdp, 1460 })) 1461 ans, _ := pc.CreateAnswer(nil) 1462 assert.NoError(t, pc.SetLocalDescription(ans)) 1463 codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0] 1464 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1465 _, matchType := codecParametersFuzzySearch(codecOfTr1, codecs) 1466 assert.Equal(t, codecMatchExact, matchType) 1467 codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0] 1468 _, matchType = codecParametersFuzzySearch(codecOfTr2, codecs) 1469 assert.Equal(t, codecMatchExact, matchType) 1470 assert.EqualValues(t, 94, codecOfTr2.PayloadType) 1471 assert.NoError(t, pc.Close()) 1472 }) 1473 1474 t.Run("Codec PartialExact Only", func(t *testing.T) { //nolint:dupl 1475 const remoteSdp = `v=0 1476 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1477 s=- 1478 t=0 0 1479 m=video 60323 UDP/TLS/RTP/SAVPF 98 106 1480 a=ice-ufrag:1/MvHwjAyVf27aLu 1481 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1482 a=ice-options:google-ice 1483 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 1484 a=mid:0 1485 a=rtpmap:98 H264/90000 1486 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1487 a=rtpmap:106 H264/90000 1488 a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 1489 a=sendonly 1490 m=video 60323 UDP/TLS/RTP/SAVPF 125 98 1491 a=ice-ufrag:1/MvHwjAyVf27aLu 1492 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1493 a=ice-options:google-ice 1494 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 1495 a=mid:1 1496 a=rtpmap:125 H264/90000 1497 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032 1498 a=rtpmap:98 H264/90000 1499 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f 1500 a=sendonly 1501 ` 1502 m := MediaEngine{} 1503 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1504 RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, 1505 PayloadType: 94, 1506 }, RTPCodecTypeVideo)) 1507 assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ 1508 RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, 1509 PayloadType: 98, 1510 }, RTPCodecTypeVideo)) 1511 1512 api := NewAPI(WithMediaEngine(&m)) 1513 pc, err := api.NewPeerConnection(Configuration{}) 1514 assert.NoError(t, err) 1515 assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ 1516 Type: SDPTypeOffer, 1517 SDP: remoteSdp, 1518 })) 1519 ans, _ := pc.CreateAnswer(nil) 1520 assert.NoError(t, pc.SetLocalDescription(ans)) 1521 codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0] 1522 codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo) 1523 _, matchType := codecParametersFuzzySearch(codecOfTr1, codecs) 1524 assert.Equal(t, codecMatchExact, matchType) 1525 codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0] 1526 _, matchType = codecParametersFuzzySearch(codecOfTr2, codecs) 1527 assert.Equal(t, codecMatchExact, matchType) 1528 // h.264/profile-id=640032 should be remap to 106 as same as transceiver 1 1529 assert.EqualValues(t, 106, codecOfTr2.PayloadType) 1530 assert.NoError(t, pc.Close()) 1531 }) 1532 } 1533 1534 // Assert that remote candidates with an unknown transport are ignored and logged. 1535 // This allows us to accept SessionDescriptions with proprietary candidates 1536 // like `ssltcp`. 1537 func TestInvalidCandidateTransport(t *testing.T) { 1538 const ( 1539 sslTCPCandidate = `candidate:1 1 ssltcp 1 127.0.0.1 443 typ host generation 0` 1540 sslTCPOffer = `v=0 1541 o=- 0 2 IN IP4 127.0.0.1 1542 s=- 1543 t=0 0 1544 a=msid-semantic: WMS 1545 m=application 9 DTLS/SCTP 5000 1546 c=IN IP4 0.0.0.0 1547 a=ice-ufrag:1/MvHwjAyVf27aLu 1548 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 1549 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 1550 a=mid:0 1551 a=` + sslTCPCandidate + "\n" 1552 ) 1553 1554 peerConnection, err := NewPeerConnection(Configuration{}) 1555 assert.NoError(t, err) 1556 1557 assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: sslTCPOffer})) 1558 assert.NoError(t, peerConnection.AddICECandidate(ICECandidateInit{Candidate: sslTCPCandidate})) 1559 1560 assert.NoError(t, peerConnection.Close()) 1561 } 1562 1563 func TestOfferWithInactiveDirection(t *testing.T) { 1564 const remoteSDP = `v=0 1565 o=- 4596489990601351948 2 IN IP4 127.0.0.1 1566 s=- 1567 t=0 0 1568 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 1569 a=group:BUNDLE 0 1 1570 a=msid-semantic:WMS * 1571 m=video 9 UDP/TLS/RTP/SAVPF 97 1572 c=IN IP4 0.0.0.0 1573 a=inactive 1574 a=ice-pwd:05d682b2902af03db90d9a9a5f2f8d7f 1575 a=ice-ufrag:93cc7e4d 1576 a=mid:0 1577 a=rtpmap:97 H264/90000 1578 a=setup:actpass 1579 a=ssrc:1455629982 cname:{61fd3093-0326-4b12-8258-86bdc1fe677a} 1580 ` 1581 1582 peerConnection, err := NewPeerConnection(Configuration{}) 1583 assert.NoError(t, err) 1584 1585 assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: remoteSDP})) 1586 assert.Equal(t, RTPTransceiverDirectionInactive, peerConnection.rtpTransceivers[0].direction.Load().(RTPTransceiverDirection)) //nolint:forcetypeassert 1587 1588 assert.NoError(t, peerConnection.Close()) 1589 } 1590 1591 func TestPeerConnectionState(t *testing.T) { 1592 pc, err := NewPeerConnection(Configuration{}) 1593 assert.NoError(t, err) 1594 assert.Equal(t, PeerConnectionStateNew, pc.ConnectionState()) 1595 1596 pc.updateConnectionState(ICEConnectionStateChecking, DTLSTransportStateNew) 1597 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1598 1599 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateNew) 1600 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1601 1602 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnecting) 1603 assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState()) 1604 1605 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnected) 1606 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1607 1608 pc.updateConnectionState(ICEConnectionStateCompleted, DTLSTransportStateConnected) 1609 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1610 1611 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateClosed) 1612 assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState()) 1613 1614 pc.updateConnectionState(ICEConnectionStateDisconnected, DTLSTransportStateConnected) 1615 assert.Equal(t, PeerConnectionStateDisconnected, pc.ConnectionState()) 1616 1617 pc.updateConnectionState(ICEConnectionStateFailed, DTLSTransportStateConnected) 1618 assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState()) 1619 1620 pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateFailed) 1621 assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState()) 1622 1623 assert.NoError(t, pc.Close()) 1624 assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState()) 1625 }