# Copyright 2014-2021 The aiosmtpd Developers # SPDX-License-Identifier: Apache-2.0 """Testing helpers.""" import os import select import socket import struct import sys import time from smtplib import SMTP as SMTP_Client from typing import List from aiosmtpd.smtp import Envelope, Session, SMTP ASYNCIO_CATCHUP_DELAY = float(os.environ.get("ASYNCIO_CATCHUP_DELAY", 0.1)) """ Delay (in seconds) to give asyncio event loop time to catch up and do things. May need to be increased for slow and/or overburdened test systems. """ def reset_connection(client: SMTP_Client): # Close the connection with a TCP RST instead of a TCP FIN. client must # be a smtplib.SMTP instance. # # https://stackoverflow.com/a/6440364/1570972 # # socket(7) SO_LINGER option. # # struct linger { # int l_onoff; /* linger active */ # int l_linger; /* how many seconds to linger for */ # }; # # Is this correct for Windows/Cygwin and macOS? struct_format = "hh" if sys.platform == "win32" else "ii" l_onoff = 1 l_linger = 0 assert client.sock is not None client.sock.setsockopt( socket.SOL_SOCKET, socket.SO_LINGER, struct.pack(struct_format, l_onoff, l_linger), ) client.close() class ReceivingHandler: def __init__(self): self.box: List[Envelope] = [] async def handle_DATA( self, server: SMTP, session: Session, envelope: Envelope ) -> str: self.box.append(envelope) return "250 OK" def catchup_delay(delay: float = ASYNCIO_CATCHUP_DELAY): """ Sleep for awhile to give asyncio's event loop time to catch up. """ time.sleep(delay) def send_recv( sock: socket.socket, data: bytes, end: bytes = b"\r\n", timeout: float = 0.1 ) -> bytes: sock.send(data + end) slist = [sock] result: List[bytes] = [] while True: read_s, _, _ = select.select(slist, [], [], timeout) if read_s: # We can use sock instead of read_s because slist only contains sock result.append(sock.recv(1024)) else: break return b"".join(result)