From 73b0780321acb14bab6707006f5136ea9998951d Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Thu, 30 Jul 2026 19:22:17 +0200 Subject: feat(receive): add maxsize argument to bound memory usage client:receive("*l") and receive("*a") are unbounded: a peer that never sends a newline, or never closes, makes LuaSocket buffer until the process runs out of memory (e.g. src/http.lua reading response headers in a loop). Add an optional maxsize argument that caps the payload a single call may accumulate, including prefix. - Hoist all argument validation ahead of timeout_markstart() so bad calls (maxsize < 1, #prefix >= maxsize, numeric pattern > maxsize) raise before any I/O and leave the socket untouched. - recvline/recvall take a budget and return a new internal BUF_OVERSIZED code, surfaced to Lua as the "oversized" error alongside "timeout"/"closed", with the partial held in the 3rd return value. - recvraw is left untouched: argument checks make the cap unreachable for numeric patterns. - Preserve three invariants: a timeout partial is always shorter than maxsize (safe to retry as prefix), completion beats the cap for *a, and no bytes are lost or skipped on overflow. - tcp.c, unixstream.c and serial.c all share this code path unchanged. Adds test coverage (argument errors, *l/*a boundaries, timeout/close at the cap, the drain idiom, numeric patterns, unix-stream mirror) and documents the new argument, error, and recovery idioms in docs/tcp.html. --- docs/tcp.html | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/tcp.html b/docs/tcp.html index a26228d..4c6c6bc 100644 --- a/docs/tcp.html +++ b/docs/tcp.html @@ -351,7 +351,7 @@ method returns nil followed by an error message.

-client:receive([pattern [, prefix]]) +client:receive([pattern [, prefix [, maxsize]]])

@@ -380,14 +380,23 @@ of bytes from the socket. of any received data before return.

+

+Maxsize is an optional positive integer bounding the number of +payload bytes the call may accumulate, including prefix. +Omitted or nil means unlimited. +

+

If successful, the method returns the received pattern. In case of error, the method returns nil followed by an error message, followed by a (possibly empty) string containing the partial that was received. The error message can be the string 'closed' in case the connection was -closed before the transmission was completed or the string -'timeout' in case there was a timeout during the operation. +closed before the transmission was completed, the string +'timeout' in case there was a timeout during the operation, or, +when maxsize was given, the string 'oversized' in case +the pattern did not complete within maxsize bytes -- in which case +the third return value holds exactly maxsize bytes.

@@ -399,6 +408,49 @@ functions should return nil on error. Thus it was changed too.

+

+Note on maxsize: passing a maxsize that is smaller +than 1, a prefix whose length is greater than or equal to +maxsize, or, for a numeric pattern, a byte count greater +than maxsize, all raise a Lua error rather than returning +nil plus a message -- these are caller logic errors, and +they are detected before any byte is read from the socket. To drain and +discard an oversized line while keeping memory bounded and the stream +aligned: +

+ +
+local data, err, part
+repeat
+    data, err, part = client:receive("*l", "", 4096)
+until err ~= "oversized"
+
+ +

+To instead retry and eventually get the whole thing, carry the partial +forward as prefix and grow maxsize: +

+ +
+local data, err, part = client:receive("*l", nil, 4096)
+if err == "oversized" then
+    data, err, part = client:receive("*l", part, 65536)   -- larger cap, or this raises
+end
+
+ +

+Retrying with prefix set to the previous partial result and an +unchanged maxsize raises the length-check error above by +design -- otherwise it would be a zero-progress spin: no I/O, no timeout, +no error, just CPU. A timeout partial is always strictly shorter +than maxsize, so it is always safe to feed straight back as +prefix with the same maxsize. Finally, note that +maxsize bounds the payload returned, not necessarily the +bytes taken off the wire: for the *l pattern the discarded CR +characters and the line terminator mean more bytes may have been consumed +than the returned length suggests. +

+

-- cgit v1.2.3-55-g6feb