peerjs icon indicating copy to clipboard operation
peerjs copied to clipboard

On-demand TURNs with dynamically generated credentials

Open maxpavlov opened this issue 2 years ago • 1 comments

In some cases, only relay-based connectivity is possible due to a restricted network setup. Right now, to ensure connectivity I supply an open TURN server with access credentials to peerjs config.

However, I would like to dynamically generate TURN credentials and try relay only when I am sure that the direct connectivity with WebRTC is not possible.

Can someone write a process flow (imaginary :) ) where a custom code orchestrates the attempt to connect with STUN, and only if there are no candidates to establish a connection, a logic retries with TURN.

I know the flow myself, however, maybe there are some limitations to peerjs config construction that casts such flow impossible? Any hints in this direction would be appreciated.

maxpavlov avatar Jan 03 '22 09:01 maxpavlov

Why not create an API endpoint on your server that returns a valid PeerJS config object, with or without the TURN credentials, as requested?

Something like:


// Assuming an ExpressJS app, for example

app.use('/peerjs_config', (res, req, next) => {

    let peerjs_config = {
        // ...other peerjs config options - path, host, secure, etc
        config: {
            ice_servers: [{
                urls: ["<YOUR STUN SERVER HERE>"]
            }]
        }
    }

    if (req.query.use_turn) {
        [turn_host, turn_user, turn_password] = GetTurnServerCredentials() // Generate and return your credentials here
        peerjs_config.config.ice_servers.push({
            urls: turn_host,
            username: turn_user,
            credential: turn_password,
            credentialType: "password"
        })
    }

    res.status(200).json(peerjs_config)
})

Then, when you connect to your PeerJS server, try connecting and establishing a call without TURN, and if it fails, retry with TURN:

function create_peer(with_turn) {
    const auth_credentials_stun = await fetch(`//peerjs_config?use_turn=${with_turn ? 1 : 0}`).json()
    return new Peer(auth_credentials_stun);
}

function start_peer(with_turn) {
    let peer = create_peer(with_turn)

    peer.on("error", (err) => {
        if ([
            "network",
            "peer-unavailable",
            "server-error",
            "socket-error",
            "socket-closed"
        ].includes(err.type)) {
            // Some kind of networking issue, try again with TURN
            peer.destroy()
            start_peer(true)
        }
    })

    peer.connect()
    let call = peer.call("some_peer_id")
    call.on("error", () => {
        // Error handling here too
        peer.destroy()
        start_peer(true)
    })
}

start_peer(false)

calmcl1 avatar Mar 03 '22 09:03 calmcl1