diff --git a/config/config.go b/config/config.go index c434fea..4f0c7f4 100644 --- a/config/config.go +++ b/config/config.go @@ -169,6 +169,9 @@ func Struct2Config(s any) (config Config) { } for i, j := 0, t.NumField(); i < j; i++ { ft := t.Field(i) + if !ft.IsExported() { + continue + } name := strings.ToLower(ft.Name) switch ft.Type.Kind() { case reflect.Struct: diff --git a/config/http.go b/config/http.go index 9eac7b4..eb27ecd 100644 --- a/config/http.go +++ b/config/http.go @@ -4,27 +4,69 @@ import ( "context" "net/http" + . "github.com/logrusorgru/aurora" "golang.org/x/sync/errgroup" + "m7s.live/engine/v4/log" + "m7s.live/engine/v4/util" ) +var _ HTTPConfig = (*HTTP)(nil) + type HTTP struct { ListenAddr string ListenAddrTLS string CertFile string KeyFile string CORS bool //是否自动添加CORS头 + UserName string + Password string + mux *http.ServeMux +} +type HTTPConfig interface { + InitMux() + GetHTTPConfig() *HTTP + Listen(ctx context.Context) error + HandleFunc(string, func(http.ResponseWriter, *http.Request)) +} + +func (config *HTTP) InitMux() { + hasOwnTLS := config.ListenAddrTLS != "" && config.ListenAddrTLS != Global.ListenAddrTLS + hasOwnHTTP := config.ListenAddr != "" && config.ListenAddr != Global.ListenAddr + if hasOwnTLS || hasOwnHTTP { + config.mux = http.NewServeMux() + } +} + +func (config *HTTP) HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) { + if config.mux != nil { + if config.CORS { + f = util.CORS(f) + } + if config.UserName != "" && config.Password != "" { + f = util.BasicAuth(config.UserName, config.Password, f) + } + config.mux.HandleFunc(path, f) + } +} + +func (config *HTTP) GetHTTPConfig() *HTTP { + return config } // ListenAddrs Listen http and https -func (config *HTTP) Listen(ctx context.Context, plugin HTTPPlugin) error { +func (config *HTTP) Listen(ctx context.Context) error { var g errgroup.Group - if config.ListenAddrTLS != "" { + if config.ListenAddrTLS != "" && (config == &Global.HTTP || config.ListenAddrTLS != Global.ListenAddrTLS) { g.Go(func() error { - return http.ListenAndServeTLS(config.ListenAddrTLS, config.CertFile, config.KeyFile, plugin) + log.Info("🌐 https listen at ", Blink(config.ListenAddrTLS)) + return http.ListenAndServeTLS(config.ListenAddrTLS, config.CertFile, config.KeyFile, config.mux) }) } - if config.ListenAddr != "" { - g.Go(func() error { return http.ListenAndServe(config.ListenAddr, plugin) }) + if config.ListenAddr != "" && (config == &Global.HTTP || config.ListenAddr != Global.ListenAddr) { + g.Go(func() error { + log.Info("🌐 http listen at ", Blink(config.ListenAddr)) + return http.ListenAndServe(config.ListenAddr, config.mux) + }) } g.Go(func() error { <-ctx.Done() diff --git a/config/types.go b/config/types.go index 7edeb7c..bd25351 100644 --- a/config/types.go +++ b/config/types.go @@ -1,5 +1,7 @@ package config +import "net/http" + type PublishConfig interface { GetPublishConfig() *Publish } @@ -77,10 +79,12 @@ type Engine struct { EnableRTP bool //启用RTP格式,rtsp、gb18181等协议使用 EnableFLV bool //开启FLV格式,hdl协议使用 } +func (cfg *Engine) OnEvent(event any) { +} var Global = &Engine{ Publish{true, true, false, 10, 0}, Subscribe{true, true, false, 10}, - HTTP{ListenAddr: ":8080", CORS: true}, + HTTP{ListenAddr: ":8080", CORS: true, mux: http.DefaultServeMux}, false, true, true, true, } diff --git a/go.mod b/go.mod index ef88ca2..daa3d39 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible github.com/pion/rtp v1.7.4 github.com/q191201771/naza v0.19.1 + github.com/shirou/gopsutil/v3 v3.22.1 go.uber.org/zap v1.21.0 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b @@ -21,4 +22,13 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -require github.com/pion/randutil v0.1.0 // indirect +require ( + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/tklauser/numcpus v0.3.0 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect +) diff --git a/go.sum b/go.sum index 05aab61..c39dbc4 100644 --- a/go.sum +++ b/go.sum @@ -9,12 +9,18 @@ github.com/cnotch/queue v0.0.0-20200326024423-6e88bdbf2ad4/go.mod h1:zOssjAlNusO github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg= github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo= github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -24,11 +30,15 @@ github.com/kelindar/rate v1.0.0/go.mod h1:AjT4G+hTItNwt30lucEGZIz8y7Uk5zPho6vurI github.com/kelindar/tcp v1.0.0/go.mod h1:JB5hj1cshLU60XrLij2BBxW3JQ4hOye8vqbyvuKb52k= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -42,9 +52,14 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/q191201771/naza v0.19.1 h1:4KLcxT2CHztO+7miPRtBG3FFgadSQYQw1gPPPKN7rnY= github.com/q191201771/naza v0.19.1/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM6rExjqt0= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/shirou/gopsutil/v3 v3.22.1 h1:33y31Q8J32+KstqPfscvFwBlNJ6xLaBy4xqBXzlYV5w= +github.com/shirou/gopsutil/v3 v3.22.1/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -52,7 +67,13 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -78,10 +99,15 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -92,10 +118,14 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -103,6 +133,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http.go b/http.go index ffa4e91..a1241c2 100644 --- a/http.go +++ b/http.go @@ -3,28 +3,22 @@ package engine import ( "encoding/json" "net/http" + "time" - . "github.com/logrusorgru/aurora" - "go.uber.org/zap" "m7s.live/engine/v4/config" - "m7s.live/engine/v4/log" + "m7s.live/engine/v4/util" ) type GlobalConfig struct { - *http.ServeMux *config.Engine } -func (cfg *GlobalConfig) OnEvent(event any) { - switch event.(type) { - case FirstConfig: - log.Info(Green("api server start at"), BrightBlue(cfg.ListenAddr), BrightBlue(cfg.ListenAddrTLS)) - cfg.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - log.Debug("visit", zap.String("path", "/"), zap.String("remote", r.RemoteAddr)) - w.Write([]byte("Monibuca API Server")) - }) - go cfg.Listen(Engine, cfg) - } +func (config *GlobalConfig) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + rw.Write([]byte("Monibuca API Server")) +} + +func (config *GlobalConfig) API_summary(rw http.ResponseWriter, r *http.Request) { + util.ReturnJson(summary.collect, time.Second, rw, r) } func (config *GlobalConfig) API_sysInfo(rw http.ResponseWriter, r *http.Request) { diff --git a/main.go b/main.go index bb839e2..a16a9a0 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "net/http" "os" "path/filepath" - "reflect" "runtime" "time" @@ -29,15 +28,12 @@ var ( StartTime time.Time //启动时间 Plugins = make(map[string]*Plugin) // Plugins 所有的插件配置 EngineConfig = &GlobalConfig{ - Engine: config.Global, - ServeMux: http.DefaultServeMux, + Engine: config.Global, } - settingDir string //配置缓存目录,该目录按照插件名称作为文件名存储修改过的配置 - Engine = InstallPlugin(EngineConfig) //复用安装插件逻辑,将全局配置信息注入,并启动server - toolManForGetHandlerFuncType http.HandlerFunc //专门用来获取HandlerFunc类型的工具人 - handlerFuncType = reflect.TypeOf(toolManForGetHandlerFuncType) //供反射使用的Handler类型的类型 - MergeConfigs = []string{"Publish", "Subscribe"} //需要合并配置的属性项,插件若没有配置则使用全局配置 - EventBus = make(chan any, 10) + settingDir string //配置缓存目录,该目录按照插件名称作为文件名存储修改过的配置 + Engine = InstallPlugin(EngineConfig) //复用安装插件逻辑,将全局配置信息注入,并启动server + MergeConfigs = []string{"Publish", "Subscribe", "HTTP"} //需要合并配置的属性项,插件若没有配置则使用全局配置 + EventBus = make(chan any, 10) ) // Run 启动Monibuca引擎,传入总的Context,可用于关闭所有 @@ -73,7 +69,7 @@ func Run(ctx context.Context, configFile string) (err error) { Engine.registerHandler() // 使得RawConfig具备全量配置信息,用于合并到插件配置中 Engine.RawConfig = config.Struct2Config(EngineConfig.Engine) - go EngineConfig.OnEvent(FirstConfig(Engine.RawConfig)) + go EngineConfig.Listen(Engine) for name, plugin := range Plugins { plugin.RawConfig = cg.GetChild(name) plugin.assign() diff --git a/plugin.go b/plugin.go index 1bb5de5..06a85ab 100644 --- a/plugin.go +++ b/plugin.go @@ -55,30 +55,28 @@ type Plugin struct { *zap.Logger } -func (opt *Plugin) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { +func (opt *Plugin) logHandler(pattern string, handler func(http.ResponseWriter, *http.Request)) http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + opt.Debug("visit", zap.String("path", pattern), zap.String("remote", r.RemoteAddr)) + handler(rw, r) + } +} +func (opt *Plugin) handleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { if opt == nil { return } - var cors bool - if v, ok := opt.RawConfig["cors"]; ok { - cors = v.(bool) - } else if EngineConfig.CORS { - cors = true - } + conf, ok := opt.Config.(config.HTTPConfig) if !strings.HasPrefix(pattern, "/") { pattern = "/" + pattern } + opt.Info("http handle added:" + pattern) + if ok { + conf.HandleFunc(pattern, opt.logHandler(pattern, handler)) + } if opt != Engine { pattern = "/" + strings.ToLower(opt.Name) + pattern + EngineConfig.HandleFunc(pattern, opt.logHandler(pattern, handler)) } - opt.Info("http handle added:" + pattern) - EngineConfig.HandleFunc(pattern, func(rw http.ResponseWriter, r *http.Request) { - if cors { - util.CORS(rw, r) - } - opt.Debug("visit", zap.String("path", pattern), zap.String("remote", r.RemoteAddr)) - handler(rw, r) - }) } // 读取独立配置合并入总配置中 @@ -111,6 +109,10 @@ func (opt *Plugin) assign() { } } } + if conf, ok := opt.Config.(config.HTTPConfig); ok { + httpConf := conf.GetHTTPConfig() + httpConf.InitMux() + } opt.registerHandler() opt.run() } @@ -120,6 +122,12 @@ func (opt *Plugin) run() { opt.RawConfig.Unmarshal(opt.Config) opt.Debug("config", zap.Any("config", opt.Config)) opt.Config.OnEvent(FirstConfig(opt.RawConfig)) + if conf, ok := opt.Config.(config.HTTPConfig); ok { + httpconf := conf.GetHTTPConfig() + if httpconf.ListenAddr != "" && httpconf.ListenAddr != EngineConfig.ListenAddr { + go conf.Listen(opt) + } + } } // Update 热更新配置 @@ -133,16 +141,13 @@ func (opt *Plugin) registerHandler() { v := reflect.ValueOf(opt.Config) // 注册http响应 for i, j := 0, t.NumMethod(); i < j; i++ { - mt := t.Method(i) - mv := v.Method(i) - if mv.CanConvert(handlerFuncType) { + name := t.Method(i).Name + if handler, ok := v.Method(i).Interface().(func(http.ResponseWriter, *http.Request)); ok { patten := "/" - if mt.Name != "ServeHTTP" { - patten = strings.ToLower(strings.ReplaceAll(mt.Name, "_", "/")) - } else if opt == Engine { - continue + if name != "ServeHTTP" { + patten = strings.ToLower(strings.ReplaceAll(name, "_", "/")) } - opt.HandleFunc(patten, mv.Interface().(func(http.ResponseWriter, *http.Request))) + opt.handleFunc(patten, handler) } } } diff --git a/publisher.go b/publisher.go index a187678..f4404e4 100644 --- a/publisher.go +++ b/publisher.go @@ -40,6 +40,8 @@ type Puller struct { } // 是否需要重连 -func (pub *Puller) Reconnect() bool { - return pub.Config.RePull == -1 || pub.ReConnectCount <= pub.Config.RePull +func (pub *Puller) Reconnect() (ok bool) { + ok = pub.Config.RePull == -1 || pub.ReConnectCount <= pub.Config.RePull + pub.ReConnectCount++ + return } diff --git a/summary.go b/summary.go new file mode 100644 index 0000000..d99efb2 --- /dev/null +++ b/summary.go @@ -0,0 +1,123 @@ +package engine + +import ( + "sync/atomic" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v3/net" + "m7s.live/engine/v4/log" + "m7s.live/engine/v4/util" +) + +var summary Summary +var children util.Map[string, *Summary] + +func init() { + children.Init() + go summary.Start() +} + +// ServerSummary 系统摘要定义 +type Summary struct { + Address string + Memory struct { + Total uint64 + Free uint64 + Used uint64 + Usage float64 + } + CPUUsage float64 + HardDisk struct { + Total uint64 + Free uint64 + Used uint64 + Usage float64 + } + NetWork []NetWorkInfo + Streams []*Stream + lastNetWork []net.IOCountersStat + ref int32 +} + +// NetWorkInfo 网速信息 +type NetWorkInfo struct { + Name string + Receive uint64 + Sent uint64 + ReceiveSpeed uint64 + SentSpeed uint64 +} + +//StartSummary 开始定时采集数据,每秒一次 +func (s *Summary) Start() { + for range time.Tick(time.Second) { + if s.ref > 0 { + summary.collect() + } + } +} +func (s *Summary) Point() *Summary { + return s +} +// Running 是否正在采集数据 +func (s *Summary) Running() bool { + return s.ref > 0 +} + +// Add 增加订阅者 +func (s *Summary) Add() { + if atomic.AddInt32(&s.ref, 1) == 1 { + log.Info("start report summary") + } +} + +// Done 删除订阅者 +func (s *Summary) Done() { + if atomic.AddInt32(&s.ref, -1) == 0 { + log.Info("stop report summary") + s.lastNetWork = nil + } +} + +// Report 上报数据 +func (s *Summary) Report(slave *Summary) { + children.Set(slave.Address, slave) +} + +func (s *Summary) collect() *Summary{ + v, _ := mem.VirtualMemory() + d, _ := disk.Usage("/") + nv, _ := net.IOCounters(true) + + s.Memory.Total = v.Total >> 20 + s.Memory.Free = v.Available >> 20 + s.Memory.Used = v.Used >> 20 + s.Memory.Usage = v.UsedPercent + + if cc, _ := cpu.Percent(time.Second, false); len(cc) > 0 { + s.CPUUsage = cc[0] + } + s.HardDisk.Free = d.Free >> 30 + s.HardDisk.Total = d.Total >> 30 + s.HardDisk.Used = d.Used >> 30 + s.HardDisk.Usage = d.UsedPercent + s.NetWork = []NetWorkInfo{} + for i, n := range nv { + info := NetWorkInfo{ + Name: n.Name, + Receive: n.BytesRecv, + Sent: n.BytesSent, + } + if s.lastNetWork != nil && len(s.lastNetWork) > i { + info.ReceiveSpeed = n.BytesRecv - s.lastNetWork[i].BytesRecv + info.SentSpeed = n.BytesSent - s.lastNetWork[i].BytesSent + } + s.NetWork = append(s.NetWork, info) + } + s.lastNetWork = nv + s.Streams = Streams.ToList() + return s +} diff --git a/util/socket.go b/util/socket.go index ae4503e..ee5c4b5 100644 --- a/util/socket.go +++ b/util/socket.go @@ -2,6 +2,8 @@ package util import ( "context" + "crypto/sha256" + "crypto/subtle" "encoding/json" "net" "net/http" @@ -69,12 +71,62 @@ func ListenUDP(address string, networkBuffer int) (*net.UDPConn, error) { return conn, err } -func CORS(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Credentials", "true") - origin := r.Header["Origin"] - if len(origin) == 0 { - w.Header().Set("Access-Control-Allow-Origin", "*") - } else { - w.Header().Set("Access-Control-Allow-Origin", origin[0]) - } +func CORS(next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Credentials", "true") + origin := r.Header["Origin"] + if len(origin) == 0 { + w.Header().Set("Access-Control-Allow-Origin", "*") + } else { + w.Header().Set("Access-Control-Allow-Origin", origin[0]) + } + if next != nil { + next.ServeHTTP(w, r) + } + }) +} + +func BasicAuth(u, p string, next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Extract the username and password from the request + // Authorization header. If no Authentication header is present + // or the header value is invalid, then the 'ok' return value + // will be false. + username, password, ok := r.BasicAuth() + if ok { + // Calculate SHA-256 hashes for the provided and expected + // usernames and passwords. + usernameHash := sha256.Sum256([]byte(username)) + passwordHash := sha256.Sum256([]byte(password)) + expectedUsernameHash := sha256.Sum256([]byte(u)) + expectedPasswordHash := sha256.Sum256([]byte(p)) + + // 使用 subtle.ConstantTimeCompare() 进行校验 + // the provided username and password hashes equal the + // expected username and password hashes. ConstantTimeCompare + // 如果值相等,则返回1,否则返回0。 + // Importantly, we should to do the work to evaluate both the + // username and password before checking the return values to + // 避免泄露信息。 + usernameMatch := (subtle.ConstantTimeCompare(usernameHash[:], expectedUsernameHash[:]) == 1) + passwordMatch := (subtle.ConstantTimeCompare(passwordHash[:], expectedPasswordHash[:]) == 1) + + // If the username and password are correct, then call + // the next handler in the chain. Make sure to return + // afterwards, so that none of the code below is run. + if usernameMatch && passwordMatch { + if next != nil { + next.ServeHTTP(w, r) + } + return + } + } + + // If the Authentication header is not present, is invalid, or the + // username or password is wrong, then set a WWW-Authenticate + // header to inform the client that we expect them to use basic + // authentication and send a 401 Unauthorized response. + w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + }) }