v / vlib / picoev / socket_util.c.v
134 lines · 125 sloc · 3.43 KB · 9e1d3ca05db333f60c4201e1d35db705c319d4dd
Raw
1module picoev
2
3import net
4import pico_http_parser
5
6#include <errno.h>
7$if windows {
8 #include <winsock2.h>
9 #include <ws2tcpip.h>
10} $else $if freebsd || macos {
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <netinet/tcp.h>
15} $else {
16 #include <netinet/tcp.h>
17 #include <sys/resource.h>
18}
19
20@[inline]
21fn get_time() i64 {
22 // time.now() is slow
23 return i64(C.time(C.NULL))
24}
25
26@[inline]
27fn accept(fd int) int {
28 return C.accept(fd, 0, 0)
29}
30
31@[inline]
32fn close_socket(fd int) {
33 trace_fd('close ${fd}')
34 $if windows {
35 C.closesocket(fd)
36 } $else {
37 C.close(fd)
38 }
39}
40
41@[inline]
42fn setup_sock(fd int) ! {
43 flag := 1
44 if C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_NODELAY, &flag, sizeof(int)) < 0 {
45 return error('setup_sock.setup_sock failed')
46 }
47 $if freebsd {
48 if C.fcntl(fd, C.F_SETFL, C.SOCK_NONBLOCK) != 0 {
49 return error('fcntl failed')
50 }
51 } $else $if windows {
52 non_blocking_mode := u32(1)
53 if C.ioctlsocket(fd, C.FIONBIO, &non_blocking_mode) == C.SOCKET_ERROR {
54 return error('icotlsocket failed')
55 }
56 } $else {
57 // linux and macos
58 if C.fcntl(fd, C.F_SETFL, C.O_NONBLOCK) != 0 {
59 return error('fcntl failed')
60 }
61 }
62}
63
64@[inline]
65fn req_read(fd int, buffer &u8, max_len int, offset int) int {
66 // use `recv` instead of `read` for windows compatibility
67 return unsafe { C.recv(fd, buffer + offset, max_len - offset, 0) }
68}
69
70fn fatal_socket_error(fd int) bool {
71 if C.errno == C.EAGAIN {
72 // try again later
73 return false
74 }
75 $if windows {
76 if C.errno == C.WSAEWOULDBLOCK {
77 // try again later
78 return false
79 }
80 } $else {
81 if C.errno == C.EWOULDBLOCK {
82 // try again later
83 return false
84 }
85 }
86 trace_fd('fatal error ${fd}: ${C.errno}')
87 return true
88}
89
90// listen creates a listening tcp socket and returns its file descriptor.
91fn listen(config Config) !int {
92 // not using the `net` modules sockets, because not all socket options are defined
93 fd := C.socket(i32(config.family), i32(net.SocketType.tcp), 0)
94 if fd == -1 {
95 return error('Failed to create socket')
96 }
97 trace_fd('listen: ${fd}')
98 // Setting flags for socket
99 flag := 1
100 flag_zero := 0
101 net.socket_error(C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)))!
102 if config.family == .ip6 {
103 // set socket to dualstack so connections to both ipv4 and ipv6 addresses
104 // can be accepted
105 net.socket_error(C.setsockopt(fd, C.IPPROTO_IPV6, C.IPV6_V6ONLY, &flag_zero, sizeof(int)))!
106 }
107 $if linux || termux {
108 // epoll socket options
109 net.socket_error(C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)))!
110 net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)))!
111 $if !support_wsl1 ? {
112 net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT,
113 &config.timeout_secs, sizeof(int)))!
114 queue_len := max_queue
115 net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len,
116 sizeof(int)))!
117 }
118 }
119 // addr settings
120 saddr := '${config.host}:${config.port}'
121 addrs := net.resolve_addrs(saddr, config.family, .tcp) or {
122 panic('Error while resolving `${saddr}`, err: ${err}')
123 }
124 addr := addrs[0]
125 alen := addr.len()
126 net.socket_error_message(C.bind(fd, voidptr(&addr), alen), 'binding to ${saddr} failed')!
127 net.socket_error_message(C.listen(fd, C.SOMAXCONN),
128 'listening on ${saddr} with maximum backlog pending queue of ${C.SOMAXCONN}, failed')!
129 setup_sock(fd) or {
130 config.err_cb(config.user_data, pico_http_parser.Request{}, mut
131 &pico_http_parser.Response{}, err)
132 }
133 return fd
134}
135