diff --git a/internal/conf/authmethod.go b/internal/conf/authmethod.go index 3c9c6e17..b2c23d5a 100644 --- a/internal/conf/authmethod.go +++ b/internal/conf/authmethod.go @@ -21,8 +21,11 @@ func (d AuthMethods) MarshalJSON() ([]byte, error) { case headers.AuthBasic: out[i] = "basic" - default: + case headers.AuthDigest: 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) default: - return fmt.Errorf("invalid authentication method: %s", v) + return fmt.Errorf("invalid authentication method: '%s'", v) } } diff --git a/internal/conf/conf.go b/internal/conf/conf.go index f847b63c..bc6a2197 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -13,6 +13,7 @@ import ( "github.com/aler9/gortsplib/v2" "github.com/aler9/gortsplib/v2/pkg/headers" + "github.com/bluenviron/gohlslib" "golang.org/x/crypto/nacl/secretbox" "gopkg.in/yaml.v2" @@ -264,6 +265,22 @@ func Load(fpath string) (*Conf, bool, error) { 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. func (conf *Conf) CheckAndFillMissing() error { // general @@ -392,7 +409,7 @@ func (conf *Conf) CheckAndFillMissing() error { conf.HLSAllowOrigin = "*" } switch conf.HLSVariant { - case HLSVariantLowLatency: + case HLSVariant(gohlslib.MuxerVariantLowLatency): if conf.HLSSegmentCount < 7 { return fmt.Errorf("Low-Latency HLS requires at least 7 segments") } diff --git a/internal/conf/encryption.go b/internal/conf/encryption.go index dc7fe5f7..e8eaa912 100644 --- a/internal/conf/encryption.go +++ b/internal/conf/encryption.go @@ -26,8 +26,11 @@ func (d Encryption) MarshalJSON() ([]byte, error) { case EncryptionOptional: out = "optional" - default: + case EncryptionStrict: out = "strict" + + default: + return nil, fmt.Errorf("invalid encryption: %v", d) } return json.Marshal(out) @@ -51,7 +54,7 @@ func (d *Encryption) UnmarshalJSON(b []byte) error { *d = EncryptionStrict default: - return fmt.Errorf("invalid encryption value: '%s'", in) + return fmt.Errorf("invalid encryption: '%s'", in) } return nil diff --git a/internal/conf/hlsvariant.go b/internal/conf/hlsvariant.go index ed9bfcc6..0e8e3514 100644 --- a/internal/conf/hlsvariant.go +++ b/internal/conf/hlsvariant.go @@ -10,26 +10,22 @@ import ( // HLSVariant is the hlsVariant parameter. 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. func (d HLSVariant) MarshalJSON() ([]byte, error) { var out string switch d { - case HLSVariantMPEGTS: + case HLSVariant(gohlslib.MuxerVariantMPEGTS): out = "mpegts" - case HLSVariantFMP4: + case HLSVariant(gohlslib.MuxerVariantFMP4): out = "fmp4" - default: + case HLSVariant(gohlslib.MuxerVariantLowLatency): out = "lowLatency" + + default: + return nil, fmt.Errorf("invalid HLS variant: %v", d) } return json.Marshal(out) @@ -44,16 +40,16 @@ func (d *HLSVariant) UnmarshalJSON(b []byte) error { switch in { case "mpegts": - *d = HLSVariantMPEGTS + *d = HLSVariant(gohlslib.MuxerVariantMPEGTS) case "fmp4": - *d = HLSVariantFMP4 + *d = HLSVariant(gohlslib.MuxerVariantFMP4) case "lowLatency": - *d = HLSVariantLowLatency + *d = HLSVariant(gohlslib.MuxerVariantLowLatency) default: - return fmt.Errorf("invalid hlsVariant value: '%s'", in) + return fmt.Errorf("invalid HLS variant: '%s'", in) } return nil diff --git a/internal/conf/logdestination.go b/internal/conf/logdestination.go index 24d37ce1..f2f652ee 100644 --- a/internal/conf/logdestination.go +++ b/internal/conf/logdestination.go @@ -27,8 +27,11 @@ func (d LogDestinations) MarshalJSON() ([]byte, error) { case logger.DestinationFile: v = "file" - default: + case logger.DestinationSyslog: v = "syslog" + + default: + return nil, fmt.Errorf("invalid log destination: %v", p) } out[i] = v diff --git a/internal/conf/loglevel.go b/internal/conf/loglevel.go index dce4c097..593dc269 100644 --- a/internal/conf/loglevel.go +++ b/internal/conf/loglevel.go @@ -24,8 +24,11 @@ func (d LogLevel) MarshalJSON() ([]byte, error) { case LogLevel(logger.Info): out = "info" - default: + case LogLevel(logger.Debug): out = "debug" + + default: + return nil, fmt.Errorf("invalid log level: %v", d) } return json.Marshal(out) @@ -52,7 +55,7 @@ func (d *LogLevel) UnmarshalJSON(b []byte) error { *d = LogLevel(logger.Debug) default: - return fmt.Errorf("invalid log level: %s", in) + return fmt.Errorf("invalid log level: '%s'", in) } return nil diff --git a/internal/conf/path.go b/internal/conf/path.go index 914fcc43..6f7e132d 100644 --- a/internal/conf/path.go +++ b/internal/conf/path.go @@ -1,6 +1,7 @@ package conf import ( + "encoding/json" "fmt" "net" gourl "net/url" @@ -341,6 +342,22 @@ func (pconf *PathConf) Equal(other *PathConf) bool { 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. func (pconf PathConf) HasStaticSource() bool { return strings.HasPrefix(pconf.Source, "rtsp://") || diff --git a/internal/conf/protocol.go b/internal/conf/protocol.go index 83c97928..44700dfa 100644 --- a/internal/conf/protocol.go +++ b/internal/conf/protocol.go @@ -30,8 +30,11 @@ func (d Protocols) MarshalJSON() ([]byte, error) { case Protocol(gortsplib.TransportUDPMulticast): v = "multicast" - default: + case Protocol(gortsplib.TransportTCP): v = "tcp" + + default: + return nil, fmt.Errorf("invalid protocol: %v", p) } out[i] = v diff --git a/internal/conf/sourceprotocol.go b/internal/conf/sourceprotocol.go index db334927..48941826 100644 --- a/internal/conf/sourceprotocol.go +++ b/internal/conf/sourceprotocol.go @@ -26,8 +26,11 @@ func (d SourceProtocol) MarshalJSON() ([]byte, error) { case gortsplib.TransportUDPMulticast: out = "multicast" - default: + case gortsplib.TransportTCP: 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 case "automatic": + d.Transport = nil default: return fmt.Errorf("invalid protocol '%s'", in) diff --git a/internal/core/api.go b/internal/core/api.go index 34da1bc0..67cfc518 100644 --- a/internal/core/api.go +++ b/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{} { var fields []reflect.StructField @@ -242,9 +237,9 @@ func (a *api) onConfigSet(ctx *gin.Context) { a.mutex.Lock() defer a.mutex.Unlock() - var newConf conf.Conf - cloneStruct(&newConf, a.conf) - fillStruct(&newConf, in) + newConf := a.conf.Clone() + + fillStruct(newConf, in) err = newConf.CheckAndFillMissing() if err != nil { @@ -252,11 +247,11 @@ func (a *api) onConfigSet(ctx *gin.Context) { return } - a.conf = &newConf + a.conf = newConf // since reloading the configuration can cause the shutdown of the API, // call it in a goroutine - go a.parent.apiConfigSet(&newConf) + go a.parent.apiConfigSet(newConf) ctx.Status(http.StatusOK) } @@ -278,8 +273,7 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) { a.mutex.Lock() defer a.mutex.Unlock() - var newConf conf.Conf - cloneStruct(&newConf, a.conf) + newConf := a.conf.Clone() if _, ok := newConf.Paths[name]; ok { ctx.AbortWithStatus(http.StatusBadRequest) @@ -297,11 +291,11 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) { return } - a.conf = &newConf + a.conf = newConf // since reloading the configuration can cause the shutdown of the API, // call it in a goroutine - go a.parent.apiConfigSet(&newConf) + go a.parent.apiConfigSet(newConf) ctx.Status(http.StatusOK) } @@ -323,8 +317,7 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) { a.mutex.Lock() defer a.mutex.Unlock() - var newConf conf.Conf - cloneStruct(&newConf, a.conf) + newConf := a.conf.Clone() newConfPath, ok := newConf.Paths[name] if !ok { @@ -340,11 +333,11 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) { return } - a.conf = &newConf + a.conf = newConf // since reloading the configuration can cause the shutdown of the API, // call it in a goroutine - go a.parent.apiConfigSet(&newConf) + go a.parent.apiConfigSet(newConf) ctx.Status(http.StatusOK) } @@ -360,8 +353,7 @@ func (a *api) onConfigPathsDelete(ctx *gin.Context) { a.mutex.Lock() defer a.mutex.Unlock() - var newConf conf.Conf - cloneStruct(&newConf, a.conf) + newConf := a.conf.Clone() if _, ok := newConf.Paths[name]; !ok { ctx.AbortWithStatus(http.StatusBadRequest) @@ -376,11 +368,11 @@ func (a *api) onConfigPathsDelete(ctx *gin.Context) { return } - a.conf = &newConf + a.conf = newConf // since reloading the configuration can cause the shutdown of the API, // call it in a goroutine - go a.parent.apiConfigSet(&newConf) + go a.parent.apiConfigSet(newConf) ctx.Status(http.StatusOK) } diff --git a/internal/core/path_manager.go b/internal/core/path_manager.go index b2cff291..b80fa017 100644 --- a/internal/core/path_manager.go +++ b/internal/core/path_manager.go @@ -11,8 +11,7 @@ import ( ) func pathConfCanBeUpdated(oldPathConf *conf.PathConf, newPathConf *conf.PathConf) bool { - var copy conf.PathConf - cloneStruct(©, oldPathConf) + copy := oldPathConf.Clone() copy.RPICameraBrightness = newPathConf.RPICameraBrightness copy.RPICameraContrast = newPathConf.RPICameraContrast @@ -27,7 +26,7 @@ func pathConfCanBeUpdated(oldPathConf *conf.PathConf, newPathConf *conf.PathConf copy.RPICameraEV = newPathConf.RPICameraEV copy.RPICameraFPS = newPathConf.RPICameraFPS - return newPathConf.Equal(©) + return newPathConf.Equal(copy) } type pathManagerHLSServer interface {