modlwip: socket.read(n) on a socket with a finite timeout can lose data
During investigation of issue #2402 I came across the following problem with modlwip: if one creates a socket that has a finite timeout (eg s.settimeout(5)) and then does a read (eg s.read(100)) but the socket doesn't have that many available bytes, then OSError(ETIMEDOUT) is raised after the timeout has passed and any partial read (eg of 50 bytes) is completely lost.
Test script for server on PC (non uPy):
import socket
s = socket.socket()
s.bind(('', 8000))
ss, _ = s.accept()
ss.send(b'1234')
Corresponding test script for modlwip (eg esp8266):
import socket
s = socket.socket()
s.settimeout(5)
s.connect(('<ip of PC>', 8000))
s.read(100)
Outcome: the s.read() in the modlwip script raises ETIMEDOUT and the b'1234' data is lost.
The problem is that the stream reading function (in this case mp_stream_rw) is trying to read exactly 100 bytes. It reads the 4 bytes (the "1234") and then tries to read 96 more. But the modlwip socket fails after 5 seconds with ETIMEDOUT and the stream function raises this error, losing the 4 bytes it did read.
A solution would be to use stream_read1 instead of stream_read for the socket.
extmod/modlwip: Make socket.connect raise ETIMEDOUT on non-zero timeout.
If the socket timeout is 0 then a failed socket.connect() raises EINPROGRESS (which is what the lwIP bindings already did), but if the socket timeout is non-zero then a failed socket.connect() should raise ETIMEDOUT. The latter is fixed in this commit.
A test is added for these timeout cases.
Also, clean up the timeout logic for socket.accept().