@@ -542,6 +542,15 @@ local isTCP do
542542end
543543
544544
545+ -- POSIX: a 0-byte read on a stream socket (TCP/SSL) is a no-op and must
546+ -- return immediately without waiting on `select`. UDP is different: a
547+ -- 0-byte datagram is a distinct payload, so it must still wait.
548+ -- See https://github.com/lunarmodules/copas/issues/223
549+ local function is_zero_byte_tcp_read (client , pattern )
550+ return pattern == 0 and isTCP (client )
551+ end
552+
553+
545554function copas .close (skt , ...)
546555 _closed [# _closed + 1 ] = skt
547556 return skt :close (... )
609618-- untrusted/remote input without an application-enforced size limit; use a
610619-- numeric (sized) pattern or receivepartial with your own cumulative cap instead.
611620function copas .receive (client , pattern , part )
621+ if is_zero_byte_tcp_read (client , pattern ) then
622+ copas .pause () -- yield so a tight receive(0) loop can't starve other coroutines
623+ return part or " " , nil , nil
624+ end
625+
612626 local s , err
613627 pattern = pattern or " *l"
614628 local current_log = _reading_log
704718-- same as above but with special treatment when reading chunks,
705719-- unblocks on any data received.
706720function copas .receivepartial (client , pattern , part )
721+ if is_zero_byte_tcp_read (client , pattern ) then
722+ copas .pause () -- yield so a tight receive(0) loop can't starve other coroutines
723+ return part or " " , nil , nil
724+ end
725+
707726 local s , err
708727 pattern = pattern or " *l"
709728 local orig_size = # (part or " " )
0 commit comments