Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback to STUN Server if TURN connection failes #16

Open
TanjaBayer opened this issue Dec 4, 2019 · 2 comments
Open

Fallback to STUN Server if TURN connection failes #16

TanjaBayer opened this issue Dec 4, 2019 · 2 comments

Comments

@TanjaBayer
Copy link

Hi,

we have a setup where sometimes a STUN server needs to be used (in a company network) and sometimes a TURN server needs to be used (e.g. mobile Hotspot), so we define a STUN server and a TURN server at the same time.

The problem with this setup is, that if we are in the network which blocks the TURN connection, the whole ICE connection failes, because the TURN creat_turn_endpoint raises an error:

Error handling request
Traceback (most recent call last):
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/home/tanja/git/cidaas/id-card-utility-backend/server.py", line 104, in offer
    await pc.setLocalDescription(answer)
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aiortc/rtcpeerconnection.py", line 666, in setLocalDescription
    await self.__gather()
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aiortc/rtcpeerconnection.py", line 865, in __gather
    await asyncio.gather(*coros)
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aiortc/rtcicetransport.py", line 174, in gather
    await self._connection.gather_candidates()
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/ice.py", line 362, in gather_candidates
    addresses=addresses)
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/ice.py", line 749, in get_component_candidates
    transport=self.turn_transport)
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/turn.py", line 301, in create_turn_endpoint
    await transport._connect()
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/turn.py", line 272, in _connect
    self.__relayed_address = await self.__inner_protocol.connect()
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/turn.py", line 80, in connect
    response, _ = await self.request(request)
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/turn.py", line 173, in request
    return await transaction.run()
  File "/home/tanja/venvs/cidaas/id-services-aggregator/lib/python3.7/site-packages/aioice/stun.py", line 250, in run
    return await self.__future
aioice.exceptions.TransactionTimeout: STUN transaction timed out

A quick fixe for overcoming this issue was to catch the error and to continue with the other servers:
In turn.py create_turn_endpoint I added:

    try:
        await transport._connect()
    except exceptions.TransactionTimeout as e:
        logging.error(e)
        return None, None
    return transport, protocol

And if the protocol is None, the canditate is just not added:
In ice.py get_component_candidates

 _, protocol = await turn.create_turn_endpoint(
                lambda: StunProtocol(self),
                server_addr=self.turn_server,
                username=self.turn_username,
                password=self.turn_password,
                ssl=self.turn_ssl,
                transport=self.turn_transport,
            )
            if not protocol is None:
                protocol = cast(StunProtocol, protocol)

                self._protocols.append(protocol)

                # add relayed candidate
                candidate_address = protocol.transport.get_extra_info("sockname")
                related_address = protocol.transport.get_extra_info("related_address")
                protocol.local_candidate = Candidate(
                    foundation=candidate_foundation("relay", "udp", candidate_address[0]),
                    component=component,
                    transport="udp",
                    priority=candidate_priority(component, "relay"),
                    host=candidate_address[0],
                    port=candidate_address[1],
                    type="relay",
                    related_address=related_address[0],
                    related_port=related_address[1],
                )
                candidates.append(protocol.local_candidate)

But in my opinion that is a dirty fix, as we first need to run into an timeout before beeing able to continue. Do you have any ideas on how to implement this in a proper way? I would also be happy to help with that.

@lgrahl
Copy link

lgrahl commented Dec 4, 2019

Linking aiortc/aiortc#232

As briefly explained in that issue, I'd propose to fan out gathering. Pseudocode:

tasks = []
for ice_server in ice_servers:
    tasks.append(gather_srflx(ice_server, timeout=...))
    if ice_server.is_turn:
        tasks.append(create_turn_endpoint(ice_server, timeout=...))
results = asyncio.gather(tasks, return_exceptions=True)
<log exceptions, append resulting candidates, only fail if there are no candidates>

@TanjaBayer
Copy link
Author

Thanks for the suggestion. I'll look into how to correctly do that in the code and provide a PR if I manage it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants