hls: use gin as HTTP router
This commit is contained in:
@@ -288,9 +288,7 @@ func newAPI(
|
||||
group.GET("/v1/rtmpconns/list", a.onRTMPConnsList)
|
||||
group.POST("/v1/rtmpconns/kick/:id", a.onRTMPConnsKick)
|
||||
|
||||
a.s = &http.Server{
|
||||
Handler: router,
|
||||
}
|
||||
a.s = &http.Server{Handler: router}
|
||||
|
||||
go a.s.Serve(ln)
|
||||
|
||||
|
||||
+49
-28
@@ -93,12 +93,17 @@ window.addEventListener('DOMContentLoaded', create);
|
||||
</html>
|
||||
`
|
||||
|
||||
type hlsMuxerResponse struct {
|
||||
Status int
|
||||
Header map[string]string
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
type hlsMuxerRequest struct {
|
||||
Dir string
|
||||
File string
|
||||
Req *http.Request
|
||||
W http.ResponseWriter
|
||||
Res chan io.Reader
|
||||
Res chan hlsMuxerResponse
|
||||
}
|
||||
|
||||
type hlsMuxerTrackIDPayloadPair struct {
|
||||
@@ -211,7 +216,7 @@ outer:
|
||||
|
||||
case req := <-r.request:
|
||||
if isReady {
|
||||
r.handleRequest(req)
|
||||
req.Res <- r.handleRequest(req)
|
||||
} else {
|
||||
r.requests = append(r.requests, req)
|
||||
}
|
||||
@@ -219,7 +224,7 @@ outer:
|
||||
case <-innerReady:
|
||||
isReady = true
|
||||
for _, req := range r.requests {
|
||||
r.handleRequest(req)
|
||||
req.Res <- r.handleRequest(req)
|
||||
}
|
||||
r.requests = nil
|
||||
|
||||
@@ -235,8 +240,7 @@ outer:
|
||||
r.ctxCancel()
|
||||
|
||||
for _, req := range r.requests {
|
||||
req.W.WriteHeader(http.StatusNotFound)
|
||||
req.Res <- nil
|
||||
req.Res <- hlsMuxerResponse{Status: http.StatusNotFound}
|
||||
}
|
||||
|
||||
r.parent.OnMuxerClose(r)
|
||||
@@ -397,7 +401,7 @@ func (r *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
|
||||
}
|
||||
}
|
||||
|
||||
func (r *hlsMuxer) handleRequest(req hlsMuxerRequest) {
|
||||
func (r *hlsMuxer) handleRequest(req hlsMuxerRequest) hlsMuxerResponse {
|
||||
atomic.StoreInt64(r.lastRequestTime, time.Now().Unix())
|
||||
|
||||
conf := r.path.Conf()
|
||||
@@ -407,48 +411,66 @@ func (r *hlsMuxer) handleRequest(req hlsMuxerRequest) {
|
||||
ip := net.ParseIP(tmp)
|
||||
if !ipEqualOrInRange(ip, conf.ReadIPs) {
|
||||
r.log(logger.Info, "ERR: ip '%s' not allowed", ip)
|
||||
req.W.WriteHeader(http.StatusUnauthorized)
|
||||
req.Res <- nil
|
||||
return
|
||||
return hlsMuxerResponse{Status: http.StatusUnauthorized}
|
||||
}
|
||||
}
|
||||
|
||||
if conf.ReadUser != "" {
|
||||
user, pass, ok := req.Req.BasicAuth()
|
||||
if !ok || user != string(conf.ReadUser) || pass != string(conf.ReadPass) {
|
||||
req.W.Header().Set("WWW-Authenticate", `Basic realm="rtsp-simple-server"`)
|
||||
req.W.WriteHeader(http.StatusUnauthorized)
|
||||
req.Res <- nil
|
||||
return
|
||||
return hlsMuxerResponse{
|
||||
Status: http.StatusUnauthorized,
|
||||
Header: map[string]string{
|
||||
"WWW-Authenticate": `Basic realm="rtsp-simple-server"`,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case req.File == "index.m3u8":
|
||||
req.W.Header().Set("Content-Type", `application/x-mpegURL`)
|
||||
req.Res <- r.muxer.PrimaryPlaylist()
|
||||
return hlsMuxerResponse{
|
||||
Status: http.StatusOK,
|
||||
Header: map[string]string{
|
||||
"Content-Type": `application/x-mpegURL`,
|
||||
},
|
||||
Body: r.muxer.PrimaryPlaylist(),
|
||||
}
|
||||
|
||||
case req.File == "stream.m3u8":
|
||||
req.W.Header().Set("Content-Type", `application/x-mpegURL`)
|
||||
req.Res <- r.muxer.StreamPlaylist()
|
||||
return hlsMuxerResponse{
|
||||
Status: http.StatusOK,
|
||||
Header: map[string]string{
|
||||
"Content-Type": `application/x-mpegURL`,
|
||||
},
|
||||
Body: r.muxer.StreamPlaylist(),
|
||||
}
|
||||
|
||||
case strings.HasSuffix(req.File, ".ts"):
|
||||
r := r.muxer.Segment(req.File)
|
||||
if r == nil {
|
||||
req.W.WriteHeader(http.StatusNotFound)
|
||||
req.Res <- nil
|
||||
return
|
||||
return hlsMuxerResponse{Status: http.StatusNotFound}
|
||||
}
|
||||
|
||||
req.W.Header().Set("Content-Type", `video/MP2T`)
|
||||
req.Res <- r
|
||||
return hlsMuxerResponse{
|
||||
Status: http.StatusOK,
|
||||
Header: map[string]string{
|
||||
"Content-Type": `video/MP2T`,
|
||||
},
|
||||
Body: r,
|
||||
}
|
||||
|
||||
case req.File == "":
|
||||
req.Res <- bytes.NewReader([]byte(index))
|
||||
return hlsMuxerResponse{
|
||||
Status: http.StatusOK,
|
||||
Header: map[string]string{
|
||||
"Content-Type": `text/html`,
|
||||
},
|
||||
Body: bytes.NewReader([]byte(index)),
|
||||
}
|
||||
|
||||
default:
|
||||
req.W.WriteHeader(http.StatusNotFound)
|
||||
req.Res <- nil
|
||||
return hlsMuxerResponse{Status: http.StatusNotFound}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,8 +479,7 @@ func (r *hlsMuxer) OnRequest(req hlsMuxerRequest) {
|
||||
select {
|
||||
case r.request <- req:
|
||||
case <-r.ctx.Done():
|
||||
req.W.WriteHeader(http.StatusNotFound)
|
||||
req.Res <- nil
|
||||
req.Res <- hlsMuxerResponse{Status: http.StatusNotFound}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-20
@@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
@@ -97,7 +99,11 @@ func (s *hlsServer) close() {
|
||||
func (s *hlsServer) run() {
|
||||
defer s.wg.Done()
|
||||
|
||||
hs := &http.Server{Handler: s}
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.New()
|
||||
router.NoRoute(s.onRequest)
|
||||
|
||||
hs := &http.Server{Handler: router}
|
||||
go hs.Serve(s.ln)
|
||||
|
||||
outer:
|
||||
@@ -130,33 +136,32 @@ outer:
|
||||
s.pathManager.OnHLSServerSet(nil)
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (s *hlsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.Log(logger.Info, "[conn %v] %s %s", r.RemoteAddr, r.Method, r.URL.Path)
|
||||
func (s *hlsServer) onRequest(ctx *gin.Context) {
|
||||
s.Log(logger.Info, "[conn %v] %s %s", ctx.Request.RemoteAddr, ctx.Request.Method, ctx.Request.URL.Path)
|
||||
|
||||
// remove leading prefix
|
||||
pa := r.URL.Path[1:]
|
||||
pa := ctx.Request.URL.Path[1:]
|
||||
|
||||
w.Header().Add("Access-Control-Allow-Origin", s.hlsAllowOrigin)
|
||||
w.Header().Add("Access-Control-Allow-Credentials", "true")
|
||||
ctx.Writer.Header().Add("Access-Control-Allow-Origin", s.hlsAllowOrigin)
|
||||
ctx.Writer.Header().Add("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
switch r.Method {
|
||||
switch ctx.Request.Method {
|
||||
case http.MethodGet:
|
||||
|
||||
case http.MethodOptions:
|
||||
w.Header().Add("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||
w.Header().Add("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Header().Add("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||
ctx.Writer.Header().Add("Access-Control-Allow-Headers", ctx.Request.Header.Get("Access-Control-Request-Headers"))
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
return
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ctx.Writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
switch pa {
|
||||
case "", "favicon.ico":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ctx.Writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -168,27 +173,32 @@ func (s *hlsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}()
|
||||
|
||||
if fname == "" && !strings.HasSuffix(dir, "/") {
|
||||
w.Header().Add("Location", "/"+dir+"/")
|
||||
w.WriteHeader(http.StatusMovedPermanently)
|
||||
ctx.Writer.Header().Add("Location", "/"+dir+"/")
|
||||
ctx.Writer.WriteHeader(http.StatusMovedPermanently)
|
||||
return
|
||||
}
|
||||
|
||||
dir = strings.TrimSuffix(dir, "/")
|
||||
|
||||
cres := make(chan io.Reader)
|
||||
cres := make(chan hlsMuxerResponse)
|
||||
hreq := hlsMuxerRequest{
|
||||
Dir: dir,
|
||||
File: fname,
|
||||
Req: r,
|
||||
W: w,
|
||||
Req: ctx.Request,
|
||||
Res: cres,
|
||||
}
|
||||
|
||||
select {
|
||||
case s.request <- hreq:
|
||||
res := <-cres
|
||||
if res != nil {
|
||||
io.Copy(w, res)
|
||||
|
||||
for k, v := range res.Header {
|
||||
ctx.Writer.Header().Set(k, v)
|
||||
}
|
||||
ctx.Writer.WriteHeader(res.Status)
|
||||
|
||||
if res.Body != nil {
|
||||
io.Copy(ctx.Writer, res.Body)
|
||||
}
|
||||
|
||||
case <-s.ctx.Done():
|
||||
|
||||
Reference in New Issue
Block a user