Skip to content

Commit b88569c

Browse files
authored
x.net: new net module (#6130)
1 parent 9b171b7 commit b88569c

File tree

13 files changed

+2056
-0
lines changed

13 files changed

+2056
-0
lines changed

examples/net_raw_http.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Simple raw HTTP head request
2+
import x.net as net
3+
import time
4+
5+
// Make a new connection
6+
mut conn := net.dial_tcp('google.com:80')?
7+
// Simple http HEAD request for a file
8+
conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n')?
9+
// Make sure to set a timeout so we can wait for a response!
10+
conn.set_read_timeout(10 * time.second)
11+
// Read all the data that is waiting
12+
result := conn.read()?
13+
// Cast to string and print result
14+
println(result.bytestr())

vlib/x/net/aasocket.c.v

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
module net
2+
3+
// Select represents a select operation
4+
enum Select {
5+
read write except
6+
}
7+
8+
// SocketType are the available sockets
9+
pub enum SocketType {
10+
udp = C.SOCK_DGRAM
11+
tcp = C.SOCK_STREAM
12+
}
13+
14+
// SocketFamily are the available address families
15+
pub enum SocketFamily {
16+
inet = C. AF_INET
17+
}
18+
19+
struct C.in_addr {
20+
mut:
21+
s_addr int
22+
}
23+
24+
struct C.sockaddr {
25+
}
26+
27+
struct C.sockaddr_in {
28+
mut:
29+
sin_family int
30+
sin_port int
31+
sin_addr C.in_addr
32+
}
33+
34+
35+
struct C.addrinfo {
36+
mut:
37+
ai_family int
38+
ai_socktype int
39+
ai_flags int
40+
ai_protocol int
41+
ai_addrlen int
42+
ai_addr voidptr
43+
ai_canonname voidptr
44+
ai_next voidptr
45+
}
46+
47+
struct C.sockaddr_storage {
48+
}
49+
50+
fn C.socket() int
51+
52+
fn C.setsockopt() int
53+
54+
fn C.htonl() int
55+
56+
fn C.htons() int
57+
58+
fn C.bind() int
59+
60+
fn C.listen() int
61+
62+
fn C.accept() int
63+
64+
fn C.getaddrinfo() int
65+
66+
fn C.connect() int
67+
68+
fn C.send() int
69+
fn C.sendto() int
70+
71+
fn C.recv() int
72+
fn C.recvfrom() int
73+
74+
fn C.shutdown() int
75+
76+
fn C.ntohs() int
77+
78+
fn C.inet_ntop() int
79+
80+
fn C.getsockname() int
81+
82+
// defined in builtin
83+
// fn C.read() int
84+
// fn C.close() int
85+
86+
fn C.ioctlsocket() int
87+
fn C.fcntl() int
88+
89+
fn C.@select() int
90+
fn C.FD_ZERO()
91+
fn C.FD_SET()
92+
fn C.FD_ISSET() bool
93+
94+
[typedef]
95+
struct C.fd_set {}

vlib/x/net/address.v

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
module net
2+
3+
// Addr represents an ip address
4+
pub struct Addr {
5+
addr C.sockaddr
6+
len int
7+
pub:
8+
saddr string
9+
port int
10+
}
11+
12+
pub fn (a Addr) str() string {
13+
return '${a.saddr}:${a.port}'
14+
}
15+
16+
const (
17+
max_ipv4_addr_len = 16
18+
)
19+
20+
fn new_addr(addr C.sockaddr, _saddr string, _port int) ?Addr {
21+
mut saddr := _saddr
22+
if saddr == '' {
23+
// Convert to string representation
24+
buf := []byte{ len: max_ipv4_addr_len, init: 0 }
25+
$if windows {
26+
res := C.inet_ntop(SocketFamily.inet, &addr, buf.data, buf.len)
27+
if res == 0 {
28+
socket_error(-1)?
29+
}
30+
} $else {
31+
res := C.inet_ntop(SocketFamily.inet, &addr, buf.data, buf.len)
32+
if res == 0 {
33+
socket_error(-1)?
34+
}
35+
}
36+
saddr = buf.bytestr()
37+
}
38+
39+
mut port := _port
40+
if port == 0 {
41+
hport := (&C.sockaddr_in(&addr)).sin_port
42+
port = C.ntohs(hport)
43+
}
44+
45+
return Addr {
46+
addr int(sizeof(C.sockaddr)) saddr port
47+
}
48+
}
49+
50+
pub fn resolve_addr(addr string, family SocketFamily, typ SocketType) ?Addr {
51+
address, port := split_address(addr)?
52+
53+
mut hints := C.addrinfo{}
54+
hints.ai_family = family
55+
hints.ai_socktype = typ
56+
hints.ai_flags = C.AI_PASSIVE
57+
hints.ai_protocol = 0
58+
hints.ai_addrlen = 0
59+
hints.ai_canonname = C.NULL
60+
hints.ai_addr = C.NULL
61+
hints.ai_next = C.NULL
62+
info := &C.addrinfo(0)
63+
64+
sport := '$port'
65+
66+
// This might look silly but is reccomended by MSDN
67+
$if windows {
68+
socket_error(0-C.getaddrinfo(address.str, sport.str, &hints, &info))?
69+
} $else {
70+
wrap_error(C.getaddrinfo(address.str, sport.str, &hints, &info))
71+
}
72+
73+
return new_addr(*info.ai_addr, address, port)
74+
}

vlib/x/net/common.v

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
module net
2+
3+
import time
4+
5+
// Shutdown shutsdown a socket and closes it
6+
fn shutdown(handle int) ? {
7+
$if windows {
8+
C.shutdown(handle, C.SD_BOTH)
9+
socket_error(C.closesocket(handle))?
10+
} $else {
11+
C.shutdown(handle, C.SHUT_RDWR)
12+
socket_error(C.close(handle))?
13+
}
14+
15+
return none
16+
}
17+
18+
// Select waits for an io operation (specified by parameter `test`) to be available
19+
fn @select(handle int, test Select, timeout time.Duration) ?bool {
20+
set := C.fd_set{}
21+
22+
C.FD_ZERO(&set)
23+
C.FD_SET(handle, &set)
24+
25+
seconds := timeout.milliseconds() / 1000
26+
microseconds := timeout - (seconds * time.second)
27+
28+
mut tt := C.timeval{
29+
tv_sec: u64(seconds)
30+
tv_usec: u64(microseconds)
31+
}
32+
33+
mut timeval_timeout := &tt
34+
35+
// infinite timeout is signaled by passing null as the timeout to
36+
// select
37+
if timeout == infinite_timeout {
38+
timeval_timeout = &C.timeval(0)
39+
}
40+
41+
match test {
42+
.read {
43+
socket_error(C.@select(handle+1, &set, C.NULL, C.NULL, timeval_timeout))?
44+
}
45+
.write {
46+
socket_error(C.@select(handle+1, C.NULL, &set, C.NULL, timeval_timeout))?
47+
}
48+
.except {
49+
socket_error(C.@select(handle+1, C.NULL, C.NULL, &set, timeval_timeout))?
50+
}
51+
}
52+
53+
return C.FD_ISSET(handle, &set)
54+
}
55+
56+
// wait_for_common wraps the common wait code
57+
fn wait_for_common(
58+
handle int,
59+
deadline time.Time,
60+
timeout time.Duration,
61+
test Select) ? {
62+
if deadline.unix == 0 {
63+
// only accept infinite_timeout as a valid
64+
// negative timeout - it is handled in @select however
65+
if timeout < 0 && timeout != infinite_timeout {
66+
return err_timed_out
67+
}
68+
ready := @select(handle, test, timeout)?
69+
if ready {
70+
return none
71+
}
72+
return err_timed_out
73+
}
74+
// Convert the deadline into a timeout
75+
// and use that
76+
d_timeout := deadline.unix - time.now().unix
77+
if d_timeout < 0 {
78+
// deadline is in the past so this has already
79+
// timed out
80+
return err_timed_out
81+
}
82+
83+
ready := @select(handle, test, d_timeout)?
84+
if ready {
85+
return none
86+
}
87+
return err_timed_out
88+
}
89+
90+
// wait_for_write waits for a write io operation to be available
91+
fn wait_for_write(
92+
handle int,
93+
deadline time.Time,
94+
timeout time.Duration) ? {
95+
return wait_for_common(handle, deadline, timeout, .write)
96+
}
97+
98+
// wait_for_read waits for a read io operation to be available
99+
fn wait_for_read(
100+
handle int,
101+
deadline time.Time,
102+
timeout time.Duration) ? {
103+
return wait_for_common(handle, deadline, timeout, .read)
104+
}
105+
106+
// no_deadline should be given to functions when no deadline is wanted (i.e. all functions
107+
// return instantly)
108+
const (
109+
no_deadline = time.Time{unix: 0}
110+
)
111+
112+
// no_timeout should be given to functions when no timeout is wanted (i.e. all functions
113+
// return instantly)
114+
const (
115+
no_timeout = time.Duration(0)
116+
)
117+
118+
// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions
119+
// only ever return with data)
120+
const (
121+
infinite_timeout = time.Duration(-1)
122+
)

vlib/x/net/errors.v

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module net
2+
3+
// Well defined errors that are returned from socket functions
4+
const (
5+
errors_base = 0
6+
err_new_socket_failed = error_with_code('net: new_socket failed to create socket', errors_base+1)
7+
err_option_not_settable = error_with_code('net: set_option_xxx option not settable', errors_base+2)
8+
err_option_wrong_type = error_with_code('net: set_option_xxx option wrong type', errors_base+3)
9+
err_port_out_of_range = error_with_code('', errors_base+5)
10+
err_no_udp_remote = error_with_code('', errors_base+6)
11+
err_connect_failed = error_with_code('net: connect failed', errors_base+7)
12+
err_connect_timed_out = error_with_code('net: connect timed out', errors_base+8)
13+
14+
err_timed_out = error_with_code('net: op timed out', errors_base+9)
15+
err_timed_out_code = errors_base+9
16+
)
17+
18+
pub fn socket_error(potential_code int) ?int {
19+
$if windows {
20+
if potential_code < 0 {
21+
last_error_int := C.WSAGetLastError()
22+
last_error := wsa_error(last_error_int)
23+
return error_with_code('net: socket error: ($last_error_int) $last_error', last_error)
24+
}
25+
}
26+
$else {
27+
if potential_code < 0 {
28+
last_error := error_code()
29+
return error_with_code('net: socket error: $last_error', last_error)
30+
}
31+
}
32+
33+
return potential_code
34+
}
35+
36+
pub fn wrap_error(error_code int) ? {
37+
$if windows {
38+
enum_error := wsa_error(error_code)
39+
return error_with_code('socket error: $enum_error', error_code)
40+
}
41+
$else {
42+
return error_with_code('net: socket error: $error_code', error_code)
43+
}
44+
}

vlib/x/net/net_nix.c.v

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module net
2+
3+
#include <unistd.h>
4+
#include <sys/socket.h>
5+
#include <sys/types.h>
6+
#include <sys/select.h>
7+
#include <arpa/inet.h>
8+
#include <netinet/in.h>
9+
#include <netdb.h>
10+
#include <errno.h>
11+
#include <fcntl.h>
12+
13+
fn error_code() int {
14+
return C.errno
15+
}
16+
17+
fn init() {
18+
}
19+
20+
pub const (
21+
msg_nosignal = 0x4000
22+
)
23+
24+
const (
25+
error_ewouldblock = C.EWOULDBLOCK
26+
)
27+
28+
#flag solaris -lsocket

0 commit comments

Comments
 (0)