mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 05:35:57 +08:00
feat: add fasthttp
This commit is contained in:
@@ -112,6 +112,7 @@ The following build tags can be used to customize your build:
|
||||
| postgres | Enables the postgres DB |
|
||||
| duckdb | Enables the duckdb DB |
|
||||
| taskpanic | Throws panic, for testing |
|
||||
| fasthttp | Enables the fasthttp server instead of net/http |
|
||||
|
||||
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||
|
||||
|
@@ -115,6 +115,7 @@ go run -tags sqlite main.go
|
||||
| postgres | 启用 PostgreSQL 存储 |
|
||||
| duckdb | 启用 DuckDB 存储 |
|
||||
| taskpanic | 抛出 panic(用于测试) |
|
||||
| fasthttp | 使用 fasthttp 服务器代替标准库 |
|
||||
|
||||
<p align="right">(<a href="#readme-top">返回顶部</a>)</p>
|
||||
|
||||
|
14
api.go
14
api.go
@@ -383,7 +383,7 @@ func (s *Server) api_VideoTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sse := util.NewSSE(rw, r.Context())
|
||||
util.NewSSE(rw, r.Context(), func(sse *util.SSE) {
|
||||
PlayBlock(suber, (func(frame *pkg.AVFrame) (err error))(nil), func(frame *pkg.AVFrame) (err error) {
|
||||
var snap pb.TrackSnapShot
|
||||
snap.Sequence = frame.Sequence
|
||||
@@ -400,10 +400,7 @@ func (s *Server) api_VideoTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return sse.WriteJSON(&snap)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) api_AudioTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
||||
@@ -419,7 +416,7 @@ func (s *Server) api_AudioTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sse := util.NewSSE(rw, r.Context())
|
||||
util.NewSSE(rw, r.Context(), func(sse *util.SSE) {
|
||||
PlayBlock(suber, func(frame *pkg.AVFrame) (err error) {
|
||||
var snap pb.TrackSnapShot
|
||||
snap.Sequence = frame.Sequence
|
||||
@@ -436,10 +433,7 @@ func (s *Server) api_AudioTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return sse.WriteJSON(&snap)
|
||||
}, (func(frame *pkg.AVFrame) (err error))(nil))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) VideoTrackSnap(ctx context.Context, req *pb.StreamSnapRequest) (res *pb.TrackSnapShotResponse, err error) {
|
||||
|
16
go.mod
16
go.mod
@@ -1,6 +1,6 @@
|
||||
module m7s.live/v5
|
||||
|
||||
go 1.23
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/IOTechSystems/onvif v1.2.0
|
||||
@@ -43,7 +43,7 @@ require (
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/yapingcat/gomedia v0.0.0-20240601043430-920523f8e5c7
|
||||
golang.org/x/image v0.22.0
|
||||
golang.org/x/text v0.20.0
|
||||
golang.org/x/text v0.24.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
@@ -58,6 +58,7 @@ require (
|
||||
github.com/VictoriaMetrics/metrics v1.35.1 // indirect
|
||||
github.com/VictoriaMetrics/metricsql v0.76.0 // indirect
|
||||
github.com/abema/go-mp4 v1.2.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/asticode/go-astikit v0.30.0 // indirect
|
||||
github.com/asticode/go-astits v1.13.0 // indirect
|
||||
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c // indirect
|
||||
@@ -81,7 +82,7 @@ require (
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/marcboeker/go-duckdb v1.0.5 // indirect
|
||||
@@ -115,6 +116,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.61.0 // indirect
|
||||
github.com/valyala/fastjson v1.6.4 // indirect
|
||||
github.com/valyala/fastrand v1.1.0 // indirect
|
||||
github.com/valyala/gozstd v1.21.1 // indirect
|
||||
@@ -124,7 +126,7 @@ require (
|
||||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
)
|
||||
|
||||
@@ -142,11 +144,11 @@ require (
|
||||
github.com/prometheus/client_golang v1.20.4
|
||||
github.com/quangngotan95/go-m3u8 v0.1.0
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sys v0.32.0
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
17
go.sum
17
go.sum
@@ -17,6 +17,8 @@ github.com/alchemy/rotoslog v0.2.2 h1:yzAOjaQBKgJvAdPi0sF5KSPMq5f2vNJZEnPr73CPDz
|
||||
github.com/alchemy/rotoslog v0.2.2/go.mod h1:pOHF0DKryPLaQzjcUlidLVRTksvk9yW75YIu1yYiiEQ=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/asavie/xdp v0.3.3 h1:b5Aa3EkMJYBeUO5TxPTIAa4wyUqYcsQr2s8f6YLJXhE=
|
||||
github.com/asavie/xdp v0.3.3/go.mod h1:Vv5p+3mZiDh7ImdSvdon3E78wXyre7df5V58ATdIYAY=
|
||||
github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA=
|
||||
@@ -139,6 +141,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -297,6 +301,8 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.61.0 h1:VV08V0AfoRaFurP1EWKvQQdPTZHiUzaVoulX1aBDgzU=
|
||||
github.com/valyala/fasthttp v1.61.0/go.mod h1:wRIV/4cMwUPWnRcDno9hGnYZGh78QzODFfo1LTUhBog=
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
|
||||
@@ -313,6 +319,7 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yapingcat/gomedia v0.0.0-20240601043430-920523f8e5c7 h1:e9n2WNcfvs20aLgpDhKoaJgrU/EeAvuNnWLBm31Q5Fw=
|
||||
github.com/yapingcat/gomedia v0.0.0-20240601043430-920523f8e5c7/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
@@ -327,6 +334,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7 h1:wDLEX9a7YQoKdKNQt88rtydkqDxeGaBUTnIYc3iG/mA=
|
||||
golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -342,9 +351,13 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -367,10 +380,14 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
|
@@ -1,13 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"m7s.live/v5/pkg/task"
|
||||
"m7s.live/v5/pkg/util"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
|
||||
@@ -46,7 +42,7 @@ func (config *HTTP) GetHandler() http.Handler {
|
||||
return config.mux
|
||||
}
|
||||
|
||||
func (config *HTTP) CreateHttpMux() *http.ServeMux {
|
||||
func (config *HTTP) CreateHttpMux() http.Handler {
|
||||
config.mux = http.NewServeMux()
|
||||
return config.mux
|
||||
}
|
||||
@@ -73,10 +69,10 @@ func (config *HTTP) Handle(path string, f http.Handler, last bool) {
|
||||
config.mux = http.NewServeMux()
|
||||
}
|
||||
if config.CORS {
|
||||
f = CORS(f)
|
||||
f = util.CORS(f)
|
||||
}
|
||||
if config.UserName != "" && config.Password != "" {
|
||||
f = BasicAuth(config.UserName, config.Password, f)
|
||||
f = util.BasicAuth(config.UserName, config.Password, f)
|
||||
}
|
||||
for _, middleware := range config.middlewares {
|
||||
f = middleware(path, f)
|
||||
@@ -91,151 +87,3 @@ func (config *HTTP) GetHTTPConfig() *HTTP {
|
||||
// func (config *HTTP) Handler(r *http.Request) (h http.Handler, pattern string) {
|
||||
// return config.mux.Handler(r)
|
||||
// }
|
||||
|
||||
func (config *HTTP) CreateHTTPWork(logger *slog.Logger) *ListenHTTPWork {
|
||||
ret := &ListenHTTPWork{HTTP: config}
|
||||
ret.Logger = logger.With("addr", config.ListenAddr)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (config *HTTP) CreateHTTPSWork(logger *slog.Logger) *ListenHTTPSWork {
|
||||
ret := &ListenHTTPSWork{ListenHTTPWork{HTTP: config}}
|
||||
ret.Logger = logger.With("addr", config.ListenAddrTLS)
|
||||
return ret
|
||||
}
|
||||
|
||||
func CORS(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
header := w.Header()
|
||||
header.Set("Access-Control-Allow-Credentials", "true")
|
||||
header.Set("Cross-Origin-Resource-Policy", "cross-origin")
|
||||
header.Set("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization")
|
||||
header.Set("Access-Control-Allow-Private-Network", "true")
|
||||
origin := r.Header["Origin"]
|
||||
if len(origin) == 0 {
|
||||
header.Set("Access-Control-Allow-Origin", "*")
|
||||
} else {
|
||||
header.Set("Access-Control-Allow-Origin", origin[0])
|
||||
}
|
||||
if next != nil && r.Method != "OPTIONS" {
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BasicAuth(u, p string, next http.Handler) http.Handler {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
type ListenHTTPWork struct {
|
||||
task.Task
|
||||
*HTTP
|
||||
*http.Server
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Start() (err error) {
|
||||
task.Server = &http.Server{
|
||||
Addr: task.ListenAddr,
|
||||
ReadTimeout: task.HTTP.ReadTimeout,
|
||||
WriteTimeout: task.HTTP.WriteTimeout,
|
||||
IdleTimeout: task.HTTP.IdleTimeout,
|
||||
Handler: task.GetHandler(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Go() error {
|
||||
task.Info("listen http")
|
||||
return task.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Dispose() {
|
||||
task.Info("http server stop")
|
||||
task.Server.Close()
|
||||
}
|
||||
|
||||
type ListenHTTPSWork struct {
|
||||
ListenHTTPWork
|
||||
}
|
||||
|
||||
func (task *ListenHTTPSWork) Start() (err error) {
|
||||
cer, _ := tls.X509KeyPair(LocalCert, LocalKey)
|
||||
task.Server = &http.Server{
|
||||
Addr: task.HTTP.ListenAddrTLS,
|
||||
ReadTimeout: task.HTTP.ReadTimeout,
|
||||
WriteTimeout: task.HTTP.WriteTimeout,
|
||||
IdleTimeout: task.HTTP.IdleTimeout,
|
||||
Handler: task.HTTP.GetHandler(),
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{cer},
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_AES_128_GCM_SHA256,
|
||||
tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_AES_256_GCM_SHA384,
|
||||
//tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
//tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
//tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
//tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (task *ListenHTTPSWork) Go() error {
|
||||
task.Info("listen https")
|
||||
return task.Server.ListenAndServeTLS(task.HTTP.CertFile, task.HTTP.KeyFile)
|
||||
}
|
||||
|
92
pkg/http_server_fasthttp.go
Normal file
92
pkg/http_server_fasthttp.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build fasthttp
|
||||
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
"m7s.live/v5/pkg/config"
|
||||
"m7s.live/v5/pkg/task"
|
||||
)
|
||||
|
||||
func CreateHTTPWork(conf *config.HTTP, logger *slog.Logger) *ListenFastHTTPWork {
|
||||
ret := &ListenFastHTTPWork{HTTP: conf}
|
||||
ret.Logger = logger.With("addr", conf.ListenAddr)
|
||||
return ret
|
||||
}
|
||||
|
||||
func CreateHTTPSWork(conf *config.HTTP, logger *slog.Logger) *ListenFastHTTPSWork {
|
||||
ret := &ListenFastHTTPSWork{ListenFastHTTPWork{HTTP: conf}}
|
||||
ret.Logger = logger.With("addr", conf.ListenAddrTLS)
|
||||
return ret
|
||||
}
|
||||
|
||||
// ListenFastHTTPWork 用于启动 FastHTTP 服务
|
||||
type ListenFastHTTPWork struct {
|
||||
task.Task
|
||||
*config.HTTP
|
||||
server *fasthttp.Server
|
||||
}
|
||||
|
||||
// 主请求处理函数
|
||||
func (task *ListenFastHTTPWork) requestHandler(ctx *fasthttp.RequestCtx) {
|
||||
// 适配到标准库处理
|
||||
// fasthttpadaptor.ConvertRequest(ctx, req, false)
|
||||
// 如果有 grpcMux,通过适配器转发
|
||||
// if string(ctx.Request.Header.Peek("Accept")) == "text/event-stream" {
|
||||
// ctx.SetContentType("text/event-stream")
|
||||
// ctx.Response.Header.Set("Cache-Control", "no-cache")
|
||||
// ctx.Response.Header.Set("Connection", "keep-alive")
|
||||
// ctx.Response.Header.Set("X-Accel-Buffering", "no")
|
||||
// ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")
|
||||
// }
|
||||
fasthttpadaptor.NewFastHTTPHandler(task.GetHandler())(ctx)
|
||||
}
|
||||
|
||||
func (task *ListenFastHTTPWork) Start() (err error) {
|
||||
// 配置 fasthttp 服务器
|
||||
task.server = &fasthttp.Server{
|
||||
Handler: task.requestHandler,
|
||||
ReadTimeout: task.HTTP.ReadTimeout,
|
||||
WriteTimeout: task.HTTP.WriteTimeout,
|
||||
IdleTimeout: task.HTTP.IdleTimeout,
|
||||
Name: "Monibuca FastHTTP Server",
|
||||
// 启用流式响应支持
|
||||
StreamRequestBody: true,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (task *ListenFastHTTPWork) Go() error {
|
||||
task.Info("listen fasthttp")
|
||||
return task.server.ListenAndServe(task.ListenAddr)
|
||||
}
|
||||
|
||||
func (task *ListenFastHTTPWork) Dispose() {
|
||||
task.Info("fasthttp server stop")
|
||||
if task.server != nil {
|
||||
if err := task.server.Shutdown(); err != nil {
|
||||
task.Error("shutdown error", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListenFastHTTPSWork 用于启动 HTTPS FastHTTP 服务
|
||||
type ListenFastHTTPSWork struct {
|
||||
ListenFastHTTPWork
|
||||
}
|
||||
|
||||
func (task *ListenFastHTTPSWork) Start() (err error) {
|
||||
// 调用基类的 Start
|
||||
if err = task.ListenFastHTTPWork.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (task *ListenFastHTTPSWork) Go() error {
|
||||
task.Info("listen https fasthttp")
|
||||
return task.server.ListenAndServeTLS(task.ListenAddrTLS, task.CertFile, task.KeyFile)
|
||||
}
|
96
pkg/http_server_std.go
Normal file
96
pkg/http_server_std.go
Normal file
@@ -0,0 +1,96 @@
|
||||
//go:build !fasthttp
|
||||
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"m7s.live/v5/pkg/config"
|
||||
"m7s.live/v5/pkg/task"
|
||||
)
|
||||
|
||||
func CreateHTTPWork(conf *config.HTTP, logger *slog.Logger) *ListenHTTPWork {
|
||||
ret := &ListenHTTPWork{HTTP: conf}
|
||||
ret.Logger = logger.With("addr", conf.ListenAddr)
|
||||
return ret
|
||||
}
|
||||
|
||||
func CreateHTTPSWork(conf *config.HTTP, logger *slog.Logger) *ListenHTTPSWork {
|
||||
ret := &ListenHTTPSWork{ListenHTTPWork{HTTP: conf}}
|
||||
ret.Logger = logger.With("addr", conf.ListenAddrTLS)
|
||||
return ret
|
||||
}
|
||||
|
||||
type ListenHTTPWork struct {
|
||||
task.Task
|
||||
*config.HTTP
|
||||
*http.Server
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Start() (err error) {
|
||||
task.Server = &http.Server{
|
||||
Addr: task.ListenAddr,
|
||||
ReadTimeout: task.HTTP.ReadTimeout,
|
||||
WriteTimeout: task.HTTP.WriteTimeout,
|
||||
IdleTimeout: task.HTTP.IdleTimeout,
|
||||
Handler: task.GetHandler(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Go() error {
|
||||
task.Info("listen http")
|
||||
return task.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
func (task *ListenHTTPWork) Dispose() {
|
||||
task.Info("http server stop")
|
||||
task.Server.Close()
|
||||
}
|
||||
|
||||
type ListenHTTPSWork struct {
|
||||
ListenHTTPWork
|
||||
}
|
||||
|
||||
func (task *ListenHTTPSWork) Start() (err error) {
|
||||
cer, _ := tls.X509KeyPair(config.LocalCert, config.LocalKey)
|
||||
task.Server = &http.Server{
|
||||
Addr: task.HTTP.ListenAddrTLS,
|
||||
ReadTimeout: task.HTTP.ReadTimeout,
|
||||
WriteTimeout: task.HTTP.WriteTimeout,
|
||||
IdleTimeout: task.HTTP.IdleTimeout,
|
||||
Handler: task.HTTP.GetHandler(),
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{cer},
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_AES_128_GCM_SHA256,
|
||||
tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_AES_256_GCM_SHA384,
|
||||
//tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
//tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
//tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
//tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (task *ListenHTTPSWork) Go() error {
|
||||
task.Info("listen https")
|
||||
return task.Server.ListenAndServeTLS(task.HTTP.CertFile, task.HTTP.KeyFile)
|
||||
}
|
@@ -150,16 +150,18 @@ func ReturnFetchValue[T any](fetch func() T, rw http.ResponseWriter, r *http.Req
|
||||
tickDur = time.Second
|
||||
}
|
||||
if r.Header.Get("Accept") == "text/event-stream" {
|
||||
sse := NewSSE(rw, r.Context())
|
||||
NewSSE(rw, r.Context(), func(sse *SSE) {
|
||||
tick := time.NewTicker(tickDur)
|
||||
defer tick.Stop()
|
||||
writer := Conditional(isYaml, sse.WriteYAML, sse.WriteJSON)
|
||||
writer(fetch())
|
||||
err := writer(fetch())
|
||||
for range tick.C {
|
||||
if writer(fetch()) != nil {
|
||||
if err = writer(fetch()); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
data := fetch()
|
||||
rw.Header().Set("Content-Type", Conditional(isYaml, "text/yaml", "application/json"))
|
||||
|
@@ -1,3 +1,5 @@
|
||||
//go:build !fasthttp
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
@@ -16,6 +18,7 @@ var (
|
||||
sseEnd = []byte("\n\n")
|
||||
)
|
||||
|
||||
// SSE 标准库实现
|
||||
type SSE struct {
|
||||
http.ResponseWriter
|
||||
context.Context
|
||||
@@ -45,7 +48,7 @@ func (sse *SSE) WriteEvent(event string, data []byte) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func NewSSE(w http.ResponseWriter, ctx context.Context) *SSE {
|
||||
func NewSSE(w http.ResponseWriter, ctx context.Context, block func(sse *SSE)) (sse *SSE) {
|
||||
header := w.Header()
|
||||
header.Set("Content-Type", "text/event-stream")
|
||||
header.Set("Cache-Control", "no-cache")
|
||||
@@ -56,10 +59,12 @@ func NewSSE(w http.ResponseWriter, ctx context.Context) *SSE {
|
||||
// rw.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
|
||||
// rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
// rw.Header().Set("Transfer-Encoding", "chunked")
|
||||
return &SSE{
|
||||
sse = &SSE{
|
||||
ResponseWriter: w,
|
||||
Context: ctx,
|
||||
}
|
||||
block(sse)
|
||||
return sse
|
||||
}
|
||||
|
||||
func (sse *SSE) WriteJSON(data any) error {
|
||||
|
87
pkg/util/sse_fasthttp.go
Normal file
87
pkg/util/sse_fasthttp.go
Normal file
@@ -0,0 +1,87 @@
|
||||
//go:build fasthttp
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 定义 SSE 常量,与 sse.go 中保持一致
|
||||
var (
|
||||
// 这些变量需要在这里重新定义,因为使用构建标签后无法共享
|
||||
sseEent = []byte("event: ")
|
||||
sseBegin = []byte("data: ")
|
||||
sseEnd = []byte("\n\n")
|
||||
)
|
||||
|
||||
// SSE 结构体在 fasthttp 构建模式下的实现
|
||||
type SSE struct {
|
||||
Writer *bufio.Writer
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (sse *SSE) Write(data []byte) (n int, err error) {
|
||||
if err = sse.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
buffers := net.Buffers{sseBegin, data, sseEnd}
|
||||
nn, err := buffers.WriteTo(sse.Writer)
|
||||
if err == nil {
|
||||
sse.Writer.Flush()
|
||||
}
|
||||
return int(nn), err
|
||||
}
|
||||
|
||||
func (sse *SSE) WriteEvent(event string, data []byte) (err error) {
|
||||
if err = sse.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
buffers := net.Buffers{sseEent, []byte(event + "\n"), sseBegin, data, sseEnd}
|
||||
_, err = buffers.WriteTo(sse.Writer)
|
||||
if err == nil {
|
||||
sse.Writer.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewSSE(w http.ResponseWriter, ctx context.Context, block func(sse *SSE)) (sse *SSE) {
|
||||
reqCtx := ctx.(*fasthttp.RequestCtx)
|
||||
header := w.Header()
|
||||
header.Set("Content-Type", "text/event-stream")
|
||||
header.Set("Cache-Control", "no-cache")
|
||||
header.Set("Connection", "keep-alive")
|
||||
header.Set("X-Accel-Buffering", "no")
|
||||
header.Set("Access-Control-Allow-Origin", "*")
|
||||
sse = &SSE{
|
||||
Context: ctx,
|
||||
}
|
||||
reqCtx.Response.SetBodyStreamWriter(func(w *bufio.Writer) {
|
||||
sse.Writer = w
|
||||
block(sse)
|
||||
<-ctx.Done()
|
||||
})
|
||||
return sse
|
||||
}
|
||||
|
||||
func (sse *SSE) WriteJSON(data any) error {
|
||||
return json.NewEncoder(sse).Encode(data)
|
||||
}
|
||||
|
||||
func (sse *SSE) WriteYAML(data any) error {
|
||||
return yaml.NewEncoder(sse).Encode(data)
|
||||
}
|
||||
|
||||
// WriteExec 执行命令并将输出写入 SSE 流
|
||||
func (sse *SSE) WriteExec(cmd *exec.Cmd) error {
|
||||
cmd.Stderr = sse
|
||||
cmd.Stdout = sse
|
||||
return cmd.Run()
|
||||
}
|
@@ -320,12 +320,12 @@ func (p *Plugin) listen() (err error) {
|
||||
|
||||
if httpConf.ListenAddrTLS != "" && (httpConf.ListenAddrTLS != p.Server.config.HTTP.ListenAddrTLS) {
|
||||
p.SetDescription("httpTLS", strings.TrimPrefix(httpConf.ListenAddrTLS, ":"))
|
||||
p.AddDependTask(httpConf.CreateHTTPSWork(p.Logger))
|
||||
p.AddDependTask(CreateHTTPSWork(httpConf, p.Logger))
|
||||
}
|
||||
|
||||
if httpConf.ListenAddr != "" && (httpConf.ListenAddr != p.Server.config.HTTP.ListenAddr) {
|
||||
p.SetDescription("http", strings.TrimPrefix(httpConf.ListenAddr, ":"))
|
||||
p.AddDependTask(httpConf.CreateHTTPWork(p.Logger))
|
||||
p.AddDependTask(CreateHTTPWork(httpConf, p.Logger))
|
||||
}
|
||||
|
||||
if tcphandler, ok := p.handler.(ITCPPlugin); ok {
|
||||
|
@@ -52,14 +52,15 @@ func (h *LogRotatePlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (l *LogRotatePlugin) API_trail(w http.ResponseWriter, r *http.Request) {
|
||||
writer := util.NewSSE(w, r.Context())
|
||||
util.NewSSE(w, r.Context(), func(sse *util.SSE) {
|
||||
file, err := os.Open(filepath.Join(l.Path, "current.log"))
|
||||
if err == nil {
|
||||
io.Copy(writer, file)
|
||||
io.Copy(sse, file)
|
||||
file.Close()
|
||||
}
|
||||
h := console.NewHandler(writer, &console.HandlerOptions{NoColor: true})
|
||||
h := console.NewHandler(sse, &console.HandlerOptions{NoColor: true})
|
||||
l.Server.LogHandler.Add(h)
|
||||
<-r.Context().Done()
|
||||
l.Server.LogHandler.Remove(h)
|
||||
})
|
||||
}
|
||||
|
@@ -331,10 +331,10 @@ func (s *Server) Start() (err error) {
|
||||
}
|
||||
|
||||
if httpConf.ListenAddrTLS != "" {
|
||||
s.AddDependTask(httpConf.CreateHTTPSWork(s.Logger))
|
||||
s.AddDependTask(pkg.CreateHTTPSWork(httpConf, s.Logger))
|
||||
}
|
||||
if httpConf.ListenAddr != "" {
|
||||
s.AddDependTask(httpConf.CreateHTTPWork(s.Logger))
|
||||
s.AddDependTask(pkg.CreateHTTPWork(httpConf, s.Logger))
|
||||
}
|
||||
|
||||
var grpcServer *GRPCServer
|
||||
|
Reference in New Issue
Block a user