Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions src/ObservedPeerConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,15 @@ export class ObservedPeerConnection extends EventEmitter {
}

public get selectedIceCandiadtePairForTurn() {
// The ICE server `url` is only exposed on *local* candidates (it identifies the server
// the candidate was obtained from); remote candidates never carry it. `turn` also
// matches `turns:` (TURN over TLS).
return this.selectedIceCandidatePairs
.filter((pair) =>
pair.getLocalCandidate()?.candidateType === 'relay' &&
pair.getRemoteCandidate()?.url?.startsWith('turn:')
);
.filter((pair) => {
const localCandidate = pair.getLocalCandidate();

return localCandidate?.candidateType === 'relay' && localCandidate?.url?.startsWith('turn') === true;
});
}

public close() {
Expand Down Expand Up @@ -539,12 +543,20 @@ export class ObservedPeerConnection extends EventEmitter {
this.usingTURN = false;

for (const selectedCandidatePair of selectedIceCandidatePairs) {
if (selectedCandidatePair.getLocalCandidate()?.protocol === 'tcp') {
const localCandidate = selectedCandidatePair.getLocalCandidate();

if (localCandidate?.protocol === 'tcp') {
this.usingTCP = true;
}
if (selectedCandidatePair.getLocalCandidate()?.candidateType === 'relay' && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith('turn:')) {
selectedCandidatePairForTurn.push(selectedCandidatePair);
// relay candidates are only obtained from TURN servers, so the local candidate's
// type alone establishes TURN usage. The server `url` is only exposed on *local*
// candidates (and not by every browser) — it is required only to attribute the
// traffic to a concrete TURN server.
if (localCandidate?.candidateType === 'relay') {
this.usingTURN = true;
if (localCandidate.url?.startsWith('turn')) {
selectedCandidatePairForTurn.push(selectedCandidatePair);
}
}
this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived;
this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent;
Expand Down
19 changes: 13 additions & 6 deletions src/ObservedTURN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,34 @@ export class ObservedTURN extends EventEmitter {
}

public addPeerConnection(peerConnection: ObservedPeerConnection) {
const turnPairs = peerConnection.selectedIceCandidatePairs.filter((pair) => pair.getLocalCandidate()?.candidateType === 'relay' && pair.getRemoteCandidate()?.url?.startsWith('turn:'));
// selected pairs whose *local* candidate is a relay candidate with a `turn(s):` url
// (the ICE server url is only exposed on local candidates).
const turnPairs = peerConnection.selectedIceCandiadtePairForTurn;

if (turnPairs.length !== 1) {
return (logger.warn(`Expected exactly one TURN pair, but found for peerconnection ${peerConnection.peerConnectionId}`, turnPairs.length), undefined);
}

const candidatePair = turnPairs[0];
const rawUrl = candidatePair.getRemoteCandidate()?.url;
const rawUrl = candidatePair.getLocalCandidate()?.url;

if (!rawUrl) {
return (logger.warn(`No remote candidate URL found for peerconnection ${peerConnection.peerConnectionId}`), undefined);
return (logger.warn(`No local candidate URL found for peerconnection ${peerConnection.peerConnectionId}`), undefined);
}

const turnUrl = new URL(rawUrl);
const turnServerUrl = `${turnUrl.protocol}//${turnUrl.hostname}:${turnUrl.port}`;
// A TURN url looks like `turn(s):host[:port][?transport=udp|tcp]` (RFC 7065). The WHATWG
// `URL` class parses these non-special schemes as opaque paths (empty hostname/port), so
// derive the server key by stripping the query instead of using `new URL()`.
const turnServerUrl = rawUrl.split('?')[0];
let turnServer = this.servers.get(turnServerUrl);

if (!turnServer) {
turnServer = new ObservedTurnServer(turnServerUrl, this);
this.servers.set(turnServerUrl, turnServer);
}

turnServer.observedPeerConnections.set(peerConnection.peerConnectionId, peerConnection);

return turnServer;
}

Expand Down
Loading