Browse Source

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

pull/1808/head
Alessandro Ros 3 years ago committed by GitHub
parent
commit
bf4d6c905f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 78
      internal/core/hls_http_server.go
  2. 1
      internal/core/hls_manager.go
  3. 50
      internal/core/hls_muxer.go
  4. 4
      internal/core/webrtc_http_server.go

78
internal/core/hls_http_server.go

@ -3,6 +3,7 @@ package core
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
_ "embed"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -16,6 +17,9 @@ import (
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
) )
//go:embed hls_index.html
var hlsIndex []byte
type hlsHTTPServerParent interface { type hlsHTTPServerParent interface {
logger.Writer logger.Writer
handleRequest(req hlsMuxerHandleRequestReq) handleRequest(req hlsMuxerHandleRequestReq)
@ -23,13 +27,14 @@ type hlsHTTPServerParent interface {
type hlsHTTPServer struct { type hlsHTTPServer struct {
allowOrigin string allowOrigin string
pathManager *pathManager
parent hlsHTTPServerParent parent hlsHTTPServerParent
ln net.Listener ln net.Listener
inner *http.Server inner *http.Server
} }
func newHLSHTTPServer( func newHLSHTTPServer( //nolint:dupl
address string, address string,
encryption bool, encryption bool,
serverKey string, serverKey string,
@ -37,6 +42,7 @@ func newHLSHTTPServer(
allowOrigin string, allowOrigin string,
trustedProxies conf.IPsOrCIDRs, trustedProxies conf.IPsOrCIDRs,
readTimeout conf.StringDuration, readTimeout conf.StringDuration,
pathManager *pathManager,
parent hlsHTTPServerParent, parent hlsHTTPServerParent,
) (*hlsHTTPServer, error) { ) (*hlsHTTPServer, error) {
ln, err := net.Listen(restrictNetwork("tcp", address)) ln, err := net.Listen(restrictNetwork("tcp", address))
@ -59,6 +65,7 @@ func newHLSHTTPServer(
s := &hlsHTTPServer{ s := &hlsHTTPServer{
allowOrigin: allowOrigin, allowOrigin: allowOrigin,
pathManager: pathManager,
parent: parent, parent: parent,
ln: ln, ln: ln,
} }
@ -113,36 +120,79 @@ func (s *hlsHTTPServer) onRequest(ctx *gin.Context) {
// remove leading prefix // remove leading prefix
pa := ctx.Request.URL.Path[1:] pa := ctx.Request.URL.Path[1:]
switch pa { var dir string
case "", "favicon.ico": var fname string
switch {
case pa == "", pa == "favicon.ico":
return return
}
dir, fname := func() (string, string) { case strings.HasSuffix(pa, ".m3u8") ||
if strings.HasSuffix(pa, ".m3u8") ||
strings.HasSuffix(pa, ".ts") || strings.HasSuffix(pa, ".ts") ||
strings.HasSuffix(pa, ".mp4") || strings.HasSuffix(pa, ".mp4") ||
strings.HasSuffix(pa, ".mp") { strings.HasSuffix(pa, ".mp"):
return gopath.Dir(pa), gopath.Base(pa) dir, fname = gopath.Dir(pa), gopath.Base(pa)
if strings.HasSuffix(fname, ".mp") {
fname += "4"
} }
return pa, ""
}()
if fname == "" && !strings.HasSuffix(dir, "/") { default:
dir, fname = pa, ""
if !strings.HasSuffix(dir, "/") {
ctx.Writer.Header().Set("Location", "/"+dir+"/") ctx.Writer.Header().Set("Location", "/"+dir+"/")
ctx.Writer.WriteHeader(http.StatusMovedPermanently) ctx.Writer.WriteHeader(http.StatusMovedPermanently)
return return
} }
if strings.HasSuffix(fname, ".mp") {
fname += "4"
} }
dir = strings.TrimSuffix(dir, "/") dir = strings.TrimSuffix(dir, "/")
if dir == "" {
return
}
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{ s.parent.handleRequest(hlsMuxerHandleRequestReq{
path: dir, path: dir,
file: fname, file: fname,
ctx: ctx, ctx: ctx,
}) })
}
} }

1
internal/core/hls_manager.go

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

50
internal/core/hls_muxer.go

@ -1,13 +1,9 @@
package core package core
import ( import (
"bytes"
"context" "context"
_ "embed"
"errors" "errors"
"fmt" "fmt"
"io"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -34,9 +30,6 @@ const (
hlsMuxerRecreatePause = 10 * time.Second hlsMuxerRecreatePause = 10 * time.Second
) )
//go:embed hls_index.html
var hlsIndex []byte
type responseWriterWithCounter struct { type responseWriterWithCounter struct {
http.ResponseWriter http.ResponseWriter
bytesSent *uint64 bytesSent *uint64
@ -55,10 +48,6 @@ type hlsMuxerHandleRequestReq struct {
res chan *hlsMuxer res chan *hlsMuxer
} }
type hlsMuxerPathManager interface {
readerAdd(req pathReaderAddReq) pathReaderSetupPlayRes
}
type hlsMuxerParent interface { type hlsMuxerParent interface {
logger.Writer logger.Writer
muxerClose(*hlsMuxer) muxerClose(*hlsMuxer)
@ -77,7 +66,7 @@ type hlsMuxer struct {
readBufferCount int readBufferCount int
wg *sync.WaitGroup wg *sync.WaitGroup
pathName string pathName string
pathManager hlsMuxerPathManager pathManager *pathManager
parent hlsMuxerParent parent hlsMuxerParent
ctx context.Context ctx context.Context
@ -109,7 +98,7 @@ func newHLSMuxer(
readBufferCount int, readBufferCount int,
wg *sync.WaitGroup, wg *sync.WaitGroup,
pathName string, pathName string,
pathManager hlsMuxerPathManager, pathManager *pathManager,
parent hlsMuxerParent, parent hlsMuxerParent,
) *hlsMuxer { ) *hlsMuxer {
ctx, ctxCancel := context.WithCancel(parentCtx) ctx, ctxCancel := context.WithCancel(parentCtx)
@ -553,41 +542,6 @@ func (m *hlsMuxer) handleRequest(ctx *gin.Context) {
bytesSent: m.bytesSent, 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) m.muxer.Handle(w, ctx.Request)
} }

4
internal/core/webrtc_http_server.go

@ -122,7 +122,7 @@ type webRTCHTTPServer struct {
inner *http.Server inner *http.Server
} }
func newWebRTCHTTPServer( func newWebRTCHTTPServer( //nolint:dupl
address string, address string,
encryption bool, encryption bool,
serverKey string, serverKey string,
@ -214,7 +214,7 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) {
var publish bool var publish bool
switch { switch {
case pa == "favicon.ico": case pa == "", pa == "favicon.ico":
return return
case strings.HasSuffix(pa, "/publish"): case strings.HasSuffix(pa, "/publish"):

Loading…
Cancel
Save