From 93db454cb5adf9b9b85759049168afb12190b9d2 Mon Sep 17 00:00:00 2001 From: joo Date: Fri, 4 Dec 2020 15:55:39 +0100 Subject: [PATCH] Add test case reproducing #146 (fixed to behave consistently) --- minecraft/networking/connection.py | 8 +++++--- tests/fake_server.py | 20 +++++++++++++++----- tests/test_connection.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/minecraft/networking/connection.py b/minecraft/networking/connection.py index c2c40ee..759416d 100644 --- a/minecraft/networking/connection.py +++ b/minecraft/networking/connection.py @@ -479,6 +479,8 @@ class Connection(object): self.write_packet(handshake) def _handle_exception(self, exc, exc_info): + final_handler = self.handle_exception + # Call the current PacketReactor's exception handler. try: if self.reactor.handle_exception(exc, exc_info): @@ -499,9 +501,9 @@ class Connection(object): caught = False # Call the user-specified final exception handler. - if self.handle_exception not in (None, False): + if final_handler not in (None, False): try: - self.handle_exception(exc, exc_info) + final_handler(exc, exc_info) except Exception as new_exc: exc, exc_info = new_exc, sys.exc_info() @@ -516,7 +518,7 @@ class Connection(object): self.disconnect(immediate=True) # If allowed by the final exception handler, re-raise the exception. - if self.handle_exception is None and not caught: + if final_handler is None and not caught: exc_value, exc_tb = exc_info[1:] raise exc_value.with_traceback(exc_tb) diff --git a/tests/fake_server.py b/tests/fake_server.py index 9afda65..cec1949 100644 --- a/tests/fake_server.py +++ b/tests/fake_server.py @@ -70,11 +70,23 @@ class FakeClientHandler(object): # Communicate with the client until disconnected. try: self._run_handshake() - self.socket.shutdown(socket.SHUT_RDWR) + try: + self.socket.shutdown(socket.SHUT_RDWR) + except IOError: + pass + except (FakeClientDisconnect, BrokenPipeError) as exc: + if not self.handle_abnormal_disconnect(exc): + raise finally: self.socket.close() self.socket_file.close() + def handle_abnormal_disconnect(self, exc): + # Called when the client disconnects in an abnormal fashion. If this + # handler returns True, the error is ignored and is treated as a normal + # disconnection. + return False + def handle_connection(self): # Called in the handshake state, just after the client connects, # before any packets have been exchanged. @@ -363,7 +375,7 @@ class FakeServer(object): 'minecraft_version', 'client_handler_type', 'server_type', \ 'packets_handshake', 'packets_login', 'packets_playing', \ 'packets_status', 'lock', 'stopping', 'private_key', \ - 'public_key_bytes', 'test_case', + 'public_key_bytes', 'test_case' def __init__(self, minecraft_version=None, compression_threshold=None, client_handler_type=FakeClientHandler, private_key=None, @@ -407,7 +419,7 @@ class FakeServer(object): self.listen_socket = socket.socket() self.listen_socket.settimeout(0.1) self.listen_socket.bind(('localhost', 0)) - self.listen_socket.listen(0) + self.listen_socket.listen(1) self.lock = threading.Lock() self.stopping = False @@ -581,8 +593,6 @@ class _FakeServerTest(unittest.TestCase): if thread is not None and thread.is_alive(): errors.append({ 'msg': 'Thread "%s" timed out.' % thread.name}) - if client_exc_info[0] is None: - client_exc_info[0] = client.exc_info except Exception: errors.insert(0, { 'msg': 'Exception in main thread', diff --git a/tests/test_connection.py b/tests/test_connection.py index 4a72118..bb10b4e 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -360,6 +360,34 @@ class HandleExceptionTest(ConnectTest): client.connect() +class ExceptionReconnectTest(ConnectTest): + class CustomException(Exception): + pass + + def setUp(self): + self.phase = 0 + + def _start_client(self, client): + @client.listener(clientbound.play.JoinGamePacket) + def handle_join_game(packet): + if self.phase == 0: + self.phase += 1 + raise self.CustomException + else: + raise fake_server.FakeServerTestSuccess + + @client.exception_handler(self.CustomException, early=True) + def handle_custom_exception(exc, exc_info): + client.disconnect(immediate=True) + client.connect() + + client.connect() + + class client_handler_type(ConnectTest.client_handler_type): + def handle_abnormal_disconnect(self, exc): + return True + + class VersionNegotiationEdgeCases(fake_server._FakeServerTest): earliest_version = SUPPORTED_PROTOCOL_VERSIONS[0] latest_version = SUPPORTED_PROTOCOL_VERSIONS[-1]