From 2716ae08bc3884cc8a26b95b6f858b1dcddb7c99 Mon Sep 17 00:00:00 2001 From: Aleteoryx Date: Sun, 1 Sep 2024 22:57:12 -0400 Subject: [PATCH] initial commit, with a lot done already --- LICENSE | 18 +++++ README.md | 4 ++ socket.nelua | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 socket.nelua diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1b599cf --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +-- TPL Public Domain Dedication v1.0 +-- +------------------------------------------------------------------------ + +This repository is dedicated entirely to the public domain. The +creator(s) waive all intellectual property rights to the work as much +as is possible in any relevant jurisdictions. + +In other words, do what you want. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ceb4032 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# nex.nelua + +nightfall express and nightfall postal service servers written in the +[nelua](https://nelua.io) programming language diff --git a/socket.nelua b/socket.nelua new file mode 100644 index 0000000..cb9e67d --- /dev/null +++ b/socket.nelua @@ -0,0 +1,190 @@ +--[[ + an asynchronous socket library. + if i feel like making a full executor work this might get more complicated, + but for now it's a standalone function. +]] + +-- c imports +## cinclude '' +local AF_INET: cint +local AF_INET6: cint +-- local AF_UNIX: cint + +local SOCK_STREAM: cint +-- local SOCK_DGRAM: cint +-- local SOCK_RAW: cint + +local MSG_DONTWAIT: cint + +local function c_socket(domain: cint, type: cint, protocol: cint): cint end +local function c_accept(fd: cint, address: pointer, address_len: *cint): cint end +local function c_listen(fd: cint, backlog: cint): cint end + +local function c_send(fd: cint, buf: *[0]byte, len: usize, flags: cint): isize end + +## cinclude '' +local function c_inet_pton(af: cint, src: cstring , dst: pointer): cint end +local function c_htons(hostshort: uint16): uint16 end +local function c_ntohs(hostshort: uint16): uint16 end + + +## cinclude '' +local EPOLL_CLOEXEC: cint + +local EPOLL_CTL_ADD: cint +local EPOLL_CTL_MOD: cint +local EPOLL_CTL_DEL: cint + +## for i,epoll_val in ipairs({ 'IN', 'OUT', 'RDHUP', 'PRI', 'ERR', 'HUP', 'ET'}) do +local #|'EPOLL' .. epoll_val|# : uint32 +## end + +local epoll_data = @union{ + ptr: pointer, + fd: cint, + u32: uint32, + u64: uint64 +} +local epoll_event = @record{ + events: uint32, + data: epoll_data +} + +local function c_epoll_create(flags: cint): cint end +local function c_epoll_ctl(epfd: cint, op: cint, fd: cint, event: *epoll_event): cint end +local function c_epoll_wait(epfd: cint, events: *[0]epoll_event, maxevents: cint, timeout: cint): cint end + + +## cinclude '' +local function c_close(fd: cint): cint end + + +## cinclude '' + + +-- end c imports + +require 'string' +require 'coroutine' +require 'math' +require 'C.stdio' +require 'allocators.pool' + + +local function fakeuse(...: varargs) end + +-- 192.168.2.1:2000 -> 192.168.2.1,2000 +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)$' + +local function die_errno(cond: boolean, msg: string): void + if cond then + C.perror(nilptr) + error(msg) + end +end + +local handler_req = @enum{ + init = 0, + read_line, + write, + close +} + +local handler_state = @record{ + fd: cint, + co: coroutine, + last_req: handler_req +} + +function handler_state:step(epfd: cint, state: *handler_state): (boolean) + return false +end + +local function listen_sock(sock: cint, 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") + + -- opt out of GC because we give references to the kernel + local alloc: PoolAllocator(@handler_state, 1024) -- probably overkill on max size + + local sock_event: epoll_event = { + events = EPOLLIN, + data = { ptr = nilptr } + } + + c_epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &sock_event) + + while true do + local maxevents = 8 + local events: [maxevents]epoll_event = {} + local event_count = c_epoll_wait(epfd, &events, maxevents, -1) + die_errno(event_count == -1, "couldn't wait on epoll instance") + print("got events from epoll_wait:", event_count) + for i = 0, < event_count do + if events[i].data.ptr == nilptr then + local fd = c_accept(sock, nilptr, nilptr) + local state = alloc:new(@handler_state) + if state == nilptr 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) + continue + end + state.fd = fd + state.co = coroutine.create(handler) + state.last_req = handler_req.init + local ok, events = state:step() + if ok then + local fd_event: epoll_event = { + events = events, + data = { ptr = (@pointer)(state) } + } + c_epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &fd_event) + else + local errmsg = "Internal error, please try again later.\n" + c_send(fd, errmsg.data, errmsg.size, 0) + c_close(fd) + alloc:delete() + end + else + print("TODO") + end + end + end +end + +local function listen_tcp(address: string, handler: function(): void): void + local matched, matches = string.match(address, inet_re) + if matched then + local c_err: cint = 0 + + local s_addr, s_port = matches:unpack(1, 2) + local addr: uint32 + die_errno(c_inet_pton(AF_INET, (@cstring)(s_addr), &addr) <= 0, + "bad IPv4 address") + local i_port = tointeger(s_port) + assert((i_port >= 0) and (i_port < 65535), "port not within range [0,65536)") + local port: uint16 = c_htons(i_port) + + local fd = c_socket(AF_INET, SOCK_STREAM, 0) + die_errno(fd == -1, "couldn't open TCP socket") + + fakeuse(port) + ##[==[ cemit [[ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = port; + sa.sin_addr.s_addr = addr; + c_err = bind(fd, &sa, sizeof(sa)); + ]] ]==] + die_errno(c_err == -1, "couldn't bind TCP socket") + + listen_sock(fd, handler) + end +end + +listen_tcp("127.0.0.1:1900", function() end) -- 2.43.4