Skip to content

Commit

Permalink
examples/network: Add example of HTTPS client using non-blocking socket.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dpgeorge committed May 13, 2024
1 parent bd610ff commit cc3550e
Showing 1 changed file with 75 additions and 0 deletions.
75 changes: 75 additions & 0 deletions examples/network/https_client_nonblocking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Example of a HTTPS client working with non-blocking sockets.
#
# Non-blocking SSL streams works differently in MicroPython compared to CPython. In
# CPython a write to an SSLSocket may raise ssl.SSLWantReadError. In MicroPython an
# SSLSocket behaves like a normal socket/stream and can be polled for reading/writing.

from errno import EINPROGRESS
import select
import socket
import ssl


def connect_nonblocking(sock, addr):
sock.setblocking(False)
try:
sock.connect(addr)
except OSError as er:
if er.errno != EINPROGRESS:
raise er


def write_nonblocking(poller, sock, data):
poller.register(sock, select.POLLOUT)
while data:
poller.poll()
n = sock.write(data)
print("Wrote:", n)
if n is not None:
data = data[n:]


def read_nonblocking(poller, sock, n):
poller.register(sock, select.POLLIN)
poller.poll()
data = sock.read(n)
print("Read:", len(data))
return data


def main(url):
# Split the given URL into components.
proto, _, host, path = url.split(b"/", 3)
assert proto == b"https:"

# Note: this getaddrinfo() call is blocking!
ai = socket.getaddrinfo(host, 443)[0]
addr = ai[-1]
print("Connect address:", addr)

# Create a TCP socket and connect to the server in non-blocking mode.
sock = socket.socket(ai[0], ai[1], ai[2])
connect_nonblocking(sock, addr)

# Wrap the TCP socket in an SSL stream in non-blocking mode.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sock = ctx.wrap_socket(sock, server_hostname=host, do_handshake_on_connect=False)
sock.setblocking(False)

# Create an object to poll the SSL stream for readability/writability.
poller = select.poll()

# Send the HTTP request on the SSL stream.
request = b"GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (path, host)
write_nonblocking(poller, sock, request)

# Receive the HTTP response from the SSL stream.
response = read_nonblocking(poller, sock, 1000)
for line in response.split(b"\n"):
print(line)

# Close the SSL stream. This will also close the underlying TCP socket.
sock.close()


main(b"https://micropython.org/ks/test.html")

0 comments on commit cc3550e

Please sign in to comment.