net: ipv6 support, merge unix+ip;[pack:x] attribute (#9904)

pull/10451/head
Emily Hudson 2021-06-13 21:53:38 +01:00 committed by GitHub
parent fa9fa77a5f
commit 535dcac8fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1277 additions and 524 deletions

View File

@ -44,7 +44,7 @@ const (
]
vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
'vlib/strconv/' /* prevent conflicts, till the new pure V string interpolation is merged */,
'vlib/net/' /* prevent conflicts, till ipv6 support is merged */,
'vlib/net/http/' /* prevent conflicts, till ipv6 support is merged */,
'vlib/term/ui/input.v' /* comment after a struct embed is removed */,
'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */,
'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */,

View File

@ -7,6 +7,10 @@ fn main() {
defer {
conn.close() or {}
}
println(' peer: $conn.peer_addr()')
println('local: $conn.addr()')
// Simple http HEAD request for a file
conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n') ?
// Read all the data that is waiting

View File

@ -0,0 +1,24 @@
import net
for addr in [
'vlang.io:80',
'google.com:80',
'steampowered.com:80',
'api.steampowered.com:80',
] {
println('$addr')
for @type in [net.SocketType.tcp, .udp] {
family := net.AddrFamily.unspec
addrs := net.resolve_addrs(addr, family, @type) or {
println('> None')
continue
}
for a in addrs {
f := a.family()
println('> $a $f ${@type}')
}
}
}

View File

@ -12,7 +12,7 @@ fn main() {
mut buf := []byte{len: 100}
if is_server {
println('UDP echo server, listening for udp packets on port: $port')
mut c := net.listen_udp(port) ?
mut c := net.listen_udp(':$port') ?
for {
read, addr := c.read(mut buf) or { continue }
println('received $read bytes from $addr')
@ -23,7 +23,7 @@ fn main() {
}
} else {
println('UDP client, sending packets to port: ${port}.\nType `exit` to exit.')
mut c := net.dial_udp('localhost', 'localhost:$port') ?
mut c := net.dial_udp('localhost:$port') ?
for {
mut line := os.input('client > ')
match line {

View File

@ -0,0 +1,40 @@
import io
import net
// This file shows how a basic TCP echo server can be implemented using
// the net module. You can connect to the server by using netcat or telnet,
// in separate shells, for example:
// nc 127.0.0.1 12345
// or
// telnet 127.0.0.1 12345
fn main() {
mut server := net.listen_tcp(.ip6, ':12345') ?
laddr := server.addr() ?
eprintln('Listen on $laddr ...')
for {
mut socket := server.accept() ?
go handle_client(mut socket)
}
}
fn handle_client(mut socket net.TcpConn) {
defer {
socket.close() or { panic(err) }
}
client_addr := socket.peer_addr() or { return }
eprintln('> new client: $client_addr')
mut reader := io.new_buffered_reader(reader: socket)
defer {
reader.free()
}
socket.write_string('server: hello\n') or { return }
for {
received_line := reader.read_line() or { return }
if received_line == '' {
return
}
println('client $client_addr: $received_line')
socket.write_string('server: $received_line\n') or { return }
}
}

View File

@ -10,7 +10,7 @@ fn main() {
}
fn start_server() ? {
mut s := websocket.new_server(30000, '')
mut s := websocket.new_server(.ip6, 30000, '')
// Make that in execution test time give time to execute at least one time
s.ping_interval = 100
s.on_connect(fn (mut s websocket.ServerClient) ?bool {

View File

@ -13,7 +13,7 @@ fn main() {
}
fn start_server() ? {
mut s := websocket.new_server(30000, '')
mut s := websocket.new_server(.ip6, 30000, '')
// Make that in execution test time give time to execute at least one time
s.ping_interval = 100
s.on_connect(fn (mut s websocket.ServerClient) ?bool {

View File

@ -1,5 +1,11 @@
module net
$if windows {
// This is mainly here for tcc on windows
// which apparently doesnt have this definition
#include "@VROOT/vlib/net/ipv6_v6only.h"
}
// Select represents a select operation
enum Select {
read
@ -11,55 +17,18 @@ enum Select {
pub enum SocketType {
udp = C.SOCK_DGRAM
tcp = C.SOCK_STREAM
dgram = C.SOCK_DGRAM
stream = C.SOCK_STREAM
seqpacket = C.SOCK_SEQPACKET
}
// SocketFamily are the available address families
pub enum SocketFamily {
// AddrFamily are the available address families
pub enum AddrFamily {
unix = C.AF_UNIX
inet = C.AF_INET
ip = C.AF_INET
ip6 = C.AF_INET6
unspec = C.AF_UNSPEC
}
struct C.in_addr {
mut:
s_addr int
}
struct C.sockaddr {
sa_family u16
}
struct C.sockaddr_in {
mut:
sin_family int
sin_port int
sin_addr C.in_addr
}
struct C.sockaddr_un {
mut:
sun_family int
sun_path charptr
}
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(domain SocketFamily, typ SocketType, protocol int) int
fn C.socket(domain AddrFamily, typ SocketType, protocol int) int
// fn C.setsockopt(sockfd int, level int, optname int, optval voidptr, optlen C.socklen_t) int
fn C.setsockopt(sockfd int, level int, optname int, optval voidptr, optlen u32) int
@ -71,44 +40,48 @@ fn C.htons(netshort u16) int
// fn C.bind(sockfd int, addr &C.sockaddr, addrlen C.socklen_t) int
// use voidptr for arg 2 becasue sockaddr is a generic descriptor for any kind of socket operation,
// it can also take sockaddr_in depending on the type of socket used in arg 1
fn C.bind(sockfd int, addr voidptr, addrlen u32) int
fn C.bind(sockfd int, addr &Addr, addrlen u32) int
fn C.listen(sockfd int, backlog int) int
// fn C.accept(sockfd int, addr &C.sockaddr, addrlen &C.socklen_t) int
fn C.accept(sockfd int, addr &C.sockaddr, addrlen &u32) int
fn C.accept(sockfd int, addr &Addr, addrlen &u32) int
fn C.getaddrinfo(node charptr, service charptr, hints &C.addrinfo, res &&C.addrinfo) int
fn C.getaddrinfo(node &char, service &char, hints &C.addrinfo, res &&C.addrinfo) int
fn C.freeaddrinfo(info &C.addrinfo)
// fn C.connect(sockfd int, addr &C.sockaddr, addrlen C.socklen_t) int
fn C.connect(sockfd int, addr &C.sockaddr, addrlen u32) int
fn C.connect(sockfd int, addr &Addr, addrlen u32) int
// fn C.send(sockfd int, buf voidptr, len size_t, flags int) size_t
fn C.send(sockfd int, buf voidptr, len size_t, flags int) int
// fn C.sendto(sockfd int, buf voidptr, len size_t, flags int, dest_add &C.sockaddr, addrlen C.socklen_t) size_t
fn C.sendto(sockfd int, buf voidptr, len size_t, flags int, dest_add &C.sockaddr, addrlen u32) int
fn C.sendto(sockfd int, buf voidptr, len size_t, flags int, dest_add &Addr, addrlen u32) int
// fn C.recv(sockfd int, buf voidptr, len size_t, flags int) size_t
fn C.recv(sockfd int, buf voidptr, len size_t, flags int) int
// fn C.recvfrom(sockfd int, buf voidptr, len size_t, flags int, src_addr &C.sockaddr, addrlen &C.socklen_t) size_t
fn C.recvfrom(sockfd int, buf voidptr, len size_t, flags int, src_addr &C.sockaddr, addrlen &u32) int
fn C.recvfrom(sockfd int, buf voidptr, len size_t, flags int, src_addr &Addr, addrlen &u32) int
fn C.shutdown(socket int, how int) int
fn C.ntohs(netshort u16) int
// fn C.getpeername(sockfd int, addr &C.sockaddr, addlen &C.socklen_t) int
fn C.getpeername(sockfd int, addr &C.sockaddr, addlen &u32) int
fn C.getpeername(sockfd int, addr &Addr, addlen &u32) int
fn C.inet_ntop(af SocketFamily, src voidptr, dst charptr, dst_size int) charptr
fn C.inet_ntop(af AddrFamily, src voidptr, dst &char, dst_size int) &char
fn C.WSAAddressToStringA(lpsaAddress &C.sockaddr, dwAddressLength u32, lpProtocolInfo voidptr, lpszAddressString charptr, lpdwAddressStringLength &u32) int
fn C.WSAAddressToStringA(lpsaAddress &Addr, dwAddressLength u32, lpProtocolInfo voidptr, lpszAddressString &char, lpdwAddressStringLength &u32) int
// fn C.getsockname(sockfd int, addr &C.sockaddr, addrlen &C.socklen_t) int
fn C.getsockname(sockfd int, addr &C.sockaddr, addrlen &u32) int
fn C.getsockopt(sockfd int, level int, optname int, optval voidptr, optlen &u32) int
// defined in builtin
// fn C.read() int
// fn C.close() int
@ -125,5 +98,7 @@ fn C.FD_SET(fd int, fdset &C.fd_set)
fn C.FD_ISSET(fd int, fdset &C.fd_set) bool
fn C.inet_pton(family AddrFamily, saddr &char, addr voidptr) int
[typedef]
pub struct C.fd_set {}

View File

@ -1,83 +1,259 @@
module net
// Addr represents an ip address
pub struct Addr {
addr C.sockaddr
len int
pub:
saddr string
port int
}
import io.util
import os
struct C.addrinfo {
}
pub fn (a Addr) str() string {
return '$a.saddr:$a.port'
union AddrData {
Unix
Ip
Ip6
}
const (
max_ipv4_addr_len = 24
ipv4_addr_size = sizeof(C.sockaddr_in)
addr_ip6_any = [16]byte{init: byte(0)}
addr_ip_any = [4]byte{init: byte(0)}
)
fn new_addr(addr C.sockaddr) ?Addr {
addr_len := if addr.sa_family == int(SocketFamily.inet) {
sizeof(C.sockaddr)
} else {
// TODO NOOOOOOOOOOOO
0
}
// Convert to string representation
buf := []byte{len: net.max_ipv4_addr_len, init: 0}
$if windows {
res := C.WSAAddressToStringA(&addr, addr_len, C.NULL, buf.data, unsafe { &u32(&buf.len) })
if res != 0 {
socket_error(-1) ?
}
} $else {
res := &char(C.inet_ntop(SocketFamily.inet, &addr, buf.data, buf.len))
if res == 0 {
socket_error(-1) ?
fn new_ip6(port u16, addr [16]byte) Addr {
a := Addr{
f: u16(AddrFamily.ip6)
addr: {
Ip6: {
port: u16(C.htons(port))
}
}
}
mut saddr := buf.bytestr()
hport := unsafe { &C.sockaddr_in(&addr) }.sin_port
port := C.ntohs(hport)
copy(a.addr.Ip6.addr[0..], addr[0..])
$if windows {
// strip the port from the address string
saddr = saddr.split(':')[0]
}
return Addr{addr, int(addr_len), saddr, port}
return a
}
pub fn resolve_addr(addr string, family SocketFamily, typ SocketType) ?Addr {
fn new_ip(port u16, addr [4]byte) Addr {
a := Addr{
f: u16(AddrFamily.ip)
addr: {
Ip: {
port: u16(C.htons(port))
}
}
}
copy(a.addr.Ip6.addr[0..], addr[0..])
return a
}
fn temp_unix() ?Addr {
// create a temp file to get a filename
// close it
// remove it
// then reuse the filename
mut file, filename := util.temp_file({}) ?
file.close()
os.rm(filename) ?
addrs := resolve_addrs(filename, .unix, .udp) ?
return addrs[0]
}
pub fn (a Addr) family() AddrFamily {
return AddrFamily(a.f)
}
const (
max_ip_len = 24
max_ip6_len = 46
)
fn (a Ip) str() string {
buf := []byte{len: net.max_ip_len, init: 0}
res := &char(C.inet_ntop(.ip, &a.addr, buf.data, buf.len))
if res == 0 {
return '<Unknown>'
}
saddr := buf.bytestr()
port := C.ntohs(a.port)
return '$saddr:$port'
}
fn (a Ip6) str() string {
buf := []byte{len: net.max_ip6_len, init: 0}
res := &char(C.inet_ntop(.ip6, &a.addr, buf.data, buf.len))
if res == 0 {
return '<Unknown>'
}
saddr := buf.bytestr()
port := C.ntohs(a.port)
return '[$saddr]:$port'
}
const aoffset = __offsetof(Addr, addr)
fn (a Addr) len() u32 {
match a.family() {
.ip {
return sizeof(Ip) + aoffset
}
.ip6 {
return sizeof(Ip6) + aoffset
}
.unix {
return sizeof(Unix) + aoffset
}
else {
panic('Unknown address family')
}
}
}
pub fn resolve_addrs(addr string, family AddrFamily, @type SocketType) ?[]Addr {
match family {
.ip, .ip6, .unspec {
return resolve_ipaddrs(addr, family, @type)
}
.unix {
resolved := Unix{}
if addr.len > max_unix_path {
return error('net: resolve_addrs Unix socket address is too long')
}
// Copy the unix path into the address struct
unsafe {
C.memcpy(&resolved.path, addr.str, addr.len)
}
return [Addr{
f: u16(AddrFamily.unix)
addr: AddrData{
Unix: resolved
}
}]
}
}
}
pub fn resolve_addrs_fuzzy(addr string, @type SocketType) ?[]Addr {
if addr.len == 0 {
return none
}
// Use a small heuristic to figure out what address family this is
// (out of the ones that we support)
if addr.contains(':') {
// Colon is a reserved character in unix paths
// so this must be an ip address
return resolve_addrs(addr, .unspec, @type)
}
return resolve_addrs(addr, .unix, @type)
}
pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ?[]Addr {
address, port := split_address(addr) ?
mut hints := C.addrinfo{}
if addr[0] == `:` {
// Use in6addr_any
return [new_ip6(port, net.addr_ip6_any)]
}
mut hints := C.addrinfo{
// ai_family: int(family)
// ai_socktype: int(typ)
// ai_flags: C.AI_PASSIVE
}
hints.ai_family = int(family)
hints.ai_socktype = int(typ)
hints.ai_flags = C.AI_PASSIVE
hints.ai_protocol = 0
hints.ai_addrlen = 0
hints.ai_canonname = C.NULL
hints.ai_addr = C.NULL
hints.ai_next = C.NULL
info := &C.addrinfo(0)
hints.ai_addr = voidptr(0)
hints.ai_canonname = voidptr(0)
hints.ai_next = voidptr(0)
results := &C.addrinfo(0)
sport := '$port'
// This might look silly but is recommended by MSDN
$if windows {
socket_error(0 - C.getaddrinfo(&char(address.str), &char(sport.str), &hints,
&info)) ?
&results)) ?
} $else {
x := C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &info)
x := C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results)
wrap_error(x) ?
}
return new_addr(*info.ai_addr)
defer {
C.freeaddrinfo(results)
}
// Now that we have our linked list of addresses
// convert them into an array
mut addresses := []Addr{}
for result := results; !isnil(result); result = result.ai_next {
match AddrFamily(result.ai_family) {
.ip, .ip6 {
new_addr := Addr{
addr: {
Ip6: {}
}
}
unsafe {
C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen)
}
addresses << new_addr
}
else {
panic('Unexpected address family $result.ai_family')
}
}
}
return addresses
}
fn (a Addr) str() string {
match AddrFamily(a.f) {
.ip {
unsafe {
return a.addr.Ip.str()
}
}
.ip6 {
unsafe {
return a.addr.Ip6.str()
}
}
.unix {
unsafe {
return tos_clone(a.addr.Unix.path[0..max_unix_path].data)
}
}
.unspec {
return '<.unspec>'
}
}
}
pub fn addr_from_socket_handle(handle int) Addr {
addr := Addr{
addr: {
Ip6: {}
}
}
size := sizeof(addr)
C.getsockname(handle, voidptr(&addr), &size)
return addr
}

View File

@ -0,0 +1,74 @@
module net
const max_unix_path = 104
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_in6 {
mut:
// 1 + 1 + 2 + 4 + 16 + 4 = 28;
sin6_len byte // 1
sin6_family byte // 1
sin6_port u16 // 2
sin6_flowinfo u32 // 4
sin6_addr [16]byte // 16
sin6_scope_id u32 // 4
}
struct C.sockaddr_in {
mut:
sin_len byte
sin_family byte
sin_port u16
sin_addr u32
sin_zero [8]char
}
struct C.sockaddr_un {
mut:
sun_len byte
sun_family byte
sun_path [max_unix_path]char
}
[_pack: '1']
struct Ip6 {
port u16
flow_info u32
addr [16]byte
scope_id u32
}
[_pack: '1']
struct Ip {
port u16
addr [4]byte
// Pad to size so that socket functions
// dont complain to us (see in.h and bind())
// TODO(emily): I would really like to use
// some constant calculations here
// so that this doesnt have to be hardcoded
sin_pad [8]byte
}
struct Unix {
path [max_unix_path]char
}
[_pack: '1']
struct Addr {
pub:
len u8
f u8
addr AddrData
}

View File

@ -0,0 +1,32 @@
module net
const max_unix_path = 104
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_in {
sin_family byte
sin_port u16
sin_addr u32
}
struct C.sockaddr_in6 {
sin6_family byte
sin6_port u16
sin6_addr [4]u32
}
struct C.sockaddr_un {
sun_family byte
sun_path [max_unix_path]char
}

View File

@ -0,0 +1,63 @@
module net
const max_unix_path = 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_in {
sin_family u16
sin_port u16
sin_addr u32
}
struct C.sockaddr_in6 {
sin6_family u16
sin6_port u16
sin6_addr [4]u32
}
struct C.sockaddr_un {
sun_family u16
sun_path [max_unix_path]char
}
[_pack: '1']
struct Ip6 {
port u16
flow_info u32
addr [16]byte
scope_id u32
}
[_pack: '1']
struct Ip {
port u16
addr [4]byte
// Pad to size so that socket functions
// dont complain to us (see in.h and bind())
// TODO(emily): I would really like to use
// some constant calculations here
// so that this doesnt have to be hardcoded
sin_pad [8]byte
}
struct Unix {
path [max_unix_path]byte
}
[_pack: '1']
struct Addr {
pub:
f u16
addr AddrData
}

View File

@ -0,0 +1,98 @@
module net
$if windows {
$if msvc {
// Force these to be included before afunix!
#include <winsock2.h>
#include <ws2tcpip.h>
#include <afunix.h>
} $else {
#include "@VROOT/vlib/net/afunix.h"
}
} $else {
#include <sys/un.h>
}
fn test_diagnostics() {
dump(net.aoffset)
eprintln('--------')
in6 := C.sockaddr_in6{}
our_ip6 := Ip6{}
$if macos {
dump(__offsetof(C.sockaddr_in6, sin6_len))
}
dump(__offsetof(C.sockaddr_in6, sin6_family))
dump(__offsetof(C.sockaddr_in6, sin6_port))
dump(__offsetof(C.sockaddr_in6, sin6_addr))
$if macos {
dump(sizeof(in6.sin6_len))
}
dump(sizeof(in6.sin6_family))
dump(sizeof(in6.sin6_port))
dump(sizeof(in6.sin6_addr))
dump(sizeof(in6))
eprintln('')
dump(__offsetof(Ip6, port))
dump(__offsetof(Ip6, addr))
dump(sizeof(our_ip6.port))
dump(sizeof(our_ip6.addr))
dump(sizeof(our_ip6))
eprintln('--------')
in4 := C.sockaddr_in{}
our_ip4 := Ip{}
$if macos {
dump(__offsetof(C.sockaddr_in, sin_len))
}
dump(__offsetof(C.sockaddr_in, sin_family))
dump(__offsetof(C.sockaddr_in, sin_port))
dump(__offsetof(C.sockaddr_in, sin_addr))
$if macos {
dump(sizeof(in4.sin_len))
}
dump(sizeof(in4.sin_family))
dump(sizeof(in4.sin_port))
dump(sizeof(in4.sin_addr))
dump(sizeof(in4))
eprintln('')
dump(__offsetof(Ip, port))
dump(__offsetof(Ip, addr))
dump(sizeof(our_ip4.port))
dump(sizeof(our_ip4.addr))
dump(sizeof(our_ip4))
eprintln('--------')
dump(__offsetof(C.sockaddr_un, sun_path))
dump(__offsetof(Unix, path))
eprintln('--------')
}
fn test_sizes_unix_sun_path() {
x1 := C.sockaddr_un{}
x2 := Unix{}
assert sizeof(x1.sun_path) == sizeof(x2.path)
}
fn test_offsets_ipv6() {
assert __offsetof(C.sockaddr_in6, sin6_addr) == __offsetof(Ip6, addr) + net.aoffset
assert __offsetof(C.sockaddr_in6, sin6_port) == __offsetof(Ip6, port) + net.aoffset
}
fn test_offsets_ipv4() {
assert __offsetof(C.sockaddr_in, sin_addr) == __offsetof(Ip, addr) + net.aoffset
assert __offsetof(C.sockaddr_in, sin_port) == __offsetof(Ip, port) + net.aoffset
}
fn test_offsets_unix() {
assert __offsetof(C.sockaddr_un, sun_path) == __offsetof(Unix, path) + net.aoffset
}
fn test_sizes_ipv6() {
assert sizeof(C.sockaddr_in6) == sizeof(Ip6) + aoffset
}
fn test_sizes_ipv4() {
assert sizeof(C.sockaddr_in) == sizeof(Ip) + aoffset
}
fn test_sizes_unix() {
assert sizeof(C.sockaddr_un) == sizeof(Unix) + aoffset
}

View File

@ -0,0 +1,58 @@
module net
const max_unix_path = 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_in {
sin_family u16
sin_port u16
sin_addr u32
}
struct C.sockaddr_in6 {
sin6_family u16
sin6_port u16
sin6_addr [4]u32
}
struct C.sockaddr_un {
sun_family u16
sun_path [max_unix_path]char
}
[_pack: '1']
struct Ip6 {
port u16
flow_info u32
addr [16]byte
scope_id u32
}
[_pack: '1']
struct Ip {
port u16
addr [4]byte
sin_pad [8]byte
}
struct Unix {
path [max_unix_path]byte
}
[_pack: '1']
struct Addr {
pub:
f u16
addr AddrData
}

26
vlib/net/afunix.h 100644
View File

@ -0,0 +1,26 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#ifndef _AFUNIX_
#define _AFUNIX_
#define UNIX_PATH_MAX 108
#if !defined(ADDRESS_FAMILY)
#define UNDEF_ADDRESS_FAMILY
#define ADDRESS_FAMILY unsigned short
#endif
typedef struct sockaddr_un {
ADDRESS_FAMILY sun_family;
char sun_path[UNIX_PATH_MAX];
} SOCKADDR_UN, *PSOCKADDR_UN;
#if defined(UNDEF_ADDRESS_FAMILY)
#undef ADDRESS_FAMILY
#endif
#endif /* _AFUNIX_ */

View File

@ -2,6 +2,20 @@ module net
import time
// 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)
pub 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)
pub const infinite_timeout = time.Duration(-1)
// Shutdown shutsdown a socket and closes it
fn shutdown(handle int) ? {
$if windows {
@ -11,8 +25,6 @@ fn shutdown(handle int) ? {
C.shutdown(handle, C.SHUT_RDWR)
socket_error(C.close(handle)) ?
}
return none
}
// Select waits for an io operation (specified by parameter `test`) to be available
@ -23,7 +35,7 @@ fn @select(handle int, test Select, timeout time.Duration) ?bool {
C.FD_SET(handle, &set)
seconds := timeout.milliseconds() / 1000
microseconds := timeout - (seconds * time.second)
microseconds := time.Duration(timeout - (seconds * time.second)).microseconds()
mut tt := C.timeval{
tv_sec: u64(seconds)
@ -63,7 +75,7 @@ fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test S
}
ready := @select(handle, test, timeout) ?
if ready {
return none
return
}
return err_timed_out
}
@ -78,7 +90,7 @@ fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test S
ready := @select(handle, test, d_timeout) ?
if ready {
return none
return
}
return err_timed_out
}
@ -92,23 +104,3 @@ fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ? {
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)
pub 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)
pub const (
infinite_timeout = time.Duration(-1)
)

View File

@ -52,12 +52,19 @@ pub fn wrap_error(error_code int) ? {
// wrap_read_result takes a read result and sees if it is 0 for graceful
// connection termination and returns none
// e.g. res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0))?
// e.g. res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))?
[inline]
fn wrap_read_result(result int) ?int {
if result > 0 || result < 0 {
return result
if result == 0 {
return none
}
return none
return result
}
[inline]
fn wrap_write_result(result int) ?int {
if result == 0 {
return none
}
return result
}

View File

@ -9,16 +9,16 @@ import time
pub struct Server {
pub mut:
port int = 8080
handler fn(Request) Response
read_timeout time.Duration = 30 * time.second
port int = 8080
handler fn (Request) Response
read_timeout time.Duration = 30 * time.second
write_timeout time.Duration = 30 * time.second
}
pub fn (mut s Server) listen_and_serve() ? {
if voidptr(s.handler) == 0 {
eprintln('Server handler not set, using debug handler')
s.handler = fn(req Request) Response {
s.handler = fn (req Request) Response {
$if debug {
eprintln('[$time.now()] $req.method $req.url\n\r$req.header\n\r$req.data - 200 OK')
} $else {
@ -33,7 +33,7 @@ pub fn (mut s Server) listen_and_serve() ? {
}
}
}
mut l := net.listen_tcp(s.port) ?
mut l := net.listen_tcp(.ip6, ':$s.port') ?
eprintln('Listening on :$s.port')
for {
mut conn := l.accept() or {
@ -67,7 +67,5 @@ fn (mut s Server) parse_and_respond(mut conn net.TcpConn) {
if resp.version == .unknown {
resp.version = req.version
}
conn.write(resp.bytes()) or {
eprintln('error sending response: $err')
}
conn.write(resp.bytes()) or { eprintln('error sending response: $err') }
}

View File

@ -0,0 +1,5 @@
#if !defined(IPV6_V6ONLY)
#define IPV6_V6ONLY 27
#endif

View File

@ -1,9 +1,5 @@
module net
// needed for unix domain sockets support
// is not included in CI rn
// #include <afunix.h>
// WsaError is all of the socket errors that WSA provides from WSAGetLastError
pub enum WsaError {
//
@ -770,11 +766,13 @@ mut:
szSystemStatus [129]byte
iMaxSockets u16
iMaxUdpDg u16
lpVendorInfo byteptr
lpVendorInfo &byte
}
fn init() {
mut wsadata := C.WSAData{}
mut wsadata := C.WSAData{
lpVendorInfo: 0
}
res := C.WSAStartup(net.wsa_v22, &wsadata)
if res != 0 {
panic('socket: WSAStartup failed')

View File

@ -37,7 +37,7 @@ pub struct SSL_METHOD {
fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO
fn C.BIO_set_conn_hostname(b &C.BIO, name charptr) int
fn C.BIO_set_conn_hostname(b &C.BIO, name &char) int
// there are actually 2 macros for BIO_get_ssl
// fn C.BIO_get_ssl(bp &C.BIO, ssl charptr, c int)
@ -48,7 +48,7 @@ fn C.BIO_do_connect(b &C.BIO) int
fn C.BIO_do_handshake(b &C.BIO) int
fn C.BIO_puts(b &C.BIO, buf charptr)
fn C.BIO_puts(b &C.BIO, buf &char)
fn C.BIO_read(b &C.BIO, buf voidptr, len int) int
@ -60,7 +60,7 @@ fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options int)
fn C.SSL_CTX_set_verify_depth(s &C.SSL_CTX, depth int)
fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, ca_file charptr, ca_path charptr) int
fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, ca_file &char, ca_path &char) int
fn C.SSL_CTX_free(ctx &C.SSL_CTX)
@ -70,7 +70,7 @@ fn C.SSL_set_fd(ssl &C.SSL, fd int) int
fn C.SSL_connect(&C.SSL) int
fn C.SSL_set_cipher_list(ctx &SSL, str charptr) int
fn C.SSL_set_cipher_list(ctx &SSL, str &char) int
fn C.SSL_get_peer_certificate(ssl &SSL) &C.X509
@ -80,7 +80,7 @@ fn C.SSL_get_error(ssl &C.SSL, ret int) int
fn C.SSL_get_verify_result(ssl &SSL) int
fn C.SSL_set_tlsext_host_name(s &SSL, name charptr) int
fn C.SSL_set_tlsext_host_name(s &SSL, name &char) int
fn C.SSL_shutdown(&C.SSL) int

View File

@ -4,24 +4,29 @@ module openssl
pub fn ssl_error(ret int, ssl voidptr) ?SSLError {
res := C.SSL_get_error(ssl, ret)
match SSLError(res) {
.ssl_error_syscall { return error_with_code('unrecoverable syscall ($res)', res) }
.ssl_error_ssl { return error_with_code('unrecoverable ssl protocol error ($res)',
res) }
else { return SSLError(res) }
.ssl_error_syscall {
return error_with_code('unrecoverable syscall ($res)', res)
}
.ssl_error_ssl {
return error_with_code('unrecoverable ssl protocol error ($res)', res)
}
else {
return SSLError(res)
}
}
}
pub enum SSLError {
ssl_error_none = 0 //SSL_ERROR_NONE
ssl_error_ssl = 1 //SSL_ERROR_SSL
ssl_error_want_read = 2 //SSL_ERROR_WANT_READ
ssl_error_want_write = 3 //SSL_ERROR_WANT_WRITE
ssl_error_want_x509_lookup = 4 //SSL_ERROR_WANT_X509_LOOKUP
ssl_error_syscall = 5 //SSL_ERROR_SYSCALL
ssl_error_zero_return = 6 //SSL_ERROR_ZERO_RETURN
ssl_error_want_connect = 7 //SSL_ERROR_WANT_CONNECT
ssl_error_want_accept = 8 //SSL_ERROR_WANT_ACCEPT
ssl_error_want_async = 9 //SSL_ERROR_WANT_ASYNC
ssl_error_want_async_job = 10 //SSL_ERROR_WANT_ASYNC_JOB
ssl_error_want_early = 11 //SSL_ERROR_WANT_EARLY
ssl_error_none = 0 // SSL_ERROR_NONE
ssl_error_ssl = 1 // SSL_ERROR_SSL
ssl_error_want_read = 2 // SSL_ERROR_WANT_READ
ssl_error_want_write = 3 // SSL_ERROR_WANT_WRITE
ssl_error_want_x509_lookup = 4 // SSL_ERROR_WANT_X509_LOOKUP
ssl_error_syscall = 5 // SSL_ERROR_SYSCALL
ssl_error_zero_return = 6 // SSL_ERROR_ZERO_RETURN
ssl_error_want_connect = 7 // SSL_ERROR_WANT_CONNECT
ssl_error_want_accept = 8 // SSL_ERROR_WANT_ACCEPT
ssl_error_want_async = 9 // SSL_ERROR_WANT_ASYNC
ssl_error_want_async_job = 10 // SSL_ERROR_WANT_ASYNC_JOB
ssl_error_want_early = 11 // SSL_ERROR_WANT_EARLY
}

View File

@ -1,5 +1,5 @@
import os
import smtp
import net.smtp
import time
// Used to test that a function call returns an error

View File

@ -3,7 +3,6 @@ module net
pub enum SocketOption {
// TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it
// and there is no easy way to define it
broadcast = C.SO_BROADCAST
debug = C.SO_DEBUG
dont_route = C.SO_DONTROUTE
@ -11,27 +10,23 @@ pub enum SocketOption {
keep_alive = C.SO_KEEPALIVE
linger = C.SO_LINGER
oob_inline = C.SO_OOBINLINE
reuse_addr = C.SO_REUSEADDR
recieve_buf_size = C.SO_RCVBUF
recieve_low_size = C.SO_RCVLOWAT
recieve_timeout = C.SO_RCVTIMEO
send_buf_size = C.SO_SNDBUF
send_low_size = C.SO_SNDLOWAT
send_timeout = C.SO_SNDTIMEO
socket_type = C.SO_TYPE
ipv6_only = C.IPV6_V6ONLY
}
const (
opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline]
opts_int = [
opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline]
opts_int = [
.recieve_buf_size,
.recieve_low_size,
.recieve_timeout,
.send_buf_size,
.send_low_size,
.send_timeout,
@ -47,9 +42,9 @@ const (
.recieve_buf_size,
.recieve_low_size,
.recieve_timeout,
.send_buf_size,
.send_low_size,
.send_timeout,
.ipv6_only,
]
)

View File

@ -10,8 +10,7 @@ const (
[heap]
pub struct TcpConn {
pub mut:
sock TcpSocket
max_write_chunk_size int = 4096
sock TcpSocket
mut:
write_deadline time.Time
read_deadline time.Time
@ -20,30 +19,36 @@ mut:
}
pub fn dial_tcp(address string) ?&TcpConn {
mut s := new_tcp_socket() ?
s.connect(address) ?
return &TcpConn{
sock: s
read_timeout: net.tcp_default_read_timeout
write_timeout: net.tcp_default_write_timeout
addrs := resolve_addrs_fuzzy(address, .tcp) ?
// Very simple dialer
for addr in addrs {
mut s := new_tcp_socket(addr.family()) ?
s.connect(addr) or {
// Connection failed
s.close() or { continue }
continue
}
return &TcpConn{
sock: s
read_timeout: net.tcp_default_read_timeout
write_timeout: net.tcp_default_write_timeout
}
}
// failed
return error('dial_tcp failed')
}
pub fn (mut c TcpConn) close() ? {
$if trace_tcp ? {
eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}')
}
c.sock.close() ?
return none
}
// write_ptr blocks and attempts to write all data
pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int {
$if trace_tcp ? {
eprintln('>>> TcpConn.write_ptr | c.sock.handle: ${c.sock.handle:6} | b: ${ptr_str(b)} len: ${len:6}')
}
$if trace_tcp_data_write ? {
eprintln('>>> TcpConn.write_ptr | data.len: ${len:6} | data: ' +
eprintln(
'>>> TcpConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' +
unsafe { b.vstring_with_len(len) })
}
unsafe {
@ -51,14 +56,8 @@ pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int {
mut total_sent := 0
for total_sent < len {
ptr := ptr_base + total_sent
mut chunk_size := len - total_sent
if chunk_size > c.max_write_chunk_size {
chunk_size = c.max_write_chunk_size
}
mut sent := C.send(c.sock.handle, ptr, chunk_size, msg_nosignal)
$if trace_tcp_data_write ? {
eprintln('>>> TcpConn.write_ptr | data chunk, total_sent: ${total_sent:6}, chunk_size: ${chunk_size:6}, sent: ${sent:6}, ptr: ${ptr_str(ptr)}')
}
remaining := len - total_sent
mut sent := C.send(c.sock.handle, ptr, remaining, msg_nosignal)
if sent < 0 {
code := error_code()
if code == int(error_ewouldblock) {
@ -91,29 +90,19 @@ pub fn (mut c TcpConn) write_string(s string) ?int {
}
pub fn (mut c TcpConn) read_ptr(buf_ptr &byte, len int) ?int {
mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ?
mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ?
$if trace_tcp ? {
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle:6} | buf_ptr: ${ptr_str(buf_ptr):8} len: ${len:6} | res: ${res:6}')
eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
}
if res > 0 {
$if trace_tcp_data_read ? {
eprintln('<<< TcpConn.read_ptr | 1 data.len: ${res:6} | data: ' +
unsafe { buf_ptr.vstring_with_len(res) })
}
return res
}
code := error_code()
if code == int(error_ewouldblock) {
c.wait_for_read() ?
res = wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ?
res = wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ?
$if trace_tcp ? {
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle:6} | buf_ptr: ${ptr_str(buf_ptr):8} len: ${len:6} | res: ${res:6}')
}
$if trace_tcp_data_read ? {
if res > 0 {
eprintln('<<< TcpConn.read_ptr | 2 data.len: ${res:6} | data: ' +
unsafe { buf_ptr.vstring_with_len(res) })
}
eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
}
return socket_error(res)
} else {
@ -175,23 +164,22 @@ pub fn (mut c TcpConn) wait_for_write() ? {
}
pub fn (c &TcpConn) peer_addr() ?Addr {
mut addr := C.sockaddr{}
len := sizeof(C.sockaddr)
socket_error(C.getpeername(c.sock.handle, &addr, &len)) ?
return new_addr(addr)
mut addr := Addr{
addr: {
Ip6: {}
}
}
mut size := sizeof(Addr)
socket_error(C.getpeername(c.sock.handle, voidptr(&addr), &size)) ?
return addr
}
pub fn (c &TcpConn) peer_ip() ?string {
buf := [44]char{}
peeraddr := C.sockaddr_in{}
speeraddr := sizeof(peeraddr)
socket_error(C.getpeername(c.sock.handle, unsafe { &C.sockaddr(&peeraddr) }, &speeraddr)) ?
cstr := &char(C.inet_ntop(SocketFamily.inet, &peeraddr.sin_addr, &buf[0], sizeof(buf)))
if cstr == 0 {
return error('net.peer_ip: inet_ntop failed')
}
res := unsafe { cstring_to_vstring(cstr) }
return res
return c.peer_addr() ?.str()
}
pub fn (c &TcpConn) addr() ?Addr {
return c.sock.address()
}
pub fn (c TcpConn) str() string {
@ -207,17 +195,18 @@ mut:
accept_deadline time.Time
}
pub fn listen_tcp(port int) ?&TcpListener {
s := new_tcp_socket() ?
validate_port(port) ?
mut addr := C.sockaddr_in{}
addr.sin_family = int(SocketFamily.inet)
addr.sin_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
size := sizeof(C.sockaddr_in)
pub fn listen_tcp(family AddrFamily, saddr string) ?&TcpListener {
s := new_tcp_socket(family) ?
addrs := resolve_addrs(saddr, family, .tcp) ?
// TODO(logic to pick here)
addr := addrs[0]
// cast to the correct type
sockaddr := unsafe { &C.sockaddr(&addr) }
socket_error(C.bind(s.handle, sockaddr, size)) ?
alen := addr.len()
bindres := C.bind(s.handle, voidptr(&addr), alen)
socket_error(bindres) ?
socket_error(C.listen(s.handle, 128)) ?
return &TcpListener{
sock: s
@ -227,26 +216,21 @@ pub fn listen_tcp(port int) ?&TcpListener {
}
pub fn (mut l TcpListener) accept() ?&TcpConn {
$if trace_tcp ? {
eprintln(' TcpListener.accept | l.sock.handle: ${l.sock.handle:6}')
addr := Addr{
addr: {
Ip6: {}
}
}
addr := C.sockaddr_storage{}
unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_storage)) }
size := sizeof(C.sockaddr_storage)
// cast to correct type
sock_addr := unsafe { &C.sockaddr(&addr) }
mut new_handle := C.accept(l.sock.handle, sock_addr, &size)
size := sizeof(Addr)
mut new_handle := C.accept(l.sock.handle, voidptr(&addr), &size)
if new_handle <= 0 {
l.wait_for_accept() ?
new_handle = C.accept(l.sock.handle, sock_addr, &size)
new_handle = C.accept(l.sock.handle, voidptr(&addr), &size)
if new_handle == -1 || new_handle == 0 {
return none
return error('accept failed')
}
}
new_sock := tcp_socket_from_handle(new_handle) ?
$if trace_tcp ? {
eprintln(' TcpListener.accept | << new_sock.handle: ${new_sock.handle:6}')
}
return &TcpConn{
sock: new_sock
read_timeout: net.tcp_default_read_timeout
@ -258,7 +242,7 @@ pub fn (c &TcpListener) accept_deadline() ?time.Time {
if c.accept_deadline.unix != 0 {
return c.accept_deadline
}
return none
return error('invalid deadline')
}
pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) {
@ -278,14 +262,10 @@ pub fn (mut c TcpListener) wait_for_accept() ? {
}
pub fn (mut c TcpListener) close() ? {
$if trace_tcp ? {
eprintln(' TcpListener.close | c.sock.handle: ${c.sock.handle:6}')
}
c.sock.close() ?
return none
}
pub fn (c &TcpListener) address() ?Addr {
pub fn (c &TcpListener) addr() ?Addr {
return c.sock.address()
}
@ -294,23 +274,23 @@ pub:
handle int
}
fn new_tcp_socket() ?TcpSocket {
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.tcp, 0)) ?
fn new_tcp_socket(family AddrFamily) ?TcpSocket {
handle := socket_error(C.socket(family, SocketType.tcp, 0)) ?
mut s := TcpSocket{
handle: sockfd
handle: handle
}
$if trace_tcp ? {
eprintln(' new_tcp_socket | s.handle: ${s.handle:6}')
}
// s.set_option_bool(.reuse_addr, true)?
// TODO(emily):
// we shouldnt be using ioctlsocket in the 21st century
// use the non-blocking socket option instead please :)
// TODO(emily):
// Move this to its own function on the socket
s.set_option_int(.reuse_addr, 1) ?
$if !net_blocking_sockets ? {
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ?
}
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(handle, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(handle, C.F_SETFL, C.fcntl(handle, C.F_GETFL) | C.O_NONBLOCK)) ?
}
return s
}
@ -319,18 +299,16 @@ fn tcp_socket_from_handle(sockfd int) ?TcpSocket {
mut s := TcpSocket{
handle: sockfd
}
$if trace_tcp ? {
eprintln(' tcp_socket_from_handle | s.handle: ${s.handle:6}')
}
// s.set_option_bool(.reuse_addr, true)?
s.set_option_int(.reuse_addr, 1) ?
$if !net_blocking_sockets ? {
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ?
}
s.set_dualstack(true) or {
// Not ipv6, we dont care
}
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ?
}
return s
}
@ -345,18 +323,19 @@ pub fn (mut s TcpSocket) set_option_bool(opt SocketOption, value bool) ? {
// }
x := int(value)
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ?
return none
}
pub fn (mut s TcpSocket) set_dualstack(on bool) ? {
x := int(!on)
socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x,
sizeof(int))) ?
}
pub fn (mut s TcpSocket) set_option_int(opt SocketOption, value int) ? {
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int))) ?
return none
}
fn (mut s TcpSocket) close() ? {
$if trace_tcp ? {
eprintln(' TcpSocket.close | s.handle: ${s.handle:6}')
}
return shutdown(s.handle)
}
@ -368,35 +347,41 @@ const (
connect_timeout = 5 * time.second
)
fn (mut s TcpSocket) connect(a string) ? {
$if trace_tcp ? {
eprintln(' TcpSocket.connect | s.handle: ${s.handle:6} | a: $a')
}
addr := resolve_addr(a, .inet, .tcp) ?
res := C.connect(s.handle, &addr.addr, addr.len)
fn (mut s TcpSocket) connect(a Addr) ? {
res := C.connect(s.handle, voidptr(&a), a.len())
if res == 0 {
return none
return
}
_ := error_code()
// The socket is nonblocking and the connection cannot be completed
// immediately. (UNIX domain sockets failed with EAGAIN instead.)
// It is possible to select(2) or poll(2) for completion by selecting
// the socket for writing. After select(2) indicates writability,
// use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to
// determine whether connect() completed successfully (SO_ERROR is zero) or
// unsuccessfully (SO_ERROR is one of the usual error codes listed here,
// ex plaining the reason for the failure).
write_result := s.@select(.write, net.connect_timeout) ?
if write_result {
// succeeded
return none
}
except_result := s.@select(.except, net.connect_timeout) ?
if except_result {
return err_connect_failed
err := 0
len := sizeof(err)
socket_error(C.getsockopt(s.handle, C.SOL_SOCKET, C.SO_ERROR, &err, &len)) ?
if err != 0 {
return wrap_error(err)
}
// Succeeded
return
}
// Get the error
socket_error(C.connect(s.handle, voidptr(&a), a.len())) ?
// otherwise we timed out
return err_connect_timed_out
}
// address gets the address of a socket
pub fn (s &TcpSocket) address() ?Addr {
mut addr := C.sockaddr_in{}
size := sizeof(C.sockaddr_in)
// cast to the correct type
sockaddr := unsafe { &C.sockaddr(&addr) }
C.getsockname(s.handle, sockaddr, &size)
return new_addr(sockaddr)
return addr_from_socket_handle(s.handle)
}

View File

@ -9,7 +9,7 @@ const (
// read_line is a *simple*, *non customizable*, blocking line reader.
// It will *always* return a line, ending with CRLF, or just '', on EOF.
// NB: if you want more control over the buffer, please use a buffered IO
// reader instead: `io.new_buffered_reader(reader: con)`
// reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})`
pub fn (mut con TcpConn) read_line() string {
mut buf := [net.max_read]byte{} // where C.recv will store the network data
mut res := '' // The final result, including the ending \n.

View File

@ -3,16 +3,24 @@ import net
import strings
const (
server_port = 22334
server_port = ':22443'
)
fn accept(mut server net.TcpListener, c chan &net.TcpConn) {
c <- server.accept() or { panic(err) }
}
fn setup() (&net.TcpListener, &net.TcpConn, &net.TcpConn) {
mut server := net.listen_tcp(server_port) or { panic(err) }
mut client := net.dial_tcp('127.0.0.1:$server_port') or { panic(err) }
mut socket := server.accept() or { panic(err) }
mut server := net.listen_tcp(.ip6, server_port) or { panic(err) }
c := chan &net.TcpConn{}
go accept(mut server, c)
mut client := net.dial_tcp('localhost$server_port') or { panic(err) }
socket := <-c
$if debug_peer_ip ? {
ip := con.peer_ip() or { '$err' }
eprintln('connection peer_ip: $ip')
eprintln('$server.addr()\n$client.peer_addr(), $client.addr()\n$socket.peer_addr(), $socket.addr()')
}
assert true
return server, client, socket

View File

@ -1,4 +1,5 @@
import net
import os
const (
test_port = 45123
@ -18,19 +19,24 @@ fn handle_conn(mut c net.TcpConn) {
}
}
fn echo_server(mut l net.TcpListener) ? {
for {
mut new_conn := l.accept() or { continue }
go handle_conn(mut new_conn)
}
return none
fn one_shot_echo_server(mut l net.TcpListener, ch_started chan int) ? {
eprintln('> one_shot_echo_server')
ch_started <- 1
mut new_conn := l.accept() or { return error('could not accept') }
eprintln(' > new_conn: $new_conn')
handle_conn(mut new_conn)
new_conn.close() or {}
}
fn echo() ? {
mut c := net.dial_tcp('127.0.0.1:$test_port') ?
fn echo(address string) ? {
mut c := net.dial_tcp(address) ?
defer {
c.close() or { }
c.close() or {}
}
println('local: ' + c.addr() ?.str())
println(' peer: ' + c.peer_addr() ?.str())
data := 'Hello from vlib/net!'
c.write_string(data) ?
mut buf := []byte{len: 4096}
@ -40,12 +46,55 @@ fn echo() ? {
assert buf[i] == data[i]
}
println('Got "$buf.bytestr()"')
return none
}
fn test_tcp() {
mut l := net.listen_tcp(test_port) or { panic(err) }
go echo_server(mut l)
echo() or { panic(err) }
l.close() or { }
fn test_tcp_ip6() {
eprintln('\n>>> ${@FN}')
address := 'localhost:$test_port'
mut l := net.listen_tcp(.ip6, ':$test_port') or { panic(err) }
dump(l)
start_echo_server(mut l)
echo(address) or { panic(err) }
l.close() or {}
// ensure there is at least one new socket created before the next test
l = net.listen_tcp(.ip6, ':${test_port + 1}') or { panic(err) }
}
fn start_echo_server(mut l net.TcpListener) {
ch_server_started := chan int{}
go one_shot_echo_server(mut l, ch_server_started)
_ := <-ch_server_started
}
fn test_tcp_ip() {
eprintln('\n>>> ${@FN}')
address := 'localhost:$test_port'
mut l := net.listen_tcp(.ip, address) or { panic(err) }
dump(l)
start_echo_server(mut l)
echo(address) or { panic(err) }
l.close() or {}
}
fn test_tcp_unix() {
eprintln('\n>>> ${@FN}')
// TODO(emily):
// whilst windows supposedly supports unix sockets
// this doesnt work (wsaeopnotsupp at the call to bind())
$if !windows {
address := os.real_path('tcp-test.sock')
// address := 'tcp-test.sock'
println('$address')
mut l := net.listen_tcp(.unix, address) or { panic(err) }
start_echo_server(mut l)
echo(address) or { panic(err) }
l.close() or {}
os.rm(address) or { panic('failed to remove socket file') }
}
}
fn testsuite_end() {
eprintln('\ndone')
}

View File

@ -9,8 +9,12 @@ const (
struct UdpSocket {
handle int
local Addr
remote ?Addr
l Addr
// TODO(emily): replace with option again
// when i figure out how to coerce it properly
mut:
has_r bool
r Addr
}
pub struct UdpConn {
@ -23,27 +27,36 @@ mut:
write_timeout time.Duration
}
pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
local := resolve_addr(laddr, .inet, .udp) ?
sbase := new_udp_socket(local.port) ?
sock := UdpSocket{
handle: sbase.handle
local: local
remote: resolve_wrapper(raddr)
}
return &UdpConn{
sock: sock
read_timeout: net.udp_default_read_timeout
write_timeout: net.udp_default_write_timeout
pub fn dial_udp(raddr string) ?&UdpConn {
addrs := resolve_addrs_fuzzy(raddr, .udp) ?
for addr in addrs {
// create a local socket for this
// bind to any port (or file) (we dont care in this
// case because we only care about the remote)
if sock := new_udp_socket_for_remote(addr) {
return &UdpConn{
sock: sock
read_timeout: net.udp_default_read_timeout
write_timeout: net.udp_default_write_timeout
}
}
}
return none
}
fn resolve_wrapper(raddr string) ?Addr {
// Dont have to do this when its fixed
// this just allows us to store this `none` optional in a struct
x := resolve_addr(raddr, .inet, .udp) or { return none }
return x
}
// pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
// local := resolve_addr(laddr, .inet, .udp) ?
// sbase := new_udp_socket() ?
// sock := UdpSocket{
// handle: sbase.handle
// l: local
// r: resolve_wrapper(raddr)
// }
// }
pub fn (mut c UdpConn) write_ptr(b &byte, len int) ?int {
remote := c.sock.remote() or { return err_no_udp_remote }
@ -64,14 +77,14 @@ pub fn (mut c UdpConn) write_string(s string) ?int {
}
pub fn (mut c UdpConn) write_to_ptr(addr Addr, b &byte, len int) ?int {
res := C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len)
res := C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())
if res >= 0 {
return res
}
code := error_code()
if code == int(error_ewouldblock) {
c.wait_for_write() ?
socket_error(C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len)) ?
socket_error(C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())) ?
} else {
wrap_error(code) ?
}
@ -90,22 +103,24 @@ pub fn (mut c UdpConn) write_to_string(addr Addr, s string) ?int {
// read reads from the socket into buf up to buf.len returning the number of bytes read
pub fn (mut c UdpConn) read(mut buf []byte) ?(int, Addr) {
mut addr_from := C.sockaddr{}
len := sizeof(C.sockaddr)
mut res := wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from,
&len)) ?
mut addr := Addr{
addr: {
Ip6: {}
}
}
len := sizeof(Addr)
mut res := wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len,
0, voidptr(&addr), &len)) ?
if res > 0 {
addr := new_addr(addr_from) ?
return res, addr
}
code := error_code()
if code == int(error_ewouldblock) {
c.wait_for_read() ?
// same setup as in tcp
res = wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from,
&len)) ?
res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len, 0,
voidptr(&addr), &len)) ?
res2 := socket_error(res) ?
addr := new_addr(addr_from) ?
return res2, addr
} else {
wrap_error(code) ?
@ -170,44 +185,79 @@ pub fn (mut c UdpConn) close() ? {
return c.sock.close()
}
pub fn listen_udp(port int) ?&UdpConn {
s := new_udp_socket(port) ?
pub fn listen_udp(laddr string) ?&UdpConn {
addrs := resolve_addrs_fuzzy(laddr, .udp) ?
// TODO(emily):
// here we are binding to the first address
// and that is probably not ideal
addr := addrs[0]
return &UdpConn{
sock: s
sock: new_udp_socket(addr) ?
read_timeout: net.udp_default_read_timeout
write_timeout: net.udp_default_write_timeout
}
}
fn new_udp_socket(local_port int) ?&UdpSocket {
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.udp, 0)) ?
fn new_udp_socket(local_addr Addr) ?&UdpSocket {
family := local_addr.family()
sockfd := socket_error(C.socket(family, SocketType.udp, 0)) ?
mut s := &UdpSocket{
handle: sockfd
}
s.set_option_bool(.reuse_addr, true) ?
$if !net_blocking_sockets ? {
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK)) ?
l: local_addr
r: Addr{
addr: {
Ip6: {}
}
}
}
// In UDP we always have to bind to a port
validate_port(local_port) ?
mut addr := C.sockaddr_in{}
addr.sin_family = int(SocketFamily.inet)
addr.sin_port = C.htons(local_port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
size := sizeof(C.sockaddr_in)
s.set_option_bool(.reuse_addr, true) ?
if family == .ip6 {
s.set_dualstack(true) ?
}
// NOTE: refer to comments in tcp.v
$if windows {
t := u32(1) // true
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
} $else {
socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK)) ?
}
// cast to the correct type
sockaddr := unsafe { &C.sockaddr(&addr) }
socket_error(C.bind(s.handle, sockaddr, size)) ?
socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len())) ?
return s
}
pub fn (s &UdpSocket) remote() ?Addr {
return s.remote
fn new_udp_socket_for_remote(raddr Addr) ?&UdpSocket {
// Invent a sutible local address for this remote addr
addr := match raddr.family() {
.ip, .ip6 {
// Use ip6 dualstack
new_ip6(0, addr_ip6_any)
}
.unix {
x := temp_unix() ?
x
}
else {
panic('Invalid family')
// Appease compiler
Addr{
addr: {
Ip6: {}
}
}
}
}
mut sock := new_udp_socket(addr) ?
sock.has_r = true
sock.r = raddr
return sock
}
pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ? {
@ -220,7 +270,12 @@ pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ? {
// }
x := int(value)
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ?
return none
}
pub fn (mut s UdpSocket) set_dualstack(on bool) ? {
x := int(!on)
socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x,
sizeof(int))) ?
}
fn (mut s UdpSocket) close() ? {
@ -230,3 +285,10 @@ fn (mut s UdpSocket) close() ? {
fn (mut s UdpSocket) @select(test Select, timeout time.Duration) ?bool {
return @select(s.handle, test, timeout)
}
fn (s &UdpSocket) remote() ?Addr {
if s.has_r {
return s.r
}
return none
}

View File

@ -5,6 +5,8 @@ fn echo_server(mut c net.UdpConn) {
mut buf := []byte{len: 100, init: 0}
read, addr := c.read(mut buf) or { continue }
println('Server got addr $addr')
c.write_to(addr, buf[..read]) or {
println('Server: connection dropped')
return
@ -12,10 +14,15 @@ fn echo_server(mut c net.UdpConn) {
}
}
const (
local_addr = ':40003'
remote_addr = 'localhost:40003'
)
fn echo() ? {
mut c := net.dial_udp('127.0.0.1:40003', '127.0.0.1:40001') ?
mut c := net.dial_udp(remote_addr) ?
defer {
c.close() or { }
c.close() or {}
}
data := 'Hello from vlib/net!'
@ -37,12 +44,10 @@ fn echo() ? {
println('Got "$buf.bytestr()"')
c.close() ?
return none
}
fn test_udp() {
mut l := net.listen_udp(40001) or {
mut l := net.listen_udp(local_addr) or {
println(err)
assert false
panic('')
@ -54,7 +59,7 @@ fn test_udp() {
assert false
}
l.close() or { }
l.close() or {}
}
fn main() {

View File

@ -9,7 +9,7 @@ const (
fn C.SUN_LEN(ptr &C.sockaddr_un) int
fn C.strncpy(charptr, charptr, int)
fn C.strncpy(&char, &char, int)
// Shutdown shutsdown a socket and closes it
fn shutdown(handle int) ? {
@ -20,8 +20,6 @@ fn shutdown(handle int) ? {
C.shutdown(handle, C.SHUT_RDWR)
net.socket_error(C.close(handle)) ?
}
return none
}
// Select waits for an io operation (specified by parameter `test`) to be available
@ -72,7 +70,7 @@ fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test S
}
ready := @select(handle, test, timeout) ?
if ready {
return none
return
}
return net.err_timed_out
}
@ -87,7 +85,7 @@ fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test S
ready := @select(handle, test, d_timeout) ?
if ready {
return none
return
}
return net.err_timed_out
}

View File

@ -41,8 +41,7 @@ fn error_code() int {
}
fn new_stream_socket() ?StreamSocket {
sockfd := net.socket_error(C.socket(net.SocketFamily.unix, net.SocketType.stream,
0)) ?
sockfd := net.socket_error(C.socket(net.AddrFamily.unix, net.SocketType.tcp, 0)) ?
mut s := StreamSocket{
handle: sockfd
}
@ -67,19 +66,18 @@ fn (mut s StreamSocket) connect(a string) ? {
addr.sun_family = C.AF_UNIX
unsafe { C.strncpy(&addr.sun_path[0], &char(a.str), max_sun_path) }
size := C.SUN_LEN(&addr)
sockaddr := unsafe { &C.sockaddr(&addr) }
res := C.connect(s.handle, sockaddr, size)
res := C.connect(s.handle, voidptr(&addr), size)
// if res != 1 {
// return none
//}
if res == 0 {
return none
return
}
_ := error_code()
write_result := s.@select(.write, unix.connect_timeout) ?
if write_result {
// succeeded
return none
return
}
except_result := s.@select(.except, unix.connect_timeout) ?
if except_result {
@ -100,8 +98,7 @@ pub fn listen_stream(sock string) ?&StreamListener {
addr.sun_family = C.AF_UNIX
unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) }
size := C.SUN_LEN(&addr)
sockaddr := unsafe { &C.sockaddr(&addr) }
net.socket_error(C.bind(s.handle, sockaddr, size)) ?
net.socket_error(C.bind(s.handle, voidptr(&addr), size)) ?
net.socket_error(C.listen(s.handle, 128)) ?
return &StreamListener{
sock: s
@ -124,7 +121,7 @@ pub fn (mut l StreamListener) accept() ?&StreamConn {
l.wait_for_accept() ?
new_handle = C.accept(l.sock.handle, 0, 0)
if new_handle == -1 || new_handle == 0 {
return none
return error('accept failed')
}
}
new_sock := StreamSocket{
@ -141,7 +138,7 @@ pub fn (c &StreamListener) accept_deadline() ?time.Time {
if c.accept_deadline.unix != 0 {
return c.accept_deadline
}
return none
return error('no deadline')
}
pub fn (mut c StreamListener) set_accept_deadline(deadline time.Time) {
@ -162,12 +159,10 @@ pub fn (mut c StreamListener) wait_for_accept() ? {
pub fn (mut c StreamListener) close() ? {
c.sock.close() ?
return none
}
pub fn (mut c StreamConn) close() ? {
c.sock.close() ?
return none
}
// write_ptr blocks and attempts to write all data
@ -216,7 +211,7 @@ pub fn (mut c StreamConn) write_string(s string) ?int {
}
pub fn (mut c StreamConn) read_ptr(buf_ptr &byte, len int) ?int {
mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ?
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')
}
@ -226,7 +221,7 @@ pub fn (mut c StreamConn) read_ptr(buf_ptr &byte, len int) ?int {
code := error_code()
if code == int(error_ewouldblock) {
c.wait_for_read() ?
res = wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ?
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')
}
@ -234,7 +229,7 @@ pub fn (mut c StreamConn) read_ptr(buf_ptr &byte, len int) ?int {
} else {
net.wrap_error(code) ?
}
return none
return net.socket_error(code)
}
pub fn (mut c StreamConn) read(mut buf []byte) ?int {

View File

@ -4,11 +4,11 @@ import net.unix
const test_port = os.join_path(os.temp_dir(), 'unix_domain_socket')
fn testsuite_begin() {
os.rm(test_port) or { }
os.rm(test_port) or {}
}
fn testsuite_end() {
os.rm(test_port) or { }
os.rm(test_port) or {}
}
fn handle_conn(mut c unix.StreamConn) {
@ -30,13 +30,12 @@ fn echo_server(mut l unix.StreamListener) ? {
mut new_conn := l.accept() or { continue }
go handle_conn(mut new_conn)
}
return none
}
fn echo() ? {
mut c := unix.connect_stream(test_port) ?
defer {
c.close() or { }
c.close() or {}
}
data := 'Hello from vlib/net!'
c.write_string(data) ?
@ -47,12 +46,12 @@ fn echo() ? {
assert buf[i] == data[i]
}
println('Got "$buf.bytestr()"')
return none
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 { }
l.close() or {}
}

View File

@ -7,7 +7,7 @@ const (
// validate_port checks whether a port is valid
// and returns the port or an error
pub fn validate_port(port int) ?u16 {
if port <= socket_max_port {
if port <= net.socket_max_port {
return u16(port)
} else {
return err_port_out_of_range
@ -19,6 +19,9 @@ pub fn split_address(addr string) ?(string, u16) {
port := addr.all_after_last(':').int()
address := addr.all_before_last(':')
p := validate_port(port)?
// TODO(emily): Maybe do some more checking here
// to validate ipv6 address sanity?
p := validate_port(port) ?
return address, p
}

View File

@ -493,14 +493,32 @@ pub fn (mut f File) write_str(s string) ? {
f.write_string(s) or { return err }
}
pub struct ErrFileNotOpened {
msg string = 'os: file not opened'
code int
}
pub struct ErrSizeOfTypeIs0 {
msg string = 'os: size of type is 0'
code int
}
fn error_file_not_opened() IError {
return IError(&ErrFileNotOpened{})
}
fn error_size_of_type_0() IError {
return IError(&ErrSizeOfTypeIs0{})
}
// read_struct reads a single struct of type `T`
pub fn (mut f File) read_struct<T>(mut t T) ? {
if !f.is_opened {
return none
return error_file_not_opened()
}
tsize := int(sizeof(*t))
if tsize == 0 {
return none
return error_size_of_type_0()
}
nbytes := fread(t, 1, tsize, f.cfile) ?
if nbytes != tsize {
@ -511,11 +529,11 @@ pub fn (mut f File) read_struct<T>(mut t T) ? {
// read_struct_at reads a single struct of type `T` at position specified in file
pub fn (mut f File) read_struct_at<T>(mut t T, pos u64) ? {
if !f.is_opened {
return none
return error_file_not_opened()
}
tsize := int(sizeof(*t))
if tsize == 0 {
return none
return error_size_of_type_0()
}
mut nbytes := 0
$if x64 {
@ -542,11 +560,11 @@ pub fn (mut f File) read_struct_at<T>(mut t T, pos u64) ? {
// read_raw reads and returns a single instance of type `T`
pub fn (mut f File) read_raw<T>() ?T {
if !f.is_opened {
return none
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return none
return error_size_of_type_0()
}
mut t := T{}
nbytes := fread(&t, 1, tsize, f.cfile) ?
@ -559,11 +577,11 @@ pub fn (mut f File) read_raw<T>() ?T {
// read_raw_at reads and returns a single instance of type `T` starting at file byte offset `pos`
pub fn (mut f File) read_raw_at<T>(pos u64) ?T {
if !f.is_opened {
return none
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return none
return error_size_of_type_0()
}
mut nbytes := 0
mut t := T{}
@ -605,11 +623,11 @@ pub fn (mut f File) read_raw_at<T>(pos u64) ?T {
// write_struct writes a single struct of type `T`
pub fn (mut f File) write_struct<T>(t &T) ? {
if !f.is_opened {
return error('file is not opened')
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
return error_size_of_type_0()
}
C.errno = 0
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
@ -624,11 +642,11 @@ pub fn (mut f File) write_struct<T>(t &T) ? {
// write_struct_at writes a single struct of type `T` at position specified in file
pub fn (mut f File) write_struct_at<T>(t &T, pos u64) ? {
if !f.is_opened {
return error('file is not opened')
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
return error_size_of_type_0()
}
C.errno = 0
mut nbytes := 0
@ -661,11 +679,11 @@ pub fn (mut f File) write_struct_at<T>(t &T, pos u64) ? {
// write_raw writes a single instance of type `T`
pub fn (mut f File) write_raw<T>(t &T) ? {
if !f.is_opened {
return error('file is not opened')
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
return error_size_of_type_0()
}
C.errno = 0
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
@ -680,11 +698,11 @@ pub fn (mut f File) write_raw<T>(t &T) ? {
// write_raw_at writes a single instance of type `T` starting at file byte offset `pos`
pub fn (mut f File) write_raw_at<T>(t &T, pos u64) ? {
if !f.is_opened {
return error('file is not opened')
return error_file_not_opened()
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
return error_size_of_type_0()
}
mut nbytes := 0

View File

@ -14,6 +14,20 @@ import picohttpparser
#flag @VEXEROOT/thirdparty/picoev/picoev.o
#include "src/picoev.h"
struct C.in_addr {
mut:
s_addr int
}
struct C.sockaddr_in {
mut:
sin_family int
sin_port int
sin_addr C.in_addr
}
struct C.sockaddr_storage {}
fn C.atoi() int
fn C.strncasecmp(s1 &char, s2 &char, n size_t) int
@ -194,7 +208,7 @@ fn default_err_cb(data voidptr, req picohttpparser.Request, mut res picohttppars
}
pub fn new(config Config) &Picoev {
fd := C.socket(net.SocketFamily.inet, net.SocketType.tcp, 0)
fd := C.socket(net.AddrFamily.ip, net.SocketType.tcp, 0)
assert fd != -1
flag := 1
assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0
@ -211,7 +225,7 @@ pub fn new(config Config) &Picoev {
addr.sin_port = C.htons(config.port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
size := 16 // sizeof(C.sockaddr_in)
bind_res := C.bind(fd, &addr, size)
bind_res := C.bind(fd, unsafe { &net.Addr(&addr) }, size)
assert bind_res == 0
listen_res := C.listen(fd, C.SOMAXCONN)
assert listen_res == 0

View File

@ -627,11 +627,10 @@ pub fn (t &Table) array_cname(elem_type Type) string {
pub fn (t &Table) array_fixed_name(elem_type Type, size int, size_expr Expr) string {
elem_type_sym := t.get_type_symbol(elem_type)
ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' }
mut size_str := size.str()
if t.is_fmt {
if size_expr is Ident {
size_str = size_expr.name
}
size_str := if size_expr is EmptyExpr || size != 987654321 {
size.str()
} else {
size_expr.str()
}
return '[$size_str]$ptr$elem_type_sym.name'
}
@ -829,7 +828,7 @@ pub fn (mut t Table) find_or_register_array_fixed(elem_type Type, size int, size
info: ArrayFixed{
elem_type: elem_type
size: size
expr: size_expr
size_expr: size_expr
}
}
return t.register_type_symbol(array_fixed_type)
@ -1062,7 +1061,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
if bit_size % 8 != 0 { // there is no way to do `i2131(32)` so this should never be reached
t.panic('compiler bug: bitsizes must be multiples of 8')
}
return new_type(t.find_or_register_array_fixed(byte_type, bit_size / 8, EmptyExpr{}))
return new_type(t.find_or_register_array_fixed(byte_type, bit_size / 8, empty_expr()))
}
}
}

View File

@ -809,8 +809,8 @@ pub mut:
pub struct ArrayFixed {
pub:
size int
expr Expr // used by fmt for e.g. ´[my_const]byte´
size int
size_expr Expr // used by fmt for e.g. ´[my_const]byte´
pub mut:
elem_type Type
}
@ -890,13 +890,11 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
.array_fixed {
info := sym.info as ArrayFixed
elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
mut size_str := info.size.str()
if t.is_fmt {
if info.expr is Ident {
size_str = info.expr.name
}
if info.size_expr is EmptyExpr {
res = '[$info.size]$elem_str'
} else {
res = '[$info.size_expr]$elem_str'
}
res = '[$size_str]$elem_str'
}
.chan {
// TODO currently the `chan` struct in builtin is not considered a struct but a chan

View File

@ -19,7 +19,7 @@ pub fn (i &Inspector) visit(node ast.Node) ? {
if i.inspector_callback(node, i.data) {
return
}
return none
return error('')
}
// inspect traverses and checks the AST node on a depth-first order and based on the data given

View File

@ -22,7 +22,7 @@ fn (mut n NodeByOffset) visit(node ast.Node) ? {
node_pos := node.position()
if n.pos >= node_pos.pos && n.pos <= node_pos.pos + node_pos.len && node !is ast.File {
n.node = node
return none
return error('')
}
return
}

View File

@ -45,7 +45,7 @@ const (
fn (mut v Builder) find_win_cc() ? {
$if !windows {
return none
return
}
ccompiler_version_res := os.execute('$v.pref.ccompiler -v')
if ccompiler_version_res.exit_code != 0 {
@ -63,7 +63,7 @@ fn (mut v Builder) find_win_cc() ? {
if v.pref.is_verbose {
println('tcc not found')
}
return none
return error('tcc not found')
}
v.pref.ccompiler = thirdparty_tcc
v.pref.ccompiler_type = .tinyc

View File

@ -144,9 +144,8 @@ fn new_windows_kit(kit_root string, target_arch string) ?WindowsKit {
fn find_windows_kit_root_by_env(target_arch string) ?WindowsKit {
kit_root := os.getenv('WindowsSdkDir')
if kit_root == '' {
return none
return error('empty WindowsSdkDir')
}
return new_windows_kit(kit_root, target_arch)
}
@ -210,12 +209,12 @@ fn find_vs_by_reg(vswhere_dir string, host_arch string, target_arch string) ?VsI
fn find_vs_by_env(host_arch string, target_arch string) ?VsInstallation {
vs_dir := os.getenv('VSINSTALLDIR')
if vs_dir == '' {
return none
return error('empty VSINSTALLDIR')
}
vc_tools_dir := os.getenv('VCToolsInstallDir')
if vc_tools_dir == '' {
return none
return error('empty VCToolsInstallDir')
}
bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch'

View File

@ -332,10 +332,9 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
}
pub fn (mut c Checker) check_expected(got ast.Type, expected ast.Type) ? {
if c.check_types(got, expected) {
return
if !c.check_types(got, expected) {
return error(c.expected_msg(got, expected))
}
return error(c.expected_msg(got, expected))
}
[inline]

View File

@ -3026,6 +3026,10 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
got_types_0_idx := got_types[0].idx()
if exp_is_optional
&& got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx] {
if got_types_0_idx == ast.none_type_idx && expected_type == ast.ovoid_type {
c.error('returning `none` in functions, that have a `?` result type is not allowed anymore, either `return error(message)` or just `return` instead',
node.pos)
}
return
}
if expected_types.len > 0 && expected_types.len != got_types.len {
@ -3802,7 +3806,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) ast.Type {
}
if array_init.is_fixed {
idx := c.table.find_or_register_array_fixed(elem_type, array_init.exprs.len,
ast.EmptyExpr{})
ast.empty_expr())
if elem_type.has_flag(.generic) {
array_init.typ = ast.new_type(idx).set_flag(.generic)
} else {
@ -7375,7 +7379,7 @@ fn (mut c Checker) trace(fbase string, message string) {
fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
if typ == 0 {
c.error('unknown type', pos)
return none
return
}
sym := c.table.get_type_symbol(typ)
match sym.kind {
@ -7383,7 +7387,7 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
if sym.language == .v && !sym.name.starts_with('C.') {
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
pos)
return none
return
}
}
.int_literal, .float_literal {
@ -7396,7 +7400,7 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
'unknown type `$sym.name`.\nDid you mean `f64`?'
}
c.error(msg, pos)
return none
return
}
}
.array {

View File

@ -5371,21 +5371,23 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
if info.is_union && struct_init.fields.len > 1 {
verror('union must not have more than 1 initializer')
}
for embed in info.embeds {
embed_sym := g.table.get_type_symbol(embed)
embed_name := embed_sym.embed_name()
if embed_name !in inited_fields {
default_init := ast.StructInit{
typ: embed
if !info.is_union {
for embed in info.embeds {
embed_sym := g.table.get_type_symbol(embed)
embed_name := embed_sym.embed_name()
if embed_name !in inited_fields {
default_init := ast.StructInit{
typ: embed
}
g.write('.$embed_name = ')
g.struct_init(default_init)
if is_multiline {
g.writeln(',')
} else {
g.write(',')
}
initialized = true
}
g.write('.$embed_name = ')
g.struct_init(default_init)
if is_multiline {
g.writeln(',')
} else {
g.write(',')
}
initialized = true
}
}
// g.zero_struct_fields(info, inited_fields)
@ -5691,6 +5693,22 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
}
// TODO avoid buffer manip
start_pos := g.type_definitions.len
mut pre_pragma := ''
mut post_pragma := ''
for attr in typ.info.attrs {
match attr.name {
'_pack' {
pre_pragma += '#pragma pack(push, $attr.arg)\n'
post_pragma += '#pragma pack(pop)'
}
else {}
}
}
g.type_definitions.writeln(pre_pragma)
if typ.info.is_union {
g.type_definitions.writeln('union $name {')
} else {
@ -5726,12 +5744,13 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
}
// g.type_definitions.writeln('} $name;\n')
//
attrs := if typ.info.attrs.contains('packed') {
ti_attrs := if typ.info.attrs.contains('packed') {
'__attribute__((__packed__))'
} else {
''
}
g.type_definitions.writeln('}$attrs;\n')
g.type_definitions.writeln('}$ti_attrs;\n')
g.type_definitions.writeln(post_pragma)
}
ast.Alias {
// ast.Alias { TODO
@ -5916,7 +5935,7 @@ fn (mut g Gen) write_expr_to_string(expr ast.Expr) string {
fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) {
cvar_name := c_name(var_name)
mr_styp := g.base_type(return_type)
is_none_ok := mr_styp == 'void'
is_none_ok := return_type == ast.ovoid_type
g.writeln(';')
if is_none_ok {
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')

View File

@ -512,13 +512,13 @@ fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInf
rec.typ = p.parse_type_with_mut(rec.is_mut)
if rec.typ.idx() == 0 {
// error is set in parse_type
return none
return error('void receiver type')
}
rec.type_pos = rec.type_pos.extend(p.prev_tok.position())
if is_amp && rec.is_mut {
p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
lpar_pos.extend(p.tok.position()))
return none
return error('invalid `mut f &Foo`')
}
if is_shared {
rec.typ = rec.typ.set_flag(.shared_f)

View File

@ -13,31 +13,38 @@ pub fn (mut p Parser) parse_array_type() ast.Type {
if p.tok.kind in [.number, .name] {
mut fixed_size := 0
size_expr := p.expr(0)
match size_expr {
ast.IntegerLiteral {
fixed_size = size_expr.val.int()
}
ast.Ident {
if const_field := p.global_scope.find_const('${p.mod}.$size_expr.name') {
if const_field.expr is ast.IntegerLiteral {
fixed_size = const_field.expr.val.int()
if p.pref.is_fmt {
fixed_size = 987654321
} else {
match size_expr {
ast.IntegerLiteral {
fixed_size = size_expr.val.int()
}
ast.Ident {
mut show_non_const_error := false
if const_field := p.global_scope.find_const('${p.mod}.$size_expr.name') {
if const_field.expr is ast.IntegerLiteral {
fixed_size = const_field.expr.val.int()
} else {
show_non_const_error = true
}
} else {
p.error_with_pos('non-constant array bound `$size_expr.name`',
size_expr.pos)
if p.pref.is_fmt {
// for vfmt purposes, pretend the constant does exist
// it may have been defined in another .v file:
fixed_size = 1
} else {
show_non_const_error = true
}
}
} else {
if p.pref.is_fmt {
// for vfmt purposes, pretend the constant does exist, it may have
// been defined in another .v file:
fixed_size = 1
} else {
if show_non_const_error {
p.error_with_pos('non-constant array bound `$size_expr.name`',
size_expr.pos)
}
}
}
else {
p.error('expecting `int` for fixed size')
else {
p.error('expecting `int` for fixed size')
}
}
}
p.check(.rsbr)

View File

@ -303,7 +303,8 @@ pub fn run<T>(global_app &T, port int) {
// mut app := &T{}
// run_app<T>(mut app, port)
mut l := net.listen_tcp(port) or { panic('failed to listen') }
mut l := net.listen_tcp(.ip6, ':$port') or { panic('failed to listen $err.code $err') }
println('[Vweb] Running app on http://localhost:$port')
// app.Context = Context{
// conn: 0

View File

@ -83,7 +83,6 @@ fn (mut ws Client) shutdown_socket() ? {
} else {
ws.conn.close() ?
}
return none
}
// dial_socket connects tcp socket and initializes default configurations

View File

@ -4,7 +4,7 @@ module main
import x.websocket
fn main() {
mut s := websocket.new_server(9002, '/')
mut s := websocket.new_server(.ip6, 9002, '/')
s.on_message(on_message)
s.listen() or { panic(err) }
}

View File

@ -15,6 +15,7 @@ mut:
message_callbacks []MessageEventHandler // new message callback functions
close_callbacks []CloseEventHandler // close message callback functions
pub:
family net.AddrFamily = .ip
port int // port used as listen to incoming connections
is_ssl bool // true if secure connection (not supported yet on server)
pub mut:
@ -34,9 +35,10 @@ pub mut:
}
// new_server instance a new websocket server on provided port and route
pub fn new_server(port int, route string) &Server {
pub fn new_server(family net.AddrFamily, port int, route string) &Server {
return &Server{
ls: 0
family: family
port: port
logger: &log.Log{
level: .info
@ -53,7 +55,7 @@ pub fn (mut s Server) set_ping_interval(seconds int) {
// listen start listen and process to incoming connections from websocket clients
pub fn (mut s Server) listen() ? {
s.logger.info('websocket server: start listen on port $s.port')
s.ls = net.listen_tcp(s.port) ?
s.ls = net.listen_tcp(s.family, ':$s.port') ?
s.set_state(.open)
go s.handle_ping()
for {

View File

@ -1,31 +1,51 @@
import os
import net
import x.websocket
import time
import rand
// TODO: fix connecting to ipv4 websockets
// (the server seems to work with .ip, but
// Client can not connect, it needs to be passed
// .ip too?)
struct WebsocketTestResults {
pub mut:
nr_messages int
nr_pong_received int
}
// Do not run these tests everytime, since they are flaky.
// They have their own specialized CI runner.
const github_job = os.getenv('GITHUB_JOB')
const should_skip = github_job != '' && github_job != 'websocket_tests'
// tests with internal ws servers
fn test_ws() {
if github_job != '' && github_job != 'websocket_tests' {
// Do not run these tests everytime, since they are flaky.
// They have their own specialized CI runner.
fn test_ws_ipv6() {
if should_skip {
return
}
port := 30000 + rand.intn(1024)
go start_server(port)
go start_server(.ip6, port)
time.sleep(500 * time.millisecond)
ws_test('ws://localhost:$port') or { assert false }
ws_test(.ip6, 'ws://localhost:$port') or { assert false }
}
fn start_server(listen_port int) ? {
mut s := websocket.new_server(listen_port, '')
// tests with internal ws servers
fn test_ws_ipv4() {
// TODO: fix client
if true || should_skip {
return
}
port := 30000 + rand.intn(1024)
go start_server(.ip, port)
time.sleep(500 * time.millisecond)
ws_test(.ip, 'ws://localhost:$port') or { assert false }
}
fn start_server(family net.AddrFamily, listen_port int) ? {
mut s := websocket.new_server(family, listen_port, '')
// make that in execution test time give time to execute at least one time
s.ping_interval = 1
@ -52,7 +72,7 @@ fn start_server(listen_port int) ? {
}
// ws_test tests connect to the websocket server from websocket client
fn ws_test(uri string) ? {
fn ws_test(family net.AddrFamily, uri string) ? {
eprintln('connecting to $uri ...')
mut test_results := WebsocketTestResults{}