mirror of
https://github.com/bluenviron/mediamtx
synced 2025-01-23 23:42:59 +00:00
return error in case of non-existent parameters in the configuration file
This commit is contained in:
parent
b748e6d0ff
commit
1f540a2aaf
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib/pkg/headers"
|
||||
@ -72,6 +73,7 @@ func loadFromFile(fpath string, conf *Conf) (bool, error) {
|
||||
m2[k.(string)] = convert(v)
|
||||
}
|
||||
return m2
|
||||
|
||||
case []interface{}:
|
||||
a2 := make([]interface{}, len(x))
|
||||
for i, v := range x {
|
||||
@ -79,10 +81,59 @@ func loadFromFile(fpath string, conf *Conf) (bool, error) {
|
||||
}
|
||||
return a2
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
temp = convert(temp)
|
||||
|
||||
// check for non-existent parameters
|
||||
var checkNonExistentFields func(what interface{}, ref interface{}) error
|
||||
checkNonExistentFields = func(what interface{}, ref interface{}) error {
|
||||
if what == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ma, ok := what.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("not a map")
|
||||
}
|
||||
|
||||
for k, v := range ma {
|
||||
fi := func() reflect.Type {
|
||||
rr := reflect.TypeOf(ref)
|
||||
for i := 0; i < rr.NumField(); i++ {
|
||||
f := rr.Field(i)
|
||||
if f.Tag.Get("json") == k {
|
||||
return f.Type
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if fi == nil {
|
||||
return fmt.Errorf("non-existent parameter: '%s'", k)
|
||||
}
|
||||
|
||||
if fi == reflect.TypeOf(map[string]*PathConf{}) && v != nil {
|
||||
ma2, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("parameter %s is not a map", k)
|
||||
}
|
||||
|
||||
for k2, v2 := range ma2 {
|
||||
err := checkNonExistentFields(v2, reflect.Zero(fi.Elem().Elem()).Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("parameter %s, key %s: %s", k, k2, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = checkNonExistentFields(temp, Conf{})
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// convert the generic map into JSON
|
||||
byts, err = json.Marshal(temp)
|
||||
if err != nil {
|
||||
@ -170,7 +221,7 @@ func Load(fpath string) (*Conf, bool, error) {
|
||||
return conf, found, nil
|
||||
}
|
||||
|
||||
// CheckAndFillMissing checks the configuration for errors and fills missing fields.
|
||||
// CheckAndFillMissing checks the configuration for errors and fills missing parameters.
|
||||
func (conf *Conf) CheckAndFillMissing() error {
|
||||
if conf.LogLevel == 0 {
|
||||
conf.LogLevel = LogLevel(logger.Info)
|
||||
|
@ -29,27 +29,57 @@ func writeTempFile(byts []byte) (string, error) {
|
||||
}
|
||||
|
||||
func TestConfFromFile(t *testing.T) {
|
||||
tmpf, err := writeTempFile([]byte(`
|
||||
paths:
|
||||
cam1:
|
||||
runOnDemandStartTimeout: 5s
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte(
|
||||
"paths:\n" +
|
||||
" cam1:\n" +
|
||||
" runOnDemandStartTimeout: 5s\n"))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
conf, hasFile, err := Load(tmpf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, hasFile)
|
||||
conf, hasFile, err := Load(tmpf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, hasFile)
|
||||
|
||||
pa, ok := conf.Paths["cam1"]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, &PathConf{
|
||||
Source: "publisher",
|
||||
SourceOnDemandStartTimeout: 10 * StringDuration(time.Second),
|
||||
SourceOnDemandCloseAfter: 10 * StringDuration(time.Second),
|
||||
RunOnDemandStartTimeout: 5 * StringDuration(time.Second),
|
||||
RunOnDemandCloseAfter: 10 * StringDuration(time.Second),
|
||||
}, pa)
|
||||
pa, ok := conf.Paths["cam1"]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, &PathConf{
|
||||
Source: "publisher",
|
||||
SourceOnDemandStartTimeout: 10 * StringDuration(time.Second),
|
||||
SourceOnDemandCloseAfter: 10 * StringDuration(time.Second),
|
||||
RunOnDemandStartTimeout: 5 * StringDuration(time.Second),
|
||||
RunOnDemandCloseAfter: 10 * StringDuration(time.Second),
|
||||
}, pa)
|
||||
}()
|
||||
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte(``))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
_, _, err = Load(tmpf)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte(`paths:`))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
_, _, err = Load(tmpf)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte(
|
||||
"paths:\n" +
|
||||
" mypath:\n"))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
_, _, err = Load(tmpf)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestConfFromFileAndEnv(t *testing.T) {
|
||||
@ -96,11 +126,9 @@ func TestConfFromEnvOnly(t *testing.T) {
|
||||
|
||||
func TestConfEncryption(t *testing.T) {
|
||||
key := "testing123testin"
|
||||
plaintext := `
|
||||
paths:
|
||||
path1:
|
||||
path2:
|
||||
`
|
||||
plaintext := "paths:\n" +
|
||||
" path1:\n" +
|
||||
" path2:\n"
|
||||
|
||||
encryptedConf := func() string {
|
||||
var secretKey [32]byte
|
||||
@ -132,3 +160,25 @@ paths:
|
||||
_, ok = conf.Paths["path2"]
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestConfErrorNonExistentParameter(t *testing.T) {
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte(`invalid: param`))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
_, _, err = Load(tmpf)
|
||||
require.Equal(t, "non-existent parameter: 'invalid'", err.Error())
|
||||
}()
|
||||
|
||||
func() {
|
||||
tmpf, err := writeTempFile([]byte("paths:\n" +
|
||||
" mypath:\n" +
|
||||
" invalid: parameter\n"))
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
_, _, err = Load(tmpf)
|
||||
require.Equal(t, "parameter paths, key mypath: non-existent parameter: 'invalid'", err.Error())
|
||||
}()
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type Protocols map[Protocol]struct{}
|
||||
// MarshalJSON marshals a Protocols into JSON.
|
||||
func (d Protocols) MarshalJSON() ([]byte, error) {
|
||||
out := make([]string, len(d))
|
||||
i := 0
|
||||
|
||||
for p := range d {
|
||||
var v string
|
||||
@ -36,7 +37,8 @@ func (d Protocols) MarshalJSON() ([]byte, error) {
|
||||
v = "tcp"
|
||||
}
|
||||
|
||||
out = append(out, v)
|
||||
out[i] = v
|
||||
i++
|
||||
}
|
||||
|
||||
return json.Marshal(out)
|
||||
|
@ -39,49 +39,49 @@ func fillStruct(dest interface{}, source interface{}) {
|
||||
}
|
||||
|
||||
func cloneStruct(dest interface{}, source interface{}) {
|
||||
enc, _ := json.Marshal(dest)
|
||||
_ = json.Unmarshal(enc, source)
|
||||
enc, _ := json.Marshal(source)
|
||||
_ = json.Unmarshal(enc, dest)
|
||||
}
|
||||
|
||||
func loadConfData(ctx *gin.Context) (interface{}, error) {
|
||||
var in struct {
|
||||
// general
|
||||
LogLevel *string `json:"logLevel"`
|
||||
LogDestinations *[]string `json:"logDestinations"`
|
||||
LogFile *string `json:"logFile"`
|
||||
ReadTimeout *conf.StringDuration `json:"readTimeout"`
|
||||
WriteTimeout *conf.StringDuration `json:"writeTimeout"`
|
||||
ReadBufferCount *int `json:"readBufferCount"`
|
||||
API *bool `json:"api"`
|
||||
APIAddress *string `json:"apiAddress"`
|
||||
Metrics *bool `json:"metrics"`
|
||||
MetricsAddress *string `json:"metricsAddress"`
|
||||
PPROF *bool `json:"pprof"`
|
||||
PPROFAddress *string `json:"pprofAddress"`
|
||||
RunOnConnect *string `json:"runOnConnect"`
|
||||
RunOnConnectRestart *bool `json:"runOnConnectRestart"`
|
||||
LogLevel *conf.LogLevel `json:"logLevel"`
|
||||
LogDestinations *conf.LogDestinations `json:"logDestinations"`
|
||||
LogFile *string `json:"logFile"`
|
||||
ReadTimeout *conf.StringDuration `json:"readTimeout"`
|
||||
WriteTimeout *conf.StringDuration `json:"writeTimeout"`
|
||||
ReadBufferCount *int `json:"readBufferCount"`
|
||||
API *bool `json:"api"`
|
||||
APIAddress *string `json:"apiAddress"`
|
||||
Metrics *bool `json:"metrics"`
|
||||
MetricsAddress *string `json:"metricsAddress"`
|
||||
PPROF *bool `json:"pprof"`
|
||||
PPROFAddress *string `json:"pprofAddress"`
|
||||
RunOnConnect *string `json:"runOnConnect"`
|
||||
RunOnConnectRestart *bool `json:"runOnConnectRestart"`
|
||||
|
||||
// rtsp
|
||||
RTSPDisable *bool `json:"rtspDisable"`
|
||||
Protocols *[]string `json:"protocols"`
|
||||
Encryption *string `json:"encryption"`
|
||||
RTSPAddress *string `json:"rtspAddress"`
|
||||
RTSPSAddress *string `json:"rtspsAddress"`
|
||||
RTPAddress *string `json:"rtpAddress"`
|
||||
RTCPAddress *string `json:"rtcpAddress"`
|
||||
MulticastIPRange *string `json:"multicastIPRange"`
|
||||
MulticastRTPPort *int `json:"multicastRTPPort"`
|
||||
MulticastRTCPPort *int `json:"multicastRTCPPort"`
|
||||
ServerKey *string `json:"serverKey"`
|
||||
ServerCert *string `json:"serverCert"`
|
||||
AuthMethods *[]string `json:"authMethods"`
|
||||
ReadBufferSize *int `json:"readBufferSize"`
|
||||
// RTSP
|
||||
RTSPDisable *bool `json:"rtspDisable"`
|
||||
Protocols *conf.Protocols `json:"protocols"`
|
||||
Encryption *conf.Encryption `json:"encryption"`
|
||||
RTSPAddress *string `json:"rtspAddress"`
|
||||
RTSPSAddress *string `json:"rtspsAddress"`
|
||||
RTPAddress *string `json:"rtpAddress"`
|
||||
RTCPAddress *string `json:"rtcpAddress"`
|
||||
MulticastIPRange *string `json:"multicastIPRange"`
|
||||
MulticastRTPPort *int `json:"multicastRTPPort"`
|
||||
MulticastRTCPPort *int `json:"multicastRTCPPort"`
|
||||
ServerKey *string `json:"serverKey"`
|
||||
ServerCert *string `json:"serverCert"`
|
||||
AuthMethods *conf.AuthMethods `json:"authMethods"`
|
||||
ReadBufferSize *int `json:"readBufferSize"`
|
||||
|
||||
// rtmp
|
||||
// RTMP
|
||||
RTMPDisable *bool `json:"rtmpDisable"`
|
||||
RTMPAddress *string `json:"rtmpAddress"`
|
||||
|
||||
// hls
|
||||
// HLS
|
||||
HLSDisable *bool `json:"hlsDisable"`
|
||||
HLSAddress *string `json:"hlsAddress"`
|
||||
HLSAlwaysRemux *bool `json:"hlsAlwaysRemux"`
|
||||
@ -101,7 +101,7 @@ func loadConfPathData(ctx *gin.Context) (interface{}, error) {
|
||||
var in struct {
|
||||
// source
|
||||
Source *string `json:"source"`
|
||||
SourceProtocol *string `json:"sourceProtocol"`
|
||||
SourceProtocol *conf.SourceProtocol `json:"sourceProtocol"`
|
||||
SourceAnyPortEnable *bool `json:"sourceAnyPortEnable"`
|
||||
SourceFingerprint *string `json:"sourceFingerprint"`
|
||||
SourceOnDemand *bool `json:"sourceOnDemand"`
|
||||
@ -112,12 +112,12 @@ func loadConfPathData(ctx *gin.Context) (interface{}, error) {
|
||||
Fallback *string `json:"fallback"`
|
||||
|
||||
// authentication
|
||||
PublishUser *string `json:"publishUser"`
|
||||
PublishPass *string `json:"publishPass"`
|
||||
PublishIPs *[]string `json:"publishIPs"`
|
||||
ReadUser *string `json:"readUser"`
|
||||
ReadPass *string `json:"readPass"`
|
||||
ReadIPs *[]string `json:"readIPs"`
|
||||
PublishUser *string `json:"publishUser"`
|
||||
PublishPass *string `json:"publishPass"`
|
||||
PublishIPs *conf.IPsOrNets `json:"publishIPs"`
|
||||
ReadUser *string `json:"readUser"`
|
||||
ReadPass *string `json:"readPass"`
|
||||
ReadIPs *conf.IPsOrNets `json:"readIPs"`
|
||||
|
||||
// custom commands
|
||||
RunOnInit *string `json:"runOnInit"`
|
||||
@ -363,7 +363,7 @@ func (a *api) onConfigSet(ctx *gin.Context) {
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
var newConf conf.Conf
|
||||
cloneStruct(a.conf, &newConf)
|
||||
cloneStruct(&newConf, a.conf)
|
||||
fillStruct(&newConf, in)
|
||||
|
||||
err = newConf.CheckAndFillMissing()
|
||||
@ -399,15 +399,16 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) {
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
var newConf conf.Conf
|
||||
cloneStruct(a.conf, &newConf)
|
||||
cloneStruct(&newConf, a.conf)
|
||||
|
||||
if _, ok := newConf.Paths[name]; ok {
|
||||
ctx.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
newConfPath := &conf.PathConf{}
|
||||
|
||||
newConfPath := &conf.PathConf{}
|
||||
fillStruct(newConfPath, in)
|
||||
|
||||
newConf.Paths[name] = newConfPath
|
||||
|
||||
err = newConf.CheckAndFillMissing()
|
||||
@ -443,7 +444,7 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) {
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
var newConf conf.Conf
|
||||
cloneStruct(a.conf, &newConf)
|
||||
cloneStruct(&newConf, a.conf)
|
||||
|
||||
newConfPath, ok := newConf.Paths[name]
|
||||
if !ok {
|
||||
@ -480,7 +481,7 @@ func (a *api) onConfigPathsDelete(ctx *gin.Context) {
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
var newConf conf.Conf
|
||||
cloneStruct(a.conf, &newConf)
|
||||
cloneStruct(&newConf, a.conf)
|
||||
|
||||
if _, ok := newConf.Paths[name]; !ok {
|
||||
ctx.AbortWithStatus(http.StatusBadRequest)
|
||||
|
Loading…
Reference in New Issue
Block a user