Skip to content

Commit 8ed1a34

Browse files
committed
extmod: Uasyncio-mbedtls fix stream async read.
This fix async stream read methods in unix port. The reason is stream ssl sockets seem to behave different from plain stream sockets.The main difference is doing partial reads on ssl sockets will clear any write/read/close "event" flag even if there is still pending data to be read. So registering the ssl socket again in the poller will wait there until a new event or timeout. The fix is check first if the ssl socket has pending data and read/return it instead of registering in the poller to wait for an event. Signed-off-by: Carlos Gil <[email protected]>
1 parent 3f88ffc commit 8ed1a34

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-0
lines changed

extmod/asyncio/stream.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ async def wait_closed(self):
3535
def read(self, n=-1):
3636
r = b""
3737
while True:
38+
if hasattr(self.s, "ssl_pending"):
39+
if self.s.ssl_pending():
40+
r2 = self.s.read(n)
41+
if r2 is not None:
42+
if n >= 0:
43+
return r2
44+
if not len(r2):
45+
return r
46+
r += r2
3847
yield core._io_queue.queue_read(self.s)
3948
r2 = self.s.read(n)
4049
if r2 is not None:
@@ -46,13 +55,25 @@ def read(self, n=-1):
4655

4756
# async
4857
def readinto(self, buf):
58+
if hasattr(self.s, "ssl_pending"):
59+
if self.s.ssl_pending():
60+
return self.s.readinto(buf)
4961
yield core._io_queue.queue_read(self.s)
5062
return self.s.readinto(buf)
5163

5264
# async
5365
def readexactly(self, n):
5466
r = b""
5567
while n:
68+
# prevent queueing if pending read in a ssl socket
69+
if hasattr(self.s, "ssl_pending"):
70+
if self.s.ssl_pending():
71+
r2 = self.s.read(n)
72+
if r2 is not None:
73+
if not len(r2):
74+
raise EOFError
75+
r += r2
76+
n -= len(r2)
5677
yield core._io_queue.queue_read(self.s)
5778
r2 = self.s.read(n)
5879
if r2 is not None:
@@ -66,6 +87,13 @@ def readexactly(self, n):
6687
def readline(self):
6788
l = b""
6889
while True:
90+
# prevent queueing if pending read in a ssl socket
91+
if hasattr(self.s, "ssl_pending"):
92+
if self.s.ssl_pending():
93+
l2 = self.s.readline()
94+
l += l2
95+
if not l2 or l[-1] == 10:
96+
return l
6997
yield core._io_queue.queue_read(self.s)
7098
l2 = self.s.readline() # may do multiple reads but won't block
7199
l += l2

extmod/modssl_mbedtls.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,14 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
684684
}
685685
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
686686

687+
STATIC mp_obj_t socket_ssl_pending(mp_obj_t self_in) {
688+
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
689+
int check_pending = 0;
690+
check_pending = mbedtls_ssl_check_pending(&o->ssl);
691+
return mp_obj_new_int(check_pending);
692+
}
693+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_ssl_pending_obj, socket_ssl_pending);
694+
687695
STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
688696
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
689697
mp_uint_t ret = 0;
@@ -753,6 +761,7 @@ STATIC const mp_rom_map_elem_t ssl_socket_locals_dict_table[] = {
753761
#endif
754762
{ MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
755763
{ MP_ROM_QSTR(MP_QSTR_cipher), MP_ROM_PTR(&mod_ssl_cipher_obj) },
764+
{ MP_ROM_QSTR(MP_QSTR_ssl_pending), MP_ROM_PTR(&socket_ssl_pending_obj) },
756765
};
757766

758767
STATIC MP_DEFINE_CONST_DICT(ssl_socket_locals_dict, ssl_socket_locals_dict_table);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Test uasyncio TCP server and client using start_server() and open_connection()
2+
3+
try:
4+
import uasyncio as asyncio
5+
6+
except ImportError:
7+
try:
8+
import asyncio
9+
except ImportError:
10+
print("SKIP")
11+
raise SystemExit
12+
13+
try:
14+
import ssl
15+
except ImportError:
16+
print("SKIP")
17+
raise SystemExit
18+
19+
import binascii
20+
21+
PORT = 8000
22+
23+
# testing/demonstration only. You should always generate your own key/cert.
24+
25+
# To generate a new self-signed key/cert pair with openssl do:
26+
# $ openssl req -x509 -newkey rsa:4096 -keyout rsa_key.pem -out rsa_cert.pem
27+
# -days 365 -nodes
28+
# In this case CN is: esphome.local
29+
#
30+
# Convert them to DER format:
31+
# $ openssl rsa -in rsa_key.pem -out rsa_key.der -outform DER
32+
# $ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER
33+
#
34+
# Then convert to hex format, eg using binascii.hexlify(data).
35+
36+
cert = binascii.unhexlify(
37+
b"308205d7308203bfa003020102020900bc63b48a700c3d49300d06092a864886f70d01010b050030"
38+
b"8181310b3009060355040613024155310c300a06035504080c03466f6f310c300a06035504070c03"
39+
b"42617231143012060355040a0c0b4d6963726f507974686f6e310c300a060355040b0c03666f6f31"
40+
b"16301406035504030c0d657370686f6d652e6c6f63616c311a301806092a864886f70d010901160b"
41+
b"666f6f406261722e636f6d301e170d3232303731323138303031335a170d32333037313231383030"
42+
b"31335a308181310b3009060355040613024155310c300a06035504080c03466f6f310c300a060355"
43+
b"04070c0342617231143012060355040a0c0b4d6963726f507974686f6e310c300a060355040b0c03"
44+
b"666f6f3116301406035504030c0d657370686f6d652e6c6f63616c311a301806092a864886f70d01"
45+
b"0901160b666f6f406261722e636f6d30820222300d06092a864886f70d01010105000382020f0030"
46+
b"82020a0282020100ce3c0f730ab34432ce605ab44d4ac0aafd8a6243133eab0dcc9d444ab7d9ff66"
47+
b"a6815a101d2d3cbd72140afc34f8c3caedce16e9528350f3e0e56343f248507d82e41b51abb515cb"
48+
b"f60e5a619f2dbca8684d174c3b0951e2c7ba576c7fb06453a3597755810a6a4c45eb0925c855ab53"
49+
b"7785df46bf29145871330ff0641a101a24f0830c20bae865ba8bb32606caac4555812acf19f59553"
50+
b"349ce70fb7ff63512f0444f8f41b973183eabf9679903087c6cd69dc3adcbe754dd0207ea57c50e9"
51+
b"2d800bce6258d1618bb749d3fc01239b6d1af6d3f9cada3acbb312a1d85a59cfabd28b2e572c56a4"
52+
b"818ce170ca2b781a04749c6239206c64ad9e057484143a4c52bdef6189c46405c1a9642489cb640a"
53+
b"937adfc2687578dfa2b40ebafa05213642a1ccbc265557cd40de53324cff1bfba6f5c215f657b8f9"
54+
b"f2260ab6293625d0e203bba975bc7ac6dff3e604c9b0d2a2a4ba5941c0dc8d2e0e9439c56447b404"
55+
b"8c0e6cfb03517742ff6f7c2140a05954aa1e29247d1ae8bfd7db0db8dd45d095710fb78284ede285"
56+
b"0fc0c21235406af83e6044addf9385316403e2a25442b9ffbfc7b01c6c9292e5a3531e6a48496c01"
57+
b"6de1373334a52f01b7c6a0ece1261936788d2161c53a8985a0946d6d319225b230d96d055ea4692f"
58+
b"eb71fdaf4b775ac9fbc38e1b943e6617cf61d33e930ab288a3ea4730b4f2784a8018e0dfc8a11e73"
59+
b"0203010001a350304e301d0603551d0e04160414bc6048fe3cd278257e8b7c90dedbbce8369b20b8"
60+
b"301f0603551d23041830168014bc6048fe3cd278257e8b7c90dedbbce8369b20b8300c0603551d13"
61+
b"040530030101ff300d06092a864886f70d01010b0500038202010009238354b43379a3d2b56e928c"
62+
b"ac8ea28e2c01cf8148e54c0bbd4055e2e57d578697d1e2c392f1fe3bc9211d4f27ed1be631e7547a"
63+
b"6390d7f121a9e20a195fdda73f755188b16cf39714924a9686dd7cc749421335038c0640c2c6b15d"
64+
b"f44d74d94a97285ee2a7b075ccc9d9d632e2a5906030cf59bde14ab10660b7cf47ec9d7ae2f35963"
65+
b"454f76735a3dac12a4a4c907183e9ccf3e07d59484c182e67edc7c35ce15c7e1072fae8c9965a126"
66+
b"1a1f31147d4af8d1ebf8ee7c142badfe67e31fb324a79a29bc94e89370b70d8cf7cd2b2aa427a49f"
67+
b"77849891e7c4d5911f6fda52733a3c169b0188c2d9918f296dd8e234f8962f0db5e47c6159448045"
68+
b"4e2d9a5850d4c696a0fb3b66534a4591c49dda8cc6f1b0008c625aa5e0091ecfbd51d9715c60b85e"
69+
b"4e89d4a6cfabb2acdf81518eb61403b8f8767c5c00216f730e08f22959dff695a081cc726c4ab35a"
70+
b"e3f6538a231f831a6e91206f3b691a94bdf95343ec02ef7aac42da2a70846cd5f13dd2955a5f1737"
71+
b"a4c3c6c03b041d334c1dadd1e305f07c83b4b4e0509ec1d23e95f820290942eaaf8bea304cd5a505"
72+
b"8fc0d4624ff1ffe1348e7bc54c756a12acb258eb5e7426fb062a82b88ec274c9c13b3eff8b010947"
73+
b"62e166f490cd25b14e762db708785859a337d8fd0008fe602a90e2933cded3359e98ce3fbc041208"
74+
b"66bd4d96d6b6f7f53def854d40021196b7a06b"
75+
)
76+
77+
key = binascii.unhexlify(
78+
b"308209290201000282020100ce3c0f730ab34432ce605ab44d4ac0aafd8a6243133eab0dcc9d444a"
79+
b"b7d9ff66a6815a101d2d3cbd72140afc34f8c3caedce16e9528350f3e0e56343f248507d82e41b51"
80+
b"abb515cbf60e5a619f2dbca8684d174c3b0951e2c7ba576c7fb06453a3597755810a6a4c45eb0925"
81+
b"c855ab537785df46bf29145871330ff0641a101a24f0830c20bae865ba8bb32606caac4555812acf"
82+
b"19f59553349ce70fb7ff63512f0444f8f41b973183eabf9679903087c6cd69dc3adcbe754dd0207e"
83+
b"a57c50e92d800bce6258d1618bb749d3fc01239b6d1af6d3f9cada3acbb312a1d85a59cfabd28b2e"
84+
b"572c56a4818ce170ca2b781a04749c6239206c64ad9e057484143a4c52bdef6189c46405c1a96424"
85+
b"89cb640a937adfc2687578dfa2b40ebafa05213642a1ccbc265557cd40de53324cff1bfba6f5c215"
86+
b"f657b8f9f2260ab6293625d0e203bba975bc7ac6dff3e604c9b0d2a2a4ba5941c0dc8d2e0e9439c5"
87+
b"6447b4048c0e6cfb03517742ff6f7c2140a05954aa1e29247d1ae8bfd7db0db8dd45d095710fb782"
88+
b"84ede2850fc0c21235406af83e6044addf9385316403e2a25442b9ffbfc7b01c6c9292e5a3531e6a"
89+
b"48496c016de1373334a52f01b7c6a0ece1261936788d2161c53a8985a0946d6d319225b230d96d05"
90+
b"5ea4692feb71fdaf4b775ac9fbc38e1b943e6617cf61d33e930ab288a3ea4730b4f2784a8018e0df"
91+
b"c8a11e73020301000102820201008efa0e8fe81c2e2cb6ed10152dfca4242750581d3e6b54f56524"
92+
b"a6a2d2613cf2727efcec6cfddebd4c285f1148bc2a2936c28919cb0da502dea8c92fe2f9856bee61"
93+
b"ac1aebdac838b5e66f7c7c799df07716f30ef362dbb5485884a180c8ce5539cb1db35699dce5f217"
94+
b"27295d811f1ce7a115111c1823b5c90ce880f5352872a7a76282f6f1fd8a015136ab274c3d30783d"
95+
b"eb6ad7096e33d826eafdf7c70398d5eab4d28f91cd3913c69c7a7ade9ef692b9f8292959be64dec4"
96+
b"6ab2c291b41a6464004b5ddd4b93bfe41b37eedeef4ba2d16dcbb9c28b96f57fb96c20ed4a9471ff"
97+
b"ae643b254f100f8c9702b5f67af6369e8d887f285e5d520c5aa5d3a79e5de96432e6d2e3dea68e58"
98+
b"208c075fb119c6d3d4149b7e1247208d6b337c70272befc41d57f278618f1a82de337173346dc135"
99+
b"4d80a7c9075af99dbb2a14733c06b71600c6677a6bb28c0e4fc63db622228047a2cb7474dc8141c3"
100+
b"5f3a597c3e2bca9911d28eb9fd1a0c915e9f9c1cfd643d4fd8cac867f215380168ec37b8cfa28564"
101+
b"e6288ab04a7d67ca44b4c8375214a7ffaa1e6be92c4b138fcfd6beaba251b31a50a6e2ef241c9554"
102+
b"a1dc710b4acb63e749f5849e53d3f4915c6eb2a9a009bab04e932841ab34ae29eb000a08777d6399"
103+
b"169c2dc3d7952df5bc2d06e90a32139c6a2793d3817e4feadac2ccac554d383a8d41569140c29168"
104+
b"89220d3a5e410282010100fd603d18feef7aac61bda3b674a57ab38748bcde5c3efdba2279638f8e"
105+
b"a413cc26b9dda0375c116a8798a295b2c283aaaad7cca0dbd9bb3322a9a815f6d0aa5fc4f9aff8fb"
106+
b"da8ff914091ede7aefdb07a119c9b2e2b2bda776ac497060b8e88a82eb20c62f26f343566697726e"
107+
b"71aa46fd4efad6f42fc8a478856324d72cbf5eb3918317162d6fc2cfd775969a2077759fa2c8220d"
108+
b"acdc2ebb03ec39feed3f2b415449cbf40a7126bcf01d1068e3a45ec01181f2c68d7e05b4720bfe4a"
109+
b"308e1648123c91214a5f8dfce58727c4cd9396a8b403b733a717449b2f1970db97a3b8467271ffa6"
110+
b"e8c7cc9e2e1c0f789284ae9efe77eaac01131463c9c1329a1ba3530282010100d05ed6ab9b9fbdf7"
111+
b"a0f5f91f68dc3bac5789332d6ece46103fb1ef109fc972fdc99edf3107a23d66d1cdfe6bdddfd1bb"
112+
b"3952ccd10b5c20ad1b3e0aa6a51271ecf3a7ef2a65e029f5d77f238d1235b52a9dca3451c165d70a"
113+
b"99cbaea5c610e5455979696db769191e7cf2db21f641959e4ba1c5c0aae260c724962b6ac2621d92"
114+
b"e9df7adeb82b522d37b42cb454003bbe60d9915bf7737aeccf88c7ed1263a22f431a734e61fe7173"
115+
b"a937ddf76ad2a79994c05238defc15f6846858e9edf27ae2a567c7c5c735ea5d2fbef65a2195bc05"
116+
b"d82cbf06a477b29c84c92e8054c2bb25d8c6f19d43ef5fd1fce13c2cdbc361c39baec37b399200b3"
117+
b"2d4a6798ba0b546102820101008e41492c4e7daff7368d1d6c64034067a94dca5461a0301e201add"
118+
b"2e0d5ccb8cb435685bfa98e362572cf8236a10d191b187a568aee688b6c60050d1bc181d7fd57c86"
119+
b"33195bf5b7576b637c6fb358dae8b52ccc15815affb99e334137dcb91a833475db2f4004164b5d20"
120+
b"2c6c1bbf094a50dc7e70ec9f0ed067bb6944b1e7e3c897aaecfc53984add1c4ff5b525034cf3ca95"
121+
b"e8a09aeba804f1c7e02be391b2bc641166c3e654eef5e72dba37d98f406f3fa520e41f2ea10f5574"
122+
b"ac5984f75145378fefbfac1d07fff3f234fec698d55e746b1da18f6f7de24ec84ed7cb446d428820"
123+
b"bef33c00693e6a0ef114b5d66e9fefa8ee059238df1ac37c87e7841ae7028201000721a7d139c34e"
124+
b"da21cd295894db2cc3aa3f4cdc1a35bf1a2143f2bdabea56202f7d5b802f15b36a4875f76633b2cc"
125+
b"57cf0f71691a2d6e04deb0d1e68031d06a5eb079b406c6944910b60e3e6ec81dca369a4c0e1c4363"
126+
b"07bed9c4c171b4f453da4b187ba3d25a04bc1c07b9f2d6adcb3c256e4238d7049eec36a387c4dd5c"
127+
b"cbc16b5fa62dc175cf8c5f83442cb7d153a3b6ee8daa3b6e929a4bc123f1042df1d6271a992d2b6b"
128+
b"309d33074ac7822c304a72069e61ab590915e10862013dd24cdd825ec8fb17724cfc2c59fc1db825"
129+
b"3641fece0ee9241b9dd5c198f0d575d0b7ebe26b3489b5b09edc3bcd366fd3110e83ce886c383d31"
130+
b"feefe6e302cc2345210282010008f77a33d0081e9be3c1b1ac8b8e0eebb72df2eb69b95d2ed74935"
131+
b"b9dab8e17023cc38465354023c5183b51a6a20288fbb2181172be1c2fdb8b444419454e5b37f7f3b"
132+
b"df11e28cf4746b25534eb62f7e87bbbf28eda37024368b3897fbc661b40a93e04a183db9219c04a8"
133+
b"7643edf5d8b5dbfe3d424e91d558d5e3e2fa02ce1984ee69fb8518470eee2e7db0e1df5ac4571f78"
134+
b"a7a2529bc1fef5e32d46994869a8d8cc47869e174d84e7976be8ebb88f2ccb71a603a8bdb06af3eb"
135+
b"2ddbd62082f40d7987e47f2e321eb5eb2a28fefab263409f89dc97ebc723a1b751418cdd3ea684ba"
136+
b"8b17a330a306a6fbcf51ba83563aed85a4f886fff1a22423748d83798c"
137+
)
138+
139+
140+
async def handle_connection(reader, writer):
141+
# Test that peername exists (but don't check its value, it changes)
142+
writer.get_extra_info("peername")
143+
144+
data = await reader.readline()
145+
print("echo:", data)
146+
data2 = await reader.readline()
147+
print("echo:", data2)
148+
writer.write(data + data2)
149+
await writer.drain()
150+
151+
print("close")
152+
writer.close()
153+
await writer.wait_closed()
154+
155+
print("done")
156+
ev.set()
157+
158+
159+
async def tcp_server():
160+
global ev
161+
162+
server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
163+
server_ctx.load_cert_chain(cert, keyfile=key)
164+
ev = asyncio.Event()
165+
server = await asyncio.start_server(handle_connection, "0.0.0.0", PORT, ssl=server_ctx)
166+
print("server running")
167+
multitest.next()
168+
async with server:
169+
await asyncio.wait_for(ev.wait(), 10)
170+
171+
172+
async def tcp_client(message):
173+
client_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
174+
client_ctx.load_verify_locations(cadata=cert)
175+
reader, writer = await asyncio.open_connection(
176+
IP, PORT, ssl=client_ctx, server_hostname="esphome.local"
177+
)
178+
print("write:", message)
179+
writer.write(message)
180+
await writer.drain()
181+
data = await reader.readline()
182+
print("read:", data)
183+
data2 = await reader.readline()
184+
print("read:", data2)
185+
186+
187+
def instance0():
188+
multitest.globals(IP=multitest.get_network_ip())
189+
asyncio.run(tcp_server())
190+
191+
192+
def instance1():
193+
multitest.next()
194+
asyncio.run(tcp_client(b"client data\nclient data2\n"))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--- instance0 ---
2+
server running
3+
echo: b'client data\n'
4+
echo: b'client data2\n'
5+
close
6+
done
7+
--- instance1 ---
8+
write: b'client data\nclient data2\n'
9+
read: b'client data\n'
10+
read: b'client data2\n'

0 commit comments

Comments
 (0)