|
| 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