fix(server): cast token URL honors X-Forwarded-Proto / Host
test-go / test (push) Successful in 36s
test-go / integration (push) Successful in 9m48s

On-device test against Sonos showed SetAVTransportURI returning UPnP
error 714 (IllegalMimeType). Logcat:

  POST /api/cast/stream-token -> 200 (token minted)
  SetAVTransportURI to http://minstrel.fabledsword.com/...
  <-- 500 from Sonos: SoapFaultException SOAP fault 714

The server is behind a TLS-terminating reverse proxy, so r.TLS is
nil and the URL builder emitted http://. Sonos does a HEAD probe to
detect the audio MIME type; against an http:// URL that 301s to
https://, the probe finds no audio body and bails with 714.

The Task 2 code-quality reviewer flagged this exact scenario at the
time. Closing it now: honor X-Forwarded-Proto + X-Forwarded-Host
before falling back to r.TLS + r.Host. Public URL the speaker
fetches now matches the scheme/host the client used to reach the
endpoint.
This commit is contained in:
2026-06-03 15:17:34 -04:00
parent a9edc12523
commit 7d15f57e86
+16 -2
View File
@@ -56,11 +56,25 @@ func (h *handlers) handleCastStreamToken(w http.ResponseWriter, r *http.Request)
exp := time.Now().Add(time.Duration(expSec) * time.Second).Unix()
token := SignStreamToken(h.streamSecret, req.TrackID, exp)
// Behind a TLS-terminating reverse proxy, r.TLS is nil even though
// the public-facing URL is https://. UPnP devices (Sonos especially)
// reject SetAVTransportURI with error 714 (IllegalMimeType) when
// they hit an http:// URL that immediately 301s to https:// — the
// MIME probe fails to find an audio body. Honor X-Forwarded-Proto +
// X-Forwarded-Host first so the URL we hand to the speaker reaches
// it on the same scheme/host the client used. Falls back to
// r.TLS-based detection for direct (no-proxy) deployments.
scheme := "http"
if r.TLS != nil {
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
scheme = proto
} else if r.TLS != nil {
scheme = "https"
}
url := scheme + "://" + r.Host + "/api/tracks/" + req.TrackID +
host := r.Host
if h := r.Header.Get("X-Forwarded-Host"); h != "" {
host = h
}
url := scheme + "://" + host + "/api/tracks/" + req.TrackID +
"/stream?token=" + token + "&exp=" + strconv.FormatInt(exp, 10)
writeJSON(w, http.StatusOK, castTokenResponse{Token: token, Exp: exp, URL: url})