hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 0473b9b0ecfca63f7b1ed4d8e1956a97a8f17ea3
parent 412ec3a523f27d46fe8c37473d3f73c13fadaad9
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 31 Mar 2021 11:28:45 -0400

net: move platform-specific code to +linux

Diffstat:
Anet/+linux.ha | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnet/socket.ha | 139-------------------------------------------------------------------------------
Mscripts/gen-stdlib | 1+
Mstdlib.mk | 2++
4 files changed, 141 insertions(+), 139 deletions(-)

diff --git a/net/+linux.ha b/net/+linux.ha @@ -0,0 +1,138 @@ +use io; +use net::ip; +use os; +use rt; +use strings; + +fn io_errstr(data: *void) str = { + const errno = data: uintptr: int: rt::errno; + return rt::errstr(errno); +}; + +fn errno_to_io(err: rt::errno) io::error = { + let err = io::os_error { + string = &io_errstr, + data = err: uintptr: *void, + }; + return err: io::error; +}; + +fn setsockopt(sockfd: int, option: int, value: bool) (void | rt::errno) = { + let val: int = if (value) 1 else 0; + rt::setsockopt(sockfd, rt::SOL_SOCKET, option, + &val: *void, size(int): u32)?; +}; + +fn setfcntl(sockfd: int, flag: int) (void | rt::errno) = { + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)?; + rt::fcntl(sockfd, rt::F_SETFL, flags | flag); // ? always detects io::error + return; +}; + +fn wrap(ie: (int | rt::errno)) (int | io::error) = { + match (ie) { + i: int => i, + er: rt::errno => errno_to_io(er) + }; +}; + +fn mksockfd(addr: ip::addr) (int | io::error) = { + const af = match (addr) { + ip::addr4 => rt::AF_INET: int, + ip::addr6 => rt::AF_INET6: int, + }; + return wrap(rt::socket(af, rt::SOCK_STREAM, 0))?; +}; + +// Binds a TCP listener to the given address. +export fn listen( + addr: ip::addr, + port: u16, + options: listen_option... +) (listener | io::error) = { + const sockfd = mksockfd(addr)?; + + let bk: u32 = 10; + let portout: nullable *u16 = null; + + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + reuseaddr => setsockopt(sockfd, rt::SO_REUSEADDR, true), + reuseport => setsockopt(sockfd, rt::SO_REUSEPORT, true), + keepalive => setsockopt(sockfd, rt::SO_KEEPALIVE, true), + b: backlog => bk = b, + p: portassignment => portout = p, + }; + }; + setfcntl(sockfd, rt::O_CLOEXEC); + + let sockaddr = ip::to_native(addr, port); + const asize = match (addr) { + v4: ip::addr4 => size(rt::sockaddr_in): u32, + v6: ip::addr6 => size(rt::sockaddr_in6): u32, + }; + wrap(rt::bind(sockfd, &sockaddr, asize))?; + wrap(rt::listen(sockfd, bk))?; + + match (portout) { + p: *u16 => { + let sn = rt::sockaddr {...}; + let al = asize; + wrap(rt::getsockname(sockfd, &sn, &al))?; + const addr = ip::from_native(sn); + *p = addr.1; + }, + null => void, + }; + return sockfd; +}; + +// Returns the remote address for a given connection, or void if none is +// available. +export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = { + let fd = match (os::streamfd(stream)) { + fd: int => fd, + void => return, + }; + let sn = rt::sockaddr {...}; + let sz = size(rt::sockaddr): u32; + if (rt::getpeername(fd, &sn, &sz) is rt::errno) { + return; + }; + return ip::from_native(sn); +}; + +// Accepts the next connection from a listener, returning an [io::stream]. +// Blocks until a new connection is available. +export fn accept(l: listener) (*io::stream | io::error) = { + let sn = rt::sockaddr {...}; + const sz = size(rt::sockaddr): u32; + const fd = wrap(rt::accept(l, &sn, &sz))?; + const addr = ip::from_native(sn).0; + return os::fdopen(fd, ip::string(addr), + io::mode::READ | io::mode::WRITE); +}; + +// Opens a TCP connection to the given host and port. Blocks until the +// connection is established. +export fn connect( + addr: ip::addr, + port: u16, + options: connect_option... +) (*io::stream | io::error) = { + const sockfd = mksockfd(addr)?; + for (let i = 0z; i < len(options); i += 1) { + // The only option is keepalive right now + setsockopt(sockfd, rt::SO_KEEPALIVE, true); + }; + + const sockaddr = ip::to_native(addr, port); + const asize = match (addr) { + v4: ip::addr4 => size(rt::sockaddr_in): u32, + v6: ip::addr6 => size(rt::sockaddr_in6): u32, + }; + wrap(rt::connect(sockfd, &sockaddr, asize))?; + + return os::fdopen(sockfd, ip::string(addr), + io::mode::READ | io::mode::WRITE); +}; diff --git a/net/socket.ha b/net/socket.ha @@ -1,9 +1,3 @@ -use io; -use rt; -use os; -use strings; -use net::ip; - // Enables keep-alive for a socket. export type keepalive = void; @@ -37,136 +31,3 @@ export type listen_option = ( // Options for [connect]. export type connect_option = keepalive; -fn setsockopt(sockfd: int, option: int, value: bool) (void | rt::errno) = { - let val: int = if (value) 1 else 0; - rt::setsockopt(sockfd, rt::SOL_SOCKET, option, - &val: *void, size(int): u32)?; -}; - -fn setfcntl(sockfd: int, flag: int) (void | rt::errno) = { - let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)?; - rt::fcntl(sockfd, rt::F_SETFL, flags | flag); // ? always detects io::error - return; -}; - -fn wrap(ie: (int | rt::errno)) (int | io::error) = { - match (ie) { - i: int => i, - er: rt::errno => errno_to_io(er) - }; -}; - -fn mksockfd(addr: ip::addr) (int | io::error) = { - const af = match (addr) { - ip::addr4 => rt::AF_INET: int, - ip::addr6 => rt::AF_INET6: int, - }; - return wrap(rt::socket(af, rt::SOCK_STREAM, 0))?; -}; - -// Binds a TCP listener to the given address. -export fn listen( - addr: ip::addr, - port: u16, - options: listen_option... -) (listener | io::error) = { - const sockfd = mksockfd(addr)?; - - let bk: u32 = 10; - let portout: nullable *u16 = null; - - for (let i = 0z; i < len(options); i += 1) { - match (options[i]) { - reuseaddr => setsockopt(sockfd, rt::SO_REUSEADDR, true), - reuseport => setsockopt(sockfd, rt::SO_REUSEPORT, true), - keepalive => setsockopt(sockfd, rt::SO_KEEPALIVE, true), - b: backlog => bk = b, - p: portassignment => portout = p, - }; - }; - setfcntl(sockfd, rt::O_CLOEXEC); - - let sockaddr = ip::to_native(addr, port); - const asize = match (addr) { - v4: ip::addr4 => size(rt::sockaddr_in): u32, - v6: ip::addr6 => size(rt::sockaddr_in6): u32, - }; - wrap(rt::bind(sockfd, &sockaddr, asize))?; - wrap(rt::listen(sockfd, bk))?; - - match (portout) { - p: *u16 => { - let sn = rt::sockaddr {...}; - let al = asize; - wrap(rt::getsockname(sockfd, &sn, &al))?; - const addr = ip::from_native(sn); - *p = addr.1; - }, - null => void, - }; - return sockfd; -}; - - -// Returns the remote address for a given connection, or void if none is -// available. -export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = { - let fd = match (os::streamfd(stream)) { - fd: int => fd, - void => return, - }; - let sn = rt::sockaddr {...}; - let sz = size(rt::sockaddr): u32; - if (rt::getpeername(fd, &sn, &sz) is rt::errno) { - return; - }; - return ip::from_native(sn); -}; - -// Accepts the next connection from a listener, returning an [io::stream]. -// Blocks until a new connection is available. -export fn accept(l: listener) (*io::stream | io::error) = { - let sn = rt::sockaddr {...}; - const sz = size(rt::sockaddr): u32; - const fd = wrap(rt::accept(l, &sn, &sz))?; - const addr = ip::from_native(sn).0; - return os::fdopen(fd, ip::string(addr), - io::mode::READ | io::mode::WRITE); -}; - -// Opens a TCP connection to the given host and port. Blocks until the -// connection is established. -export fn connect( - addr: ip::addr, - port: u16, - options: connect_option... -) (*io::stream | io::error) = { - const sockfd = mksockfd(addr)?; - for (let i = 0z; i < len(options); i += 1) { - // The only option is keepalive right now - setsockopt(sockfd, rt::SO_KEEPALIVE, true); - }; - - const sockaddr = ip::to_native(addr, port); - const asize = match (addr) { - v4: ip::addr4 => size(rt::sockaddr_in): u32, - v6: ip::addr6 => size(rt::sockaddr_in6): u32, - }; - wrap(rt::connect(sockfd, &sockaddr, asize))?; - - return os::fdopen(sockfd, ip::string(addr), - io::mode::READ | io::mode::WRITE); -}; - -fn io_errstr(data: *void) str = { - const errno = data: uintptr: int: rt::errno; - return rt::errstr(errno); -}; - -fn errno_to_io(err: rt::errno) io::error = { - let err = io::os_error { - string = &io_errstr, - data = err: uintptr: *void, - }; - return err: io::error; -}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -376,6 +376,7 @@ linux_vdso() { net() { printf '# net\n' gen_srcs net \ + '$(PLATFORM).ha' \ socket.ha gen_ssa net io os strings net::ip } diff --git a/stdlib.mk b/stdlib.mk @@ -521,6 +521,7 @@ $(HARECACHE)/linux/vdso/linux_vdso.ssa: $(stdlib_linux_vdso_srcs) $(stdlib_rt) $ # net # net stdlib_net_srcs= \ + $(STDLIB)/net/$(PLATFORM).ha \ $(STDLIB)/net/socket.ha $(HARECACHE)/net/net.ssa: $(stdlib_net_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_os) $(stdlib_strings) $(stdlib_net_ip) @@ -1260,6 +1261,7 @@ $(TESTCACHE)/linux/vdso/linux_vdso.ssa: $(testlib_linux_vdso_srcs) $(testlib_rt) # net # net testlib_net_srcs= \ + $(STDLIB)/net/$(PLATFORM).ha \ $(STDLIB)/net/socket.ha $(TESTCACHE)/net/net.ssa: $(testlib_net_srcs) $(testlib_rt) $(testlib_io) $(testlib_os) $(testlib_strings) $(testlib_net_ip)