aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkhazhyk <[email protected]>2017-03-05 20:46:55 -0800
committerRapptz <[email protected]>2017-04-22 01:50:36 -0400
commit6dca5035df3cd6225f4b7761491614b31bcccc29 (patch)
tree929bcc2acb35b1d077d39a1e02fe914fbb247f5e
parentUse an asyncio.Event instead of an asyncio.Lock for global rate limits. (diff)
downloaddiscord.py-6dca5035df3cd6225f4b7761491614b31bcccc29.tar.xz
discord.py-6dca5035df3cd6225f4b7761491614b31bcccc29.zip
Add timeouts for websocket initial connections.
In DiscordWebSocket and DiscordVoiceWebsocket the from_client factory methods can hang indefintely on all websocket ops. We set static timeouts and attempt to reconnect if we time out. Additionally, do not return from DiscordVoiceWebSocket.from_client until we set up the keep alive thread, which is created after we receive the READY payload.
-rw-r--r--discord/gateway.py39
1 files changed, 34 insertions, 5 deletions
diff --git a/discord/gateway.py b/discord/gateway.py
index 5940c6ba..d388d12a 100644
--- a/discord/gateway.py
+++ b/discord/gateway.py
@@ -71,7 +71,7 @@ class KeepAliveHandler(threading.Thread):
while not self._stop_ev.wait(self.interval):
if self._last_ack + 2 * self.interval < time.time():
log.warn("We have stopped responding to the gateway.")
- coro = self.ws.close(1006)
+ coro = self.ws.close(1001)
f = compat.run_coroutine_threadsafe(coro, loop=self.ws.loop)
try:
@@ -191,7 +191,13 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
This is for internal use only.
"""
gateway = yield from client.http.get_gateway()
- ws = yield from websockets.connect(gateway, loop=client.loop, klass=cls)
+ try:
+ ws = yield from asyncio.wait_for(
+ websockets.connect(gateway, loop=client.loop, klass=cls),
+ timeout=60, loop=client.loop)
+ except asyncio.TimeoutError:
+ log.warn('timed out waiting for client connect')
+ return (yield from cls.from_client(client, resume=resume))
# dynamically add attributes needed
ws.token = client.http.token
@@ -206,7 +212,12 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
log.info('Created websocket connected to {}'.format(gateway))
# poll event for OP Hello
- yield from ws.poll_event()
+ try:
+ yield from asyncio.wait_for(ws.poll_event(), timeout=60, loop=client.loop)
+ except asyncio.TimeoutError:
+ log.warn("timed out waiting for client HELLO")
+ yield from ws.close(1001)
+ return (yield from cls.from_client(client, resume=resume))
if not resume:
yield from ws.identify()
@@ -219,7 +230,7 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
yield from ws.ensure_open()
except websockets.exceptions.ConnectionClosed:
# ws got closed so let's just do a regular IDENTIFY connect.
- log.info('RESUME failure.')
+ log.warn('RESUME failure.')
return (yield from cls.from_client(client))
else:
return ws
@@ -523,6 +534,7 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
HEARTBEAT = 3
SESSION_DESCRIPTION = 4
SPEAKING = 5
+ HELLO = 8
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -538,7 +550,14 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
def from_client(cls, client):
"""Creates a voice websocket for the :class:`VoiceClient`."""
gateway = 'wss://' + client.endpoint
- ws = yield from websockets.connect(gateway, loop=client.loop, klass=cls)
+ try:
+ ws = yield from asyncio.wait_for(
+ websockets.connect(gateway, loop=client.loop, klass=cls),
+ timeout=60, loop=client.loop)
+ except asyncio.TimeoutError:
+ log.warn("timed out waiting for voice client connect")
+ return (yield from cls.from_client(client))
+
ws.gateway = gateway
ws._connection = client
@@ -553,6 +572,16 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
}
yield from ws.send_as_json(identify)
+
+ try:
+ # Wait until we have processed READY and keep alive is running
+ while not ws._keep_alive:
+ yield from asyncio.wait_for(ws.poll_event(), timeout=60, loop=client.loop)
+ except asyncio.TimeoutError:
+ log.warn("timed out waiting for voice client READY")
+ yield from ws.close(1001)
+ return (yield from cls.from_client(client))
+
return ws
@asyncio.coroutine