Fix deadlocks due to invalid handling of SSL sockets and select
SSL sockets can't be used with select() in the same way raw sockets would, since the OS socket readable/writable state is not always identical to the one of the SSL stack. See also:
https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets
For instance, websockify is currently unable to proxy a connection to a SSL target - unless the server-side sends data first.
In this case, the OS socket is reported as readable - due to SSL protocol data like sessions tickets being exchanged - while the SSL socket is not readable since application data hasn't been received from the server-side yet. As SSL sockets are read from (and written to) in a blocking fashion, the current implementation then simply stalls. For an example, see here:
https://user-images.githubusercontent.com/12437061/148658553-3dbffd73-bfed-495d-b79c-79eb79d61940.mov
The following patch tries to leave most of the existing socket handling logic intact while addressing the special handling required for SSL sockets. In particular, SSL sockets are now put in non-blocking mode, so that false-positive readable reports from select() don't lead to a deadlock but an exception. (Unfortunately, the SSLSocket.pending() API doesn't seem to work reliably to archive the same with blocking SSL sockets.. :-/)
In addition, the patch introduces an assertion to ensure the internal buffer size is equal or above 16kb so that all available data can be directly read from a SSL socket in a single call. Otherwise, there would be additional (annoying) handling required to make sure no data is accidentally left behind when a SSL socket would be only partially read. In this case, select() wouldn't report this socket as readable again - unless new data is received - since the rest of the data has already been read and buffered by the SSL socket layer.