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) }) } // 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 }