Browse Source

general: add Clone() method to Conf and PathConf (#1633)

this allows to detect errors during cloning
pull/1610/head
Alessandro Ros 3 years ago committed by GitHub
parent
commit
bca3013bd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      internal/conf/authmethod.go
  2. 19
      internal/conf/conf.go
  3. 7
      internal/conf/encryption.go
  4. 24
      internal/conf/hlsvariant.go
  5. 5
      internal/conf/logdestination.go
  6. 7
      internal/conf/loglevel.go
  7. 17
      internal/conf/path.go
  8. 5
      internal/conf/protocol.go
  9. 6
      internal/conf/sourceprotocol.go
  10. 36
      internal/core/api.go
  11. 5
      internal/core/path_manager.go

7
internal/conf/authmethod.go

@ -21,8 +21,11 @@ func (d AuthMethods) MarshalJSON() ([]byte, error) {
case headers.AuthBasic: case headers.AuthBasic:
out[i] = "basic" out[i] = "basic"
default: case headers.AuthDigest:
out[i] = "digest" out[i] = "digest"
default:
return nil, fmt.Errorf("invalid authentication method: %v", v)
} }
} }
@ -47,7 +50,7 @@ func (d *AuthMethods) UnmarshalJSON(b []byte) error {
*d = append(*d, headers.AuthDigest) *d = append(*d, headers.AuthDigest)
default: default:
return fmt.Errorf("invalid authentication method: %s", v) return fmt.Errorf("invalid authentication method: '%s'", v)
} }
} }

19
internal/conf/conf.go

@ -13,6 +13,7 @@ import (
"github.com/aler9/gortsplib/v2" "github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/headers" "github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/bluenviron/gohlslib"
"golang.org/x/crypto/nacl/secretbox" "golang.org/x/crypto/nacl/secretbox"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -264,6 +265,22 @@ func Load(fpath string) (*Conf, bool, error) {
return conf, found, nil return conf, found, nil
} }
// Clone clones the configuration.
func (conf Conf) Clone() *Conf {
enc, err := json.Marshal(conf)
if err != nil {
panic(err)
}
var dest Conf
err = json.Unmarshal(enc, &dest)
if err != nil {
panic(err)
}
return &dest
}
// CheckAndFillMissing checks the configuration for errors and fills missing parameters. // CheckAndFillMissing checks the configuration for errors and fills missing parameters.
func (conf *Conf) CheckAndFillMissing() error { func (conf *Conf) CheckAndFillMissing() error {
// general // general
@ -392,7 +409,7 @@ func (conf *Conf) CheckAndFillMissing() error {
conf.HLSAllowOrigin = "*" conf.HLSAllowOrigin = "*"
} }
switch conf.HLSVariant { switch conf.HLSVariant {
case HLSVariantLowLatency: case HLSVariant(gohlslib.MuxerVariantLowLatency):
if conf.HLSSegmentCount < 7 { if conf.HLSSegmentCount < 7 {
return fmt.Errorf("Low-Latency HLS requires at least 7 segments") return fmt.Errorf("Low-Latency HLS requires at least 7 segments")
} }

7
internal/conf/encryption.go

@ -26,8 +26,11 @@ func (d Encryption) MarshalJSON() ([]byte, error) {
case EncryptionOptional: case EncryptionOptional:
out = "optional" out = "optional"
default: case EncryptionStrict:
out = "strict" out = "strict"
default:
return nil, fmt.Errorf("invalid encryption: %v", d)
} }
return json.Marshal(out) return json.Marshal(out)
@ -51,7 +54,7 @@ func (d *Encryption) UnmarshalJSON(b []byte) error {
*d = EncryptionStrict *d = EncryptionStrict
default: default:
return fmt.Errorf("invalid encryption value: '%s'", in) return fmt.Errorf("invalid encryption: '%s'", in)
} }
return nil return nil

24
internal/conf/hlsvariant.go

@ -10,26 +10,22 @@ import (
// HLSVariant is the hlsVariant parameter. // HLSVariant is the hlsVariant parameter.
type HLSVariant gohlslib.MuxerVariant type HLSVariant gohlslib.MuxerVariant
// supported HLS variants.
const (
HLSVariantMPEGTS HLSVariant = HLSVariant(gohlslib.MuxerVariantMPEGTS)
HLSVariantFMP4 HLSVariant = HLSVariant(gohlslib.MuxerVariantFMP4)
HLSVariantLowLatency HLSVariant = HLSVariant(gohlslib.MuxerVariantLowLatency)
)
// MarshalJSON implements json.Marshaler. // MarshalJSON implements json.Marshaler.
func (d HLSVariant) MarshalJSON() ([]byte, error) { func (d HLSVariant) MarshalJSON() ([]byte, error) {
var out string var out string
switch d { switch d {
case HLSVariantMPEGTS: case HLSVariant(gohlslib.MuxerVariantMPEGTS):
out = "mpegts" out = "mpegts"
case HLSVariantFMP4: case HLSVariant(gohlslib.MuxerVariantFMP4):
out = "fmp4" out = "fmp4"
default: case HLSVariant(gohlslib.MuxerVariantLowLatency):
out = "lowLatency" out = "lowLatency"
default:
return nil, fmt.Errorf("invalid HLS variant: %v", d)
} }
return json.Marshal(out) return json.Marshal(out)
@ -44,16 +40,16 @@ func (d *HLSVariant) UnmarshalJSON(b []byte) error {
switch in { switch in {
case "mpegts": case "mpegts":
*d = HLSVariantMPEGTS *d = HLSVariant(gohlslib.MuxerVariantMPEGTS)
case "fmp4": case "fmp4":
*d = HLSVariantFMP4 *d = HLSVariant(gohlslib.MuxerVariantFMP4)
case "lowLatency": case "lowLatency":
*d = HLSVariantLowLatency *d = HLSVariant(gohlslib.MuxerVariantLowLatency)
default: default:
return fmt.Errorf("invalid hlsVariant value: '%s'", in) return fmt.Errorf("invalid HLS variant: '%s'", in)
} }
return nil return nil

5
internal/conf/logdestination.go

@ -27,8 +27,11 @@ func (d LogDestinations) MarshalJSON() ([]byte, error) {
case logger.DestinationFile: case logger.DestinationFile:
v = "file" v = "file"
default: case logger.DestinationSyslog:
v = "syslog" v = "syslog"
default:
return nil, fmt.Errorf("invalid log destination: %v", p)
} }
out[i] = v out[i] = v

7
internal/conf/loglevel.go

@ -24,8 +24,11 @@ func (d LogLevel) MarshalJSON() ([]byte, error) {
case LogLevel(logger.Info): case LogLevel(logger.Info):
out = "info" out = "info"
default: case LogLevel(logger.Debug):
out = "debug" out = "debug"
default:
return nil, fmt.Errorf("invalid log level: %v", d)
} }
return json.Marshal(out) return json.Marshal(out)
@ -52,7 +55,7 @@ func (d *LogLevel) UnmarshalJSON(b []byte) error {
*d = LogLevel(logger.Debug) *d = LogLevel(logger.Debug)
default: default:
return fmt.Errorf("invalid log level: %s", in) return fmt.Errorf("invalid log level: '%s'", in)
} }
return nil return nil

17
internal/conf/path.go

@ -1,6 +1,7 @@
package conf package conf
import ( import (
"encoding/json"
"fmt" "fmt"
"net" "net"
gourl "net/url" gourl "net/url"
@ -341,6 +342,22 @@ func (pconf *PathConf) Equal(other *PathConf) bool {
return reflect.DeepEqual(pconf, other) return reflect.DeepEqual(pconf, other)
} }
// Clone clones the configuration.
func (pconf PathConf) Clone() *PathConf {
enc, err := json.Marshal(pconf)
if err != nil {
panic(err)
}
var dest PathConf
err = json.Unmarshal(enc, &dest)
if err != nil {
panic(err)
}
return &dest
}
// HasStaticSource checks whether the path has a static source. // HasStaticSource checks whether the path has a static source.
func (pconf PathConf) HasStaticSource() bool { func (pconf PathConf) HasStaticSource() bool {
return strings.HasPrefix(pconf.Source, "rtsp://") || return strings.HasPrefix(pconf.Source, "rtsp://") ||

5
internal/conf/protocol.go

@ -30,8 +30,11 @@ func (d Protocols) MarshalJSON() ([]byte, error) {
case Protocol(gortsplib.TransportUDPMulticast): case Protocol(gortsplib.TransportUDPMulticast):
v = "multicast" v = "multicast"
default: case Protocol(gortsplib.TransportTCP):
v = "tcp" v = "tcp"
default:
return nil, fmt.Errorf("invalid protocol: %v", p)
} }
out[i] = v out[i] = v

6
internal/conf/sourceprotocol.go

@ -26,8 +26,11 @@ func (d SourceProtocol) MarshalJSON() ([]byte, error) {
case gortsplib.TransportUDPMulticast: case gortsplib.TransportUDPMulticast:
out = "multicast" out = "multicast"
default: case gortsplib.TransportTCP:
out = "tcp" out = "tcp"
default:
return nil, fmt.Errorf("invalid protocol: %v", d.Transport)
} }
} }
@ -55,6 +58,7 @@ func (d *SourceProtocol) UnmarshalJSON(b []byte) error {
d.Transport = &v d.Transport = &v
case "automatic": case "automatic":
d.Transport = nil
default: default:
return fmt.Errorf("invalid protocol '%s'", in) return fmt.Errorf("invalid protocol '%s'", in)

36
internal/core/api.go

@ -36,11 +36,6 @@ func fillStruct(dest interface{}, source interface{}) {
} }
} }
func cloneStruct(dest interface{}, source interface{}) {
enc, _ := json.Marshal(source)
_ = json.Unmarshal(enc, dest)
}
func generateStructWithOptionalFields(model interface{}) interface{} { func generateStructWithOptionalFields(model interface{}) interface{} {
var fields []reflect.StructField var fields []reflect.StructField
@ -242,9 +237,9 @@ func (a *api) onConfigSet(ctx *gin.Context) {
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
var newConf conf.Conf newConf := a.conf.Clone()
cloneStruct(&newConf, a.conf)
fillStruct(&newConf, in) fillStruct(newConf, in)
err = newConf.CheckAndFillMissing() err = newConf.CheckAndFillMissing()
if err != nil { if err != nil {
@ -252,11 +247,11 @@ func (a *api) onConfigSet(ctx *gin.Context) {
return return
} }
a.conf = &newConf a.conf = newConf
// since reloading the configuration can cause the shutdown of the API, // since reloading the configuration can cause the shutdown of the API,
// call it in a goroutine // call it in a goroutine
go a.parent.apiConfigSet(&newConf) go a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK) ctx.Status(http.StatusOK)
} }
@ -278,8 +273,7 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) {
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
var newConf conf.Conf newConf := a.conf.Clone()
cloneStruct(&newConf, a.conf)
if _, ok := newConf.Paths[name]; ok { if _, ok := newConf.Paths[name]; ok {
ctx.AbortWithStatus(http.StatusBadRequest) ctx.AbortWithStatus(http.StatusBadRequest)
@ -297,11 +291,11 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) {
return return
} }
a.conf = &newConf a.conf = newConf
// since reloading the configuration can cause the shutdown of the API, // since reloading the configuration can cause the shutdown of the API,
// call it in a goroutine // call it in a goroutine
go a.parent.apiConfigSet(&newConf) go a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK) ctx.Status(http.StatusOK)
} }
@ -323,8 +317,7 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) {
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
var newConf conf.Conf newConf := a.conf.Clone()
cloneStruct(&newConf, a.conf)
newConfPath, ok := newConf.Paths[name] newConfPath, ok := newConf.Paths[name]
if !ok { if !ok {
@ -340,11 +333,11 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) {
return return
} }
a.conf = &newConf a.conf = newConf
// since reloading the configuration can cause the shutdown of the API, // since reloading the configuration can cause the shutdown of the API,
// call it in a goroutine // call it in a goroutine
go a.parent.apiConfigSet(&newConf) go a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK) ctx.Status(http.StatusOK)
} }
@ -360,8 +353,7 @@ func (a *api) onConfigPathsDelete(ctx *gin.Context) {
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
var newConf conf.Conf newConf := a.conf.Clone()
cloneStruct(&newConf, a.conf)
if _, ok := newConf.Paths[name]; !ok { if _, ok := newConf.Paths[name]; !ok {
ctx.AbortWithStatus(http.StatusBadRequest) ctx.AbortWithStatus(http.StatusBadRequest)
@ -376,11 +368,11 @@ func (a *api) onConfigPathsDelete(ctx *gin.Context) {
return return
} }
a.conf = &newConf a.conf = newConf
// since reloading the configuration can cause the shutdown of the API, // since reloading the configuration can cause the shutdown of the API,
// call it in a goroutine // call it in a goroutine
go a.parent.apiConfigSet(&newConf) go a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK) ctx.Status(http.StatusOK)
} }

5
internal/core/path_manager.go

@ -11,8 +11,7 @@ import (
) )
func pathConfCanBeUpdated(oldPathConf *conf.PathConf, newPathConf *conf.PathConf) bool { func pathConfCanBeUpdated(oldPathConf *conf.PathConf, newPathConf *conf.PathConf) bool {
var copy conf.PathConf copy := oldPathConf.Clone()
cloneStruct(&copy, oldPathConf)
copy.RPICameraBrightness = newPathConf.RPICameraBrightness copy.RPICameraBrightness = newPathConf.RPICameraBrightness
copy.RPICameraContrast = newPathConf.RPICameraContrast copy.RPICameraContrast = newPathConf.RPICameraContrast
@ -27,7 +26,7 @@ func pathConfCanBeUpdated(oldPathConf *conf.PathConf, newPathConf *conf.PathConf
copy.RPICameraEV = newPathConf.RPICameraEV copy.RPICameraEV = newPathConf.RPICameraEV
copy.RPICameraFPS = newPathConf.RPICameraFPS copy.RPICameraFPS = newPathConf.RPICameraFPS
return newPathConf.Equal(&copy) return newPathConf.Equal(copy)
} }
type pathManagerHLSServer interface { type pathManagerHLSServer interface {

Loading…
Cancel
Save