Browse Source

hls: use gin as HTTP router

pull/666/head
aler9 5 years ago
parent
commit
ef3fab477e
  1. 4
      internal/core/api.go
  2. 77
      internal/core/hls_muxer.go
  3. 50
      internal/core/hls_server.go

4
internal/core/api.go

@ -288,9 +288,7 @@ func newAPI( @@ -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)

77
internal/core/hls_muxer.go

@ -93,12 +93,17 @@ window.addEventListener('DOMContentLoaded', create); @@ -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: @@ -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: @@ -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: @@ -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{}) @@ -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) { @@ -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) { @@ -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}
}
}

50
internal/core/hls_server.go

@ -9,6 +9,8 @@ import ( @@ -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() { @@ -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: @@ -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) { @@ -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():

Loading…
Cancel
Save