Browse Source

convert the configuration into JSON before loading it

pull/639/head
aler9 5 years ago committed by Alessandro Ros
parent
commit
54292d712e
  1. 4
      apidocs/openapi.yaml
  2. 2
      go.mod
  3. 3
      go.sum
  4. 195
      internal/conf/conf.go
  5. 68
      internal/conf/path.go
  6. 28
      internal/conf/stringduration.go
  7. 2
      internal/core/path_manager.go
  8. 2
      rtsp-simple-server.yml

4
apidocs/openapi.yaml

@ -303,7 +303,7 @@ paths: @@ -303,7 +303,7 @@ paths:
/v1/config/get:
get:
operationId: configGet
summary: returns the current configuration.
summary: returns the configuration.
description: ''
responses:
'200':
@ -318,7 +318,7 @@ paths: @@ -318,7 +318,7 @@ paths:
/v1/config/set:
post:
operationId: configSet
summary: changes the current configuration.
summary: changes the configuration.
description: all fields are optional.
requestBody:
required: true

2
go.mod

@ -17,7 +17,7 @@ require ( @@ -17,7 +17,7 @@ require (
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2 v2.4.0
)
replace github.com/notedit/rtmp => github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927

3
go.sum

@ -100,7 +100,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks @@ -100,7 +100,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

195
internal/conf/conf.go

@ -2,6 +2,7 @@ package conf @@ -2,6 +2,7 @@ package conf
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -53,98 +54,134 @@ func decrypt(key string, byts []byte) ([]byte, error) { @@ -53,98 +54,134 @@ func decrypt(key string, byts []byte) ([]byte, error) {
return decrypted, nil
}
// Conf is the main program configuration.
type Conf struct {
// general
LogLevel string `yaml:"logLevel" json:"logLevel"`
LogLevelParsed logger.Level `yaml:"-" json:"-"`
LogDestinations []string `yaml:"logDestinations" json:"logDestinations"`
LogDestinationsParsed map[logger.Destination]struct{} `yaml:"-" json:"-"`
LogFile string `yaml:"logFile" json:"logFile"`
ReadTimeout StringDuration `yaml:"readTimeout" json:"readTimeout"`
WriteTimeout StringDuration `yaml:"writeTimeout" json:"writeTimeout"`
ReadBufferCount int `yaml:"readBufferCount" json:"readBufferCount"`
API bool `yaml:"api" json:"api"`
APIAddress string `yaml:"apiAddress" json:"apiAddress"`
Metrics bool `yaml:"metrics" json:"metrics"`
MetricsAddress string `yaml:"metricsAddress" json:"metricsAddress"`
PPROF bool `yaml:"pprof" json:"pprof"`
PPROFAddress string `yaml:"pprofAddress" json:"pprofAddress"`
RunOnConnect string `yaml:"runOnConnect" json:"runOnConnect"`
RunOnConnectRestart bool `yaml:"runOnConnectRestart" json:"runOnConnectRestart"`
// rtsp
RTSPDisable bool `yaml:"rtspDisable" json:"rtspDisable"`
Protocols []string `yaml:"protocols" json:"protocols"`
ProtocolsParsed map[Protocol]struct{} `yaml:"-" json:"-"`
Encryption string `yaml:"encryption" json:"encryption"`
EncryptionParsed Encryption `yaml:"-" json:"-"`
RTSPAddress string `yaml:"rtspAddress" json:"rtspAddress"`
RTSPSAddress string `yaml:"rtspsAddress" json:"rtspsAddress"`
RTPAddress string `yaml:"rtpAddress" json:"rtpAddress"`
RTCPAddress string `yaml:"rtcpAddress" json:"rtcpAddress"`
MulticastIPRange string `yaml:"multicastIPRange" json:"multicastIPRange"`
MulticastRTPPort int `yaml:"multicastRTPPort" json:"multicastRTPPort"`
MulticastRTCPPort int `yaml:"multicastRTCPPort" json:"multicastRTCPPort"`
ServerKey string `yaml:"serverKey" json:"serverKey"`
ServerCert string `yaml:"serverCert" json:"serverCert"`
AuthMethods []string `yaml:"authMethods" json:"authMethods"`
AuthMethodsParsed []headers.AuthMethod `yaml:"-" json:"-"`
ReadBufferSize int `yaml:"readBufferSize" json:"readBufferSize"`
// rtmp
RTMPDisable bool `yaml:"rtmpDisable" json:"rtmpDisable"`
RTMPAddress string `yaml:"rtmpAddress" json:"rtmpAddress"`
// hls
HLSDisable bool `yaml:"hlsDisable" json:"hlsDisable"`
HLSAddress string `yaml:"hlsAddress" json:"hlsAddress"`
HLSAlwaysRemux bool `yaml:"hlsAlwaysRemux" json:"hlsAlwaysRemux"`
HLSSegmentCount int `yaml:"hlsSegmentCount" json:"hlsSegmentCount"`
HLSSegmentDuration StringDuration `yaml:"hlsSegmentDuration" json:"hlsSegmentDuration"`
HLSAllowOrigin string `yaml:"hlsAllowOrigin" json:"hlsAllowOrigin"`
// paths
Paths map[string]*PathConf `yaml:"paths" json:"paths"`
}
// Load loads a Conf.
func Load(fpath string) (*Conf, bool, error) {
conf := &Conf{}
// read from file
found, err := func() (bool, error) {
// rtsp-simple-server.yml is optional
if fpath == "rtsp-simple-server.yml" {
if _, err := os.Stat(fpath); err != nil {
return false, nil
}
func loadFromFile(fpath string, conf *Conf) (bool, error) {
// rtsp-simple-server.yml is optional
// other configuration files are not
if fpath == "rtsp-simple-server.yml" {
if _, err := os.Stat(fpath); err != nil {
return false, nil
}
}
byts, err := ioutil.ReadFile(fpath)
byts, err := ioutil.ReadFile(fpath)
if err != nil {
return true, err
}
if key, ok := os.LookupEnv("RTSP_CONFKEY"); ok {
byts, err = decrypt(key, byts)
if err != nil {
return true, err
}
}
if key, ok := os.LookupEnv("RTSP_CONFKEY"); ok {
byts, err = decrypt(key, byts)
if err != nil {
return true, err
// load YAML config into a generic map
var temp interface{}
err = yaml.Unmarshal(byts, &temp)
if err != nil {
return true, err
}
// convert interface{} keys into string keys to avoid JSON errors
var convert func(i interface{}) interface{}
convert = func(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case []interface{}:
a2 := make([]interface{}, len(x))
for i, v := range x {
a2[i] = convert(v)
}
return a2
}
return i
}
temp = convert(temp)
err = yaml.Unmarshal(byts, conf)
if err != nil {
return true, err
}
// convert the generic map into JSON
byts, err = json.Marshal(temp)
if err != nil {
return true, err
}
// load the configuration from JSON
err = json.Unmarshal(byts, conf)
if err != nil {
return true, err
}
return true, nil
}
// Conf is a configuration.
type Conf struct {
// general
LogLevel string `json:"logLevel"`
LogLevelParsed logger.Level `json:"-"`
LogDestinations []string `json:"logDestinations"`
LogDestinationsParsed map[logger.Destination]struct{} `json:"-"`
LogFile string `json:"logFile"`
ReadTimeout StringDuration `json:"readTimeout"`
WriteTimeout 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"`
ProtocolsParsed map[Protocol]struct{} `json:"-"`
Encryption string `json:"encryption"`
EncryptionParsed Encryption `json:"-"`
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"`
AuthMethodsParsed []headers.AuthMethod `json:"-"`
ReadBufferSize int `json:"readBufferSize"`
// RTMP
RTMPDisable bool `json:"rtmpDisable"`
RTMPAddress string `json:"rtmpAddress"`
// HLS
HLSDisable bool `json:"hlsDisable"`
HLSAddress string `json:"hlsAddress"`
HLSAlwaysRemux bool `json:"hlsAlwaysRemux"`
HLSSegmentCount int `json:"hlsSegmentCount"`
HLSSegmentDuration StringDuration `json:"hlsSegmentDuration"`
HLSAllowOrigin string `json:"hlsAllowOrigin"`
// paths
Paths map[string]*PathConf `json:"paths"`
}
// Load loads a Conf.
func Load(fpath string) (*Conf, bool, error) {
conf := &Conf{}
return true, nil
}()
found, err := loadFromFile(fpath, conf)
if err != nil {
return nil, false, err
}
// read from environment
err = loadFromEnvironment("RTSP", conf)
if err != nil {
return nil, false, err
@ -158,7 +195,7 @@ func Load(fpath string) (*Conf, bool, error) { @@ -158,7 +195,7 @@ func Load(fpath string) (*Conf, bool, error) {
return conf, found, nil
}
// CheckAndFillMissing checks the configuration for errors and fill missing fields.
// CheckAndFillMissing checks the configuration for errors and fills missing fields.
func (conf *Conf) CheckAndFillMissing() error {
if conf.LogLevel == "" {
conf.LogLevel = "info"

68
internal/conf/path.go

@ -43,8 +43,8 @@ func parseIPCidrList(in []string) ([]interface{}, error) { @@ -43,8 +43,8 @@ func parseIPCidrList(in []string) ([]interface{}, error) {
return ret, nil
}
// CheckPathName checks if a path name is valid.
func CheckPathName(name string) error {
// IsValidPathName checks if a path name is valid.
func IsValidPathName(name string) error {
if name == "" {
return fmt.Errorf("cannot be empty")
}
@ -66,42 +66,42 @@ func CheckPathName(name string) error { @@ -66,42 +66,42 @@ func CheckPathName(name string) error {
// PathConf is a path configuration.
type PathConf struct {
Regexp *regexp.Regexp `yaml:"-" json:"-"`
Regexp *regexp.Regexp `json:"-"`
// source
Source string `yaml:"source" json:"source"`
SourceProtocol string `yaml:"sourceProtocol" json:"sourceProtocol"`
SourceProtocolParsed *gortsplib.ClientProtocol `yaml:"-" json:"-"`
SourceAnyPortEnable bool `yaml:"sourceAnyPortEnable" json:"sourceAnyPortEnable"`
SourceFingerprint string `yaml:"sourceFingerprint" json:"sourceFingerprint"`
SourceOnDemand bool `yaml:"sourceOnDemand" json:"sourceOnDemand"`
SourceOnDemandStartTimeout StringDuration `yaml:"sourceOnDemandStartTimeout" json:"sourceOnDemandStartTimeout"` //nolint:lll
SourceOnDemandCloseAfter StringDuration `yaml:"sourceOnDemandCloseAfter" json:"sourceOnDemandCloseAfter"`
SourceRedirect string `yaml:"sourceRedirect" json:"sourceRedirect"`
DisablePublisherOverride bool `yaml:"disablePublisherOverride" json:"disablePublisherOverride"`
Fallback string `yaml:"fallback" json:"fallback"`
Source string `json:"source"`
SourceProtocol string `json:"sourceProtocol"`
SourceProtocolParsed *gortsplib.ClientProtocol `json:"-"`
SourceAnyPortEnable bool `json:"sourceAnyPortEnable"`
SourceFingerprint string `json:"sourceFingerprint"`
SourceOnDemand bool `json:"sourceOnDemand"`
SourceOnDemandStartTimeout StringDuration `json:"sourceOnDemandStartTimeout"`
SourceOnDemandCloseAfter StringDuration `json:"sourceOnDemandCloseAfter"`
SourceRedirect string `json:"sourceRedirect"`
DisablePublisherOverride bool `json:"disablePublisherOverride"`
Fallback string `json:"fallback"`
// authentication
PublishUser string `yaml:"publishUser" json:"publishUser"`
PublishPass string `yaml:"publishPass" json:"publishPass"`
PublishIPs []string `yaml:"publishIPs" json:"publishIPs"`
PublishIPsParsed []interface{} `yaml:"-" json:"-"`
ReadUser string `yaml:"readUser" json:"readUser"`
ReadPass string `yaml:"readPass" json:"readPass"`
ReadIPs []string `yaml:"readIPs" json:"readIPs"`
ReadIPsParsed []interface{} `yaml:"-" json:"-"`
PublishUser string `json:"publishUser"`
PublishPass string `json:"publishPass"`
PublishIPs []string `json:"publishIPs"`
PublishIPsParsed []interface{} `json:"-"`
ReadUser string `json:"readUser"`
ReadPass string `json:"readPass"`
ReadIPs []string `json:"readIPs"`
ReadIPsParsed []interface{} `json:"-"`
// custom commands
RunOnInit string `yaml:"runOnInit" json:"runOnInit"`
RunOnInitRestart bool `yaml:"runOnInitRestart" json:"runOnInitRestart"`
RunOnDemand string `yaml:"runOnDemand" json:"runOnDemand"`
RunOnDemandRestart bool `yaml:"runOnDemandRestart" json:"runOnDemandRestart"`
RunOnDemandStartTimeout StringDuration `yaml:"runOnDemandStartTimeout" json:"runOnDemandStartTimeout"`
RunOnDemandCloseAfter StringDuration `yaml:"runOnDemandCloseAfter" json:"runOnDemandCloseAfter"`
RunOnPublish string `yaml:"runOnPublish" json:"runOnPublish"`
RunOnPublishRestart bool `yaml:"runOnPublishRestart" json:"runOnPublishRestart"`
RunOnRead string `yaml:"runOnRead" json:"runOnRead"`
RunOnReadRestart bool `yaml:"runOnReadRestart" json:"runOnReadRestart"`
RunOnInit string `json:"runOnInit"`
RunOnInitRestart bool `json:"runOnInitRestart"`
RunOnDemand string `json:"runOnDemand"`
RunOnDemandRestart bool `json:"runOnDemandRestart"`
RunOnDemandStartTimeout StringDuration `json:"runOnDemandStartTimeout"`
RunOnDemandCloseAfter StringDuration `json:"runOnDemandCloseAfter"`
RunOnPublish string `json:"runOnPublish"`
RunOnPublishRestart bool `json:"runOnPublishRestart"`
RunOnRead string `json:"runOnRead"`
RunOnReadRestart bool `json:"runOnReadRestart"`
}
func (pconf *PathConf) checkAndFillMissing(name string) error {
@ -111,7 +111,7 @@ func (pconf *PathConf) checkAndFillMissing(name string) error { @@ -111,7 +111,7 @@ func (pconf *PathConf) checkAndFillMissing(name string) error {
// normal path
if name[0] != '~' {
err := CheckPathName(name)
err := IsValidPathName(name)
if err != nil {
return fmt.Errorf("invalid path name: %s (%s)", err, name)
}
@ -245,7 +245,7 @@ func (pconf *PathConf) checkAndFillMissing(name string) error { @@ -245,7 +245,7 @@ func (pconf *PathConf) checkAndFillMissing(name string) error {
if pconf.Fallback != "" {
if strings.HasPrefix(pconf.Fallback, "/") {
err := CheckPathName(pconf.Fallback[1:])
err := IsValidPathName(pconf.Fallback[1:])
if err != nil {
return fmt.Errorf("'%s': %s", pconf.Fallback, err)
}

28
internal/conf/stringduration.go

@ -4,8 +4,6 @@ import ( @@ -4,8 +4,6 @@ import (
"encoding/json"
"errors"
"time"
"gopkg.in/yaml.v2"
)
// StringDuration is a duration that is unmarshaled from a string.
@ -37,29 +35,3 @@ func (d *StringDuration) UnmarshalJSON(b []byte) error { @@ -37,29 +35,3 @@ func (d *StringDuration) UnmarshalJSON(b []byte) error {
*d = StringDuration(du)
return nil
}
// MarshalYAML marshals a StringDuration into a string.
func (d StringDuration) MarshalYAML() ([]byte, error) {
return yaml.Marshal(time.Duration(d).String())
}
// UnmarshalYAML unmarshals a StringDuration from a string.
func (d *StringDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var v interface{}
if err := unmarshal(&v); err != nil {
return err
}
value, ok := v.(string)
if !ok {
return errors.New("invalid duration")
}
du, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = StringDuration(du)
return nil
}

2
internal/core/path_manager.go

@ -290,7 +290,7 @@ func (pm *pathManager) createPath(confName string, conf *conf.PathConf, name str @@ -290,7 +290,7 @@ func (pm *pathManager) createPath(confName string, conf *conf.PathConf, name str
}
func (pm *pathManager) findPathConf(name string) (string, *conf.PathConf, error) {
err := conf.CheckPathName(name)
err := conf.IsValidPathName(name)
if err != nil {
return "", nil, fmt.Errorf("invalid path name: %s (%s)", err, name)
}

2
rtsp-simple-server.yml

@ -54,7 +54,7 @@ rtspDisable: no @@ -54,7 +54,7 @@ rtspDisable: no
protocols: [udp, multicast, tcp]
# encrypt handshake and TCP streams with TLS (RTSPS).
# available values are "no", "strict", "optional".
encryption: no
encryption: "no"
# address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional".
rtspAddress: :8554
# address of the TCP/TLS/RTSPS listener. This is needed only when encryption is "strict" or "optional".

Loading…
Cancel
Save