Skip to content

Commit cc3550e

Browse files
committed
examples/network: Add example of HTTPS client using non-blocking socket.
Non-blocking SSL streams can be difficult to get right, so provide a working example, of a HTTPS client. Signed-off-by: Damien George <damien@micropython.org>
1 parent bd610ff commit cc3550e

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Example of a HTTPS client working with non-blocking sockets.
2+
#
3+
# Non-blocking SSL streams works differently in MicroPython compared to CPython. In
4+
# CPython a write to an SSLSocket may raise ssl.SSLWantReadError. In MicroPython an
5+
# SSLSocket behaves like a normal socket/stream and can be polled for reading/writing.
6+
7+
from errno import EINPROGRESS
8+
import select
9+
import socket
10+
import ssl
11+
12+
13+
def connect_nonblocking(sock, addr):
14+
sock.setblocking(False)
15+
try:
16+
sock.connect(addr)
17+
except OSError as er:
18+
if er.errno != EINPROGRESS:
19+
raise er
20+
21+
22+
def write_nonblocking(poller, sock, data):
23+
poller.register(sock, select.POLLOUT)
24+
while data:
25+
poller.poll()
26+
n = sock.write(data)
27+
print("Wrote:", n)
28+
if n is not None:
29+
data = data[n:]
30+
31+
32+
def read_nonblocking(poller, sock, n):
33+
poller.register(sock, select.POLLIN)
34+
poller.poll()
35+
data = sock.read(n)
36+
print("Read:", len(data))
37+
return data
38+
39+
40+
def main(url):
41+
# Split the given URL into components.
42+
proto, _, host, path = url.split(b"/", 3)
43+
assert proto == b"https:"
44+
45+
# Note: this getaddrinfo() call is blocking!
46+
ai = socket.getaddrinfo(host, 443)[0]
47+
addr = ai[-1]
48+
print("Connect address:", addr)
49+
50+
# Create a TCP socket and connect to the server in non-blocking mode.
51+
sock = socket.socket(ai[0], ai[1], ai[2])
52+
connect_nonblocking(sock, addr)
53+
54+
# Wrap the TCP socket in an SSL stream in non-blocking mode.
55+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
56+
sock = ctx.wrap_socket(sock, server_hostname=host, do_handshake_on_connect=False)
57+
sock.setblocking(False)
58+
59+
# Create an object to poll the SSL stream for readability/writability.
60+
poller = select.poll()
61+
62+
# Send the HTTP request on the SSL stream.
63+
request = b"GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (path, host)
64+
write_nonblocking(poller, sock, request)
65+
66+
# Receive the HTTP response from the SSL stream.
67+
response = read_nonblocking(poller, sock, 1000)
68+
for line in response.split(b"\n"):
69+
print(line)
70+
71+
# Close the SSL stream. This will also close the underlying TCP socket.
72+
sock.close()
73+
74+
75+
main(b"https://micropython.org/ks/test.html")

0 commit comments

Comments
 (0)