From bf4d6c905fc45127636d49681cb67acdbed9f321 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Tue, 16 May 2023 19:23:02 +0200 Subject: [PATCH] hls muxer: show index page even if stream is not present (#1806) --- internal/core/hls_http_server.go | 98 ++++++++++++++++++++++------- internal/core/hls_manager.go | 1 + internal/core/hls_muxer.go | 50 +-------------- internal/core/webrtc_http_server.go | 4 +- 4 files changed, 79 insertions(+), 74 deletions(-) diff --git a/internal/core/hls_http_server.go b/internal/core/hls_http_server.go index a7c03ce7..0526283e 100644 --- a/internal/core/hls_http_server.go +++ b/internal/core/hls_http_server.go @@ -3,6 +3,7 @@ package core import ( "context" "crypto/tls" + _ "embed" "log" "net" "net/http" @@ -16,6 +17,9 @@ import ( "github.com/bluenviron/mediamtx/internal/logger" ) +//go:embed hls_index.html +var hlsIndex []byte + type hlsHTTPServerParent interface { logger.Writer handleRequest(req hlsMuxerHandleRequestReq) @@ -23,13 +27,14 @@ type hlsHTTPServerParent interface { type hlsHTTPServer struct { allowOrigin string + pathManager *pathManager parent hlsHTTPServerParent ln net.Listener inner *http.Server } -func newHLSHTTPServer( +func newHLSHTTPServer( //nolint:dupl address string, encryption bool, serverKey string, @@ -37,6 +42,7 @@ func newHLSHTTPServer( allowOrigin string, trustedProxies conf.IPsOrCIDRs, readTimeout conf.StringDuration, + pathManager *pathManager, parent hlsHTTPServerParent, ) (*hlsHTTPServer, error) { ln, err := net.Listen(restrictNetwork("tcp", address)) @@ -59,6 +65,7 @@ func newHLSHTTPServer( s := &hlsHTTPServer{ allowOrigin: allowOrigin, + pathManager: pathManager, parent: parent, ln: ln, } @@ -113,36 +120,79 @@ func (s *hlsHTTPServer) onRequest(ctx *gin.Context) { // remove leading prefix pa := ctx.Request.URL.Path[1:] - switch pa { - case "", "favicon.ico": - return - } + var dir string + var fname string - dir, fname := func() (string, string) { - if strings.HasSuffix(pa, ".m3u8") || - strings.HasSuffix(pa, ".ts") || - strings.HasSuffix(pa, ".mp4") || - strings.HasSuffix(pa, ".mp") { - return gopath.Dir(pa), gopath.Base(pa) + switch { + case pa == "", pa == "favicon.ico": + return + + case strings.HasSuffix(pa, ".m3u8") || + strings.HasSuffix(pa, ".ts") || + strings.HasSuffix(pa, ".mp4") || + strings.HasSuffix(pa, ".mp"): + dir, fname = gopath.Dir(pa), gopath.Base(pa) + + if strings.HasSuffix(fname, ".mp") { + fname += "4" } - return pa, "" - }() - if fname == "" && !strings.HasSuffix(dir, "/") { - ctx.Writer.Header().Set("Location", "/"+dir+"/") - ctx.Writer.WriteHeader(http.StatusMovedPermanently) - return - } + default: + dir, fname = pa, "" - if strings.HasSuffix(fname, ".mp") { - fname += "4" + if !strings.HasSuffix(dir, "/") { + ctx.Writer.Header().Set("Location", "/"+dir+"/") + ctx.Writer.WriteHeader(http.StatusMovedPermanently) + return + } } dir = strings.TrimSuffix(dir, "/") + if dir == "" { + return + } - s.parent.handleRequest(hlsMuxerHandleRequestReq{ - path: dir, - file: fname, - ctx: ctx, + user, pass, hasCredentials := ctx.Request.BasicAuth() + + res := s.pathManager.getPathConf(pathGetPathConfReq{ + name: dir, + publish: false, + credentials: authCredentials{ + query: ctx.Request.URL.RawQuery, + ip: net.ParseIP(ctx.ClientIP()), + user: user, + pass: pass, + proto: authProtocolWebRTC, + }, }) + if res.err != nil { + if terr, ok := res.err.(pathErrAuth); ok { + if !hasCredentials { + ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) + ctx.Writer.WriteHeader(http.StatusUnauthorized) + return + } + + s.Log(logger.Info, "authentication error: %v", terr.wrapped) + ctx.Writer.WriteHeader(http.StatusUnauthorized) + return + } + + ctx.Writer.WriteHeader(http.StatusNotFound) + return + } + + switch fname { + case "": + ctx.Writer.Header().Set("Content-Type", "text/html") + ctx.Writer.WriteHeader(http.StatusOK) + ctx.Writer.Write(hlsIndex) + + default: + s.parent.handleRequest(hlsMuxerHandleRequestReq{ + path: dir, + file: fname, + ctx: ctx, + }) + } } diff --git a/internal/core/hls_manager.go b/internal/core/hls_manager.go index 8a40070d..b91e438b 100644 --- a/internal/core/hls_manager.go +++ b/internal/core/hls_manager.go @@ -129,6 +129,7 @@ func newHLSManager( allowOrigin, trustedProxies, readTimeout, + m.pathManager, m, ) if err != nil { diff --git a/internal/core/hls_muxer.go b/internal/core/hls_muxer.go index 7f2c9733..ecd47ba6 100644 --- a/internal/core/hls_muxer.go +++ b/internal/core/hls_muxer.go @@ -1,13 +1,9 @@ package core import ( - "bytes" "context" - _ "embed" "errors" "fmt" - "io" - "net" "net/http" "os" "path/filepath" @@ -34,9 +30,6 @@ const ( hlsMuxerRecreatePause = 10 * time.Second ) -//go:embed hls_index.html -var hlsIndex []byte - type responseWriterWithCounter struct { http.ResponseWriter bytesSent *uint64 @@ -55,10 +48,6 @@ type hlsMuxerHandleRequestReq struct { res chan *hlsMuxer } -type hlsMuxerPathManager interface { - readerAdd(req pathReaderAddReq) pathReaderSetupPlayRes -} - type hlsMuxerParent interface { logger.Writer muxerClose(*hlsMuxer) @@ -77,7 +66,7 @@ type hlsMuxer struct { readBufferCount int wg *sync.WaitGroup pathName string - pathManager hlsMuxerPathManager + pathManager *pathManager parent hlsMuxerParent ctx context.Context @@ -109,7 +98,7 @@ func newHLSMuxer( readBufferCount int, wg *sync.WaitGroup, pathName string, - pathManager hlsMuxerPathManager, + pathManager *pathManager, parent hlsMuxerParent, ) *hlsMuxer { ctx, ctxCancel := context.WithCancel(parentCtx) @@ -553,41 +542,6 @@ func (m *hlsMuxer) handleRequest(ctx *gin.Context) { bytesSent: m.bytesSent, } - user, pass, hasCredentials := ctx.Request.BasicAuth() - - err := authenticate( - m.externalAuthenticationURL, - nil, - m.pathName, - m.path.safeConf(), - false, - authCredentials{ - query: ctx.Request.URL.RawQuery, - ip: net.ParseIP(ctx.ClientIP()), - user: user, - pass: pass, - proto: authProtocolHLS, - }, - ) - if err != nil { - if !hasCredentials { - ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) - w.WriteHeader(http.StatusUnauthorized) - return - } - - m.Log(logger.Info, "authentication error: %s", err) - w.WriteHeader(http.StatusUnauthorized) - return - } - - if ctx.Request.URL.Path == "" { - ctx.Header("Content-Type", `text/html`) - w.WriteHeader(http.StatusOK) - io.Copy(w, bytes.NewReader(hlsIndex)) - return - } - m.muxer.Handle(w, ctx.Request) } diff --git a/internal/core/webrtc_http_server.go b/internal/core/webrtc_http_server.go index 7bfff25d..f003e64e 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -122,7 +122,7 @@ type webRTCHTTPServer struct { inner *http.Server } -func newWebRTCHTTPServer( +func newWebRTCHTTPServer( //nolint:dupl address string, encryption bool, serverKey string, @@ -214,7 +214,7 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { var publish bool switch { - case pa == "favicon.ico": + case pa == "", pa == "favicon.ico": return case strings.HasSuffix(pa, "/publish"):