mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
net: ipv6 support, merge unix+ip;[pack:x] attribute (#9904)
This commit is contained in:
parent
fa9fa77a5f
commit
535dcac8fa
@ -44,7 +44,7 @@ const (
|
|||||||
]
|
]
|
||||||
vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
|
vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
|
||||||
'vlib/strconv/' /* prevent conflicts, till the new pure V string interpolation is merged */,
|
'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/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/regex/regex_test.v' /* contains meaningfull formatting of the test case data */,
|
||||||
'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */,
|
'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */,
|
||||||
|
@ -7,6 +7,10 @@ fn main() {
|
|||||||
defer {
|
defer {
|
||||||
conn.close() or {}
|
conn.close() or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println(' peer: $conn.peer_addr()')
|
||||||
|
println('local: $conn.addr()')
|
||||||
|
|
||||||
// Simple http HEAD request for a file
|
// Simple http HEAD request for a file
|
||||||
conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n') ?
|
conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n') ?
|
||||||
// Read all the data that is waiting
|
// Read all the data that is waiting
|
||||||
|
24
examples/net_resolve.v
Normal file
24
examples/net_resolve.v
Normal 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}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ fn main() {
|
|||||||
mut buf := []byte{len: 100}
|
mut buf := []byte{len: 100}
|
||||||
if is_server {
|
if is_server {
|
||||||
println('UDP echo server, listening for udp packets on port: $port')
|
println('UDP echo server, listening for udp packets on port: $port')
|
||||||
mut c := net.listen_udp(port) ?
|
mut c := net.listen_udp(':$port') ?
|
||||||
for {
|
for {
|
||||||
read, addr := c.read(mut buf) or { continue }
|
read, addr := c.read(mut buf) or { continue }
|
||||||
println('received $read bytes from $addr')
|
println('received $read bytes from $addr')
|
||||||
@ -23,7 +23,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println('UDP client, sending packets to port: ${port}.\nType `exit` to exit.')
|
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 {
|
for {
|
||||||
mut line := os.input('client > ')
|
mut line := os.input('client > ')
|
||||||
match line {
|
match line {
|
||||||
|
40
examples/tcp_echo_server.v
Normal file
40
examples/tcp_echo_server.v
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_server() ? {
|
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
|
// Make that in execution test time give time to execute at least one time
|
||||||
s.ping_interval = 100
|
s.ping_interval = 100
|
||||||
s.on_connect(fn (mut s websocket.ServerClient) ?bool {
|
s.on_connect(fn (mut s websocket.ServerClient) ?bool {
|
||||||
|
@ -13,7 +13,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_server() ? {
|
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
|
// Make that in execution test time give time to execute at least one time
|
||||||
s.ping_interval = 100
|
s.ping_interval = 100
|
||||||
s.on_connect(fn (mut s websocket.ServerClient) ?bool {
|
s.on_connect(fn (mut s websocket.ServerClient) ?bool {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
module net
|
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
|
// Select represents a select operation
|
||||||
enum Select {
|
enum Select {
|
||||||
read
|
read
|
||||||
@ -11,55 +17,18 @@ enum Select {
|
|||||||
pub enum SocketType {
|
pub enum SocketType {
|
||||||
udp = C.SOCK_DGRAM
|
udp = C.SOCK_DGRAM
|
||||||
tcp = C.SOCK_STREAM
|
tcp = C.SOCK_STREAM
|
||||||
dgram = C.SOCK_DGRAM
|
|
||||||
stream = C.SOCK_STREAM
|
|
||||||
seqpacket = C.SOCK_SEQPACKET
|
seqpacket = C.SOCK_SEQPACKET
|
||||||
}
|
}
|
||||||
|
|
||||||
// SocketFamily are the available address families
|
// AddrFamily are the available address families
|
||||||
pub enum SocketFamily {
|
pub enum AddrFamily {
|
||||||
unix = C.AF_UNIX
|
unix = C.AF_UNIX
|
||||||
inet = C.AF_INET
|
ip = C.AF_INET
|
||||||
|
ip6 = C.AF_INET6
|
||||||
|
unspec = C.AF_UNSPEC
|
||||||
}
|
}
|
||||||
|
|
||||||
struct C.in_addr {
|
fn C.socket(domain AddrFamily, typ SocketType, protocol int) int
|
||||||
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.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 C.socklen_t) int
|
||||||
fn C.setsockopt(sockfd int, level int, optname int, optval voidptr, optlen u32) 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
|
// 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,
|
// 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
|
// 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.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 &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 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) size_t
|
||||||
fn C.send(sockfd int, buf voidptr, len size_t, flags int) int
|
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 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) size_t
|
||||||
fn C.recv(sockfd int, buf voidptr, len size_t, flags int) int
|
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 &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.shutdown(socket int, how int) int
|
||||||
|
|
||||||
fn C.ntohs(netshort u16) 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 &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 &C.socklen_t) int
|
||||||
fn C.getsockname(sockfd int, addr &C.sockaddr, addrlen &u32) 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
|
// defined in builtin
|
||||||
// fn C.read() int
|
// fn C.read() int
|
||||||
// fn C.close() 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.FD_ISSET(fd int, fdset &C.fd_set) bool
|
||||||
|
|
||||||
|
fn C.inet_pton(family AddrFamily, saddr &char, addr voidptr) int
|
||||||
|
|
||||||
[typedef]
|
[typedef]
|
||||||
pub struct C.fd_set {}
|
pub struct C.fd_set {}
|
||||||
|
@ -1,83 +1,259 @@
|
|||||||
module net
|
module net
|
||||||
|
|
||||||
// Addr represents an ip address
|
import io.util
|
||||||
pub struct Addr {
|
import os
|
||||||
addr C.sockaddr
|
|
||||||
len int
|
|
||||||
pub:
|
|
||||||
saddr string
|
|
||||||
port int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct C.addrinfo {
|
union AddrData {
|
||||||
}
|
Unix
|
||||||
|
Ip
|
||||||
pub fn (a Addr) str() string {
|
Ip6
|
||||||
return '$a.saddr:$a.port'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
max_ipv4_addr_len = 24
|
addr_ip6_any = [16]byte{init: byte(0)}
|
||||||
ipv4_addr_size = sizeof(C.sockaddr_in)
|
addr_ip_any = [4]byte{init: byte(0)}
|
||||||
)
|
)
|
||||||
|
|
||||||
fn new_addr(addr C.sockaddr) ?Addr {
|
fn new_ip6(port u16, addr [16]byte) Addr {
|
||||||
addr_len := if addr.sa_family == int(SocketFamily.inet) {
|
a := Addr{
|
||||||
sizeof(C.sockaddr)
|
f: u16(AddrFamily.ip6)
|
||||||
} else {
|
addr: {
|
||||||
// TODO NOOOOOOOOOOOO
|
Ip6: {
|
||||||
0
|
port: u16(C.htons(port))
|
||||||
}
|
}
|
||||||
// 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) ?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut saddr := buf.bytestr()
|
|
||||||
|
|
||||||
hport := unsafe { &C.sockaddr_in(&addr) }.sin_port
|
copy(a.addr.Ip6.addr[0..], addr[0..])
|
||||||
port := C.ntohs(hport)
|
|
||||||
|
|
||||||
$if windows {
|
return a
|
||||||
// strip the port from the address string
|
|
||||||
saddr = saddr.split(':')[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return Addr{addr, int(addr_len), saddr, port}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) ?
|
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_family = int(family)
|
||||||
hints.ai_socktype = int(typ)
|
hints.ai_socktype = int(typ)
|
||||||
hints.ai_flags = C.AI_PASSIVE
|
hints.ai_flags = C.AI_PASSIVE
|
||||||
hints.ai_protocol = 0
|
hints.ai_protocol = 0
|
||||||
hints.ai_addrlen = 0
|
hints.ai_addrlen = 0
|
||||||
hints.ai_canonname = C.NULL
|
hints.ai_addr = voidptr(0)
|
||||||
hints.ai_addr = C.NULL
|
hints.ai_canonname = voidptr(0)
|
||||||
hints.ai_next = C.NULL
|
hints.ai_next = voidptr(0)
|
||||||
info := &C.addrinfo(0)
|
results := &C.addrinfo(0)
|
||||||
|
|
||||||
sport := '$port'
|
sport := '$port'
|
||||||
|
|
||||||
// This might look silly but is recommended by MSDN
|
// This might look silly but is recommended by MSDN
|
||||||
$if windows {
|
$if windows {
|
||||||
socket_error(0 - C.getaddrinfo(&char(address.str), &char(sport.str), &hints,
|
socket_error(0 - C.getaddrinfo(&char(address.str), &char(sport.str), &hints,
|
||||||
&info)) ?
|
&results)) ?
|
||||||
} $else {
|
} $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) ?
|
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
|
||||||
}
|
}
|
||||||
|
74
vlib/net/address_darwin.c.v
Normal file
74
vlib/net/address_darwin.c.v
Normal 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
|
||||||
|
}
|
32
vlib/net/address_default.c.v
Normal file
32
vlib/net/address_default.c.v
Normal 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
|
||||||
|
}
|
63
vlib/net/address_linux.c.v
Normal file
63
vlib/net/address_linux.c.v
Normal 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
|
||||||
|
}
|
98
vlib/net/address_test.v
Normal file
98
vlib/net/address_test.v
Normal 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
|
||||||
|
}
|
58
vlib/net/address_windows.c.v
Normal file
58
vlib/net/address_windows.c.v
Normal 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
Normal file
26
vlib/net/afunix.h
Normal 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_ */
|
@ -2,6 +2,20 @@ module net
|
|||||||
|
|
||||||
import time
|
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
|
// Shutdown shutsdown a socket and closes it
|
||||||
fn shutdown(handle int) ? {
|
fn shutdown(handle int) ? {
|
||||||
$if windows {
|
$if windows {
|
||||||
@ -11,8 +25,6 @@ fn shutdown(handle int) ? {
|
|||||||
C.shutdown(handle, C.SHUT_RDWR)
|
C.shutdown(handle, C.SHUT_RDWR)
|
||||||
socket_error(C.close(handle)) ?
|
socket_error(C.close(handle)) ?
|
||||||
}
|
}
|
||||||
|
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select waits for an io operation (specified by parameter `test`) to be available
|
// 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)
|
C.FD_SET(handle, &set)
|
||||||
|
|
||||||
seconds := timeout.milliseconds() / 1000
|
seconds := timeout.milliseconds() / 1000
|
||||||
microseconds := timeout - (seconds * time.second)
|
microseconds := time.Duration(timeout - (seconds * time.second)).microseconds()
|
||||||
|
|
||||||
mut tt := C.timeval{
|
mut tt := C.timeval{
|
||||||
tv_sec: u64(seconds)
|
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) ?
|
ready := @select(handle, test, timeout) ?
|
||||||
if ready {
|
if ready {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
return err_timed_out
|
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) ?
|
ready := @select(handle, test, d_timeout) ?
|
||||||
if ready {
|
if ready {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
return err_timed_out
|
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) ? {
|
fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ? {
|
||||||
return wait_for_common(handle, deadline, timeout, .read)
|
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)
|
|
||||||
)
|
|
||||||
|
@ -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
|
// wrap_read_result takes a read result and sees if it is 0 for graceful
|
||||||
// connection termination and returns none
|
// 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]
|
[inline]
|
||||||
fn wrap_read_result(result int) ?int {
|
fn wrap_read_result(result int) ?int {
|
||||||
if result > 0 || result < 0 {
|
if result == 0 {
|
||||||
return result
|
return none
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
return none
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
fn wrap_write_result(result int) ?int {
|
||||||
|
if result == 0 {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,16 @@ import time
|
|||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
pub mut:
|
pub mut:
|
||||||
port int = 8080
|
port int = 8080
|
||||||
handler fn(Request) Response
|
handler fn (Request) Response
|
||||||
read_timeout time.Duration = 30 * time.second
|
read_timeout time.Duration = 30 * time.second
|
||||||
write_timeout time.Duration = 30 * time.second
|
write_timeout time.Duration = 30 * time.second
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Server) listen_and_serve() ? {
|
pub fn (mut s Server) listen_and_serve() ? {
|
||||||
if voidptr(s.handler) == 0 {
|
if voidptr(s.handler) == 0 {
|
||||||
eprintln('Server handler not set, using debug handler')
|
eprintln('Server handler not set, using debug handler')
|
||||||
s.handler = fn(req Request) Response {
|
s.handler = fn (req Request) Response {
|
||||||
$if debug {
|
$if debug {
|
||||||
eprintln('[$time.now()] $req.method $req.url\n\r$req.header\n\r$req.data - 200 OK')
|
eprintln('[$time.now()] $req.method $req.url\n\r$req.header\n\r$req.data - 200 OK')
|
||||||
} $else {
|
} $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')
|
eprintln('Listening on :$s.port')
|
||||||
for {
|
for {
|
||||||
mut conn := l.accept() or {
|
mut conn := l.accept() or {
|
||||||
@ -67,7 +67,5 @@ fn (mut s Server) parse_and_respond(mut conn net.TcpConn) {
|
|||||||
if resp.version == .unknown {
|
if resp.version == .unknown {
|
||||||
resp.version = req.version
|
resp.version = req.version
|
||||||
}
|
}
|
||||||
conn.write(resp.bytes()) or {
|
conn.write(resp.bytes()) or { eprintln('error sending response: $err') }
|
||||||
eprintln('error sending response: $err')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
5
vlib/net/ipv6_v6only.h
Normal file
5
vlib/net/ipv6_v6only.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#if !defined(IPV6_V6ONLY)
|
||||||
|
|
||||||
|
#define IPV6_V6ONLY 27
|
||||||
|
|
||||||
|
#endif
|
@ -1,9 +1,5 @@
|
|||||||
module net
|
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
|
// WsaError is all of the socket errors that WSA provides from WSAGetLastError
|
||||||
pub enum WsaError {
|
pub enum WsaError {
|
||||||
//
|
//
|
||||||
@ -770,11 +766,13 @@ mut:
|
|||||||
szSystemStatus [129]byte
|
szSystemStatus [129]byte
|
||||||
iMaxSockets u16
|
iMaxSockets u16
|
||||||
iMaxUdpDg u16
|
iMaxUdpDg u16
|
||||||
lpVendorInfo byteptr
|
lpVendorInfo &byte
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init() {
|
fn init() {
|
||||||
mut wsadata := C.WSAData{}
|
mut wsadata := C.WSAData{
|
||||||
|
lpVendorInfo: 0
|
||||||
|
}
|
||||||
res := C.WSAStartup(net.wsa_v22, &wsadata)
|
res := C.WSAStartup(net.wsa_v22, &wsadata)
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
panic('socket: WSAStartup failed')
|
panic('socket: WSAStartup failed')
|
||||||
|
@ -37,7 +37,7 @@ pub struct SSL_METHOD {
|
|||||||
|
|
||||||
fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO
|
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
|
// there are actually 2 macros for BIO_get_ssl
|
||||||
// fn C.BIO_get_ssl(bp &C.BIO, ssl charptr, c int)
|
// 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_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
|
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_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)
|
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_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
|
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_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
|
fn C.SSL_shutdown(&C.SSL) int
|
||||||
|
|
||||||
|
@ -4,24 +4,29 @@ module openssl
|
|||||||
pub fn ssl_error(ret int, ssl voidptr) ?SSLError {
|
pub fn ssl_error(ret int, ssl voidptr) ?SSLError {
|
||||||
res := C.SSL_get_error(ssl, ret)
|
res := C.SSL_get_error(ssl, ret)
|
||||||
match SSLError(res) {
|
match SSLError(res) {
|
||||||
.ssl_error_syscall { return error_with_code('unrecoverable syscall ($res)', res) }
|
.ssl_error_syscall {
|
||||||
.ssl_error_ssl { return error_with_code('unrecoverable ssl protocol error ($res)',
|
return error_with_code('unrecoverable syscall ($res)', res)
|
||||||
res) }
|
}
|
||||||
else { return SSLError(res) }
|
.ssl_error_ssl {
|
||||||
|
return error_with_code('unrecoverable ssl protocol error ($res)', res)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return SSLError(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SSLError {
|
pub enum SSLError {
|
||||||
ssl_error_none = 0 //SSL_ERROR_NONE
|
ssl_error_none = 0 // SSL_ERROR_NONE
|
||||||
ssl_error_ssl = 1 //SSL_ERROR_SSL
|
ssl_error_ssl = 1 // SSL_ERROR_SSL
|
||||||
ssl_error_want_read = 2 //SSL_ERROR_WANT_READ
|
ssl_error_want_read = 2 // SSL_ERROR_WANT_READ
|
||||||
ssl_error_want_write = 3 //SSL_ERROR_WANT_WRITE
|
ssl_error_want_write = 3 // SSL_ERROR_WANT_WRITE
|
||||||
ssl_error_want_x509_lookup = 4 //SSL_ERROR_WANT_X509_LOOKUP
|
ssl_error_want_x509_lookup = 4 // SSL_ERROR_WANT_X509_LOOKUP
|
||||||
ssl_error_syscall = 5 //SSL_ERROR_SYSCALL
|
ssl_error_syscall = 5 // SSL_ERROR_SYSCALL
|
||||||
ssl_error_zero_return = 6 //SSL_ERROR_ZERO_RETURN
|
ssl_error_zero_return = 6 // SSL_ERROR_ZERO_RETURN
|
||||||
ssl_error_want_connect = 7 //SSL_ERROR_WANT_CONNECT
|
ssl_error_want_connect = 7 // SSL_ERROR_WANT_CONNECT
|
||||||
ssl_error_want_accept = 8 //SSL_ERROR_WANT_ACCEPT
|
ssl_error_want_accept = 8 // SSL_ERROR_WANT_ACCEPT
|
||||||
ssl_error_want_async = 9 //SSL_ERROR_WANT_ASYNC
|
ssl_error_want_async = 9 // SSL_ERROR_WANT_ASYNC
|
||||||
ssl_error_want_async_job = 10 //SSL_ERROR_WANT_ASYNC_JOB
|
ssl_error_want_async_job = 10 // SSL_ERROR_WANT_ASYNC_JOB
|
||||||
ssl_error_want_early = 11 //SSL_ERROR_WANT_EARLY
|
ssl_error_want_early = 11 // SSL_ERROR_WANT_EARLY
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import smtp
|
import net.smtp
|
||||||
import time
|
import time
|
||||||
|
|
||||||
// Used to test that a function call returns an error
|
// Used to test that a function call returns an error
|
||||||
|
@ -3,7 +3,6 @@ module net
|
|||||||
pub enum SocketOption {
|
pub enum SocketOption {
|
||||||
// TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it
|
// TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it
|
||||||
// and there is no easy way to define it
|
// and there is no easy way to define it
|
||||||
|
|
||||||
broadcast = C.SO_BROADCAST
|
broadcast = C.SO_BROADCAST
|
||||||
debug = C.SO_DEBUG
|
debug = C.SO_DEBUG
|
||||||
dont_route = C.SO_DONTROUTE
|
dont_route = C.SO_DONTROUTE
|
||||||
@ -11,27 +10,23 @@ pub enum SocketOption {
|
|||||||
keep_alive = C.SO_KEEPALIVE
|
keep_alive = C.SO_KEEPALIVE
|
||||||
linger = C.SO_LINGER
|
linger = C.SO_LINGER
|
||||||
oob_inline = C.SO_OOBINLINE
|
oob_inline = C.SO_OOBINLINE
|
||||||
|
|
||||||
reuse_addr = C.SO_REUSEADDR
|
reuse_addr = C.SO_REUSEADDR
|
||||||
|
|
||||||
recieve_buf_size = C.SO_RCVBUF
|
recieve_buf_size = C.SO_RCVBUF
|
||||||
recieve_low_size = C.SO_RCVLOWAT
|
recieve_low_size = C.SO_RCVLOWAT
|
||||||
recieve_timeout = C.SO_RCVTIMEO
|
recieve_timeout = C.SO_RCVTIMEO
|
||||||
|
|
||||||
send_buf_size = C.SO_SNDBUF
|
send_buf_size = C.SO_SNDBUF
|
||||||
send_low_size = C.SO_SNDLOWAT
|
send_low_size = C.SO_SNDLOWAT
|
||||||
send_timeout = C.SO_SNDTIMEO
|
send_timeout = C.SO_SNDTIMEO
|
||||||
|
|
||||||
socket_type = C.SO_TYPE
|
socket_type = C.SO_TYPE
|
||||||
|
ipv6_only = C.IPV6_V6ONLY
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline]
|
opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline]
|
||||||
opts_int = [
|
opts_int = [
|
||||||
.recieve_buf_size,
|
.recieve_buf_size,
|
||||||
.recieve_low_size,
|
.recieve_low_size,
|
||||||
.recieve_timeout,
|
.recieve_timeout,
|
||||||
|
|
||||||
.send_buf_size,
|
.send_buf_size,
|
||||||
.send_low_size,
|
.send_low_size,
|
||||||
.send_timeout,
|
.send_timeout,
|
||||||
@ -47,9 +42,9 @@ const (
|
|||||||
.recieve_buf_size,
|
.recieve_buf_size,
|
||||||
.recieve_low_size,
|
.recieve_low_size,
|
||||||
.recieve_timeout,
|
.recieve_timeout,
|
||||||
|
|
||||||
.send_buf_size,
|
.send_buf_size,
|
||||||
.send_low_size,
|
.send_low_size,
|
||||||
.send_timeout,
|
.send_timeout,
|
||||||
|
.ipv6_only,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
245
vlib/net/tcp.v
245
vlib/net/tcp.v
@ -10,8 +10,7 @@ const (
|
|||||||
[heap]
|
[heap]
|
||||||
pub struct TcpConn {
|
pub struct TcpConn {
|
||||||
pub mut:
|
pub mut:
|
||||||
sock TcpSocket
|
sock TcpSocket
|
||||||
max_write_chunk_size int = 4096
|
|
||||||
mut:
|
mut:
|
||||||
write_deadline time.Time
|
write_deadline time.Time
|
||||||
read_deadline time.Time
|
read_deadline time.Time
|
||||||
@ -20,30 +19,36 @@ mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn dial_tcp(address string) ?&TcpConn {
|
pub fn dial_tcp(address string) ?&TcpConn {
|
||||||
mut s := new_tcp_socket() ?
|
addrs := resolve_addrs_fuzzy(address, .tcp) ?
|
||||||
s.connect(address) ?
|
|
||||||
return &TcpConn{
|
// Very simple dialer
|
||||||
sock: s
|
for addr in addrs {
|
||||||
read_timeout: net.tcp_default_read_timeout
|
mut s := new_tcp_socket(addr.family()) ?
|
||||||
write_timeout: net.tcp_default_write_timeout
|
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() ? {
|
pub fn (mut c TcpConn) close() ? {
|
||||||
$if trace_tcp ? {
|
|
||||||
eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}')
|
|
||||||
}
|
|
||||||
c.sock.close() ?
|
c.sock.close() ?
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write_ptr blocks and attempts to write all data
|
// write_ptr blocks and attempts to write all data
|
||||||
pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int {
|
pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int {
|
||||||
$if trace_tcp ? {
|
$if trace_tcp ? {
|
||||||
eprintln('>>> TcpConn.write_ptr | c.sock.handle: ${c.sock.handle:6} | b: ${ptr_str(b)} len: ${len:6}')
|
eprintln(
|
||||||
}
|
'>>> TcpConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' +
|
||||||
$if trace_tcp_data_write ? {
|
|
||||||
eprintln('>>> TcpConn.write_ptr | data.len: ${len:6} | data: ' +
|
|
||||||
unsafe { b.vstring_with_len(len) })
|
unsafe { b.vstring_with_len(len) })
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -51,14 +56,8 @@ pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int {
|
|||||||
mut total_sent := 0
|
mut total_sent := 0
|
||||||
for total_sent < len {
|
for total_sent < len {
|
||||||
ptr := ptr_base + total_sent
|
ptr := ptr_base + total_sent
|
||||||
mut chunk_size := len - total_sent
|
remaining := len - total_sent
|
||||||
if chunk_size > c.max_write_chunk_size {
|
mut sent := C.send(c.sock.handle, ptr, remaining, msg_nosignal)
|
||||||
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)}')
|
|
||||||
}
|
|
||||||
if sent < 0 {
|
if sent < 0 {
|
||||||
code := error_code()
|
code := error_code()
|
||||||
if code == int(error_ewouldblock) {
|
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 {
|
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 ? {
|
$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 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
|
return res
|
||||||
}
|
}
|
||||||
code := error_code()
|
code := error_code()
|
||||||
if code == int(error_ewouldblock) {
|
if code == int(error_ewouldblock) {
|
||||||
c.wait_for_read() ?
|
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 ? {
|
$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 trace_tcp_data_read ? {
|
|
||||||
if res > 0 {
|
|
||||||
eprintln('<<< TcpConn.read_ptr | 2 data.len: ${res:6} | data: ' +
|
|
||||||
unsafe { buf_ptr.vstring_with_len(res) })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return socket_error(res)
|
return socket_error(res)
|
||||||
} else {
|
} else {
|
||||||
@ -175,23 +164,22 @@ pub fn (mut c TcpConn) wait_for_write() ? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c &TcpConn) peer_addr() ?Addr {
|
pub fn (c &TcpConn) peer_addr() ?Addr {
|
||||||
mut addr := C.sockaddr{}
|
mut addr := Addr{
|
||||||
len := sizeof(C.sockaddr)
|
addr: {
|
||||||
socket_error(C.getpeername(c.sock.handle, &addr, &len)) ?
|
Ip6: {}
|
||||||
return new_addr(addr)
|
}
|
||||||
|
}
|
||||||
|
mut size := sizeof(Addr)
|
||||||
|
socket_error(C.getpeername(c.sock.handle, voidptr(&addr), &size)) ?
|
||||||
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c &TcpConn) peer_ip() ?string {
|
pub fn (c &TcpConn) peer_ip() ?string {
|
||||||
buf := [44]char{}
|
return c.peer_addr() ?.str()
|
||||||
peeraddr := C.sockaddr_in{}
|
}
|
||||||
speeraddr := sizeof(peeraddr)
|
|
||||||
socket_error(C.getpeername(c.sock.handle, unsafe { &C.sockaddr(&peeraddr) }, &speeraddr)) ?
|
pub fn (c &TcpConn) addr() ?Addr {
|
||||||
cstr := &char(C.inet_ntop(SocketFamily.inet, &peeraddr.sin_addr, &buf[0], sizeof(buf)))
|
return c.sock.address()
|
||||||
if cstr == 0 {
|
|
||||||
return error('net.peer_ip: inet_ntop failed')
|
|
||||||
}
|
|
||||||
res := unsafe { cstring_to_vstring(cstr) }
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c TcpConn) str() string {
|
pub fn (c TcpConn) str() string {
|
||||||
@ -207,17 +195,18 @@ mut:
|
|||||||
accept_deadline time.Time
|
accept_deadline time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_tcp(port int) ?&TcpListener {
|
pub fn listen_tcp(family AddrFamily, saddr string) ?&TcpListener {
|
||||||
s := new_tcp_socket() ?
|
s := new_tcp_socket(family) ?
|
||||||
validate_port(port) ?
|
|
||||||
mut addr := C.sockaddr_in{}
|
addrs := resolve_addrs(saddr, family, .tcp) ?
|
||||||
addr.sin_family = int(SocketFamily.inet)
|
|
||||||
addr.sin_port = C.htons(port)
|
// TODO(logic to pick here)
|
||||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
addr := addrs[0]
|
||||||
size := sizeof(C.sockaddr_in)
|
|
||||||
// cast to the correct type
|
// cast to the correct type
|
||||||
sockaddr := unsafe { &C.sockaddr(&addr) }
|
alen := addr.len()
|
||||||
socket_error(C.bind(s.handle, sockaddr, size)) ?
|
bindres := C.bind(s.handle, voidptr(&addr), alen)
|
||||||
|
socket_error(bindres) ?
|
||||||
socket_error(C.listen(s.handle, 128)) ?
|
socket_error(C.listen(s.handle, 128)) ?
|
||||||
return &TcpListener{
|
return &TcpListener{
|
||||||
sock: s
|
sock: s
|
||||||
@ -227,26 +216,21 @@ pub fn listen_tcp(port int) ?&TcpListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut l TcpListener) accept() ?&TcpConn {
|
pub fn (mut l TcpListener) accept() ?&TcpConn {
|
||||||
$if trace_tcp ? {
|
addr := Addr{
|
||||||
eprintln(' TcpListener.accept | l.sock.handle: ${l.sock.handle:6}')
|
addr: {
|
||||||
|
Ip6: {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addr := C.sockaddr_storage{}
|
size := sizeof(Addr)
|
||||||
unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_storage)) }
|
mut new_handle := C.accept(l.sock.handle, voidptr(&addr), &size)
|
||||||
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)
|
|
||||||
if new_handle <= 0 {
|
if new_handle <= 0 {
|
||||||
l.wait_for_accept() ?
|
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 {
|
if new_handle == -1 || new_handle == 0 {
|
||||||
return none
|
return error('accept failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_sock := tcp_socket_from_handle(new_handle) ?
|
new_sock := tcp_socket_from_handle(new_handle) ?
|
||||||
$if trace_tcp ? {
|
|
||||||
eprintln(' TcpListener.accept | << new_sock.handle: ${new_sock.handle:6}')
|
|
||||||
}
|
|
||||||
return &TcpConn{
|
return &TcpConn{
|
||||||
sock: new_sock
|
sock: new_sock
|
||||||
read_timeout: net.tcp_default_read_timeout
|
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 {
|
if c.accept_deadline.unix != 0 {
|
||||||
return c.accept_deadline
|
return c.accept_deadline
|
||||||
}
|
}
|
||||||
return none
|
return error('invalid deadline')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) {
|
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() ? {
|
pub fn (mut c TcpListener) close() ? {
|
||||||
$if trace_tcp ? {
|
|
||||||
eprintln(' TcpListener.close | c.sock.handle: ${c.sock.handle:6}')
|
|
||||||
}
|
|
||||||
c.sock.close() ?
|
c.sock.close() ?
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c &TcpListener) address() ?Addr {
|
pub fn (c &TcpListener) addr() ?Addr {
|
||||||
return c.sock.address()
|
return c.sock.address()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,23 +274,23 @@ pub:
|
|||||||
handle int
|
handle int
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_tcp_socket() ?TcpSocket {
|
fn new_tcp_socket(family AddrFamily) ?TcpSocket {
|
||||||
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.tcp, 0)) ?
|
handle := socket_error(C.socket(family, SocketType.tcp, 0)) ?
|
||||||
mut s := TcpSocket{
|
mut s := TcpSocket{
|
||||||
handle: sockfd
|
handle: handle
|
||||||
}
|
}
|
||||||
$if trace_tcp ? {
|
// TODO(emily):
|
||||||
eprintln(' new_tcp_socket | s.handle: ${s.handle:6}')
|
// we shouldnt be using ioctlsocket in the 21st century
|
||||||
}
|
// use the non-blocking socket option instead please :)
|
||||||
// s.set_option_bool(.reuse_addr, true)?
|
|
||||||
|
// TODO(emily):
|
||||||
|
// Move this to its own function on the socket
|
||||||
s.set_option_int(.reuse_addr, 1) ?
|
s.set_option_int(.reuse_addr, 1) ?
|
||||||
$if !net_blocking_sockets ? {
|
$if windows {
|
||||||
$if windows {
|
t := u32(1) // true
|
||||||
t := u32(1) // true
|
socket_error(C.ioctlsocket(handle, fionbio, &t)) ?
|
||||||
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
|
} $else {
|
||||||
} $else {
|
socket_error(C.fcntl(handle, C.F_SETFL, C.fcntl(handle, C.F_GETFL) | C.O_NONBLOCK)) ?
|
||||||
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -319,18 +299,16 @@ fn tcp_socket_from_handle(sockfd int) ?TcpSocket {
|
|||||||
mut s := TcpSocket{
|
mut s := TcpSocket{
|
||||||
handle: sockfd
|
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_bool(.reuse_addr, true)?
|
||||||
s.set_option_int(.reuse_addr, 1) ?
|
s.set_option_int(.reuse_addr, 1) ?
|
||||||
$if !net_blocking_sockets ? {
|
s.set_dualstack(true) or {
|
||||||
$if windows {
|
// Not ipv6, we dont care
|
||||||
t := u32(1) // true
|
}
|
||||||
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
|
$if windows {
|
||||||
} $else {
|
t := u32(1) // true
|
||||||
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ?
|
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
|
return s
|
||||||
}
|
}
|
||||||
@ -345,18 +323,19 @@ pub fn (mut s TcpSocket) set_option_bool(opt SocketOption, value bool) ? {
|
|||||||
// }
|
// }
|
||||||
x := int(value)
|
x := int(value)
|
||||||
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ?
|
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) ? {
|
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))) ?
|
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int))) ?
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut s TcpSocket) close() ? {
|
fn (mut s TcpSocket) close() ? {
|
||||||
$if trace_tcp ? {
|
|
||||||
eprintln(' TcpSocket.close | s.handle: ${s.handle:6}')
|
|
||||||
}
|
|
||||||
return shutdown(s.handle)
|
return shutdown(s.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,35 +347,41 @@ const (
|
|||||||
connect_timeout = 5 * time.second
|
connect_timeout = 5 * time.second
|
||||||
)
|
)
|
||||||
|
|
||||||
fn (mut s TcpSocket) connect(a string) ? {
|
fn (mut s TcpSocket) connect(a Addr) ? {
|
||||||
$if trace_tcp ? {
|
res := C.connect(s.handle, voidptr(&a), a.len())
|
||||||
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)
|
|
||||||
if res == 0 {
|
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) ?
|
write_result := s.@select(.write, net.connect_timeout) ?
|
||||||
if write_result {
|
if write_result {
|
||||||
// succeeded
|
err := 0
|
||||||
return none
|
len := sizeof(err)
|
||||||
}
|
socket_error(C.getsockopt(s.handle, C.SOL_SOCKET, C.SO_ERROR, &err, &len)) ?
|
||||||
except_result := s.@select(.except, net.connect_timeout) ?
|
|
||||||
if except_result {
|
if err != 0 {
|
||||||
return err_connect_failed
|
return wrap_error(err)
|
||||||
|
}
|
||||||
|
// Succeeded
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the error
|
||||||
|
socket_error(C.connect(s.handle, voidptr(&a), a.len())) ?
|
||||||
|
|
||||||
// otherwise we timed out
|
// otherwise we timed out
|
||||||
return err_connect_timed_out
|
return err_connect_timed_out
|
||||||
}
|
}
|
||||||
|
|
||||||
// address gets the address of a socket
|
// address gets the address of a socket
|
||||||
pub fn (s &TcpSocket) address() ?Addr {
|
pub fn (s &TcpSocket) address() ?Addr {
|
||||||
mut addr := C.sockaddr_in{}
|
return addr_from_socket_handle(s.handle)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ const (
|
|||||||
// read_line is a *simple*, *non customizable*, blocking line reader.
|
// read_line is a *simple*, *non customizable*, blocking line reader.
|
||||||
// It will *always* return a line, ending with CRLF, or just '', on EOF.
|
// 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
|
// 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 {
|
pub fn (mut con TcpConn) read_line() string {
|
||||||
mut buf := [net.max_read]byte{} // where C.recv will store the network data
|
mut buf := [net.max_read]byte{} // where C.recv will store the network data
|
||||||
mut res := '' // The final result, including the ending \n.
|
mut res := '' // The final result, including the ending \n.
|
||||||
|
@ -3,16 +3,24 @@ import net
|
|||||||
import strings
|
import strings
|
||||||
|
|
||||||
const (
|
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) {
|
fn setup() (&net.TcpListener, &net.TcpConn, &net.TcpConn) {
|
||||||
mut server := net.listen_tcp(server_port) or { panic(err) }
|
mut server := net.listen_tcp(.ip6, 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) }
|
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 ? {
|
$if debug_peer_ip ? {
|
||||||
ip := con.peer_ip() or { '$err' }
|
eprintln('$server.addr()\n$client.peer_addr(), $client.addr()\n$socket.peer_addr(), $socket.addr()')
|
||||||
eprintln('connection peer_ip: $ip')
|
|
||||||
}
|
}
|
||||||
assert true
|
assert true
|
||||||
return server, client, socket
|
return server, client, socket
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import net
|
import net
|
||||||
|
import os
|
||||||
|
|
||||||
const (
|
const (
|
||||||
test_port = 45123
|
test_port = 45123
|
||||||
@ -18,19 +19,24 @@ fn handle_conn(mut c net.TcpConn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn echo_server(mut l net.TcpListener) ? {
|
fn one_shot_echo_server(mut l net.TcpListener, ch_started chan int) ? {
|
||||||
for {
|
eprintln('> one_shot_echo_server')
|
||||||
mut new_conn := l.accept() or { continue }
|
ch_started <- 1
|
||||||
go handle_conn(mut new_conn)
|
mut new_conn := l.accept() or { return error('could not accept') }
|
||||||
}
|
eprintln(' > new_conn: $new_conn')
|
||||||
return none
|
handle_conn(mut new_conn)
|
||||||
|
new_conn.close() or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn echo() ? {
|
fn echo(address string) ? {
|
||||||
mut c := net.dial_tcp('127.0.0.1:$test_port') ?
|
mut c := net.dial_tcp(address) ?
|
||||||
defer {
|
defer {
|
||||||
c.close() or { }
|
c.close() or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println('local: ' + c.addr() ?.str())
|
||||||
|
println(' peer: ' + c.peer_addr() ?.str())
|
||||||
|
|
||||||
data := 'Hello from vlib/net!'
|
data := 'Hello from vlib/net!'
|
||||||
c.write_string(data) ?
|
c.write_string(data) ?
|
||||||
mut buf := []byte{len: 4096}
|
mut buf := []byte{len: 4096}
|
||||||
@ -40,12 +46,55 @@ fn echo() ? {
|
|||||||
assert buf[i] == data[i]
|
assert buf[i] == data[i]
|
||||||
}
|
}
|
||||||
println('Got "$buf.bytestr()"')
|
println('Got "$buf.bytestr()"')
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_tcp() {
|
fn test_tcp_ip6() {
|
||||||
mut l := net.listen_tcp(test_port) or { panic(err) }
|
eprintln('\n>>> ${@FN}')
|
||||||
go echo_server(mut l)
|
address := 'localhost:$test_port'
|
||||||
echo() or { panic(err) }
|
mut l := net.listen_tcp(.ip6, ':$test_port') or { panic(err) }
|
||||||
l.close() or { }
|
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')
|
||||||
}
|
}
|
||||||
|
172
vlib/net/udp.v
172
vlib/net/udp.v
@ -9,8 +9,12 @@ const (
|
|||||||
|
|
||||||
struct UdpSocket {
|
struct UdpSocket {
|
||||||
handle int
|
handle int
|
||||||
local Addr
|
l Addr
|
||||||
remote ?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 {
|
pub struct UdpConn {
|
||||||
@ -23,27 +27,36 @@ mut:
|
|||||||
write_timeout time.Duration
|
write_timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
|
pub fn dial_udp(raddr string) ?&UdpConn {
|
||||||
local := resolve_addr(laddr, .inet, .udp) ?
|
addrs := resolve_addrs_fuzzy(raddr, .udp) ?
|
||||||
sbase := new_udp_socket(local.port) ?
|
|
||||||
sock := UdpSocket{
|
for addr in addrs {
|
||||||
handle: sbase.handle
|
// create a local socket for this
|
||||||
local: local
|
// bind to any port (or file) (we dont care in this
|
||||||
remote: resolve_wrapper(raddr)
|
// case because we only care about the remote)
|
||||||
}
|
if sock := new_udp_socket_for_remote(addr) {
|
||||||
return &UdpConn{
|
return &UdpConn{
|
||||||
sock: sock
|
sock: sock
|
||||||
read_timeout: net.udp_default_read_timeout
|
read_timeout: net.udp_default_read_timeout
|
||||||
write_timeout: net.udp_default_write_timeout
|
write_timeout: net.udp_default_write_timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_wrapper(raddr string) ?Addr {
|
// pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
|
||||||
// Dont have to do this when its fixed
|
// local := resolve_addr(laddr, .inet, .udp) ?
|
||||||
// this just allows us to store this `none` optional in a struct
|
|
||||||
x := resolve_addr(raddr, .inet, .udp) or { return none }
|
// sbase := new_udp_socket() ?
|
||||||
return x
|
|
||||||
}
|
// sock := UdpSocket{
|
||||||
|
// handle: sbase.handle
|
||||||
|
// l: local
|
||||||
|
// r: resolve_wrapper(raddr)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn (mut c UdpConn) write_ptr(b &byte, len int) ?int {
|
pub fn (mut c UdpConn) write_ptr(b &byte, len int) ?int {
|
||||||
remote := c.sock.remote() or { return err_no_udp_remote }
|
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 {
|
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 {
|
if res >= 0 {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
code := error_code()
|
code := error_code()
|
||||||
if code == int(error_ewouldblock) {
|
if code == int(error_ewouldblock) {
|
||||||
c.wait_for_write() ?
|
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 {
|
} else {
|
||||||
wrap_error(code) ?
|
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
|
// 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) {
|
pub fn (mut c UdpConn) read(mut buf []byte) ?(int, Addr) {
|
||||||
mut addr_from := C.sockaddr{}
|
mut addr := Addr{
|
||||||
len := sizeof(C.sockaddr)
|
addr: {
|
||||||
mut res := wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from,
|
Ip6: {}
|
||||||
&len)) ?
|
}
|
||||||
|
}
|
||||||
|
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 {
|
if res > 0 {
|
||||||
addr := new_addr(addr_from) ?
|
|
||||||
return res, addr
|
return res, addr
|
||||||
}
|
}
|
||||||
code := error_code()
|
code := error_code()
|
||||||
if code == int(error_ewouldblock) {
|
if code == int(error_ewouldblock) {
|
||||||
c.wait_for_read() ?
|
c.wait_for_read() ?
|
||||||
// same setup as in tcp
|
// same setup as in tcp
|
||||||
res = wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from,
|
res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len, 0,
|
||||||
&len)) ?
|
voidptr(&addr), &len)) ?
|
||||||
res2 := socket_error(res) ?
|
res2 := socket_error(res) ?
|
||||||
addr := new_addr(addr_from) ?
|
|
||||||
return res2, addr
|
return res2, addr
|
||||||
} else {
|
} else {
|
||||||
wrap_error(code) ?
|
wrap_error(code) ?
|
||||||
@ -170,44 +185,79 @@ pub fn (mut c UdpConn) close() ? {
|
|||||||
return c.sock.close()
|
return c.sock.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_udp(port int) ?&UdpConn {
|
pub fn listen_udp(laddr string) ?&UdpConn {
|
||||||
s := new_udp_socket(port) ?
|
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{
|
return &UdpConn{
|
||||||
sock: s
|
sock: new_udp_socket(addr) ?
|
||||||
read_timeout: net.udp_default_read_timeout
|
read_timeout: net.udp_default_read_timeout
|
||||||
write_timeout: net.udp_default_write_timeout
|
write_timeout: net.udp_default_write_timeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_udp_socket(local_port int) ?&UdpSocket {
|
fn new_udp_socket(local_addr Addr) ?&UdpSocket {
|
||||||
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.udp, 0)) ?
|
family := local_addr.family()
|
||||||
|
|
||||||
|
sockfd := socket_error(C.socket(family, SocketType.udp, 0)) ?
|
||||||
mut s := &UdpSocket{
|
mut s := &UdpSocket{
|
||||||
handle: sockfd
|
handle: sockfd
|
||||||
}
|
l: local_addr
|
||||||
s.set_option_bool(.reuse_addr, true) ?
|
r: Addr{
|
||||||
$if !net_blocking_sockets ? {
|
addr: {
|
||||||
$if windows {
|
Ip6: {}
|
||||||
t := u32(1) // true
|
}
|
||||||
socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ?
|
|
||||||
} $else {
|
|
||||||
socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK)) ?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// In UDP we always have to bind to a port
|
|
||||||
validate_port(local_port) ?
|
s.set_option_bool(.reuse_addr, true) ?
|
||||||
mut addr := C.sockaddr_in{}
|
|
||||||
addr.sin_family = int(SocketFamily.inet)
|
if family == .ip6 {
|
||||||
addr.sin_port = C.htons(local_port)
|
s.set_dualstack(true) ?
|
||||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
}
|
||||||
size := sizeof(C.sockaddr_in)
|
|
||||||
|
// 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
|
// cast to the correct type
|
||||||
sockaddr := unsafe { &C.sockaddr(&addr) }
|
socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len())) ?
|
||||||
socket_error(C.bind(s.handle, sockaddr, size)) ?
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s &UdpSocket) remote() ?Addr {
|
fn new_udp_socket_for_remote(raddr Addr) ?&UdpSocket {
|
||||||
return s.remote
|
// 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) ? {
|
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)
|
x := int(value)
|
||||||
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ?
|
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() ? {
|
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 {
|
fn (mut s UdpSocket) @select(test Select, timeout time.Duration) ?bool {
|
||||||
return @select(s.handle, test, timeout)
|
return @select(s.handle, test, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (s &UdpSocket) remote() ?Addr {
|
||||||
|
if s.has_r {
|
||||||
|
return s.r
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
@ -5,6 +5,8 @@ fn echo_server(mut c net.UdpConn) {
|
|||||||
mut buf := []byte{len: 100, init: 0}
|
mut buf := []byte{len: 100, init: 0}
|
||||||
read, addr := c.read(mut buf) or { continue }
|
read, addr := c.read(mut buf) or { continue }
|
||||||
|
|
||||||
|
println('Server got addr $addr')
|
||||||
|
|
||||||
c.write_to(addr, buf[..read]) or {
|
c.write_to(addr, buf[..read]) or {
|
||||||
println('Server: connection dropped')
|
println('Server: connection dropped')
|
||||||
return
|
return
|
||||||
@ -12,10 +14,15 @@ fn echo_server(mut c net.UdpConn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
local_addr = ':40003'
|
||||||
|
remote_addr = 'localhost:40003'
|
||||||
|
)
|
||||||
|
|
||||||
fn echo() ? {
|
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 {
|
defer {
|
||||||
c.close() or { }
|
c.close() or {}
|
||||||
}
|
}
|
||||||
data := 'Hello from vlib/net!'
|
data := 'Hello from vlib/net!'
|
||||||
|
|
||||||
@ -37,12 +44,10 @@ fn echo() ? {
|
|||||||
println('Got "$buf.bytestr()"')
|
println('Got "$buf.bytestr()"')
|
||||||
|
|
||||||
c.close() ?
|
c.close() ?
|
||||||
|
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_udp() {
|
fn test_udp() {
|
||||||
mut l := net.listen_udp(40001) or {
|
mut l := net.listen_udp(local_addr) or {
|
||||||
println(err)
|
println(err)
|
||||||
assert false
|
assert false
|
||||||
panic('')
|
panic('')
|
||||||
@ -54,7 +59,7 @@ fn test_udp() {
|
|||||||
assert false
|
assert false
|
||||||
}
|
}
|
||||||
|
|
||||||
l.close() or { }
|
l.close() or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -9,7 +9,7 @@ const (
|
|||||||
|
|
||||||
fn C.SUN_LEN(ptr &C.sockaddr_un) int
|
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
|
// Shutdown shutsdown a socket and closes it
|
||||||
fn shutdown(handle int) ? {
|
fn shutdown(handle int) ? {
|
||||||
@ -20,8 +20,6 @@ fn shutdown(handle int) ? {
|
|||||||
C.shutdown(handle, C.SHUT_RDWR)
|
C.shutdown(handle, C.SHUT_RDWR)
|
||||||
net.socket_error(C.close(handle)) ?
|
net.socket_error(C.close(handle)) ?
|
||||||
}
|
}
|
||||||
|
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select waits for an io operation (specified by parameter `test`) to be available
|
// 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) ?
|
ready := @select(handle, test, timeout) ?
|
||||||
if ready {
|
if ready {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
return net.err_timed_out
|
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) ?
|
ready := @select(handle, test, d_timeout) ?
|
||||||
if ready {
|
if ready {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
return net.err_timed_out
|
return net.err_timed_out
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,7 @@ fn error_code() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_stream_socket() ?StreamSocket {
|
fn new_stream_socket() ?StreamSocket {
|
||||||
sockfd := net.socket_error(C.socket(net.SocketFamily.unix, net.SocketType.stream,
|
sockfd := net.socket_error(C.socket(net.AddrFamily.unix, net.SocketType.tcp, 0)) ?
|
||||||
0)) ?
|
|
||||||
mut s := StreamSocket{
|
mut s := StreamSocket{
|
||||||
handle: sockfd
|
handle: sockfd
|
||||||
}
|
}
|
||||||
@ -67,19 +66,18 @@ fn (mut s StreamSocket) connect(a string) ? {
|
|||||||
addr.sun_family = C.AF_UNIX
|
addr.sun_family = C.AF_UNIX
|
||||||
unsafe { C.strncpy(&addr.sun_path[0], &char(a.str), max_sun_path) }
|
unsafe { C.strncpy(&addr.sun_path[0], &char(a.str), max_sun_path) }
|
||||||
size := C.SUN_LEN(&addr)
|
size := C.SUN_LEN(&addr)
|
||||||
sockaddr := unsafe { &C.sockaddr(&addr) }
|
res := C.connect(s.handle, voidptr(&addr), size)
|
||||||
res := C.connect(s.handle, sockaddr, size)
|
|
||||||
// if res != 1 {
|
// if res != 1 {
|
||||||
// return none
|
// return none
|
||||||
//}
|
//}
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
_ := error_code()
|
_ := error_code()
|
||||||
write_result := s.@select(.write, unix.connect_timeout) ?
|
write_result := s.@select(.write, unix.connect_timeout) ?
|
||||||
if write_result {
|
if write_result {
|
||||||
// succeeded
|
// succeeded
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
except_result := s.@select(.except, unix.connect_timeout) ?
|
except_result := s.@select(.except, unix.connect_timeout) ?
|
||||||
if except_result {
|
if except_result {
|
||||||
@ -100,8 +98,7 @@ pub fn listen_stream(sock string) ?&StreamListener {
|
|||||||
addr.sun_family = C.AF_UNIX
|
addr.sun_family = C.AF_UNIX
|
||||||
unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) }
|
unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) }
|
||||||
size := C.SUN_LEN(&addr)
|
size := C.SUN_LEN(&addr)
|
||||||
sockaddr := unsafe { &C.sockaddr(&addr) }
|
net.socket_error(C.bind(s.handle, voidptr(&addr), size)) ?
|
||||||
net.socket_error(C.bind(s.handle, sockaddr, size)) ?
|
|
||||||
net.socket_error(C.listen(s.handle, 128)) ?
|
net.socket_error(C.listen(s.handle, 128)) ?
|
||||||
return &StreamListener{
|
return &StreamListener{
|
||||||
sock: s
|
sock: s
|
||||||
@ -124,7 +121,7 @@ pub fn (mut l StreamListener) accept() ?&StreamConn {
|
|||||||
l.wait_for_accept() ?
|
l.wait_for_accept() ?
|
||||||
new_handle = C.accept(l.sock.handle, 0, 0)
|
new_handle = C.accept(l.sock.handle, 0, 0)
|
||||||
if new_handle == -1 || new_handle == 0 {
|
if new_handle == -1 || new_handle == 0 {
|
||||||
return none
|
return error('accept failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_sock := StreamSocket{
|
new_sock := StreamSocket{
|
||||||
@ -141,7 +138,7 @@ pub fn (c &StreamListener) accept_deadline() ?time.Time {
|
|||||||
if c.accept_deadline.unix != 0 {
|
if c.accept_deadline.unix != 0 {
|
||||||
return c.accept_deadline
|
return c.accept_deadline
|
||||||
}
|
}
|
||||||
return none
|
return error('no deadline')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c StreamListener) set_accept_deadline(deadline time.Time) {
|
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() ? {
|
pub fn (mut c StreamListener) close() ? {
|
||||||
c.sock.close() ?
|
c.sock.close() ?
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c StreamConn) close() ? {
|
pub fn (mut c StreamConn) close() ? {
|
||||||
c.sock.close() ?
|
c.sock.close() ?
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write_ptr blocks and attempts to write all data
|
// 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 {
|
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 ? {
|
$if trace_unix ? {
|
||||||
eprintln('<<< StreamConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
|
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()
|
code := error_code()
|
||||||
if code == int(error_ewouldblock) {
|
if code == int(error_ewouldblock) {
|
||||||
c.wait_for_read() ?
|
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 ? {
|
$if trace_unix ? {
|
||||||
eprintln('<<< StreamConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
|
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 {
|
} else {
|
||||||
net.wrap_error(code) ?
|
net.wrap_error(code) ?
|
||||||
}
|
}
|
||||||
return none
|
return net.socket_error(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c StreamConn) read(mut buf []byte) ?int {
|
pub fn (mut c StreamConn) read(mut buf []byte) ?int {
|
||||||
|
@ -4,11 +4,11 @@ import net.unix
|
|||||||
const test_port = os.join_path(os.temp_dir(), 'unix_domain_socket')
|
const test_port = os.join_path(os.temp_dir(), 'unix_domain_socket')
|
||||||
|
|
||||||
fn testsuite_begin() {
|
fn testsuite_begin() {
|
||||||
os.rm(test_port) or { }
|
os.rm(test_port) or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testsuite_end() {
|
fn testsuite_end() {
|
||||||
os.rm(test_port) or { }
|
os.rm(test_port) or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_conn(mut c unix.StreamConn) {
|
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 }
|
mut new_conn := l.accept() or { continue }
|
||||||
go handle_conn(mut new_conn)
|
go handle_conn(mut new_conn)
|
||||||
}
|
}
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn echo() ? {
|
fn echo() ? {
|
||||||
mut c := unix.connect_stream(test_port) ?
|
mut c := unix.connect_stream(test_port) ?
|
||||||
defer {
|
defer {
|
||||||
c.close() or { }
|
c.close() or {}
|
||||||
}
|
}
|
||||||
data := 'Hello from vlib/net!'
|
data := 'Hello from vlib/net!'
|
||||||
c.write_string(data) ?
|
c.write_string(data) ?
|
||||||
@ -47,12 +46,12 @@ fn echo() ? {
|
|||||||
assert buf[i] == data[i]
|
assert buf[i] == data[i]
|
||||||
}
|
}
|
||||||
println('Got "$buf.bytestr()"')
|
println('Got "$buf.bytestr()"')
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_tcp() {
|
fn test_tcp() {
|
||||||
mut l := unix.listen_stream(test_port) or { panic(err) }
|
mut l := unix.listen_stream(test_port) or { panic(err) }
|
||||||
go echo_server(mut l)
|
go echo_server(mut l)
|
||||||
echo() or { panic(err) }
|
echo() or { panic(err) }
|
||||||
l.close() or { }
|
l.close() or {}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ const (
|
|||||||
// validate_port checks whether a port is valid
|
// validate_port checks whether a port is valid
|
||||||
// and returns the port or an error
|
// and returns the port or an error
|
||||||
pub fn validate_port(port int) ?u16 {
|
pub fn validate_port(port int) ?u16 {
|
||||||
if port <= socket_max_port {
|
if port <= net.socket_max_port {
|
||||||
return u16(port)
|
return u16(port)
|
||||||
} else {
|
} else {
|
||||||
return err_port_out_of_range
|
return err_port_out_of_range
|
||||||
@ -19,6 +19,9 @@ pub fn split_address(addr string) ?(string, u16) {
|
|||||||
port := addr.all_after_last(':').int()
|
port := addr.all_after_last(':').int()
|
||||||
address := addr.all_before_last(':')
|
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
|
return address, p
|
||||||
}
|
}
|
||||||
|
@ -493,14 +493,32 @@ pub fn (mut f File) write_str(s string) ? {
|
|||||||
f.write_string(s) or { return err }
|
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`
|
// read_struct reads a single struct of type `T`
|
||||||
pub fn (mut f File) read_struct<T>(mut t T) ? {
|
pub fn (mut f File) read_struct<T>(mut t T) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return none
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(*t))
|
tsize := int(sizeof(*t))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return none
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
nbytes := fread(t, 1, tsize, f.cfile) ?
|
nbytes := fread(t, 1, tsize, f.cfile) ?
|
||||||
if nbytes != tsize {
|
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
|
// 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) ? {
|
pub fn (mut f File) read_struct_at<T>(mut t T, pos u64) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return none
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(*t))
|
tsize := int(sizeof(*t))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return none
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
mut nbytes := 0
|
mut nbytes := 0
|
||||||
$if x64 {
|
$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`
|
// read_raw reads and returns a single instance of type `T`
|
||||||
pub fn (mut f File) read_raw<T>() ?T {
|
pub fn (mut f File) read_raw<T>() ?T {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return none
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return none
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
mut t := T{}
|
mut t := T{}
|
||||||
nbytes := fread(&t, 1, tsize, f.cfile) ?
|
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`
|
// 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 {
|
pub fn (mut f File) read_raw_at<T>(pos u64) ?T {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return none
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return none
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
mut nbytes := 0
|
mut nbytes := 0
|
||||||
mut t := T{}
|
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`
|
// write_struct writes a single struct of type `T`
|
||||||
pub fn (mut f File) write_struct<T>(t &T) ? {
|
pub fn (mut f File) write_struct<T>(t &T) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 0')
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
C.errno = 0
|
C.errno = 0
|
||||||
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
|
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
|
// 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) ? {
|
pub fn (mut f File) write_struct_at<T>(t &T, pos u64) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 0')
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
C.errno = 0
|
C.errno = 0
|
||||||
mut nbytes := 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`
|
// write_raw writes a single instance of type `T`
|
||||||
pub fn (mut f File) write_raw<T>(t &T) ? {
|
pub fn (mut f File) write_raw<T>(t &T) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 0')
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
C.errno = 0
|
C.errno = 0
|
||||||
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
|
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`
|
// 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) ? {
|
pub fn (mut f File) write_raw_at<T>(t &T, pos u64) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error_file_not_opened()
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(T))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 0')
|
return error_size_of_type_0()
|
||||||
}
|
}
|
||||||
mut nbytes := 0
|
mut nbytes := 0
|
||||||
|
|
||||||
|
@ -14,6 +14,20 @@ import picohttpparser
|
|||||||
#flag @VEXEROOT/thirdparty/picoev/picoev.o
|
#flag @VEXEROOT/thirdparty/picoev/picoev.o
|
||||||
#include "src/picoev.h"
|
#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.atoi() int
|
||||||
|
|
||||||
fn C.strncasecmp(s1 &char, s2 &char, n size_t) 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 {
|
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
|
assert fd != -1
|
||||||
flag := 1
|
flag := 1
|
||||||
assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0
|
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_port = C.htons(config.port)
|
||||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
||||||
size := 16 // sizeof(C.sockaddr_in)
|
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
|
assert bind_res == 0
|
||||||
listen_res := C.listen(fd, C.SOMAXCONN)
|
listen_res := C.listen(fd, C.SOMAXCONN)
|
||||||
assert listen_res == 0
|
assert listen_res == 0
|
||||||
|
@ -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 {
|
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)
|
elem_type_sym := t.get_type_symbol(elem_type)
|
||||||
ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' }
|
ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' }
|
||||||
mut size_str := size.str()
|
size_str := if size_expr is EmptyExpr || size != 987654321 {
|
||||||
if t.is_fmt {
|
size.str()
|
||||||
if size_expr is Ident {
|
} else {
|
||||||
size_str = size_expr.name
|
size_expr.str()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return '[$size_str]$ptr$elem_type_sym.name'
|
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{
|
info: ArrayFixed{
|
||||||
elem_type: elem_type
|
elem_type: elem_type
|
||||||
size: size
|
size: size
|
||||||
expr: size_expr
|
size_expr: size_expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t.register_type_symbol(array_fixed_type)
|
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
|
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')
|
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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -809,8 +809,8 @@ pub mut:
|
|||||||
|
|
||||||
pub struct ArrayFixed {
|
pub struct ArrayFixed {
|
||||||
pub:
|
pub:
|
||||||
size int
|
size int
|
||||||
expr Expr // used by fmt for e.g. ´[my_const]byte´
|
size_expr Expr // used by fmt for e.g. ´[my_const]byte´
|
||||||
pub mut:
|
pub mut:
|
||||||
elem_type Type
|
elem_type Type
|
||||||
}
|
}
|
||||||
@ -890,13 +890,11 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
|
|||||||
.array_fixed {
|
.array_fixed {
|
||||||
info := sym.info as ArrayFixed
|
info := sym.info as ArrayFixed
|
||||||
elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
|
elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
|
||||||
mut size_str := info.size.str()
|
if info.size_expr is EmptyExpr {
|
||||||
if t.is_fmt {
|
res = '[$info.size]$elem_str'
|
||||||
if info.expr is Ident {
|
} else {
|
||||||
size_str = info.expr.name
|
res = '[$info.size_expr]$elem_str'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
res = '[$size_str]$elem_str'
|
|
||||||
}
|
}
|
||||||
.chan {
|
.chan {
|
||||||
// TODO currently the `chan` struct in builtin is not considered a struct but a chan
|
// TODO currently the `chan` struct in builtin is not considered a struct but a chan
|
||||||
|
@ -19,7 +19,7 @@ pub fn (i &Inspector) visit(node ast.Node) ? {
|
|||||||
if i.inspector_callback(node, i.data) {
|
if i.inspector_callback(node, i.data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return none
|
return error('')
|
||||||
}
|
}
|
||||||
|
|
||||||
// inspect traverses and checks the AST node on a depth-first order and based on the data given
|
// inspect traverses and checks the AST node on a depth-first order and based on the data given
|
||||||
|
@ -22,7 +22,7 @@ fn (mut n NodeByOffset) visit(node ast.Node) ? {
|
|||||||
node_pos := node.position()
|
node_pos := node.position()
|
||||||
if n.pos >= node_pos.pos && n.pos <= node_pos.pos + node_pos.len && node !is ast.File {
|
if n.pos >= node_pos.pos && n.pos <= node_pos.pos + node_pos.len && node !is ast.File {
|
||||||
n.node = node
|
n.node = node
|
||||||
return none
|
return error('')
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ const (
|
|||||||
|
|
||||||
fn (mut v Builder) find_win_cc() ? {
|
fn (mut v Builder) find_win_cc() ? {
|
||||||
$if !windows {
|
$if !windows {
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
ccompiler_version_res := os.execute('$v.pref.ccompiler -v')
|
ccompiler_version_res := os.execute('$v.pref.ccompiler -v')
|
||||||
if ccompiler_version_res.exit_code != 0 {
|
if ccompiler_version_res.exit_code != 0 {
|
||||||
@ -63,7 +63,7 @@ fn (mut v Builder) find_win_cc() ? {
|
|||||||
if v.pref.is_verbose {
|
if v.pref.is_verbose {
|
||||||
println('tcc not found')
|
println('tcc not found')
|
||||||
}
|
}
|
||||||
return none
|
return error('tcc not found')
|
||||||
}
|
}
|
||||||
v.pref.ccompiler = thirdparty_tcc
|
v.pref.ccompiler = thirdparty_tcc
|
||||||
v.pref.ccompiler_type = .tinyc
|
v.pref.ccompiler_type = .tinyc
|
||||||
|
@ -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 {
|
fn find_windows_kit_root_by_env(target_arch string) ?WindowsKit {
|
||||||
kit_root := os.getenv('WindowsSdkDir')
|
kit_root := os.getenv('WindowsSdkDir')
|
||||||
if kit_root == '' {
|
if kit_root == '' {
|
||||||
return none
|
return error('empty WindowsSdkDir')
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_windows_kit(kit_root, target_arch)
|
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 {
|
fn find_vs_by_env(host_arch string, target_arch string) ?VsInstallation {
|
||||||
vs_dir := os.getenv('VSINSTALLDIR')
|
vs_dir := os.getenv('VSINSTALLDIR')
|
||||||
if vs_dir == '' {
|
if vs_dir == '' {
|
||||||
return none
|
return error('empty VSINSTALLDIR')
|
||||||
}
|
}
|
||||||
|
|
||||||
vc_tools_dir := os.getenv('VCToolsInstallDir')
|
vc_tools_dir := os.getenv('VCToolsInstallDir')
|
||||||
if vc_tools_dir == '' {
|
if vc_tools_dir == '' {
|
||||||
return none
|
return error('empty VCToolsInstallDir')
|
||||||
}
|
}
|
||||||
|
|
||||||
bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch'
|
bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch'
|
||||||
|
@ -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) ? {
|
pub fn (mut c Checker) check_expected(got ast.Type, expected ast.Type) ? {
|
||||||
if c.check_types(got, expected) {
|
if !c.check_types(got, expected) {
|
||||||
return
|
return error(c.expected_msg(got, expected))
|
||||||
}
|
}
|
||||||
return error(c.expected_msg(got, expected))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
|
@ -3026,6 +3026,10 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
|
|||||||
got_types_0_idx := got_types[0].idx()
|
got_types_0_idx := got_types[0].idx()
|
||||||
if exp_is_optional
|
if exp_is_optional
|
||||||
&& got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx] {
|
&& 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
|
return
|
||||||
}
|
}
|
||||||
if expected_types.len > 0 && expected_types.len != got_types.len {
|
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 {
|
if array_init.is_fixed {
|
||||||
idx := c.table.find_or_register_array_fixed(elem_type, array_init.exprs.len,
|
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) {
|
if elem_type.has_flag(.generic) {
|
||||||
array_init.typ = ast.new_type(idx).set_flag(.generic)
|
array_init.typ = ast.new_type(idx).set_flag(.generic)
|
||||||
} else {
|
} 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) ? {
|
fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
|
||||||
if typ == 0 {
|
if typ == 0 {
|
||||||
c.error('unknown type', pos)
|
c.error('unknown type', pos)
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
sym := c.table.get_type_symbol(typ)
|
sym := c.table.get_type_symbol(typ)
|
||||||
match sym.kind {
|
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.') {
|
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`'),
|
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
|
||||||
pos)
|
pos)
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.int_literal, .float_literal {
|
.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`?'
|
'unknown type `$sym.name`.\nDid you mean `f64`?'
|
||||||
}
|
}
|
||||||
c.error(msg, pos)
|
c.error(msg, pos)
|
||||||
return none
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.array {
|
.array {
|
||||||
|
@ -5371,21 +5371,23 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||||||
if info.is_union && struct_init.fields.len > 1 {
|
if info.is_union && struct_init.fields.len > 1 {
|
||||||
verror('union must not have more than 1 initializer')
|
verror('union must not have more than 1 initializer')
|
||||||
}
|
}
|
||||||
for embed in info.embeds {
|
if !info.is_union {
|
||||||
embed_sym := g.table.get_type_symbol(embed)
|
for embed in info.embeds {
|
||||||
embed_name := embed_sym.embed_name()
|
embed_sym := g.table.get_type_symbol(embed)
|
||||||
if embed_name !in inited_fields {
|
embed_name := embed_sym.embed_name()
|
||||||
default_init := ast.StructInit{
|
if embed_name !in inited_fields {
|
||||||
typ: embed
|
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)
|
// g.zero_struct_fields(info, inited_fields)
|
||||||
@ -5691,6 +5693,22 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
|
|||||||
}
|
}
|
||||||
// TODO avoid buffer manip
|
// TODO avoid buffer manip
|
||||||
start_pos := g.type_definitions.len
|
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 {
|
if typ.info.is_union {
|
||||||
g.type_definitions.writeln('union $name {')
|
g.type_definitions.writeln('union $name {')
|
||||||
} else {
|
} else {
|
||||||
@ -5726,12 +5744,13 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
|
|||||||
}
|
}
|
||||||
// g.type_definitions.writeln('} $name;\n')
|
// g.type_definitions.writeln('} $name;\n')
|
||||||
//
|
//
|
||||||
attrs := if typ.info.attrs.contains('packed') {
|
ti_attrs := if typ.info.attrs.contains('packed') {
|
||||||
'__attribute__((__packed__))'
|
'__attribute__((__packed__))'
|
||||||
} else {
|
} else {
|
||||||
''
|
''
|
||||||
}
|
}
|
||||||
g.type_definitions.writeln('}$attrs;\n')
|
g.type_definitions.writeln('}$ti_attrs;\n')
|
||||||
|
g.type_definitions.writeln(post_pragma)
|
||||||
}
|
}
|
||||||
ast.Alias {
|
ast.Alias {
|
||||||
// ast.Alias { TODO
|
// 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) {
|
fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) {
|
||||||
cvar_name := c_name(var_name)
|
cvar_name := c_name(var_name)
|
||||||
mr_styp := g.base_type(return_type)
|
mr_styp := g.base_type(return_type)
|
||||||
is_none_ok := mr_styp == 'void'
|
is_none_ok := return_type == ast.ovoid_type
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
if is_none_ok {
|
if is_none_ok {
|
||||||
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
|
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
|
||||||
|
@ -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)
|
rec.typ = p.parse_type_with_mut(rec.is_mut)
|
||||||
if rec.typ.idx() == 0 {
|
if rec.typ.idx() == 0 {
|
||||||
// error is set in parse_type
|
// error is set in parse_type
|
||||||
return none
|
return error('void receiver type')
|
||||||
}
|
}
|
||||||
rec.type_pos = rec.type_pos.extend(p.prev_tok.position())
|
rec.type_pos = rec.type_pos.extend(p.prev_tok.position())
|
||||||
if is_amp && rec.is_mut {
|
if is_amp && rec.is_mut {
|
||||||
p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
|
p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`',
|
||||||
lpar_pos.extend(p.tok.position()))
|
lpar_pos.extend(p.tok.position()))
|
||||||
return none
|
return error('invalid `mut f &Foo`')
|
||||||
}
|
}
|
||||||
if is_shared {
|
if is_shared {
|
||||||
rec.typ = rec.typ.set_flag(.shared_f)
|
rec.typ = rec.typ.set_flag(.shared_f)
|
||||||
|
@ -13,31 +13,38 @@ pub fn (mut p Parser) parse_array_type() ast.Type {
|
|||||||
if p.tok.kind in [.number, .name] {
|
if p.tok.kind in [.number, .name] {
|
||||||
mut fixed_size := 0
|
mut fixed_size := 0
|
||||||
size_expr := p.expr(0)
|
size_expr := p.expr(0)
|
||||||
match size_expr {
|
if p.pref.is_fmt {
|
||||||
ast.IntegerLiteral {
|
fixed_size = 987654321
|
||||||
fixed_size = size_expr.val.int()
|
} else {
|
||||||
}
|
match size_expr {
|
||||||
ast.Ident {
|
ast.IntegerLiteral {
|
||||||
if const_field := p.global_scope.find_const('${p.mod}.$size_expr.name') {
|
fixed_size = size_expr.val.int()
|
||||||
if const_field.expr is ast.IntegerLiteral {
|
}
|
||||||
fixed_size = const_field.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 {
|
} else {
|
||||||
p.error_with_pos('non-constant array bound `$size_expr.name`',
|
if p.pref.is_fmt {
|
||||||
size_expr.pos)
|
// 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 show_non_const_error {
|
||||||
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 {
|
|
||||||
p.error_with_pos('non-constant array bound `$size_expr.name`',
|
p.error_with_pos('non-constant array bound `$size_expr.name`',
|
||||||
size_expr.pos)
|
size_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
p.error('expecting `int` for fixed size')
|
||||||
p.error('expecting `int` for fixed size')
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.check(.rsbr)
|
p.check(.rsbr)
|
||||||
|
@ -303,7 +303,8 @@ pub fn run<T>(global_app &T, port int) {
|
|||||||
// mut app := &T{}
|
// mut app := &T{}
|
||||||
// run_app<T>(mut app, port)
|
// 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')
|
println('[Vweb] Running app on http://localhost:$port')
|
||||||
// app.Context = Context{
|
// app.Context = Context{
|
||||||
// conn: 0
|
// conn: 0
|
||||||
|
@ -83,7 +83,6 @@ fn (mut ws Client) shutdown_socket() ? {
|
|||||||
} else {
|
} else {
|
||||||
ws.conn.close() ?
|
ws.conn.close() ?
|
||||||
}
|
}
|
||||||
return none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dial_socket connects tcp socket and initializes default configurations
|
// dial_socket connects tcp socket and initializes default configurations
|
||||||
|
@ -4,7 +4,7 @@ module main
|
|||||||
import x.websocket
|
import x.websocket
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
mut s := websocket.new_server(9002, '/')
|
mut s := websocket.new_server(.ip6, 9002, '/')
|
||||||
s.on_message(on_message)
|
s.on_message(on_message)
|
||||||
s.listen() or { panic(err) }
|
s.listen() or { panic(err) }
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ mut:
|
|||||||
message_callbacks []MessageEventHandler // new message callback functions
|
message_callbacks []MessageEventHandler // new message callback functions
|
||||||
close_callbacks []CloseEventHandler // close message callback functions
|
close_callbacks []CloseEventHandler // close message callback functions
|
||||||
pub:
|
pub:
|
||||||
|
family net.AddrFamily = .ip
|
||||||
port int // port used as listen to incoming connections
|
port int // port used as listen to incoming connections
|
||||||
is_ssl bool // true if secure connection (not supported yet on server)
|
is_ssl bool // true if secure connection (not supported yet on server)
|
||||||
pub mut:
|
pub mut:
|
||||||
@ -34,9 +35,10 @@ pub mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// new_server instance a new websocket server on provided port and route
|
// 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{
|
return &Server{
|
||||||
ls: 0
|
ls: 0
|
||||||
|
family: family
|
||||||
port: port
|
port: port
|
||||||
logger: &log.Log{
|
logger: &log.Log{
|
||||||
level: .info
|
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
|
// listen start listen and process to incoming connections from websocket clients
|
||||||
pub fn (mut s Server) listen() ? {
|
pub fn (mut s Server) listen() ? {
|
||||||
s.logger.info('websocket server: start listen on port $s.port')
|
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)
|
s.set_state(.open)
|
||||||
go s.handle_ping()
|
go s.handle_ping()
|
||||||
for {
|
for {
|
||||||
|
@ -1,31 +1,51 @@
|
|||||||
import os
|
import os
|
||||||
|
import net
|
||||||
import x.websocket
|
import x.websocket
|
||||||
import time
|
import time
|
||||||
import rand
|
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 {
|
struct WebsocketTestResults {
|
||||||
pub mut:
|
pub mut:
|
||||||
nr_messages int
|
nr_messages int
|
||||||
nr_pong_received 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 github_job = os.getenv('GITHUB_JOB')
|
||||||
|
|
||||||
|
const should_skip = github_job != '' && github_job != 'websocket_tests'
|
||||||
|
|
||||||
// tests with internal ws servers
|
// tests with internal ws servers
|
||||||
fn test_ws() {
|
fn test_ws_ipv6() {
|
||||||
if github_job != '' && github_job != 'websocket_tests' {
|
if should_skip {
|
||||||
// Do not run these tests everytime, since they are flaky.
|
|
||||||
// They have their own specialized CI runner.
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
port := 30000 + rand.intn(1024)
|
port := 30000 + rand.intn(1024)
|
||||||
go start_server(port)
|
go start_server(.ip6, port)
|
||||||
time.sleep(500 * time.millisecond)
|
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) ? {
|
// tests with internal ws servers
|
||||||
mut s := websocket.new_server(listen_port, '')
|
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
|
// make that in execution test time give time to execute at least one time
|
||||||
s.ping_interval = 1
|
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
|
// 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 ...')
|
eprintln('connecting to $uri ...')
|
||||||
|
|
||||||
mut test_results := WebsocketTestResults{}
|
mut test_results := WebsocketTestResults{}
|
||||||
|
Loading…
Reference in New Issue
Block a user