aboutsummaryrefslogtreecommitdiff
path: root/tests/net_hosted
diff options
context:
space:
mode:
authorThorsten von Eicken2020-04-02 10:01:16 -0700
committerDamien George2021-02-17 11:50:54 +1100
commit2c1299b0071c2c528cc01e3cde9eb22743820176 (patch)
tree0679eb4daf9522f30cec65b3d7bce494029482b9 /tests/net_hosted
parent2eed9780ba7074de9e464a2bc771ad14f0332a6c (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.py6
-rw-r--r--tests/net_hosted/connect_nonblock_xfer.py147
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)