fix: limit proxy m3u8 file size

pull/249/head
zijiren233 2 years ago
parent fc04e412cb
commit 035db82f09

@ -600,7 +600,7 @@ func ProxyMovie(ctx *gin.Context) {
// TODO: cache mpd file
fallthrough
default:
err = proxy.AuthProxyURL(ctx, m.Movie.MovieBase.Url, m.Movie.MovieBase.Type, m.Movie.MovieBase.Headers, ctx.GetString("token"), room.ID, m.ID)
err = proxy.AutoProxyURL(ctx, m.Movie.MovieBase.Url, m.Movie.MovieBase.Type, m.Movie.MovieBase.Headers, ctx.GetString("token"), room.ID, m.ID)
if err != nil {
log.Errorf("proxy movie error: %v", err)
return
@ -785,7 +785,10 @@ func JoinHlsLive(ctx *gin.Context) {
}
if utils.IsM3u8Url(m.Movie.MovieBase.Url) {
_ = proxy.ProxyM3u8(ctx, m.Movie.MovieBase.Url, m.Movie.MovieBase.Headers, true, ctx.GetString("token"), room.ID, m.ID)
err = proxy.ProxyM3u8(ctx, m.Movie.MovieBase.Url, m.Movie.MovieBase.Headers, true, ctx.GetString("token"), room.ID, m.ID)
if err != nil {
log.Errorf("proxy m3u8 hls live error: %v", err)
}
return
}
channel, err := m.Channel()

@ -10,6 +10,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"github.com/synctv-org/synctv/utils/m3u8"
"github.com/zijiren233/go-uhc"
@ -52,6 +53,8 @@ func NewM3u8TargetToken(targetUrl, roomId, movieId string, isM3u8File bool) (str
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret))
}
const maxM3u8FileSize = 3 * 1024 * 1024 //
func ProxyM3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File bool, token, roomId, movieId string) error {
if !isM3u8File {
return ProxyURL(ctx, u, headers)
@ -59,6 +62,11 @@ func ProxyM3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("new request error: %v", err),
),
)
return fmt.Errorf("new request error: %w", err)
}
for k, v := range headers {
@ -69,11 +77,32 @@ func ProxyM3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File
}
resp, err := uhc.Do(req)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("do request error: %v", err),
),
)
return fmt.Errorf("do request error: %w", err)
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
// if contentType := resp.Header.Get("Content-Type"); !strings.HasPrefix(contentType, "application/vnd.apple.mpegurl") {
// return fmt.Errorf("m3u8 file is not a valid m3u8 file, content type: %s", contentType)
// }
if resp.ContentLength > maxM3u8FileSize {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("m3u8 file is too large: %d, max: %d (3MB)", resp.ContentLength, maxM3u8FileSize),
),
)
return fmt.Errorf("m3u8 file is too large: %d, max: %d (3MB)", resp.ContentLength, maxM3u8FileSize)
}
b, err := io.ReadAll(io.LimitReader(resp.Body, maxM3u8FileSize))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("read response body error: %v", err),
),
)
return fmt.Errorf("read response body error: %w", err)
}
hasM3u8File := false
@ -85,6 +114,11 @@ func ProxyM3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File
return true, nil
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("range m3u8 segments with base url error: %v", err),
),
)
return fmt.Errorf("range m3u8 segments with base url error: %w", err)
}
m3u8Str, err := m3u8.ReplaceM3u8SegmentsWithBaseUrl(stream.BytesToString(b), u, func(segmentUrl string) (string, error) {
@ -95,6 +129,11 @@ func ProxyM3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File
return fmt.Sprintf("/api/room/movie/proxy/%s/m3u8/%s?token=%s&roomId=%s", movieId, targetToken, token, roomId), nil
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("replace m3u8 segments with base url error: %v", err),
),
)
return fmt.Errorf("replace m3u8 segments with base url error: %w", err)
}
ctx.Data(http.StatusOK, hls.M3U8ContentType, stream.StringToBytes(m3u8Str))

@ -10,6 +10,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/synctv-org/synctv/internal/settings"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/go-uhc"
)
@ -17,8 +18,18 @@ import (
func ProxyURL(ctx *gin.Context, u string, headers map[string]string) error {
if !settings.AllowProxyToLocal.Get() {
if l, err := utils.ParseURLIsLocalIP(u); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("check url is local ip error: %v", err),
),
)
return fmt.Errorf("check url is local ip error: %w", err)
} else if l {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
"not allow proxy to local",
),
)
return errors.New("not allow proxy to local")
}
}
@ -26,6 +37,11 @@ func ProxyURL(ctx *gin.Context, u string, headers map[string]string) error {
defer cf()
req, err := http.NewRequestWithContext(ctx2, http.MethodGet, u, nil)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("new request error: %v", err),
),
)
return fmt.Errorf("new request error: %w", err)
}
for k, v := range headers {
@ -61,6 +77,11 @@ func ProxyURL(ctx *gin.Context, u string, headers map[string]string) error {
}
resp, err := cli.Do(req)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("request url error: %v", err),
),
)
return fmt.Errorf("request url error: %w", err)
}
defer resp.Body.Close()
@ -72,12 +93,17 @@ func ProxyURL(ctx *gin.Context, u string, headers map[string]string) error {
ctx.Header("Content-Type", resp.Header.Get("Content-Type"))
_, err = copyBuffer(ctx.Writer, resp.Body)
if err != nil && err != io.EOF {
ctx.AbortWithStatusJSON(http.StatusBadRequest,
model.NewApiErrorStringResp(
fmt.Sprintf("copy response body error: %v", err),
),
)
return fmt.Errorf("copy response body error: %w", err)
}
return nil
}
func AuthProxyURL(ctx *gin.Context, u, t string, headers map[string]string, token, roomId, movieId string) error {
func AutoProxyURL(ctx *gin.Context, u, t string, headers map[string]string, token, roomId, movieId string) error {
if strings.HasPrefix(t, "m3u") || utils.IsM3u8Url(u) {
return ProxyM3u8(ctx, u, headers, true, token, roomId, movieId)
}

@ -134,7 +134,7 @@ func (s *alistVendorService) ProxyMovie(ctx *gin.Context) {
ctx.Data(http.StatusOK, "audio/mpegurl", data.Ali.M3U8ListFile)
return
case "raw":
err := proxy.AuthProxyURL(ctx, data.URL, s.movie.MovieBase.Type, nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
err := proxy.AutoProxyURL(ctx, data.URL, s.movie.MovieBase.Type, nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
if err != nil {
log.Errorf("proxy vendor movie error: %v", err)
}
@ -173,7 +173,7 @@ func (s *alistVendorService) ProxyMovie(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("proxy is not enabled"))
return
}
err = proxy.AuthProxyURL(ctx, data.URL, s.movie.MovieBase.Type, nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
err = proxy.AutoProxyURL(ctx, data.URL, s.movie.MovieBase.Type, nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
if err != nil {
log.Errorf("proxy vendor movie error: %v", err)
}

@ -151,7 +151,7 @@ func (s *embyVendorService) ProxyMovie(ctx *gin.Context) {
ctx.Redirect(http.StatusFound, embyC.Sources[source].URL)
return
}
err = proxy.AuthProxyURL(ctx, embyC.Sources[source].URL, "", nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
err = proxy.AutoProxyURL(ctx, embyC.Sources[source].URL, "", nil, ctx.GetString("token"), s.movie.RoomID, s.movie.ID)
if err != nil {
log.Errorf("proxy vendor movie error: %v", err)
}

Loading…
Cancel
Save