Support multiple STUN and TURN servers - #113
Open
Mohit-Ak wants to merge 1 commit into
Open
Conversation
Connection accepted a single STUN server and a single TURN server, spread over six scalar arguments. Applications with more than one server had no way to pass them, and aiortc drops every server after the first when it builds its kwargs, with a comment noting only one is supported. Add `stun_servers` and `turn_servers`, taking StunServer and TurnServer objects. Bundling the turn_* arguments into a dataclass also addresses the concern raised in aiortc#3 about the proliferation of turn_XXX parameters: a second TURN server no longer means six more arguments. Candidate gathering now iterates over both lists, so each STUN server is queried from every IPv4 host protocol and each TURN server gets its own allocation. These already ran concurrently through asyncio.wait, so a slow or broken server still cannot hold up the others. Server-reflexive candidates that duplicate one already obtained are dropped, since several STUN servers usually observe the same public address. The singular arguments continue to work and are folded into the plural form. stun_server and turn_server remain readable as properties returning the first entry. Passing both the singular and plural form of the same argument raises ValueError rather than silently ignoring one.
Mohit-Ak
force-pushed
the
multiple-stun-turn-servers
branch
from
August 5, 2026 03:32
9aa9e8f to
e37efe4
Compare
Mohit-Ak
marked this pull request as ready for review
August 10, 2026 14:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3.
Connectionaccepts a single STUN server and a single TURN server, spread over six scalar arguments (turn_server,turn_username,turn_password,turn_ssl,turn_transport). Applications with more than one server have no way to pass them. Downstream, aiortc drops everything after the first —connection_kwargs()inrtcicetransport.pyhas literal# only a single STUN server is supported/# only a single TURN server is supportedcomments andcontinues past the rest — so anRTCConfigurationlisting a primary and a fallback TURN server silently uses only the primary.What this does
Adds
stun_serversandturn_servers, takingStunServerandTurnServerobjects:On the API shape — you noted in #3 that the
turn_XXXparameters were proliferating and that multiple-server support "could actually clean this up a little if done right". Bundling the per-server settings into a dataclass is my attempt at that: adding a second TURN server doesn't mean six more arguments, and each server carries its own transport, TLS flag and credentials rather than sharing one global set. If you'd rather see plain tuples/dicts, or a different split between the two classes, I'm happy to rework it — the gathering side is independent of that choice.get_component_candidates()now iterates both lists: each STUN server is queried from every IPv4 host protocol, and each TURN server gets its own allocation. These already ran concurrently throughasyncio.wait(..., timeout=timeout), so one slow or broken server still can't hold up the others — that part needed no change. Server-reflexive candidates duplicating one already obtained are dropped, since several STUN servers normally observe the same public address; the discarded protocol is closed rather than leaked.Backwards compatibility
The singular arguments keep working and are folded into the plural form internally.
stun_serverandturn_serverremain readable as properties returning the first entry, so existing code that reads them is unaffected. Passing both forms of the same argument raisesValueErrorinstead of silently ignoring one.I checked this against the real downstream consumer rather than assuming: released aiortc 1.15.0, unmodified, still builds a
Connectionfrom its own singularconnection_kwargs()output and completes an offer with candidates gathered against this branch.Testing
run_turn_server()is started twice to get genuinely distinct servers: two TURN servers yield two distinct relayed candidates; mixed UDP/TCP transports both allocate; a server with bad credentials doesn't stop a healthy one; a STUN server that fails DNS doesn't stop the other; STUN and TURN combine to givehost/srflx/relay.self.turn_servers[:1]) failstest_connect_with_multiple_turn_serversand..._mixed_transportswithAssertionError: 1 != 2, so the tests genuinely pin the new behaviour.ruff check --diff,ruff format --diffandmypy examples src testsare all clean, matching the lint job.Not included
The aiortc side is a separate change in a different repo and needs a released aioice to depend on, so I've left it out. Once this lands I'm happy to follow up there with the
connection_kwargs()change that stops discarding the extra servers.