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