Record why the camera player cannot reach a gateway directly
Measured against go2rtc 1.9.14: it sends no Access-Control-Allow-Origin
on any response, auth or not; the SDP POST needs a preflight anyway
because application/sdp is not a CORS-safelisted Content-Type; and
go2rtc checks basic auth before CORS, so the preflight - which browsers
always send without credentials - gets a 401.
So this component has never worked from a browser on another origin, and
no credential handed to the frontend could fix it. The gateway's API now
requires a password, which closes the hole where anything on the
workshop LAN could watch, and changes the symptom here from a CORS error
to a 401. Both are the same missing piece: the relay.
Also stops the two most misleading errors in this path. A blocked
cross-origin request rejects as an opaque TypeError ("Failed to fetch")
that reads as the gateway being down, and a 401 reads as a wrong URL or
a missing camera. Both now say what they are.
This commit is contained in:
@@ -16,9 +16,28 @@ const error = ref<string | null>(null);
|
||||
*
|
||||
* Note what is deliberately absent: no `iceServers`. Both ends are on the
|
||||
* same LAN - the gateway is a box in the workshop - so host candidates are
|
||||
* enough and a STUN round-trip would only add latency. The same fact is why
|
||||
* this component cannot work from outside that network at all; there is no
|
||||
* relay, and the backend's /printers/:id/stream says so too.
|
||||
* enough and a STUN round-trip would only add latency.
|
||||
*
|
||||
* ## This cannot work from a browser on another origin, and that is measured
|
||||
*
|
||||
* Not a caveat about reachability - a flat blocker, against go2rtc 1.9.14:
|
||||
*
|
||||
* - go2rtc sends **no** `Access-Control-Allow-Origin` on any response, with
|
||||
* or without auth configured. A cross-origin response is unreadable.
|
||||
* - The `POST` below needs a preflight regardless, because `application/sdp`
|
||||
* is not a CORS-safelisted `Content-Type`.
|
||||
* - go2rtc checks basic auth *before* CORS, so the preflight - which a
|
||||
* browser always sends without credentials - gets a 401. Sending the
|
||||
* gateway's password from here could not fix it even if the password were
|
||||
* worth handing to every viewer.
|
||||
*
|
||||
* So this component works when it is same-origin with go2rtc, or through a
|
||||
* relay that proxies server-to-server. The gateway's API now requires a
|
||||
* password (continuum-proxy's camera.rs), which closes the hole where
|
||||
* anything on the workshop LAN could watch - and which also means a browser
|
||||
* pointed straight at a gateway now gets a 401 rather than a CORS error.
|
||||
* Both are the same missing piece. The relay design is in PLAN.md; note it
|
||||
* cannot carry WebRTC, so whatever lands there will replace this transport.
|
||||
*/
|
||||
async function connect() {
|
||||
status.value = "connecting";
|
||||
@@ -53,6 +72,15 @@ async function connect() {
|
||||
// whoever is debugging it looking at the wrong layer, when the real
|
||||
// answer is that this printer is not in go2rtc's config.
|
||||
if (!response.ok) {
|
||||
// 401 has one meaning and it is worth naming: go2rtc's API is
|
||||
// password-protected, and a browser cannot send that password across
|
||||
// origins (see above). It is not a wrong URL or a missing camera.
|
||||
if (response.status === 401) {
|
||||
throw new Error(
|
||||
"the gateway requires a password for camera access, which a browser cannot " +
|
||||
"supply across origins - this needs the relay",
|
||||
);
|
||||
}
|
||||
throw new Error(`the gateway refused the stream request (HTTP ${response.status})`);
|
||||
}
|
||||
|
||||
@@ -76,7 +104,18 @@ watch(
|
||||
() => {
|
||||
connect().catch((err: unknown) => {
|
||||
status.value = "failed";
|
||||
error.value = err instanceof Error ? err.message : String(err);
|
||||
// A blocked cross-origin request rejects as an opaque TypeError
|
||||
// ("Failed to fetch") with nothing about CORS in it, which is the
|
||||
// single most misleading error in this path - it reads as the gateway
|
||||
// being down. Say the likely cause instead of repeating the browser.
|
||||
error.value =
|
||||
err instanceof TypeError
|
||||
? "could not reach the gateway's camera API from this origin - either it is " +
|
||||
"unreachable from here, or the browser blocked the request (see this " +
|
||||
"component's notes)"
|
||||
: err instanceof Error
|
||||
? err.message
|
||||
: String(err);
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
Reference in New Issue
Block a user