← index #3155PR #15223
Related · high · value 0.971
QUERY · ISSUE

modlwip: socket.read(n) on a socket with a finite timeout can lose data

openby dpgeorgeopened 2017-06-20updated 2024-08-28
extmod

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.

CANDIDATE · PULL REQUEST

extmod/modlwip: Make socket.connect raise ETIMEDOUT on non-zero timeout.

mergedby dpgeorgeopened 2024-06-07updated 2024-06-07
extmod

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().

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied