How do I get information about the type of connection of a WebRTC PeerConnection?

According to the specification, which is currently implemented in Firefox, but not in Chrome, you can indeed suss out the active candidate from the statistics available for candidate pairs, which are:

dictionary RTCIceCandidatePairStats : RTCStats {
    DOMString                     transportId;
    DOMString                     localCandidateId;
    DOMString                     remoteCandidateId;
    RTCStatsIceCandidatePairState state;
    unsigned long long            priority;
    boolean                       nominated;
    boolean                       writable;
    boolean                       readable;
    unsigned long long            bytesSent;
    unsigned long long            bytesReceived;
    double                        roundTripTime;
    double                        availableOutgoingBitrate;
    double                        availableIncomingBitrate;
};

Combined with the stats on the individual candidates:

dictionary RTCIceCandidateAttributes : RTCStats {
    DOMString                ipAddress;
    long                     portNumber;
    DOMString                transport;
    RTCStatsIceCandidateType candidateType;
    long                     priority;
    DOMString                addressSourceUrl;
};

Use peerConnection.getStats() to look for an ice candidate pair that is both nominated and has succeeded:

pc.getStats(null))
.then(function(stats) {
  return Object.keys(stats).forEach(function(key) {
    if (stats[key].type == "candidatepair" &&
        stats[key].nominated && stats[key].state == "succeeded") {
      var remote = stats[stats[key].remoteCandidateId];
      console.log("Connected to: " + remote.ipAddress +":"+
                  remote.portNumber +" "+ remote.transport +
                  " "+ remote.candidateType);
    }
  });
})
.catch(function(e) { console.log(e.name); });

This might output something like:

Connected to: 192.168.1.2:49190 udp host

which you could test against the LAN range. If instead it returned something like:

Connected to: 24.57.143.7:61102 udp relayed

then you'd have a TURN connection.

Here's a jsfiddle that shows this (requires Firefox Developer Edition for other reasons).


It took me a long time to get this right, so hopefully this helps someone.

The new way

You can now get the selected candidate pair from the RTCPeerConnection without the stats api:

const pair = rtcConnection.sctp.transport.iceTransport.getSelectedCandidatePair();
console.log(pair.remote.type);

At the time of writing (October 2, 2020) this only works in Chromium however. You can still use the stats api for other browsers. Also note the comment below by jib that this only works if you have a DataChannel.

For browsers without getSelectedCandidatePair() support

According to https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats (at the bottom of the page at the selected property.

The spec-compliant way to determine the selected candidate pair is to look for a stats object of type transport, which is an RTCTransportStats object. That object's selectedCandidatePairId property indicates whether or not the specified transport is the one being used.

So trying to find the selected pair using stat.nominated && stats.state == "succeeded" is not the right way to do it.

Instead, you can get it by looking at the selected pair in the transport stat. Firefox doesn't support this, but fortunately there is a non-standard selected property in candidate pairs for firefox.

const stats = await rtcConnection.getStats();
if(stats){
    let selectedPairId = null;
    for(const [key, stat] of stats){
        if(stat.type == "transport"){
            selectedPairId = stat.selectedCandidatePairId;
            break;
        }
    }
    let candidatePair = stats.get(selectedPairId);
    if(!candidatePair){
        for(const [key, stat] of stats){
            if(stat.type == "candidate-pair" && stat.selected){
                candidatePair = stat;
                break;
            }
        }
    }

    if(candidatePair){
        for(const [key, stat] of stats){
            if(key == candidatePair.remoteCandidateId){
                return stat.candidateType;
            }
        }
    }
}