diff options
author | Indrajith K L | 2022-12-03 17:00:20 +0530 |
---|---|---|
committer | Indrajith K L | 2022-12-03 17:00:20 +0530 |
commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/vlib/net/unix | |
download | cli-tools-windows-master.tar.gz cli-tools-windows-master.tar.bz2 cli-tools-windows-master.zip |
Diffstat (limited to 'v_windows/v/old/vlib/net/unix')
-rw-r--r-- | v_windows/v/old/vlib/net/unix/aasocket.c.v | 104 | ||||
-rw-r--r-- | v_windows/v/old/vlib/net/unix/common.v | 128 | ||||
-rw-r--r-- | v_windows/v/old/vlib/net/unix/stream_nix.v | 284 | ||||
-rw-r--r-- | v_windows/v/old/vlib/net/unix/unix_test.v | 57 |
4 files changed, 573 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/net/unix/aasocket.c.v b/v_windows/v/old/vlib/net/unix/aasocket.c.v new file mode 100644 index 0000000..7f762a5 --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/aasocket.c.v @@ -0,0 +1,104 @@ +module unix + +#include <sys/un.h> + +// Select represents a select operation +enum Select { + read + write + except +} + +// SocketType are the available sockets +// enum SocketType { +// dgram = C.SOCK_DGRAM +// stream = C.SOCK_STREAM +// seqpacket = C.SOCK_SEQPACKET +// } + +struct C.sockaddr { + sa_family u16 +} + +const max_sun_path = 104 + +// 104 for macos, 108 for linux => use the minimum + +struct C.sockaddr_un { +mut: + // sun_len byte // only on macos + sun_family int + sun_path [104]char // on linux that is 108 +} + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_storage { +} + +// fn C.socket() int + +// fn C.setsockopt() int + +// fn C.htonl() int + +// fn C.htons() int + +// fn C.bind() int + +// fn C.listen() int + +// fn C.accept() int + +// fn C.getaddrinfo() int + +// fn C.connect() int + +// fn C.send() int + +// fn C.sendto() int + +// fn C.recv() int + +// fn C.recvfrom() int + +// fn C.shutdown() int + +// fn C.ntohs() int + +// fn C.getpeername() int + +// fn C.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr + +fn C.WSAAddressToStringA() int + +// fn C.getsockname() int + +// defined in builtin +// fn C.read() int +// fn C.close() int + +fn C.ioctlsocket() int + +// fn C.fcntl() int + +// fn C.@select() int + +// fn C.FD_ZERO() + +// fn C.FD_SET() + +// fn C.FD_ISSET() bool + +[typedef] +struct C.fd_set {} diff --git a/v_windows/v/old/vlib/net/unix/common.v b/v_windows/v/old/vlib/net/unix/common.v new file mode 100644 index 0000000..75e591f --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/common.v @@ -0,0 +1,128 @@ +module unix + +import time +import net + +const ( + error_ewouldblock = C.EWOULDBLOCK +) + +fn C.SUN_LEN(ptr &C.sockaddr_un) int + +fn C.strncpy(&char, &char, int) + +// Shutdown shutsdown a socket and closes it +fn shutdown(handle int) ? { + $if windows { + C.shutdown(handle, C.SD_BOTH) + net.socket_error(C.closesocket(handle)) ? + } $else { + C.shutdown(handle, C.SHUT_RDWR) + net.socket_error(C.close(handle)) ? + } +} + +// Select waits for an io operation (specified by parameter `test`) to be available +fn @select(handle int, test Select, timeout time.Duration) ?bool { + set := C.fd_set{} + + C.FD_ZERO(&set) + C.FD_SET(handle, &set) + + seconds := timeout / time.second + microseconds := time.Duration(timeout - (seconds * time.second)).microseconds() + + mut tt := C.timeval{ + tv_sec: u64(seconds) + tv_usec: u64(microseconds) + } + + mut timeval_timeout := &tt + + // infinite timeout is signaled by passing null as the timeout to + // select + if timeout == unix.infinite_timeout { + timeval_timeout = &C.timeval(0) + } + + match test { + .read { + net.socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ? + } + .write { + net.socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ? + } + .except { + net.socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ? + } + } + + return C.FD_ISSET(handle, &set) +} + +// wait_for_common wraps the common wait code +fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? { + if deadline.unix == 0 { + // do not accept negative timeout + if timeout < 0 { + return net.err_timed_out + } + ready := @select(handle, test, timeout) ? + if ready { + return + } + return net.err_timed_out + } + // Convert the deadline into a timeout + // and use that + d_timeout := deadline.unix - time.now().unix + if d_timeout < 0 { + // deadline is in the past so this has already + // timed out + return net.err_timed_out + } + + ready := @select(handle, test, d_timeout) ? + if ready { + return + } + return net.err_timed_out +} + +// wait_for_write waits for a write io operation to be available +fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .write) +} + +// wait_for_read waits for a read io operation to be available +fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .read) +} + +// no_deadline should be given to functions when no deadline is wanted (i.e. all functions +// return instantly) +const ( + no_deadline = time.Time{ + unix: 0 + } +) + +// no_timeout should be given to functions when no timeout is wanted (i.e. all functions +// return instantly) +const ( + no_timeout = time.Duration(0) +) + +// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions +// only ever return with data) +const ( + infinite_timeout = time.infinite +) + +[inline] +fn wrap_read_result(result int) ?int { + if result != 0 { + return result + } + return none +} diff --git a/v_windows/v/old/vlib/net/unix/stream_nix.v b/v_windows/v/old/vlib/net/unix/stream_nix.v new file mode 100644 index 0000000..d13c811 --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/stream_nix.v @@ -0,0 +1,284 @@ +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 +) + +struct StreamSocket { +pub: + handle int +mut: + path string +} + +struct StreamConn { +pub mut: + sock StreamSocket +mut: + write_deadline time.Time + read_deadline time.Time + read_timeout time.Duration + write_timeout time.Duration +} + +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() ? { + os.rm(s.path) ? + return shutdown(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 = 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 = C.AF_UNIX + unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) } + size := C.SUN_LEN(&addr) + net.socket_error(C.bind(s.handle, voidptr(&addr), size)) ? + 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() ? { + 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 &byte, 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 := &byte(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 []byte) ?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 &byte, 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 []byte) ?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 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 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 }' +} diff --git a/v_windows/v/old/vlib/net/unix/unix_test.v b/v_windows/v/old/vlib/net/unix/unix_test.v new file mode 100644 index 0000000..007d62d --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/unix_test.v @@ -0,0 +1,57 @@ +import os +import net.unix + +const test_port = os.join_path(os.temp_dir(), 'unix_domain_socket') + +fn testsuite_begin() { + os.rm(test_port) or {} +} + +fn testsuite_end() { + os.rm(test_port) or {} +} + +fn handle_conn(mut c unix.StreamConn) { + for { + mut buf := []byte{len: 100, init: 0} + read := c.read(mut buf) or { + println('Server: connection dropped') + return + } + c.write(buf[..read]) or { + println('Server: connection dropped') + return + } + } +} + +fn echo_server(mut l unix.StreamListener) ? { + for { + mut new_conn := l.accept() or { continue } + go handle_conn(mut new_conn) + } +} + +fn echo() ? { + mut c := unix.connect_stream(test_port) ? + defer { + c.close() or {} + } + data := 'Hello from vlib/net!' + c.write_string(data) ? + mut buf := []byte{len: 4096} + read := c.read(mut buf) ? + assert read == data.len + for i := 0; i < read; i++ { + assert buf[i] == data[i] + } + println('Got "$buf.bytestr()"') + return +} + +fn test_tcp() { + mut l := unix.listen_stream(test_port) or { panic(err) } + go echo_server(mut l) + echo() or { panic(err) } + l.close() or {} +} |