aboutsummaryrefslogtreecommitdiff
path: root/diplomacy
diff options
context:
space:
mode:
authornotoraptor <stevenbocco@gmail.com>2019-08-26 15:02:44 -0400
committerPhilip Paquette <pcpaquette@gmail.com>2019-08-28 06:15:47 -0400
commit2e26ae581062ca636dd02718e7cbbf71e022b04a (patch)
tree1a122316ece505b9ec3324e04ec3e7926f9d5ba4 /diplomacy
parenta42d870221c251bebfd3cc6cc1e1c0d23b9c53b6 (diff)
Client sends a 'UnknownToken' request when it receives a notification for an unknown token.
Diffstat (limited to 'diplomacy')
-rw-r--r--diplomacy/client/connection.py20
-rw-r--r--diplomacy/communication/requests.py7
-rw-r--r--diplomacy/communication/responses.py8
-rw-r--r--diplomacy/server/connection_handler.py9
-rw-r--r--diplomacy/server/request_managers.py16
5 files changed, 54 insertions, 6 deletions
diff --git a/diplomacy/client/connection.py b/diplomacy/client/connection.py
index b56a610..a6ecef8 100644
--- a/diplomacy/client/connection.py
+++ b/diplomacy/client/connection.py
@@ -253,9 +253,10 @@ class Connection():
context is added to this dictionary to be send later once reconnected.
- requests_waiting_responses: a dictionary mapping a request ID to the context of a
request **sent**. Contains requests that are waiting for a server response.
+ - unknown_tokens: a set of unknown tokens. We can safely ignore them, as the server has been notified.
"""
__slots__ = ['hostname', 'port', 'use_ssl', 'connection', 'is_connecting', 'is_reconnecting', 'connection_count',
- 'channels', 'requests_to_send', 'requests_waiting_responses']
+ 'channels', 'requests_to_send', 'requests_waiting_responses', 'unknown_tokens']
def __init__(self, hostname, port, use_ssl=False):
self.hostname = hostname
@@ -272,6 +273,7 @@ class Connection():
self.requests_to_send = {} # type: dict{str, RequestFutureContext}
self.requests_waiting_responses = {} # type: dict{str, RequestFutureContext}
+ self.unknown_tokens = set()
# When connection is created, we are not yet connected, but reconnection does not matter
# (we consider we are reconnected).
@@ -418,7 +420,9 @@ class Connection():
elif notification_id:
notification = notifications.parse_dict(json_message)
if notification.token not in self.channels:
- LOGGER.error('Unknown notification: %s', notification.name)
+ if notification.token not in self.unknown_tokens:
+ LOGGER.error('Unknown notification: %s', notification.name)
+ self._handle_unknown_token(notification.token)
return
notification_managers.handle_notification(self, notification)
else:
@@ -437,6 +441,18 @@ class Connection():
# Check response format and run callback (if defined).
self._on_socket_message(msg)
+ def _handle_unknown_token(self, token):
+ """ Notify server about an unknown channel token.
+ This is likely because the channel has gone out of scope.
+ :param token: token to notify server with.
+ """
+ # Send notification request without waiting any server response. Ignore errors if any.
+ try:
+ self.unknown_tokens.add(token)
+ self.connection.write_message(requests.UnknownToken(token=token).json())
+ except (WebSocketClosedError, StreamClosedError):
+ pass
+
# Public methods.
@gen.coroutine
def get_daide_port(self, game_id):
diff --git a/diplomacy/communication/requests.py b/diplomacy/communication/requests.py
index a6bb622..6f99f15 100644
--- a/diplomacy/communication/requests.py
+++ b/diplomacy/communication/requests.py
@@ -304,6 +304,13 @@ class Logout(_AbstractChannelRequest):
"""
__slots__ = []
+class UnknownToken(_AbstractChannelRequest):
+ """ Request to tell server that a channel token is unknown.
+ Expected response: Nothing - Client does not even wait for a server response.
+ Expected response handler result: None
+ """
+ __slots__ = []
+
class SetGrade(_AbstractChannelRequest):
""" SetGrade request.
Expected response: responses.Ok
diff --git a/diplomacy/communication/responses.py b/diplomacy/communication/responses.py
index f9f9fce..6353150 100644
--- a/diplomacy/communication/responses.py
+++ b/diplomacy/communication/responses.py
@@ -52,6 +52,14 @@ class Ok(_AbstractResponse):
""" Ok response sent by default after handling a request. """
__slots__ = []
+class NoResponse(_AbstractResponse):
+ """ Indicates that no responses are required """
+ __slots__ = []
+
+ def __bool__(self):
+ """ This response always evaluate to false """
+ return False
+
class DataGameSchedule(_AbstractResponse):
""" Response with info about current scheduling for a game. """
__slots__ = ['game_id', 'phase', 'schedule']
diff --git a/diplomacy/server/connection_handler.py b/diplomacy/server/connection_handler.py
index 6c1351f..a70db7d 100644
--- a/diplomacy/server/connection_handler.py
+++ b/diplomacy/server/connection_handler.py
@@ -121,7 +121,8 @@ class ConnectionHandler(WebSocketHandler):
response = responses.Error(message='%s/%s' % (type(exc).__name__, exc.message),
request_id=json_request.get(strings.REQUEST_ID, None))
- try:
- yield self.write_message(response.json())
- except WebSocketClosedError:
- LOGGER.error('Websocket is closed.')
+ if response:
+ try:
+ yield self.write_message(response.json())
+ except WebSocketClosedError:
+ LOGGER.error('Websocket is closed.')
diff --git a/diplomacy/server/request_managers.py b/diplomacy/server/request_managers.py
index cedff5f..259147a 100644
--- a/diplomacy/server/request_managers.py
+++ b/diplomacy/server/request_managers.py
@@ -1126,6 +1126,21 @@ def on_synchronize(server, request, connection_handler):
timestamp_created=level.game.timestamp_created,
request_id=request.request_id)
+def on_unknown_token(server, request, connection_handler):
+ """ Manage notification request UnknownToken.
+ :param server: server which receives the request.
+ :param request: request to manage.
+ :param connection_handler: connection handler from which the request was sent.
+ :return: NoResponse. No responses are sent back to the server
+ :type server: diplomacy.Server
+ :type request: diplomacy.communication.requests.UnknownToken
+ """
+ del connection_handler # Unused - Not sending any responses back
+ LOGGER.debug('Removing token %s', request.token)
+ if server.users.has_token(request.token):
+ server.remove_token(request.token)
+ return responses.NoResponse()
+
def on_vote(server, request, connection_handler):
""" Manage request Vote.
:param server: server which receives the request.
@@ -1185,6 +1200,7 @@ MAPPING = {
requests.SetWaitFlag: on_set_wait_flag,
requests.SignIn: on_sign_in,
requests.Synchronize: on_synchronize,
+ requests.UnknownToken: on_unknown_token,
requests.Vote: on_vote,
}