diff --git a/app/webrtc/handler.go b/app/webrtc/handler.go index 2ca9305..f0b346e 100644 --- a/app/webrtc/handler.go +++ b/app/webrtc/handler.go @@ -204,6 +204,12 @@ func (h *Handler) Subscribe(c echo.Context) error { h.recordRequest("subscribe", id, http.StatusCreated, t0) + // RFC 9429 §4.3: emit one Link header per configured ICE server so + // that the browser can discover STUN/TURN without a separate + // signalling round-trip. + for _, uri := range h.sub.ICEServerURIs() { + c.Response().Header().Add("Link", "<"+uri+">; rel=\"ice-server\"") + } c.Response().Header().Set("Content-Type", "application/sdp") c.Response().Header().Set("Location", "/whep/"+id+"/"+rid) c.Response().Header().Set("ETag", `"`+rid+`"`) @@ -405,14 +411,15 @@ func (h *Handler) tearDownStreamPeers(streamID string) { } // addCORS emits the response headers a browser-side WHEP player -// expects. WHEP's Location and ETag headers must be exposed for -// fetch() to read them across origins. +// expects. WHEP's Location, ETag, and Link headers must be exposed +// for fetch() to read them across origins. Link carries the ICE +// server URIs per RFC 9429 §4.3. func addCORS(c echo.Context) { hh := c.Response().Header() hh.Set("Access-Control-Allow-Origin", "*") hh.Set("Access-Control-Allow-Methods", "POST, DELETE, PATCH, OPTIONS") hh.Set("Access-Control-Allow-Headers", "Content-Type, Authorization, If-Match, If-None-Match") - hh.Set("Access-Control-Expose-Headers", "Location, ETag") + hh.Set("Access-Control-Expose-Headers", "Location, ETag, Link") } // requireH264AndOpus does a coarse SDP scan to confirm the offer