mediamtx/internal/core/hls_http_server.go
2023-05-16 16:14:20 +02:00

149 lines
3.0 KiB
Go

package core
import (
"context"
"crypto/tls"
"log"
"net"
"net/http"
gopath "path"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
)
type hlsHTTPServerParent interface {
logger.Writer
handleRequest(req hlsMuxerHandleRequestReq)
}
type hlsHTTPServer struct {
allowOrigin string
parent hlsHTTPServerParent
ln net.Listener
inner *http.Server
}
func newHLSHTTPServer(
address string,
encryption bool,
serverKey string,
serverCert string,
allowOrigin string,
trustedProxies conf.IPsOrCIDRs,
readTimeout conf.StringDuration,
parent hlsHTTPServerParent,
) (*hlsHTTPServer, error) {
ln, err := net.Listen(restrictNetwork("tcp", address))
if err != nil {
return nil, err
}
var tlsConfig *tls.Config
if encryption {
crt, err := tls.LoadX509KeyPair(serverCert, serverKey)
if err != nil {
ln.Close()
return nil, err
}
tlsConfig = &tls.Config{
Certificates: []tls.Certificate{crt},
}
}
s := &hlsHTTPServer{
allowOrigin: allowOrigin,
parent: parent,
ln: ln,
}
router := gin.New()
httpSetTrustedProxies(router, trustedProxies)
router.NoRoute(httpLoggerMiddleware(s), httpServerHeaderMiddleware, s.onRequest)
s.inner = &http.Server{
Handler: router,
TLSConfig: tlsConfig,
ReadHeaderTimeout: time.Duration(readTimeout),
ErrorLog: log.New(&nilWriter{}, "", 0),
}
if tlsConfig != nil {
go s.inner.ServeTLS(s.ln, "", "")
} else {
go s.inner.Serve(s.ln)
}
return s, nil
}
func (s *hlsHTTPServer) Log(level logger.Level, format string, args ...interface{}) {
s.parent.Log(level, format, args...)
}
func (s *hlsHTTPServer) close() {
s.inner.Shutdown(context.Background())
s.ln.Close() // in case Shutdown() is called before Serve()
}
func (s *hlsHTTPServer) onRequest(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", s.allowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
switch ctx.Request.Method {
case http.MethodGet:
case http.MethodOptions:
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", ctx.Request.Header.Get("Access-Control-Request-Headers"))
ctx.Writer.WriteHeader(http.StatusOK)
return
default:
return
}
// remove leading prefix
pa := ctx.Request.URL.Path[1:]
switch pa {
case "", "favicon.ico":
return
}
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)
}
return pa, ""
}()
if fname == "" && !strings.HasSuffix(dir, "/") {
ctx.Writer.Header().Set("Location", "/"+dir+"/")
ctx.Writer.WriteHeader(http.StatusMovedPermanently)
return
}
if strings.HasSuffix(fname, ".mp") {
fname += "4"
}
dir = strings.TrimSuffix(dir, "/")
s.parent.handleRequest(hlsMuxerHandleRequestReq{
path: dir,
file: fname,
ctx: ctx,
})
}