Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tcp:receive(0) blocks #427

Open
Tieske opened this issue Mar 14, 2024 · 2 comments
Open

tcp:receive(0) blocks #427

Tieske opened this issue Mar 14, 2024 · 2 comments
Labels

Comments

@Tieske
Copy link
Member

Tieske commented Mar 14, 2024

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:

  • server accepts a connection and then:
    1. sends 10 bytes
    2. pauses for 5 seconds
    3. sends another 10 bytes
  • client set up the connection and then:
    1. reads 10 bytes
    2. reads 0 bytes
    3. reads 10 bytes
    4. it runs the above again, but now with a LuaSocket socket instead of a OpenResty one

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

  • the first 10 bytes were read in appr. 0 seconds
  • the 0 bytes were read in appr. 5 seconds (exactly the wait time the server introduced between sending the bytes)
  • the second 10 bytes were read again in appr. 0 seconds.
@Tieske
Copy link
Member Author

Tieske commented Mar 15, 2024

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
Copy link
Member Author

Tieske commented Mar 21, 2024

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

  • if we do not know socket state:
    • if "pattern == 0" then return "", nil, nil, 0
  • if we do know the socket state and it wasn't closed:
    • if "pattern == 0" then return "", nil, nil, 0
  • if we do know the socket state and it was closed:
    • if "pattern == 0" then return nil, "closed"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants