live-sfu-demo/internal/server/proxy.go

94 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package server
import (
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
// srsProxyHandler 反向代理 SRS HTTP API(WHIP/WHEP 在 /rtc/v1/)。
// 信令可反代,媒体由浏览器直连 SRS :8000。推流可按需校验服务端下发的 JWT。
func (s *Server) srsProxyHandler() http.Handler {
target, err := url.Parse(s.cfg.SRSBaseURL)
if err != nil {
target, _ = url.Parse("http://localhost:1985")
}
rp := httputil.NewSingleHostReverseProxy(target)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if s.cfg.TokenRequired && strings.HasPrefix(r.URL.Path, "/rtc/v1/whip/") {
room := r.URL.Query().Get("stream")
tok := r.URL.Query().Get("token")
claims, verr := verifyStreamToken(s.cfg.TokenSecret, tok)
if verr != nil || claims.Room != room || claims.Role != "publish" {
http.Error(w, "forbidden: valid publish token required", http.StatusForbidden)
return
}
}
rp.ServeHTTP(w, r)
})
}
// srsHlsProxyHandler 反向代理 SRS http_server 的 HLS/FLV 静态资源(8080)。
// 浏览器通过 Go 网关拉 HLS,避免直连 8080 的跨域和端口暴露问题。
// 支持 /live/*.m3u8 /live/*.ts /live/*.flv
func (s *Server) srsHlsProxyHandler() http.Handler {
target, err := url.Parse(s.cfg.SRSHttpURL)
if err != nil {
target, _ = url.Parse("http://localhost:8080")
}
rp := httputil.NewSingleHostReverseProxy(target)
origDirector := rp.Director
rp.Director = func(r *http.Request) {
origDirector(r)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "*")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
rp.ServeHTTP(w, r)
})
}
// cdnProxyHandler 占位代理第三方 CDN HLS。未配置时 502,便于前端感知与联调。
func (s *Server) cdnProxyHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if s.cfg.CDNRTMPURL == "" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(`{"error":"cdn not configured: set CDN_RTMP_URL"}`))
return
}
// 已配置但无实际 HLS 边缘时,返回 502 并附带 CDN 信息
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(`{"error":"cdn hls not yet available: forward via RTMP not implemented"}`))
})
}
// cfProxyHandler 反向代理 Cloudflare Realtime REST,注入 AppSecret。
// 浏览器只交换 SDP(tracks/new),凭证不出服务端。
func (s *Server) cfProxyHandler() http.Handler {
base := strings.TrimRight(s.cfg.CFBaseURL, "/") + "/apps/" + s.cfg.CFAppID
target, err := url.Parse(base)
if err != nil {
target, _ = url.Parse("https://rtc.live.cloudflare.com/v1/apps/" + s.cfg.CFAppID)
}
rp := httputil.NewSingleHostReverseProxy(target)
rp.Director = func(r *http.Request) {
rest := strings.TrimPrefix(r.URL.Path, "/api/cf")
r.URL.Path = target.Path + rest
r.URL.Scheme = target.Scheme
r.URL.Host = target.Host
r.Host = target.Host
if s.cfg.CFAppSecret != "" {
r.Header.Set("Authorization", "Bearer "+s.cfg.CFAppSecret)
}
}
return rp
}