~aleteoryx/nex.nelua

b12592a5d5e562781216e7e3e33d0baca47042f1 — Aleteoryx 16 days ago e47d43e
configurable connection cap
4 files changed, 11 insertions(+), 9 deletions(-)

M README.md
M nex.nelua
M nps.nelua
M socket.nelua
M README.md => README.md +4 -2
@@ 33,19 33,21 @@ protocols, so long as they are not reliant on multiple sockets.

it exposes the following methods from its return value:

### `socket.listen_tcp(addr: string, handler: function(): void)`
### `socket.listen_tcp(addr: string, conn_cap: uinteger, handler: function(): void)`

listens on the host/port in `addr`. panics if it can't connect or if
`addr` is malformed. `addr` is either of the format `IPV4:PORT` or
`{IPV6}:PORT`. passes the connection to `socket.listen_sock`.

### `socket.listen_sock(fd: cint, handler: function(): void)`
### `socket.listen_sock(fd: cint, conn_cap: uinteger, handler: function(): void)`

`fd` must be a valid socket, bound to some address and ready to
`accept` incoming connections. as connections are made, `handler` will
be executed in the context of a coroutine. it is expected to call one
of the provided [yielding functions](#yielding-functions) eventually.

will begin refusing incoming requests at `conn_cap` open connections

if there is any error reading from or writing to the socket, the
handler coroutine is deleted and the connection is dropped.


M nex.nelua => nex.nelua +1 -1
@@ 8,7 8,7 @@ if #arg ~= 1 then
  print("  `{<ipv6 addr>}:<port>'")
end

socket.listen_tcp(arg[1], function()
socket.listen_tcp(arg[1], 1024, function()
  local line = socket.recv_line():gsub("\r", "")

  if line:subview(1, 1) == "/" then line = line:subview(2) end

M nps.nelua => nps.nelua +1 -1
@@ 10,7 10,7 @@ if #arg ~= 1 then
  print("  `{<ipv6 addr>}:<port>'")
end

socket.listen_tcp(arg[1], function()
socket.listen_tcp(arg[1], 1024, function()
  local target = socket.recv_line():gsub("\r", "")

  local lines: vector(string)

M socket.nelua => socket.nelua +5 -5
@@ 192,7 192,7 @@ function handler_state:step(epfd: cint, key: uint32): (boolean)
  return true
end

function export.listen_sock(sock: cint, handler: function(): void): void
function export.listen_sock(sock: cint, conn_cap: uinteger, handler: function(): void): void
  die_errno(c_listen(sock, 32) ~= 0, "couldn't listen on TCP socket")
  local epfd = c_epoll_create(0)
  die_errno(epfd == -1, "couldn't create epoll instance")


@@ 214,7 214,7 @@ function export.listen_sock(sock: cint, handler: function(): void): void
    for i = 0, < event_count do
      if events[i].data.u32 == 0 then
        local fd = c_accept(sock, nilptr, nilptr)
        if #handlers >= 1024 then -- drop connections if allocation fails
        if #handlers >= conn_cap then -- drop connections if allocation fails
          local errmsg = "The server is overloaded, please try again later.\n"
          c_send(fd, errmsg.data, errmsg.size, 0)
          c_close(fd)


@@ 254,7 254,7 @@ end
local inet_re = '^(%d?%d?%d%.%d?%d?%d%.%d?%d?%d%.%d?%d?%d):(%d?%d?%d?%d?%d)$'
-- {::1}:2000 -> ::1,2000
local inet6_re = '^{([^}]+)}:(%d?%d?%d?%d?%d)$'
function export.listen_tcp(address: string, handler: function(): void): void
function export.listen_tcp(address: string, conn_cap: uinteger, handler: function(): void): void
  local matched, matches
  if (do matched, matches = address:match(inet_re); in matched end) then
    local c_err: cint = 0


@@ 283,7 283,7 @@ function export.listen_tcp(address: string, handler: function(): void): void

    defer c_close(fd) end

    export.listen_sock(fd, handler)
    export.listen_sock(fd, conn_cap, handler)
  elseif (do matched, matches = address:match(inet6_re); in matched end) then
    local c_err: cint = 0



@@ 311,7 311,7 @@ function export.listen_tcp(address: string, handler: function(): void): void

    defer c_close(fd) end

    export.listen_sock(fd, handler)
    export.listen_sock(fd, conn_cap, handler)
  end
end