mirror of
https://github.com/pion/webrtc.git
synced 2025-12-24 11:51:03 +08:00
Add fully working RTCIceServer with 100% unit-test code coverage
This commit is contained in:
committed by
Sean DuBois
parent
c32dd50b97
commit
e8364dc5e9
83
errors.go
83
errors.go
@@ -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")
|
||||
|
||||
4
media.go
4
media.go
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
104
pkg/ice/url.go
104
pkg/ice/url.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user