lunarmodules / luasocket

Network support for the Lua language
http://lunarmodules.github.io/luasocket/
MIT License
1.86k stars 633 forks source link

tcp:receive(0) blocks #427

Open Tieske opened 8 months ago

Tieske commented 8 months ago

When reading from a socket, 0 bytes, it blocks until data becomes available on the socket.

I ran into this due to a compatibility issue between OpenResty and LuaSocket. The http client in use simply reads the body based on the Content-Length header, even if that is 0. This returned us timeout errors when LuaSocket was in use, but not with the OpenResty sockets.

To reproduce (using Copas for the server):

#!/usr/bin/env copas

-- Save this file as "server.lua"

local port = 20000
local address = "127.0.0.1"

local socket = require("socket")
local server_sock = assert(socket.bind(address, port))

local function handle_client(client_sock)

  local function print(...)
    _G.print(copas.getthreadname(), ...)
  end

  print"received connection, sending 10 bytes..."
  print("send results: ", client_sock:send("1234567890"))
  print"sent 10 bytes, now pausing for 5 seconds..."
  copas.pause(5)
  print("send 10 more results: ", client_sock:send("1234567890"))
  print"closing"
  client_sock:close()
end

copas.addserver(server_sock, copas.handler(handle_client), "my_TCP_server")
print("server started on port "..port)

Using openresty for the Client:

#!/usr/bin/env resty
setmetatable(_G, nil) -- disable global warnings

print "Resty sockets"
local cl1 = ngx.socket.tcp()
cl1:settimeout(30000)
print("cl1: connecting: ", cl1:connect("127.0.0.1", 20000))
print("cl1: read 10 bytes: ", cl1:receive(10))
print("cl1: read 0 bytes: ", cl1:receive(0))
print("cl1: read 10 bytes: ", cl1:receive(10))
cl1:close()

ngx.sleep(10)

print "Lua sockets"
local cl2 = require("socket").tcp()
cl1:settimeout(30)
print("cl2: connecting: ", cl2:connect("127.0.0.1", 20000))
print("cl2: read 10 bytes: ", cl2:receive(10))
print("cl2: read 0 bytes: ", cl2:receive(0))
print("cl2: read 10 bytes: ", cl2:receive(10))
cl1:close()

What the code does:

Here's the client output:

$ ./client.lua
Resty sockets
cl1: connecting: 1
cl1: read 10 bytes: 1234567890
cl1: read 0 bytes: 
cl1: read 10 bytes: 1234567890
Lua sockets
cl2: connecting: 1
cl2: read 10 bytes: 1234567890nilnil0.0002138614654541
cl2: read 0 bytes: nilnil5.0009708404541
cl2: read 10 bytes: 1234567890nilnil9.5367431640625e-07
$

Note that LuaSocket also returns the time it took (OpenResty doesn't).

Tieske commented 8 months ago

There seems to be a bit more to it: https://stackoverflow.com/questions/67727200/is-reading-zero-bytes-from-a-socket-a-valid-way-for-monitoring-a-tcp-ip-discon, so now I have doubts whether this actually is a bug, or as designed.

Tieske commented 8 months ago

I think the proper thing to do depends on whether we have info on the state of the socket.