hls muxer: show index page even if stream is not present (#1806)

This commit is contained in:
Alessandro Ros 2023-05-16 19:23:02 +02:00 committed by GitHub
parent 35b67a9ec1
commit bf4d6c905f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 74 deletions

View file

@ -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,
})
}
}

View file

@ -129,6 +129,7 @@ func newHLSManager(
allowOrigin,
trustedProxies,
readTimeout,
m.pathManager,
m,
)
if err != nil {

View file

@ -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)
}

View file

@ -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"):