diff options
| author | Thorsten von Eicken | 2020-04-02 10:01:16 -0700 |
|---|---|---|
| committer | Damien George | 2021-02-17 11:50:54 +1100 |
| commit | 2c1299b0071c2c528cc01e3cde9eb22743820176 (patch) | |
| tree | 0679eb4daf9522f30cec65b3d7bce494029482b9 /tests/net_hosted | |
| parent | 2eed9780ba7074de9e464a2bc771ad14f0332a6c (diff) | |
extmod/modussl: Fix ussl read/recv/send/write errors when non-blocking.
Also fix related problems with socket on esp32, improve docs for
wrap_socket, and add more tests.
Diffstat (limited to 'tests/net_hosted')
| -rw-r--r-- | tests/net_hosted/accept_timeout.py | 6 | ||||
| -rw-r--r-- | tests/net_hosted/connect_nonblock_xfer.py | 147 |
2 files changed, 150 insertions, 3 deletions
diff --git a/tests/net_hosted/accept_timeout.py b/tests/net_hosted/accept_timeout.py index ff989110a..5f528d557 100644 --- a/tests/net_hosted/accept_timeout.py +++ b/tests/net_hosted/accept_timeout.py @@ -1,9 +1,9 @@ # test that socket.accept() on a socket with timeout raises ETIMEDOUT try: - import usocket as socket + import uerrno as errno, usocket as socket except: - import socket + import errno, socket try: socket.socket.settimeout @@ -18,5 +18,5 @@ s.listen(1) try: s.accept() except OSError as er: - print(er.args[0] in (110, "timed out")) # 110 is ETIMEDOUT; CPython uses a string + print(er.args[0] in (errno.ETIMEDOUT, "timed out")) # CPython uses a string instead of errno s.close() diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py new file mode 100644 index 000000000..feb648ea0 --- /dev/null +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -0,0 +1,147 @@ +# test that socket.connect() on a non-blocking socket raises EINPROGRESS +# and that an immediate write/send/read/recv does the right thing + +try: + import sys, time + import uerrno as errno, usocket as socket, ussl as ssl +except: + import socket, errno, ssl +isMP = sys.implementation.name == "micropython" + + +def dp(e): + # uncomment next line for development and testing, to print the actual exceptions + # print(repr(e)) + pass + + +# do_connect establishes the socket and wraps it if tls is True. +# If handshake is true, the initial connect (and TLS handshake) is +# allowed to be performed before returning. +def do_connect(peer_addr, tls, handshake): + s = socket.socket() + s.setblocking(False) + try: + # print("Connecting to", peer_addr) + s.connect(peer_addr) + except OSError as er: + print("connect:", er.args[0] == errno.EINPROGRESS) + if er.args[0] != errno.EINPROGRESS: + print(" got", er.args[0]) + # wrap with ssl/tls if desired + if tls: + try: + if sys.implementation.name == "micropython": + s = ssl.wrap_socket(s, do_handshake=handshake) + else: + s = ssl.wrap_socket(s, do_handshake_on_connect=handshake) + print("wrap: True") + except Exception as e: + dp(e) + print("wrap:", e) + elif handshake: + # just sleep a little bit, this allows any connect() errors to happen + time.sleep(0.2) + return s + + +# test runs the test against a specific peer address. +def test(peer_addr, tls=False, handshake=False): + # MicroPython plain sockets have read/write, but CPython's don't + # MicroPython TLS sockets and CPython's have read/write + # hasRW captures this wonderful state of affairs + hasRW = isMP or tls + + # MicroPython plain sockets and CPython's have send/recv + # MicroPython TLS sockets don't have send/recv, but CPython's do + # hasSR captures this wonderful state of affairs + hasSR = not (isMP and tls) + + # connect + send + if hasSR: + s = do_connect(peer_addr, tls, handshake) + # send -> 4 or EAGAIN + try: + ret = s.send(b"1234") + print("send:", handshake and ret == 4) + except OSError as er: + # + dp(er) + print("send:", er.args[0] in (errno.EAGAIN, errno.EINPROGRESS)) + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("send:", True) + + # connect + write + if hasRW: + s = do_connect(peer_addr, tls, handshake) + # write -> None + try: + ret = s.write(b"1234") + print("write:", ret in (4, None)) # SSL may accept 4 into buffer + except OSError as er: + dp(er) + print("write:", False) # should not raise + except ValueError as er: # CPython + dp(er) + print("write:", er.args[0] == "Write on closed or unwrapped SSL socket.") + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("write:", True) + + if hasSR: + # connect + recv + s = do_connect(peer_addr, tls, handshake) + # recv -> EAGAIN + try: + print("recv:", s.recv(10)) + except OSError as er: + dp(er) + print("recv:", er.args[0] == errno.EAGAIN) + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("recv:", True) + + # connect + read + if hasRW: + s = do_connect(peer_addr, tls, handshake) + # read -> None + try: + ret = s.read(10) + print("read:", ret is None) + except OSError as er: + dp(er) + print("read:", False) # should not raise + except ValueError as er: # CPython + dp(er) + print("read:", er.args[0] == "Read on closed or unwrapped SSL socket.") + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("read:", True) + + +if __name__ == "__main__": + # these tests use a non-existent test IP address, this way the connect takes forever and + # we can see EAGAIN/None (https://tools.ietf.org/html/rfc5737) + print("--- Plain sockets to nowhere ---") + test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) + print("--- SSL sockets to nowhere ---") + # this test fails with AXTLS because do_handshake=False blocks on first read/write and + # there it times out until the connect is aborted + test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) + print("--- Plain sockets ---") + test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, True) + print("--- SSL sockets ---") + test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) |
