diff options
-rw-r--r-- | diplomacy/client/connection.py | 20 | ||||
-rw-r--r-- | diplomacy/communication/requests.py | 7 | ||||
-rw-r--r-- | diplomacy/communication/responses.py | 8 | ||||
-rw-r--r-- | diplomacy/server/connection_handler.py | 9 | ||||
-rw-r--r-- | diplomacy/server/request_managers.py | 16 |
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, } |