module unix import time import os import net const ( unix_default_read_timeout = 30 * time.second unix_default_write_timeout = 30 * time.second connect_timeout = 5 * time.second msg_nosignal = 0x4000 ) pub struct StreamSocket { pub: handle int mut: path string } pub struct StreamConn { pub mut: sock StreamSocket mut: write_deadline time.Time read_deadline time.Time read_timeout time.Duration write_timeout time.Duration } pub struct StreamListener { pub mut: sock StreamSocket mut: accept_timeout time.Duration accept_deadline time.Time } fn error_code() int { return C.errno } fn new_stream_socket() !StreamSocket { sockfd := net.socket_error(C.socket(net.AddrFamily.unix, net.SocketType.tcp, 0))! mut s := StreamSocket{ handle: sockfd } return s } fn (mut s StreamSocket) close() ! { shutdown(s.handle) return close(s.handle) } fn (mut s StreamSocket) @select(test Select, timeout time.Duration) !bool { return @select(s.handle, test, timeout) } fn (mut s StreamSocket) connect(a string) ! { if a.len >= max_sun_path { return error('Socket path too long! Max length: ${max_sun_path - 1} chars.') } mut addr := C.sockaddr_un{} unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_un)) } addr.sun_family = u8(C.AF_UNIX) unsafe { C.strncpy(&addr.sun_path[0], &char(a.str), max_sun_path) } size := C.SUN_LEN(&addr) res := C.connect(s.handle, voidptr(&addr), size) // if res != 1 { // return none //} if res == 0 { return } _ := error_code() write_result := s.@select(.write, unix.connect_timeout)! if write_result { // succeeded return } except_result := s.@select(.except, unix.connect_timeout)! if except_result { return net.err_connect_failed } // otherwise we timed out return net.err_connect_timed_out } pub fn listen_stream(sock string) !&StreamListener { if sock.len >= max_sun_path { return error('Socket path too long! Max length: ${max_sun_path - 1} chars.') } mut s := new_stream_socket()! s.path = sock mut addr := C.sockaddr_un{} unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_un)) } addr.sun_family = u8(C.AF_UNIX) unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) } size := C.SUN_LEN(&addr) if os.exists(sock) { os.rm(sock)! } net.socket_error(C.bind(s.handle, voidptr(&addr), size))! os.chmod(sock, 0o777)! net.socket_error(C.listen(s.handle, 128))! return &StreamListener{ sock: s } } pub fn connect_stream(path string) !&StreamConn { mut s := new_stream_socket()! s.connect(path)! return &StreamConn{ sock: s read_timeout: unix.unix_default_read_timeout write_timeout: unix.unix_default_write_timeout } } pub fn (mut l StreamListener) accept() !&StreamConn { mut new_handle := C.accept(l.sock.handle, 0, 0) if new_handle <= 0 { l.wait_for_accept()! new_handle = C.accept(l.sock.handle, 0, 0) if new_handle == -1 || new_handle == 0 { return error('accept failed') } } new_sock := StreamSocket{ handle: new_handle } return &StreamConn{ sock: new_sock read_timeout: unix.unix_default_read_timeout write_timeout: unix.unix_default_write_timeout } } pub fn (c &StreamListener) accept_deadline() !time.Time { if c.accept_deadline.unix != 0 { return c.accept_deadline } return error('no deadline') } pub fn (mut c StreamListener) set_accept_deadline(deadline time.Time) { c.accept_deadline = deadline } pub fn (c &StreamListener) accept_timeout() time.Duration { return c.accept_timeout } pub fn (mut c StreamListener) set_accept_timeout(t time.Duration) { c.accept_timeout = t } pub fn (mut c StreamListener) wait_for_accept() ! { return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout) } pub fn (mut c StreamListener) close() ! { os.rm(c.sock.path)! c.sock.close()! } pub fn (mut c StreamConn) close() ! { c.sock.close()! } // write_ptr blocks and attempts to write all data pub fn (mut c StreamConn) write_ptr(b &u8, len int) !int { $if trace_unix ? { eprintln( '>>> StreamConn.write_ptr | c.sock.handle: ${c.sock.handle} | b: ${ptr_str(b)} len: ${len} |\n' + unsafe { b.vstring_with_len(len) }) } unsafe { mut ptr_base := &u8(b) mut total_sent := 0 for total_sent < len { ptr := ptr_base + total_sent remaining := len - total_sent mut sent := C.send(c.sock.handle, ptr, remaining, unix.msg_nosignal) if sent < 0 { code := error_code() if code == int(error_ewouldblock) { c.wait_for_write()! continue } else { net.wrap_error(code)! } } total_sent += sent } return total_sent } } // write blocks and attempts to write all data pub fn (mut c StreamConn) write(bytes []u8) !int { return c.write_ptr(bytes.data, bytes.len) } // write_string blocks and attempts to write all data pub fn (mut c StreamConn) write_string(s string) !int { return c.write_ptr(s.str, s.len) } pub fn (mut c StreamConn) read_ptr(buf_ptr &u8, len int) !int { mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))! $if trace_unix ? { eprintln('<<< StreamConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}') } if res > 0 { return res } code := error_code() if code == int(error_ewouldblock) { c.wait_for_read()! res = wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))! $if trace_unix ? { eprintln('<<< StreamConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}') } return net.socket_error(res) } else { net.wrap_error(code)! } return net.socket_error(code) } pub fn (mut c StreamConn) read(mut buf []u8) !int { return c.read_ptr(buf.data, buf.len) } pub fn (mut c StreamConn) read_deadline() !time.Time { if c.read_deadline.unix == 0 { return c.read_deadline } return error('none') } pub fn (mut c StreamConn) set_read_deadline(deadline time.Time) { c.read_deadline = deadline } pub fn (mut c StreamConn) write_deadline() !time.Time { if c.write_deadline.unix == 0 { return c.write_deadline } return error('none') } pub fn (mut c StreamConn) set_write_deadline(deadline time.Time) { c.write_deadline = deadline } pub fn (c &StreamConn) read_timeout() time.Duration { return c.read_timeout } pub fn (mut c StreamConn) set_read_timeout(t time.Duration) { c.read_timeout = t } pub fn (c &StreamConn) write_timeout() time.Duration { return c.write_timeout } pub fn (mut c StreamConn) set_write_timeout(t time.Duration) { c.write_timeout = t } [inline] pub fn (mut c StreamConn) wait_for_read() ! { return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) } [inline] pub fn (mut c StreamConn) wait_for_write() ! { return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) } pub fn (c StreamConn) str() string { s := c.sock.str().replace('\n', ' ').replace(' ', ' ') return 'StreamConn{ write_deadline: ${c.write_deadline}, read_deadline: ${c.read_deadline}, read_timeout: ${c.read_timeout}, write_timeout: ${c.write_timeout}, sock: ${s} }' }