mirror of
https://github.com/pion/webrtc.git
synced 2025-10-29 09:52:51 +08:00
Add json support to ICECredentialType
This allows the interface to be slightly closer to upstream interfaces.
This commit is contained in:
committed by
Sean DuBois
parent
cecdbc9980
commit
c9aaf819bc
@@ -41,7 +41,7 @@ func TestConfiguration_getICEServers(t *testing.T) {
|
||||
|
||||
func TestConfigurationJSON(t *testing.T) {
|
||||
j := `{
|
||||
"iceServers": [{"URLs": ["turn:turn.example.org"],
|
||||
"iceServers": [{"urls": ["turn:turn.example.org"],
|
||||
"username": "jch",
|
||||
"credential": "topsecret"
|
||||
}],
|
||||
|
||||
@@ -233,6 +233,9 @@ var (
|
||||
|
||||
errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state")
|
||||
|
||||
errInvalidICECredentialTypeString = errors.New("invalid ICECredentialType")
|
||||
errInvalidICEServer = errors.New("invalid ICEServer")
|
||||
|
||||
errICETransportNotInNew = errors.New("ICETransport can only be called in ICETransportStateNew")
|
||||
|
||||
errCertificatePEMFormatError = errors.New("bad Certificate PEM format")
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ICECredentialType indicates the type of credentials used to connect to
|
||||
// an ICE server.
|
||||
type ICECredentialType int
|
||||
@@ -7,7 +12,7 @@ type ICECredentialType int
|
||||
const (
|
||||
// ICECredentialTypePassword describes username and password based
|
||||
// credentials as described in https://tools.ietf.org/html/rfc5389.
|
||||
ICECredentialTypePassword ICECredentialType = iota
|
||||
ICECredentialTypePassword ICECredentialType = iota + 1
|
||||
|
||||
// ICECredentialTypeOauth describes token based credential as described
|
||||
// in https://tools.ietf.org/html/rfc7635.
|
||||
@@ -33,6 +38,8 @@ func newICECredentialType(raw string) ICECredentialType {
|
||||
|
||||
func (t ICECredentialType) String() string {
|
||||
switch t {
|
||||
case Unknown:
|
||||
return ""
|
||||
case ICECredentialTypePassword:
|
||||
return iceCredentialTypePasswordStr
|
||||
case ICECredentialTypeOauth:
|
||||
@@ -41,3 +48,26 @@ func (t ICECredentialType) String() string {
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (t *ICECredentialType) UnmarshalJSON(b []byte) error {
|
||||
var val string
|
||||
var tmp ICECredentialType
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp = newICECredentialType(val)
|
||||
|
||||
if (tmp == ICECredentialType(Unknown)) && (val != "") {
|
||||
return fmt.Errorf("%w: (%s)", errInvalidICECredentialTypeString, val)
|
||||
}
|
||||
|
||||
*t = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (t ICECredentialType) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -41,3 +42,59 @@ func TestICECredentialType_String(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestICECredentialType_new(t *testing.T) {
|
||||
testCases := []struct {
|
||||
credentialType ICECredentialType
|
||||
expectedString string
|
||||
}{
|
||||
{ICECredentialTypePassword, "password"},
|
||||
{ICECredentialTypeOauth, "oauth"},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
assert.Equal(t,
|
||||
newICECredentialType(testCase.expectedString),
|
||||
testCase.credentialType,
|
||||
"testCase: %d %v", i, testCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestICECredentialType_Json(t *testing.T) {
|
||||
testCases := []struct {
|
||||
credentialType ICECredentialType
|
||||
jsonRepresentation []byte
|
||||
}{
|
||||
{ICECredentialTypePassword, []byte("\"password\"")},
|
||||
{ICECredentialTypeOauth, []byte("\"oauth\"")},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
m, err := json.Marshal(testCase.credentialType)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
testCase.jsonRepresentation,
|
||||
m,
|
||||
"Marshal testCase: %d %v", i, testCase,
|
||||
)
|
||||
var ct ICECredentialType
|
||||
err = json.Unmarshal(testCase.jsonRepresentation, &ct)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
testCase.credentialType,
|
||||
ct,
|
||||
"Unmarshal testCase: %d %v", i, testCase,
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
ct := ICECredentialType(1000)
|
||||
err := json.Unmarshal([]byte("\"invalid\""), &ct)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, ct, ICECredentialType(1000))
|
||||
err = json.Unmarshal([]byte("\"invalid"), &ct)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, ct, ICECredentialType(1000))
|
||||
}
|
||||
}
|
||||
|
||||
110
iceserver.go
110
iceserver.go
@@ -4,6 +4,8 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
@@ -67,3 +69,111 @@ func (s ICEServer) urls() ([]*ice.URL, error) {
|
||||
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
func iceserverUnmarshalUrls(val interface{}) (*[]string, error) {
|
||||
s, ok := val.([]interface{})
|
||||
if !ok {
|
||||
return nil, errInvalidICEServer
|
||||
}
|
||||
out := make([]string, len(s))
|
||||
for idx, url := range s {
|
||||
out[idx], ok = url.(string)
|
||||
if !ok {
|
||||
return nil, errInvalidICEServer
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) {
|
||||
c, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errInvalidICEServer
|
||||
}
|
||||
MACKey, ok := c["MACKey"].(string)
|
||||
if !ok {
|
||||
return nil, errInvalidICEServer
|
||||
}
|
||||
AccessToken, ok := c["AccessToken"].(string)
|
||||
if !ok {
|
||||
return nil, errInvalidICEServer
|
||||
}
|
||||
return &OAuthCredential{
|
||||
MACKey: MACKey,
|
||||
AccessToken: AccessToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error {
|
||||
if val, ok := m["urls"]; ok {
|
||||
u, err := iceserverUnmarshalUrls(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.URLs = *u
|
||||
} else {
|
||||
s.URLs = []string{}
|
||||
}
|
||||
|
||||
if val, ok := m["username"]; ok {
|
||||
s.Username, ok = val.(string)
|
||||
if !ok {
|
||||
return errInvalidICEServer
|
||||
}
|
||||
}
|
||||
if val, ok := m["credentialType"]; ok {
|
||||
ct, ok := val.(string)
|
||||
if !ok || (newICECredentialType(ct) == ICECredentialType(Unknown)) {
|
||||
return errInvalidICEServer
|
||||
}
|
||||
s.CredentialType = newICECredentialType(ct)
|
||||
} else {
|
||||
s.CredentialType = ICECredentialType(Unknown)
|
||||
}
|
||||
if val, ok := m["credential"]; ok {
|
||||
switch s.CredentialType {
|
||||
case ICECredentialType(Unknown):
|
||||
s.Credential = val
|
||||
case ICECredentialTypePassword:
|
||||
s.Credential = val
|
||||
case ICECredentialTypeOauth:
|
||||
c, err := iceserverUnmarshalOauth(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Credential = *c
|
||||
default:
|
||||
return errInvalidICECredentialTypeString
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (s *ICEServer) UnmarshalJSON(b []byte) error {
|
||||
var tmp interface{}
|
||||
err := json.Unmarshal(b, &tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m, ok := tmp.(map[string]interface{}); ok {
|
||||
return s.iceserverUnmarshalFields(m)
|
||||
}
|
||||
return errInvalidICEServer
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (s ICEServer) MarshalJSON() ([]byte, error) {
|
||||
m := make(map[string]interface{})
|
||||
m["urls"] = s.URLs
|
||||
if s.Username != "" {
|
||||
m["username"] = s.Username
|
||||
}
|
||||
if s.Credential != nil {
|
||||
m["credential"] = s.Credential
|
||||
}
|
||||
if s.CredentialType != ICECredentialType(Unknown) {
|
||||
m["credentialType"] = s.CredentialType
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
@@ -41,7 +42,13 @@ func TestICEServer_validate(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
_, err := testCase.iceServer.urls()
|
||||
var iceServer ICEServer
|
||||
jsonobj, err := json.Marshal(testCase.iceServer)
|
||||
assert.NoError(t, err)
|
||||
err = json.Unmarshal(jsonobj, &iceServer)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, iceServer, testCase.iceServer)
|
||||
_, err = testCase.iceServer.urls()
|
||||
assert.Nil(t, err, "testCase: %d %v", i, testCase)
|
||||
}
|
||||
})
|
||||
@@ -88,4 +95,20 @@ func TestICEServer_validate(t *testing.T) {
|
||||
)
|
||||
}
|
||||
})
|
||||
t.Run("JsonFailure", func(t *testing.T) {
|
||||
testCases := [][]byte{
|
||||
[]byte(`{"urls":"NOTAURL","username":"unittest","credential":"placeholder","credentialType":"password"}`),
|
||||
[]byte(`{"urls":["turn:[2001:db8:1234:5678::1]?transport=udp"],"username":"unittest","credential":"placeholder","credentialType":"invalid"}`),
|
||||
[]byte(`{"urls":["turn:[2001:db8:1234:5678::1]?transport=udp"],"username":6,"credential":"placeholder","credentialType":"password"}`),
|
||||
[]byte(`{"urls":["turn:192.158.29.39?transport=udp"],"username":"unittest","credential":{"Bad Object": true},"credentialType":"oauth"}`),
|
||||
[]byte(`{"urls":["turn:192.158.29.39?transport=udp"],"username":"unittest","credential":{"MACKey":"WmtzanB3ZW9peFhtdm42NzUzNG0=","AccessToken":null,"credentialType":"oauth"}`),
|
||||
[]byte(`{"urls":["turn:192.158.29.39?transport=udp"],"username":"unittest","credential":{"MACKey":"WmtzanB3ZW9peFhtdm42NzUzNG0=","AccessToken":null,"credentialType":"password"}`),
|
||||
[]byte(`{"urls":["turn:192.158.29.39?transport=udp"],"username":"unittest","credential":{"MACKey":1337,"AccessToken":"AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ5VhNDgeMR3+ZlZ35byg972fW8QjpEl7bx91YLBPFsIhsxloWcXPhA=="},"credentialType":"oauth"}`),
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
var tc ICEServer
|
||||
err := json.Unmarshal(testCase, &tc)
|
||||
assert.Error(t, err, "testCase: %d %v", i, string(testCase))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -566,13 +566,33 @@ func iceServersToValue(iceServers []ICEServer) js.Value {
|
||||
return js.ValueOf(maps)
|
||||
}
|
||||
|
||||
func oauthCredentialToValue(o OAuthCredential) js.Value {
|
||||
out := map[string]interface{}{
|
||||
"MACKey": o.MACKey,
|
||||
"AccessToken": o.AccessToken,
|
||||
}
|
||||
return js.ValueOf(out)
|
||||
}
|
||||
|
||||
func iceServerToValue(server ICEServer) js.Value {
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"urls": stringsToValue(server.URLs), // required
|
||||
"username": stringToValueOrUndefined(server.Username),
|
||||
"credential": interfaceToValueOrUndefined(server.Credential),
|
||||
"credentialType": stringEnumToValueOrUndefined(server.CredentialType.String()),
|
||||
})
|
||||
out := map[string]interface{}{
|
||||
"urls": stringsToValue(server.URLs), // required
|
||||
}
|
||||
if server.Username != "" {
|
||||
out["username"] = stringToValueOrUndefined(server.Username)
|
||||
}
|
||||
if server.Credential != nil {
|
||||
switch t := server.Credential.(type) {
|
||||
case string:
|
||||
out["credential"] = stringToValueOrUndefined(t)
|
||||
case OAuthCredential:
|
||||
out["credential"] = oauthCredentialToValue(t)
|
||||
}
|
||||
}
|
||||
if server.CredentialType != ICECredentialType(Unknown) {
|
||||
out["credentialType"] = stringEnumToValueOrUndefined(server.CredentialType.String())
|
||||
}
|
||||
return js.ValueOf(out)
|
||||
}
|
||||
|
||||
func valueToConfiguration(configValue js.Value) Configuration {
|
||||
@@ -603,14 +623,36 @@ func valueToICEServers(iceServersValue js.Value) []ICEServer {
|
||||
return iceServers
|
||||
}
|
||||
|
||||
func valueToICECredential(iceCredentialValue js.Value) interface{} {
|
||||
if iceCredentialValue.IsNull() || iceCredentialValue.IsUndefined() {
|
||||
return nil
|
||||
}
|
||||
if iceCredentialValue.Type() == js.TypeString {
|
||||
return iceCredentialValue.String()
|
||||
}
|
||||
if iceCredentialValue.Type() == js.TypeObject {
|
||||
return OAuthCredential{
|
||||
MACKey: iceCredentialValue.Get("MACKey").String(),
|
||||
AccessToken: iceCredentialValue.Get("AccessToken").String(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func valueToICEServer(iceServerValue js.Value) ICEServer {
|
||||
return ICEServer{
|
||||
s := ICEServer{
|
||||
URLs: valueToStrings(iceServerValue.Get("urls")), // required
|
||||
Username: valueToStringOrZero(iceServerValue.Get("username")),
|
||||
// Note: Credential and CredentialType are not currently supported.
|
||||
// Credential: iceServerValue.Get("credential"),
|
||||
// CredentialType: newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))),
|
||||
Credential: valueToICECredential(iceServerValue.Get("credential")),
|
||||
CredentialType: newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))),
|
||||
}
|
||||
|
||||
// default to password
|
||||
if s.CredentialType == ICECredentialType(Unknown) {
|
||||
s.CredentialType = ICECredentialTypePassword
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func valueToICECandidate(val js.Value) *ICECandidate {
|
||||
|
||||
@@ -69,3 +69,35 @@ func TestValueToICECandidate(t *testing.T) {
|
||||
assert.Equal(t, testCase.expect, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueToICEServer(t *testing.T) {
|
||||
testCases := []ICEServer{
|
||||
{
|
||||
URLs: []string{"turn:192.158.29.39?transport=udp"},
|
||||
Username: "unittest",
|
||||
Credential: "placeholder",
|
||||
CredentialType: ICECredentialTypePassword,
|
||||
},
|
||||
{
|
||||
URLs: []string{"turn:[2001:db8:1234:5678::1]?transport=udp"},
|
||||
Username: "unittest",
|
||||
Credential: "placeholder",
|
||||
CredentialType: ICECredentialTypePassword,
|
||||
},
|
||||
{
|
||||
URLs: []string{"turn:192.158.29.39?transport=udp"},
|
||||
Username: "unittest",
|
||||
Credential: OAuthCredential{
|
||||
MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
|
||||
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ5VhNDgeMR3+ZlZ35byg972fW8QjpEl7bx91YLBPFsIhsxloWcXPhA==",
|
||||
},
|
||||
CredentialType: ICECredentialTypeOauth,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
v := iceServerToValue(testCase)
|
||||
s := valueToICEServer(v)
|
||||
assert.Equal(t, testCase, s)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user