Add test case reproducing #146 (fixed to behave consistently)

This commit is contained in:
joo 2020-12-04 15:55:39 +01:00
parent 7693961fb9
commit 93db454cb5
3 changed files with 48 additions and 8 deletions

View File

@ -479,6 +479,8 @@ class Connection(object):
self.write_packet(handshake) self.write_packet(handshake)
def _handle_exception(self, exc, exc_info): def _handle_exception(self, exc, exc_info):
final_handler = self.handle_exception
# Call the current PacketReactor's exception handler. # Call the current PacketReactor's exception handler.
try: try:
if self.reactor.handle_exception(exc, exc_info): if self.reactor.handle_exception(exc, exc_info):
@ -499,9 +501,9 @@ class Connection(object):
caught = False caught = False
# Call the user-specified final exception handler. # Call the user-specified final exception handler.
if self.handle_exception not in (None, False): if final_handler not in (None, False):
try: try:
self.handle_exception(exc, exc_info) final_handler(exc, exc_info)
except Exception as new_exc: except Exception as new_exc:
exc, exc_info = new_exc, sys.exc_info() exc, exc_info = new_exc, sys.exc_info()
@ -516,7 +518,7 @@ class Connection(object):
self.disconnect(immediate=True) self.disconnect(immediate=True)
# If allowed by the final exception handler, re-raise the exception. # 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:] exc_value, exc_tb = exc_info[1:]
raise exc_value.with_traceback(exc_tb) raise exc_value.with_traceback(exc_tb)

View File

@ -70,11 +70,23 @@ class FakeClientHandler(object):
# Communicate with the client until disconnected. # Communicate with the client until disconnected.
try: try:
self._run_handshake() 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: finally:
self.socket.close() self.socket.close()
self.socket_file.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): def handle_connection(self):
# Called in the handshake state, just after the client connects, # Called in the handshake state, just after the client connects,
# before any packets have been exchanged. # before any packets have been exchanged.
@ -363,7 +375,7 @@ class FakeServer(object):
'minecraft_version', 'client_handler_type', 'server_type', \ 'minecraft_version', 'client_handler_type', 'server_type', \
'packets_handshake', 'packets_login', 'packets_playing', \ 'packets_handshake', 'packets_login', 'packets_playing', \
'packets_status', 'lock', 'stopping', 'private_key', \ '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, def __init__(self, minecraft_version=None, compression_threshold=None,
client_handler_type=FakeClientHandler, private_key=None, client_handler_type=FakeClientHandler, private_key=None,
@ -407,7 +419,7 @@ class FakeServer(object):
self.listen_socket = socket.socket() self.listen_socket = socket.socket()
self.listen_socket.settimeout(0.1) self.listen_socket.settimeout(0.1)
self.listen_socket.bind(('localhost', 0)) self.listen_socket.bind(('localhost', 0))
self.listen_socket.listen(0) self.listen_socket.listen(1)
self.lock = threading.Lock() self.lock = threading.Lock()
self.stopping = False self.stopping = False
@ -581,8 +593,6 @@ class _FakeServerTest(unittest.TestCase):
if thread is not None and thread.is_alive(): if thread is not None and thread.is_alive():
errors.append({ errors.append({
'msg': 'Thread "%s" timed out.' % thread.name}) 'msg': 'Thread "%s" timed out.' % thread.name})
if client_exc_info[0] is None:
client_exc_info[0] = client.exc_info
except Exception: except Exception:
errors.insert(0, { errors.insert(0, {
'msg': 'Exception in main thread', 'msg': 'Exception in main thread',

View File

@ -360,6 +360,34 @@ class HandleExceptionTest(ConnectTest):
client.connect() 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): class VersionNegotiationEdgeCases(fake_server._FakeServerTest):
earliest_version = SUPPORTED_PROTOCOL_VERSIONS[0] earliest_version = SUPPORTED_PROTOCOL_VERSIONS[0]
latest_version = SUPPORTED_PROTOCOL_VERSIONS[-1] latest_version = SUPPORTED_PROTOCOL_VERSIONS[-1]