# 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")
