Add fully working RTCIceServer with 100% unit-test code coverage

This commit is contained in:
Konstantin Itskov
2018-08-26 16:46:51 -04:00
committed by Sean DuBois
parent c32dd50b97
commit e8364dc5e9
12 changed files with 266 additions and 208 deletions

View File

@@ -5,122 +5,93 @@ import (
"fmt"
)
var (
// ErrUnknownType indicates a Unknown info
ErrUnknownType = errors.New("unknown")
ErrNoConfig = errors.New("no configuration provided")
ErrConnectionClosed = errors.New("connection closed")
ErrCertificateExpired = errors.New("x509Cert expired")
ErrNoTurnCredencials = errors.New("turn server credentials required")
ErrTurnCredencials = errors.New("invalid turn server credentials")
ErrExistingTrack = errors.New("track aready exists")
ErrPrivateKeyType = errors.New("private key type not supported")
ErrModifyingPeerIdentity = errors.New("peerIdentity cannot be modified")
ErrModifyingCertificates = errors.New("certificates cannot be modified")
ErrModifyingBundlePolicy = errors.New("bundle policy cannot be modified")
ErrModifyingRtcpMuxPolicy = errors.New("rtcp mux policy cannot be modified")
ErrModifyingIceCandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
ErrInvalidValue = errors.New("invalid value")
ErrMaxDataChannels = errors.New("maximum number of datachannels reached")
)
// InvalidStateError indicates the object is in an invalid state.
type InvalidStateError struct {
Err error
}
func (e InvalidStateError) Error() string {
func (e *InvalidStateError) Error() string {
return fmt.Sprintf("webrtc: InvalidStateError: %v", e.Err)
}
// Types of InvalidStateErrors
var (
ErrConnectionClosed = errors.New("connection closed")
)
// UnknownError indicates the operation failed for an unknown transient reason
type UnknownError struct {
Err error
}
func (e UnknownError) Error() string {
func (e *UnknownError) Error() string {
return fmt.Sprintf("webrtc: UnknownError: %v", e.Err)
}
// Types of UnknownErrors
var (
ErrNoConfig = errors.New("no configuration provided")
)
// InvalidAccessError indicates the object does not support the operation or argument.
type InvalidAccessError struct {
Err error
}
func (e InvalidAccessError) Error() string {
func (e *InvalidAccessError) Error() string {
return fmt.Sprintf("webrtc: InvalidAccessError: %v", e.Err)
}
// Types of InvalidAccessErrors
var (
ErrCertificateExpired = errors.New("x509Cert expired")
ErrNoTurnCredencials = errors.New("turn server credentials required")
ErrTurnCredencials = errors.New("invalid turn server credentials")
ErrExistingTrack = errors.New("track aready exists")
)
// NotSupportedError indicates the operation is not supported.
type NotSupportedError struct {
Err error
}
func (e NotSupportedError) Error() string {
func (e *NotSupportedError) Error() string {
return fmt.Sprintf("webrtc: NotSupportedError: %v", e.Err)
}
// Types of NotSupportedErrors
var (
ErrPrivateKeyType = errors.New("private key type not supported")
)
// InvalidModificationError indicates the object can not be modified in this way.
type InvalidModificationError struct {
Err error
}
func (e InvalidModificationError) Error() string {
func (e *InvalidModificationError) Error() string {
return fmt.Sprintf("webrtc: InvalidModificationError: %v", e.Err)
}
// Types of InvalidModificationErrors
var (
ErrModifyingPeerIdentity = errors.New("peerIdentity cannot be modified")
ErrModifyingCertificates = errors.New("certificates cannot be modified")
ErrModifyingBundlePolicy = errors.New("bundle policy cannot be modified")
ErrModifyingRtcpMuxPolicy = errors.New("rtcp mux policy cannot be modified")
ErrModifyingIceCandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
)
// SyntaxError indicates the string did not match the expected pattern.
type SyntaxError struct {
Err error
}
func (e SyntaxError) Error() string {
func (e *SyntaxError) Error() string {
return fmt.Sprintf("webrtc: SyntaxError: %v", e.Err)
}
// Types of SyntaxErrors
var ()
// TypeError indicates an issue with a supplied value
type TypeError struct {
Err error
}
func (e TypeError) Error() string {
func (e *TypeError) Error() string {
return fmt.Sprintf("webrtc: TypeError: %v", e.Err)
}
// Types of TypeError
var (
ErrInvalidValue = errors.New("invalid value")
)
// OperationError indicates an issue with execution
type OperationError struct {
Err error
}
func (e OperationError) Error() string {
func (e *OperationError) Error() string {
return fmt.Sprintf("webrtc: OperationError: %v", e.Err)
}
// Types of OperationError
var (
ErrMaxDataChannels = errors.New("maximum number of datachannels reached")
)
// ErrUnknownType indicates a Unknown info
var ErrUnknownType = errors.New("Unknown")

View File

@@ -181,14 +181,14 @@ func (pc *RTCPeerConnection) NewRTCTrack(payloadType uint8, id, label string) (*
// AddTrack adds a RTCTrack to the RTCPeerConnection
func (pc *RTCPeerConnection) AddTrack(track *RTCTrack) (*RTCRtpSender, error) {
if pc.IsClosed {
return nil, InvalidStateError{Err: ErrConnectionClosed}
return nil, &InvalidStateError{ErrConnectionClosed}
}
for _, transceiver := range pc.rtpTransceivers {
if transceiver.Sender.Track == nil {
continue
}
if track.ID == transceiver.Sender.Track.ID {
return nil, InvalidAccessError{Err: ErrExistingTrack}
return nil, &InvalidAccessError{ErrExistingTrack}
}
}
var transceiver *RTCRtpTransceiver

View File

@@ -12,6 +12,8 @@ import (
"github.com/pkg/errors"
)
const Unknown = iota
// OutboundCallback is the user defined Callback that is called when ICE traffic needs to sent
type OutboundCallback func(raw []byte, local *stun.TransportAddr, remote *net.UDPAddr)

View File

@@ -1,8 +1,32 @@
package ice
import (
"errors"
"fmt"
"github.com/pkg/errors"
)
// Types of InvalidStateErrors
var (
// ErrUnknownType indicates a Unknown info
ErrUnknownType = errors.New("Unknown")
// ErrServerType indicates the scheme type could not be parsed
ErrSchemeType = errors.New("unknown scheme type")
// ErrSTUNQuery indicates query arguments are provided in a STUN URL
ErrSTUNQuery = errors.New("queries not supported in stun address")
// ErrInvalidQuery indicates an malformed query is provided
ErrInvalidQuery = errors.New("invalid query")
// ErrHost indicates malformed hostname is provided
ErrHost = errors.New("invalid hostname")
// ErrPort indicates malformed port is provided
ErrPort = errors.New("invalid port")
// ErrProtoType indicates an unsupported transport type was provided
ErrProtoType = errors.New("invalid transport protocol type")
)
// SyntaxError indicates the string did not match the expected pattern.
@@ -10,39 +34,24 @@ type SyntaxError struct {
Err error
}
func (e SyntaxError) Error() string {
return fmt.Sprintf("ice: SyntaxError: %v", e.Err)
func (e *SyntaxError) Error() string {
return fmt.Sprintf("ice: SyntaxError: %#v", e.Err)
}
// Types of InvalidStateErrors
var (
// ErrServerType indicates the scheme type could not be parsed
ErrSchemeType = errors.New("unknown scheme type")
// ErrSTUNQuery indicates query arguments are provided in a STUN URL
ErrSTUNQuery = errors.New("queries not supported in stun address")
// ErrInvalidQuery indicates an unsupported query is provided
ErrInvalidQuery = errors.New("invalid query")
// ErrProtoType indicates an unsupported transport type was provided
ErrProtoType = errors.New("invalid transport protocol type")
// ErrHost indicates the server hostname could not be parsed
ErrHost = errors.New("invalid hostname")
// ErrPort indicates the server port could not be parsed
ErrPort = errors.New("invalid port")
)
// UnknownError indicates the operation failed for an unknown transient reason
type UnknownError struct {
Err error
}
func (e UnknownError) Error() string {
func (e *UnknownError) Error() string {
return fmt.Sprintf("ice: UnknownError: %v", e.Err)
}
// Types of UnknownErrors
var ()
// NotSupportedError indicates the operation is not supported.
type NotSupportedError struct {
Err error
}
func (e *NotSupportedError) Error() string {
return fmt.Sprintf("ice: NotSupportedError: %v", e.Err)
}

View File

@@ -1,10 +1,5 @@
package ice
import "github.com/pkg/errors"
// ErrUnknownType indicates a Unknown info
var ErrUnknownType = errors.New("Unknown")
// ConnectionState is an enum showing the state of a ICE Connection
type ConnectionState int

View File

@@ -95,20 +95,18 @@ type URL struct {
func ParseURL(raw string) (*URL, error) {
rawParts, err := url.Parse(raw)
if err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
var u URL
u.Scheme = NewSchemeType(rawParts.Scheme)
if u.Scheme == SchemeType(0) {
return nil, SyntaxError{Err: ErrSchemeType}
if u.Scheme == SchemeType(Unknown) {
return nil, &SyntaxError{ErrSchemeType}
}
var rawPort string
u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque)
if err != nil {
switch e := err.(type) {
case *net.AddrError:
if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil {
if e, ok := err.(*net.AddrError); ok {
if e.Err == "missing port in address" {
nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque
switch {
@@ -126,59 +124,85 @@ func ParseURL(raw string) (*URL, error) {
return ParseURL(nextRawURL)
}
}
return nil, SyntaxError{Err: ErrHost}
case *strconv.NumError:
return nil, SyntaxError{Err: ErrPort}
default:
return nil, UnknownError{Err: err}
}
return nil, &UnknownError{err}
}
if u.Host == "" {
return nil, &SyntaxError{ErrHost}
}
if u.Port, err = strconv.Atoi(rawPort); err != nil {
return nil, SyntaxError{Err: ErrPort}
return nil, &SyntaxError{ErrPort}
}
switch {
case u.Scheme == SchemeTypeSTUN:
u.Proto = ProtoTypeUDP
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil || (err == nil && len(qArgs) > 0) {
return nil, SyntaxError{Err: ErrSTUNQuery}
return nil, &SyntaxError{ErrSTUNQuery}
}
u.Proto = ProtoTypeUDP
case u.Scheme == SchemeTypeSTUNS:
u.Proto = ProtoTypeTCP
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil || (err == nil && len(qArgs) > 0) {
return nil, SyntaxError{Err: ErrSTUNQuery}
}
case u.Scheme == SchemeTypeTURN:
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil {
return nil, SyntaxError{Err: ErrInvalidQuery}
}
if proto := qArgs.Get("transport"); proto != "" {
if u.Proto = NewProtoType(proto); u.Proto == ProtoType(0) {
return nil, SyntaxError{Err: ErrProtoType}
}
break
}
u.Proto = ProtoTypeUDP
case u.Scheme == SchemeTypeTURNS:
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil {
return nil, SyntaxError{Err: ErrInvalidQuery}
}
if proto := qArgs.Get("transport"); proto != "" {
if u.Proto = NewProtoType(proto); u.Proto == ProtoType(0) {
return nil, SyntaxError{Err: ErrProtoType}
}
break
return nil, &SyntaxError{ErrSTUNQuery}
}
u.Proto = ProtoTypeTCP
case u.Scheme == SchemeTypeTURN:
proto, err := parseProto(rawParts.RawQuery)
if err != nil {
return nil, err
}
u.Proto = proto
if u.Proto == ProtoType(Unknown) {
u.Proto = ProtoTypeUDP
}
case u.Scheme == SchemeTypeTURNS:
proto, err := parseProto(rawParts.RawQuery)
if err != nil {
return nil, err
}
u.Proto = proto
if u.Proto == ProtoType(Unknown) {
u.Proto = ProtoTypeTCP
}
}
return &u, nil
}
func parseProto(raw string) (ProtoType, error) {
qArgs, err := url.ParseQuery(raw)
if err != nil || len(qArgs) > 1 {
return ProtoType(Unknown), &SyntaxError{ErrInvalidQuery}
}
var proto ProtoType
if rawProto := qArgs.Get("transport"); rawProto != "" {
if proto = NewProtoType(rawProto); proto == ProtoType(0) {
return ProtoType(Unknown), &NotSupportedError{ErrProtoType}
}
return proto, nil
}
if len(qArgs) > 0 {
return ProtoType(Unknown), &SyntaxError{ErrInvalidQuery}
}
return proto, nil
}
func (u URL) String() string {
rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port))
if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS {
rawURL += "?transport=" + u.Proto.String()
}
return rawURL
}
func (u URL) IsSecure() bool {
return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS
}

View File

@@ -9,20 +9,22 @@ import (
func TestParseURL(t *testing.T) {
t.Run("Success", func(t *testing.T) {
testCases := []struct {
rawURL string
expectedScheme SchemeType
expectedHost string
expectedPort int
expectedProto ProtoType
rawURL string
expectedURLString string
expectedScheme SchemeType
expectedSecure bool
expectedHost string
expectedPort int
expectedProto ProtoType
}{
{"stun:google.de", SchemeTypeSTUN, "google.de", 3478, ProtoTypeUDP},
{"stun:google.de:1234", SchemeTypeSTUN, "google.de", 1234, ProtoTypeUDP},
{"stuns:google.de", SchemeTypeSTUNS, "google.de", 5349, ProtoTypeTCP},
{"stun:[::1]:123", SchemeTypeSTUN, "::1", 123, ProtoTypeUDP},
{"turn:google.de", SchemeTypeTURN, "google.de", 3478, ProtoTypeUDP},
{"turns:google.de", SchemeTypeTURNS, "google.de", 5349, ProtoTypeTCP},
{"turn:google.de?transport=udp", SchemeTypeTURN, "google.de", 3478, ProtoTypeUDP},
{"turn:google.de?transport=tcp", SchemeTypeTURN, "google.de", 3478, ProtoTypeTCP},
{"stun:google.de", "stun:google.de:3478", SchemeTypeSTUN, false, "google.de", 3478, ProtoTypeUDP},
{"stun:google.de:1234", "stun:google.de:1234", SchemeTypeSTUN, false, "google.de", 1234, ProtoTypeUDP},
{"stuns:google.de", "stuns:google.de:5349", SchemeTypeSTUNS, true, "google.de", 5349, ProtoTypeTCP},
{"stun:[::1]:123", "stun:[::1]:123", SchemeTypeSTUN, false, "::1", 123, ProtoTypeUDP},
{"turn:google.de", "turn:google.de:3478?transport=udp", SchemeTypeTURN, false, "google.de", 3478, ProtoTypeUDP},
{"turns:google.de", "turns:google.de:5349?transport=tcp", SchemeTypeTURNS, true, "google.de", 5349, ProtoTypeTCP},
{"turn:google.de?transport=udp", "turn:google.de:3478?transport=udp", SchemeTypeTURN, false, "google.de", 3478, ProtoTypeUDP},
{"turns:google.de?transport=tcp", "turns:google.de:5349?transport=tcp", SchemeTypeTURNS, true, "google.de", 5349, ProtoTypeTCP},
}
for i, testCase := range testCases {
@@ -33,6 +35,8 @@ func TestParseURL(t *testing.T) {
}
assert.Equal(t, testCase.expectedScheme, url.Scheme, "testCase: %d %v", i, testCase)
assert.Equal(t, testCase.expectedURLString, url.String(), "testCase: %d %v", i, testCase)
assert.Equal(t, testCase.expectedSecure, url.IsSecure(), "testCase: %d %v", i, testCase)
assert.Equal(t, testCase.expectedHost, url.Host, "testCase: %d %v", i, testCase)
assert.Equal(t, testCase.expectedPort, url.Port, "testCase: %d %v", i, testCase)
assert.Equal(t, testCase.expectedProto, url.Proto, "testCase: %d %v", i, testCase)
@@ -43,14 +47,19 @@ func TestParseURL(t *testing.T) {
rawURL string
expectedErr error
}{
{"", SyntaxError{Err: ErrSchemeType}},
{":::", UnknownError{Err: errors.New("parse :::: missing protocol scheme")}},
{"google.de", SyntaxError{Err: ErrSchemeType}},
{"stun:", SyntaxError{Err: ErrHost}},
{"stun:google.de:abc", SyntaxError{Err: ErrPort}},
{"stun:google.de?transport=udp", SyntaxError{Err: ErrSTUNQuery}},
{"turn:google.de?trans=udp", SyntaxError{Err: ErrInvalidQuery}},
{"turn:google.de?transport=ip", SyntaxError{Err: ErrProtoType}},
{"", &SyntaxError{ErrSchemeType}},
{":::", &UnknownError{errors.New("parse :::: missing protocol scheme")}},
{"stun:[::1]:123:", &UnknownError{errors.New("address [::1]:123:: too many colons in address")}},
{"stun:[::1]:123a", &SyntaxError{ErrPort}},
{"google.de", &SyntaxError{ErrSchemeType}},
{"stun:", &SyntaxError{ErrHost}},
{"stun:google.de:abc", &SyntaxError{ErrPort}},
{"stun:google.de?transport=udp", &SyntaxError{ErrSTUNQuery}},
{"stuns:google.de?transport=udp", &SyntaxError{ErrSTUNQuery}},
{"turn:google.de?trans=udp", &SyntaxError{ErrInvalidQuery}},
{"turns:google.de?trans=udp", &SyntaxError{ErrInvalidQuery}},
{"turns:google.de?transport=udp&another=1", &SyntaxError{ErrInvalidQuery}},
{"turn:google.de?transport=ip", &NotSupportedError{ErrProtoType}},
}
for i, testCase := range testCases {

View File

@@ -21,7 +21,7 @@ type RTCCertificate struct {
func GenerateCertificate(secretKey crypto.PrivateKey) (*RTCCertificate, error) {
origin := make([]byte, 16)
if _, err := rand.Read(origin); err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
// Max random value, a 130-bits integer, i.e 2^130 - 1
@@ -29,7 +29,7 @@ func GenerateCertificate(secretKey crypto.PrivateKey) (*RTCCertificate, error) {
maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
serialNumber, err := rand.Int(rand.Reader, maxBigInt)
if err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
temp := &x509.Certificate{
@@ -56,22 +56,22 @@ func GenerateCertificate(secretKey crypto.PrivateKey) (*RTCCertificate, error) {
temp.SignatureAlgorithm = x509.SHA256WithRSA
certDER, err = x509.CreateCertificate(rand.Reader, temp, temp, pk, sk)
if err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
case *ecdsa.PrivateKey:
pk := sk.Public()
temp.SignatureAlgorithm = x509.ECDSAWithSHA256
certDER, err = x509.CreateCertificate(rand.Reader, temp, temp, pk, sk)
if err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
default:
return nil, NotSupportedError{Err: ErrPrivateKeyType}
return nil, &NotSupportedError{ErrPrivateKeyType}
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
return nil, UnknownError{Err: err}
return nil, &UnknownError{err}
}
return &RTCCertificate{secretKey: secretKey, x509Cert: cert}, nil

View File

@@ -65,11 +65,11 @@ type RTCDataChannelInit struct {
// CreateDataChannel creates a new RTCDataChannel object with the given label and optitional options.
func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataChannelInit) (*RTCDataChannel, error) {
if pc.IsClosed {
return nil, InvalidStateError{Err: ErrConnectionClosed}
return nil, &InvalidStateError{ErrConnectionClosed}
}
if len(label) > 65535 {
return nil, TypeError{Err: ErrInvalidValue}
return nil, &TypeError{ErrInvalidValue}
}
// Defaults
@@ -95,12 +95,12 @@ func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataCha
}
if id > 65534 {
return nil, TypeError{Err: ErrInvalidValue}
return nil, &TypeError{ErrInvalidValue}
}
if pc.sctp.State == RTCSctpTransportStateConnected &&
id >= pc.sctp.MaxChannels {
return nil, OperationError{Err: ErrMaxDataChannels}
return nil, &OperationError{ErrMaxDataChannels}
}
_ = ordered // TODO
@@ -132,13 +132,13 @@ func (pc *RTCPeerConnection) generateDataChannelID(client bool) (uint16, error)
return id, nil
}
}
return 0, OperationError{Err: ErrMaxDataChannels}
return 0, &OperationError{ErrMaxDataChannels}
}
// SendOpenChannelMessage is a test to send OpenChannel manually
func (d *RTCDataChannel) SendOpenChannelMessage() error {
if err := d.rtcPeerConnection.networkManager.SendOpenChannelMessage(d.ID, d.Label); err != nil {
return UnknownError{Err: err}
return &UnknownError{err}
}
return nil
@@ -147,7 +147,7 @@ func (d *RTCDataChannel) SendOpenChannelMessage() error {
// Send sends the passed message to the DataChannel peer
func (d *RTCDataChannel) Send(p datachannel.Payload) error {
if err := d.rtcPeerConnection.networkManager.SendDataChannelMessage(p, d.ID); err != nil {
return UnknownError{Err: err}
return &UnknownError{err}
}
return nil
}

View File

@@ -21,29 +21,29 @@ func (s RTCIceServer) validate() error {
for i := range s.URLs {
url, err := s.parseURL(i)
if err != nil {
return err // TODO Need proper error
return err
}
if url.Scheme == ice.SchemeTypeTURN {
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2)
if s.Username == "" || s.Credential == nil {
return InvalidAccessError{Err: ErrNoTurnCredencials}
return &InvalidAccessError{ErrNoTurnCredencials}
}
switch s.CredentialType {
case RTCIceCredentialTypePassword:
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.3)
if _, ok := s.Credential.(string); !ok {
return InvalidAccessError{Err: ErrTurnCredencials}
return &InvalidAccessError{ErrTurnCredencials}
}
case RTCIceCredentialTypeOauth:
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.4)
if _, ok := s.Credential.(RTCOAuthCredential); !ok {
return InvalidAccessError{Err: ErrTurnCredencials}
return &InvalidAccessError{ErrTurnCredencials}
}
default:
return InvalidAccessError{Err: ErrTurnCredencials}
return &InvalidAccessError{ErrTurnCredencials}
}
}
}

View File

@@ -1,32 +1,78 @@
package webrtc
import (
"github.com/pions/webrtc/pkg/ice"
"github.com/stretchr/testify/assert"
"testing"
)
func TestRTCIceServer_validate(t *testing.T) {
assert.Nil(t, (RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
}).validate())
t.Run("Success", func(t *testing.T) {
testCases := []struct {
iceServer RTCIceServer
expectedValidate bool
}{
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
Username: "unittest",
Credential: "placeholder",
CredentialType: RTCIceCredentialTypePassword,
}, true},
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
Username: "unittest",
Credential: RTCOAuthCredential{
MacKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ5VhNDgeMR3+ZlZ35byg972fW8QjpEl7bx91YLBPFsIhsxloWcXPhA==",
},
CredentialType: RTCIceCredentialTypeOauth,
}, true},
}
// pc, err := New(RTCConfiguration{})
// assert.Nil(t, err)
//
// expected := RTCConfiguration{
// IceServers: []RTCIceServer{},
// IceTransportPolicy: RTCIceTransportPolicyAll,
// BundlePolicy: RTCBundlePolicyBalanced,
// RtcpMuxPolicy: RTCRtcpMuxPolicyRequire,
// Certificates: []RTCCertificate{},
// IceCandidatePoolSize: 0,
// }
// actual := pc.GetConfiguration()
// assert.True(t, &expected != &actual)
// assert.Equal(t, expected.IceServers, actual.IceServers)
// assert.Equal(t, expected.IceTransportPolicy, actual.IceTransportPolicy)
// assert.Equal(t, expected.BundlePolicy, actual.BundlePolicy)
// assert.Equal(t, expected.RtcpMuxPolicy, actual.RtcpMuxPolicy)
// assert.NotEqual(t, len(expected.Certificates), len(actual.Certificates))
// assert.Equal(t, expected.IceCandidatePoolSize, actual.IceCandidatePoolSize)
for i, testCase := range testCases {
assert.Nil(t, testCase.iceServer.validate(), "testCase: %d %v", i, testCase)
}
})
t.Run("Failure", func(t *testing.T) {
testCases := []struct {
iceServer RTCIceServer
expectedErr error
}{
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
}, &InvalidAccessError{ErrNoTurnCredencials}},
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
Username: "unittest",
Credential: false,
CredentialType: RTCIceCredentialTypePassword,
}, &InvalidAccessError{ErrTurnCredencials}},
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
Username: "unittest",
Credential: false,
CredentialType: RTCIceCredentialTypeOauth,
}, &InvalidAccessError{ErrTurnCredencials}},
{RTCIceServer{
URLs: []string{"turn:192.158.29.39?transport=udp"},
Username: "unittest",
Credential: false,
CredentialType: Unknown,
}, &InvalidAccessError{ErrTurnCredencials}},
{RTCIceServer{
URLs: []string{"stun:google.de?transport=udp"},
Username: "unittest",
Credential: false,
CredentialType: RTCIceCredentialTypeOauth,
}, &ice.SyntaxError{Err: ice.ErrSTUNQuery}},
}
for i, testCase := range testCases {
assert.EqualError(t,
testCase.iceServer.validate(),
testCase.expectedErr.Error(),
"testCase: %d %v", i, testCase,
)
}
})
}

View File

@@ -17,6 +17,8 @@ import (
"time"
)
const Unknown = iota
func init() {
// TODO Must address this and either revert or delete.
// rand.Seed(time.Now().UTC().UnixNano())
@@ -119,13 +121,13 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
now := time.Now()
for _, x509Cert := range configuration.Certificates {
if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
return InvalidAccessError{Err: ErrCertificateExpired}
return &InvalidAccessError{ErrCertificateExpired}
}
}
} else {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return UnknownError{Err: err}
return &UnknownError{err}
}
certificate, err := GenerateCertificate(sk)
if err != nil {
@@ -134,11 +136,11 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
pc.configuration.Certificates = []RTCCertificate{*certificate}
}
if configuration.BundlePolicy != 0 {
if configuration.BundlePolicy != RTCBundlePolicy(Unknown) {
pc.configuration.BundlePolicy = configuration.BundlePolicy
}
if configuration.RtcpMuxPolicy != 0 {
if configuration.RtcpMuxPolicy != RTCRtcpMuxPolicy(Unknown) {
pc.configuration.RtcpMuxPolicy = configuration.RtcpMuxPolicy
}
@@ -146,7 +148,7 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
pc.configuration.IceCandidatePoolSize = configuration.IceCandidatePoolSize
}
if configuration.IceTransportPolicy != 0 {
if configuration.IceTransportPolicy != RTCIceTransportPolicy(Unknown) {
pc.configuration.IceTransportPolicy = configuration.IceTransportPolicy
}
@@ -167,13 +169,13 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
func (pc *RTCPeerConnection) SetConfiguration(configuration RTCConfiguration) error {
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
if pc.IsClosed {
return InvalidStateError{Err: ErrConnectionClosed}
return &InvalidStateError{ErrConnectionClosed}
}
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
if configuration.PeerIdentity != "" {
if configuration.PeerIdentity != pc.configuration.PeerIdentity {
return InvalidModificationError{Err: ErrModifyingPeerIdentity}
return &InvalidModificationError{ErrModifyingPeerIdentity}
}
pc.configuration.PeerIdentity = configuration.PeerIdentity
}
@@ -181,29 +183,29 @@ func (pc *RTCPeerConnection) SetConfiguration(configuration RTCConfiguration) er
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
if len(configuration.Certificates) > 0 {
if len(configuration.Certificates) != len(pc.configuration.Certificates) {
return InvalidModificationError{Err: ErrModifyingCertificates}
return &InvalidModificationError{ErrModifyingCertificates}
}
for i, certificate := range configuration.Certificates {
if !pc.configuration.Certificates[i].Equals(certificate) {
return InvalidModificationError{Err: ErrModifyingCertificates}
return &InvalidModificationError{ErrModifyingCertificates}
}
}
pc.configuration.Certificates = configuration.Certificates
}
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
if configuration.BundlePolicy != 0 {
if configuration.BundlePolicy != RTCBundlePolicy(Unknown) {
if configuration.BundlePolicy != pc.configuration.BundlePolicy {
return InvalidModificationError{Err: ErrModifyingBundlePolicy}
return &InvalidModificationError{ErrModifyingBundlePolicy}
}
pc.configuration.BundlePolicy = configuration.BundlePolicy
}
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
if configuration.RtcpMuxPolicy != 0 {
if configuration.RtcpMuxPolicy != RTCRtcpMuxPolicy(Unknown) {
if configuration.RtcpMuxPolicy != pc.configuration.RtcpMuxPolicy {
return InvalidModificationError{Err: ErrModifyingRtcpMuxPolicy}
return &InvalidModificationError{ErrModifyingRtcpMuxPolicy}
}
pc.configuration.RtcpMuxPolicy = configuration.RtcpMuxPolicy
}
@@ -212,13 +214,13 @@ func (pc *RTCPeerConnection) SetConfiguration(configuration RTCConfiguration) er
if configuration.IceCandidatePoolSize != 0 {
if pc.configuration.IceCandidatePoolSize != configuration.IceCandidatePoolSize &&
pc.LocalDescription() != nil {
return InvalidModificationError{Err: ErrModifyingIceCandidatePoolSize}
return &InvalidModificationError{ErrModifyingIceCandidatePoolSize}
}
pc.configuration.IceCandidatePoolSize = configuration.IceCandidatePoolSize
}
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #8)
if configuration.IceTransportPolicy != 0 {
if configuration.IceTransportPolicy != RTCIceTransportPolicy(Unknown) {
pc.configuration.IceTransportPolicy = configuration.IceTransportPolicy
}
@@ -435,7 +437,7 @@ func (pc *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDe
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.IsClosed {
return RTCSessionDescription{}, InvalidStateError{Err: ErrConnectionClosed}
return RTCSessionDescription{}, &InvalidStateError{ErrConnectionClosed}
}
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
@@ -474,7 +476,7 @@ func (pc *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSession
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.IsClosed {
return RTCSessionDescription{}, InvalidStateError{Err: ErrConnectionClosed}
return RTCSessionDescription{}, &InvalidStateError{ErrConnectionClosed}
}
candidates := pc.generateLocalCandidates()