2020-08-21 00:01:37 +03:00
|
|
|
|
module net
|
|
|
|
|
|
|
|
|
|
import time
|
2022-08-08 02:33:25 +03:00
|
|
|
|
import io
|
2022-08-07 10:40:05 +03:00
|
|
|
|
import strings
|
2020-08-21 00:01:37 +03:00
|
|
|
|
|
2020-12-15 18:39:11 +03:00
|
|
|
|
const (
|
|
|
|
|
tcp_default_read_timeout = 30 * time.second
|
|
|
|
|
tcp_default_write_timeout = 30 * time.second
|
|
|
|
|
)
|
|
|
|
|
|
2021-05-16 04:28:11 +03:00
|
|
|
|
[heap]
|
2020-08-21 00:01:37 +03:00
|
|
|
|
pub struct TcpConn {
|
2021-01-20 13:11:01 +03:00
|
|
|
|
pub mut:
|
2021-06-13 23:53:38 +03:00
|
|
|
|
sock TcpSocket
|
2020-08-21 00:01:37 +03:00
|
|
|
|
mut:
|
2023-04-02 02:24:33 +03:00
|
|
|
|
handle int
|
2020-08-21 00:01:37 +03:00
|
|
|
|
write_deadline time.Time
|
2020-12-15 18:39:11 +03:00
|
|
|
|
read_deadline time.Time
|
|
|
|
|
read_timeout time.Duration
|
|
|
|
|
write_timeout time.Duration
|
2021-08-07 23:20:58 +03:00
|
|
|
|
is_blocking bool
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn dial_tcp(address string) !&TcpConn {
|
2022-01-08 18:02:03 +03:00
|
|
|
|
addrs := resolve_addrs_fuzzy(address, .tcp) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not resolve address ${address} in dial_tcp')
|
2022-01-08 18:02:03 +03:00
|
|
|
|
}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
|
2022-08-07 10:40:05 +03:00
|
|
|
|
// Keep track of dialing errors that take place
|
|
|
|
|
mut errs := []IError{}
|
|
|
|
|
|
2021-06-13 23:53:38 +03:00
|
|
|
|
// Very simple dialer
|
|
|
|
|
for addr in addrs {
|
2022-01-08 18:02:03 +03:00
|
|
|
|
mut s := new_tcp_socket(addr.family()) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not create new tcp socket in dial_tcp')
|
2022-01-08 18:02:03 +03:00
|
|
|
|
}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
s.connect(addr) or {
|
2022-08-07 10:40:05 +03:00
|
|
|
|
errs << err
|
2021-06-13 23:53:38 +03:00
|
|
|
|
// 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
|
|
|
|
|
}
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
2022-08-07 10:40:05 +03:00
|
|
|
|
|
|
|
|
|
// Once we've failed now try and explain why we failed to connect
|
|
|
|
|
// to any of these addresses
|
|
|
|
|
mut err_builder := strings.new_builder(1024)
|
2022-11-15 16:53:13 +03:00
|
|
|
|
err_builder.write_string('dial_tcp failed for address ${address}\n')
|
2022-08-07 10:40:05 +03:00
|
|
|
|
err_builder.write_string('tried addrs:\n')
|
|
|
|
|
for i := 0; i < errs.len; i++ {
|
|
|
|
|
addr := addrs[i]
|
|
|
|
|
why := errs[i]
|
2022-11-15 16:53:13 +03:00
|
|
|
|
err_builder.write_string('\t${addr}: ${why}\n')
|
2022-08-07 10:40:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 23:53:38 +03:00
|
|
|
|
// failed
|
2022-08-07 10:40:05 +03:00
|
|
|
|
return error(err_builder.str())
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-07 16:35:28 +03:00
|
|
|
|
// bind local address and dial.
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn dial_tcp_with_bind(saddr string, laddr string) !&TcpConn {
|
2022-07-15 12:38:17 +03:00
|
|
|
|
addrs := resolve_addrs_fuzzy(saddr, .tcp) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not resolve address ${saddr} in dial_tcp_with_bind')
|
2022-07-15 12:38:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Very simple dialer
|
|
|
|
|
for addr in addrs {
|
|
|
|
|
mut s := new_tcp_socket(addr.family()) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not create new tcp socket in dial_tcp_with_bind')
|
2022-07-15 12:38:17 +03:00
|
|
|
|
}
|
|
|
|
|
s.bind(laddr) or {
|
|
|
|
|
s.close() or { continue }
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
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
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('dial_tcp_with_bind failed for address ${saddr}')
|
2022-07-15 12:38:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) close() ! {
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}')
|
|
|
|
|
}
|
2022-10-16 09:28:57 +03:00
|
|
|
|
c.sock.close()!
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
|
|
|
|
|
mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))!
|
2021-09-28 09:20:49 +03:00
|
|
|
|
$if trace_tcp ? {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}')
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
|
|
|
|
if res > 0 {
|
|
|
|
|
$if trace_tcp_data_read ? {
|
2023-01-15 14:25:20 +03:00
|
|
|
|
eprintln(
|
|
|
|
|
'<<< TcpConn.read_ptr | 1 data.len: ${res:6} | hex: ${unsafe { buf_ptr.vbytes(res) }.hex()} | data: ' +
|
2021-09-28 09:20:49 +03:00
|
|
|
|
unsafe { buf_ptr.vstring_with_len(res) })
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
code := error_code()
|
|
|
|
|
if code == int(error_ewouldblock) {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
c.wait_for_read()!
|
|
|
|
|
res = wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))!
|
2021-09-28 09:20:49 +03:00
|
|
|
|
$if trace_tcp ? {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}')
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
|
|
|
|
$if trace_tcp_data_read ? {
|
|
|
|
|
if res > 0 {
|
2023-01-15 14:25:20 +03:00
|
|
|
|
eprintln(
|
|
|
|
|
'<<< TcpConn.read_ptr | 2 data.len: ${res:6} | hex: ${unsafe { buf_ptr.vbytes(res) }.hex()} | data: ' +
|
2021-09-28 09:20:49 +03:00
|
|
|
|
unsafe { buf_ptr.vstring_with_len(res) })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return socket_error(res)
|
|
|
|
|
} else {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
wrap_error(code)!
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
2022-10-16 09:28:57 +03:00
|
|
|
|
return error('none')
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 02:33:25 +03:00
|
|
|
|
pub fn (c TcpConn) read(mut buf []u8) !int {
|
|
|
|
|
return c.read_ptr(buf.data, buf.len) or {
|
2022-10-28 19:08:30 +03:00
|
|
|
|
return io.NotExpected{
|
2022-08-08 02:33:25 +03:00
|
|
|
|
cause: 'unexpected error in `read_ptr` function'
|
|
|
|
|
code: -1
|
2022-10-28 19:08:30 +03:00
|
|
|
|
}
|
2022-08-08 02:33:25 +03:00
|
|
|
|
}
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) read_deadline() !time.Time {
|
2021-09-28 09:20:49 +03:00
|
|
|
|
if c.read_deadline.unix == 0 {
|
|
|
|
|
return c.read_deadline
|
|
|
|
|
}
|
2022-10-16 09:28:57 +03:00
|
|
|
|
return error('none')
|
2021-09-28 09:20:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
|
// write_ptr blocks and attempts to write all data
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) write_ptr(b &u8, len int) !int {
|
2022-12-17 20:08:59 +03:00
|
|
|
|
$if trace_tcp_sock_handle ? {
|
|
|
|
|
eprintln('>>> TcpConn.write_ptr | c: ${ptr_str(c)} | c.sock.handle: ${c.sock.handle} | b: ${ptr_str(b)} | len: ${len}')
|
|
|
|
|
}
|
2020-12-29 16:41:46 +03:00
|
|
|
|
$if trace_tcp ? {
|
2021-06-13 23:53:38 +03:00
|
|
|
|
eprintln(
|
2022-11-15 16:53:13 +03:00
|
|
|
|
'>>> TcpConn.write_ptr | c.sock.handle: ${c.sock.handle} | b: ${ptr_str(b)} len: ${len} |\n' +
|
2021-04-23 15:18:09 +03:00
|
|
|
|
unsafe { b.vstring_with_len(len) })
|
2021-04-23 15:12:37 +03:00
|
|
|
|
}
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp_data_write ? {
|
2023-01-15 14:25:20 +03:00
|
|
|
|
eprintln(
|
|
|
|
|
'>>> TcpConn.write_ptr | data.len: ${len:6} | hex: ${unsafe { b.vbytes(len) }.hex()} | data: ' +
|
2021-08-27 13:10:34 +03:00
|
|
|
|
unsafe { b.vstring_with_len(len) })
|
|
|
|
|
}
|
2020-08-21 00:01:37 +03:00
|
|
|
|
unsafe {
|
2022-04-15 14:58:56 +03:00
|
|
|
|
mut ptr_base := &u8(b)
|
2020-08-21 00:01:37 +03:00
|
|
|
|
mut total_sent := 0
|
|
|
|
|
for total_sent < len {
|
|
|
|
|
ptr := ptr_base + total_sent
|
2021-06-13 23:53:38 +03:00
|
|
|
|
remaining := len - total_sent
|
|
|
|
|
mut sent := C.send(c.sock.handle, ptr, remaining, msg_nosignal)
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp_data_write ? {
|
2023-01-15 14:25:20 +03:00
|
|
|
|
eprintln('>>> TcpConn.write_ptr | data chunk, total_sent: ${total_sent:6}, remaining: ${remaining:6}, ptr: ${voidptr(ptr):x} => sent: ${sent:6}')
|
2021-08-27 13:10:34 +03:00
|
|
|
|
}
|
2020-08-21 00:01:37 +03:00
|
|
|
|
if sent < 0 {
|
|
|
|
|
code := error_code()
|
2021-01-08 19:41:52 +03:00
|
|
|
|
if code == int(error_ewouldblock) {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
c.wait_for_write()!
|
2021-01-08 19:41:52 +03:00
|
|
|
|
continue
|
|
|
|
|
} else {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
wrap_error(code)!
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
total_sent += sent
|
|
|
|
|
}
|
2021-02-27 11:29:18 +03:00
|
|
|
|
return total_sent
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// write blocks and attempts to write all data
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) write(bytes []u8) !int {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return c.write_ptr(bytes.data, bytes.len)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-19 04:51:31 +03:00
|
|
|
|
// write_string blocks and attempts to write all data
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) write_string(s string) !int {
|
2021-03-19 04:51:31 +03:00
|
|
|
|
return c.write_ptr(s.str, s.len)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
|
pub fn (mut c TcpConn) set_read_deadline(deadline time.Time) {
|
|
|
|
|
c.read_deadline = deadline
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) write_deadline() !time.Time {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
if c.write_deadline.unix == 0 {
|
|
|
|
|
return c.write_deadline
|
|
|
|
|
}
|
2022-10-16 09:28:57 +03:00
|
|
|
|
return error('none')
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn (mut c TcpConn) set_write_deadline(deadline time.Time) {
|
|
|
|
|
c.write_deadline = deadline
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 13:11:01 +03:00
|
|
|
|
pub fn (c &TcpConn) read_timeout() time.Duration {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return c.read_timeout
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-15 18:39:11 +03:00
|
|
|
|
pub fn (mut c TcpConn) set_read_timeout(t time.Duration) {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
c.read_timeout = t
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 13:11:01 +03:00
|
|
|
|
pub fn (c &TcpConn) write_timeout() time.Duration {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return c.write_timeout
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn (mut c TcpConn) set_write_timeout(t time.Duration) {
|
|
|
|
|
c.write_timeout = t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[inline]
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c TcpConn) wait_for_read() ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[inline]
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpConn) wait_for_write() ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-02 02:24:33 +03:00
|
|
|
|
// set_sock initialises the c.sock field. It should be called after `.accept_only()!`.
|
|
|
|
|
// Note: just use `.accept()!`. In most cases it is simpler, and calls `.set_sock()!` for you.
|
|
|
|
|
pub fn (mut c TcpConn) set_sock() ! {
|
|
|
|
|
c.sock = tcp_socket_from_handle(c.handle)!
|
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' TcpListener.accept | << new_sock.handle: ${c.handle:6}')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c &TcpConn) peer_addr() !Addr {
|
2021-06-13 23:53:38 +03:00
|
|
|
|
mut addr := Addr{
|
2021-07-20 11:17:08 +03:00
|
|
|
|
addr: AddrData{
|
|
|
|
|
Ip6: Ip6{}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mut size := sizeof(Addr)
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error_message(C.getpeername(c.sock.handle, voidptr(&addr), &size), 'peer_addr failed')!
|
2021-06-13 23:53:38 +03:00
|
|
|
|
return addr
|
2020-11-15 23:54:47 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c &TcpConn) peer_ip() !string {
|
|
|
|
|
return c.peer_addr()!.str()
|
2021-06-13 23:53:38 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c &TcpConn) addr() !Addr {
|
2021-06-13 23:53:38 +03:00
|
|
|
|
return c.sock.address()
|
2020-11-15 23:54:47 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-26 17:43:10 +03:00
|
|
|
|
pub fn (c TcpConn) str() string {
|
|
|
|
|
s := c.sock.str().replace('\n', ' ').replace(' ', ' ')
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return 'TcpConn{ write_deadline: ${c.write_deadline}, read_deadline: ${c.read_deadline}, read_timeout: ${c.read_timeout}, write_timeout: ${c.write_timeout}, sock: ${s} }'
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct TcpListener {
|
2021-01-20 13:11:01 +03:00
|
|
|
|
pub mut:
|
|
|
|
|
sock TcpSocket
|
2020-08-21 00:01:37 +03:00
|
|
|
|
mut:
|
2020-12-15 18:39:11 +03:00
|
|
|
|
accept_timeout time.Duration
|
2020-08-21 00:01:37 +03:00
|
|
|
|
accept_deadline time.Time
|
|
|
|
|
}
|
|
|
|
|
|
net: change default of the socket used by net.listen_tcp, to dualstack, even if the OS has a different default. Allow changing the listen backlog too
With this change, example vweb programs, will continue to be available to both
ipv6 and ipv4 connections from the same machine, even after doing (on linux):
`echo 1 | sudo tee /proc/sys/net/ipv6/bindv6only`
Previously, after that, vweb programs responded only to ipv6 connections, but not to ipv4 ones,
i.e. opening http://127.0.0.1:8082/ stopped working, for `v run examples/vweb/vweb_example.v` .
Note: GO web servers have the same behaviour, which is convenient for development/testing,
since it makes the programs more consistent and robust in the face of OS settings changes.
2023-08-01 11:26:39 +03:00
|
|
|
|
[params]
|
|
|
|
|
pub struct ListenOptions {
|
|
|
|
|
pub:
|
|
|
|
|
dualstack bool = true
|
|
|
|
|
backlog int = 128
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn listen_tcp(family AddrFamily, saddr string, options ListenOptions) !&TcpListener {
|
|
|
|
|
mut s := new_tcp_socket(family) or { return error('${err.msg()}; could not create new socket') }
|
|
|
|
|
s.set_dualstack(options.dualstack) or {}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
|
2022-01-08 18:02:03 +03:00
|
|
|
|
addrs := resolve_addrs(saddr, family, .tcp) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not resolve address ${saddr}')
|
2022-01-08 18:02:03 +03:00
|
|
|
|
}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
// TODO(logic to pick here)
|
|
|
|
|
addr := addrs[0]
|
|
|
|
|
|
2020-08-21 00:01:37 +03:00
|
|
|
|
// cast to the correct type
|
2021-06-13 23:53:38 +03:00
|
|
|
|
alen := addr.len()
|
2022-11-15 16:53:13 +03:00
|
|
|
|
socket_error_message(C.bind(s.handle, voidptr(&addr), alen), 'binding to ${saddr} failed')!
|
net: change default of the socket used by net.listen_tcp, to dualstack, even if the OS has a different default. Allow changing the listen backlog too
With this change, example vweb programs, will continue to be available to both
ipv6 and ipv4 connections from the same machine, even after doing (on linux):
`echo 1 | sudo tee /proc/sys/net/ipv6/bindv6only`
Previously, after that, vweb programs responded only to ipv6 connections, but not to ipv4 ones,
i.e. opening http://127.0.0.1:8082/ stopped working, for `v run examples/vweb/vweb_example.v` .
Note: GO web servers have the same behaviour, which is convenient for development/testing,
since it makes the programs more consistent and robust in the face of OS settings changes.
2023-08-01 11:26:39 +03:00
|
|
|
|
socket_error_message(C.listen(s.handle, options.backlog), 'listening on ${saddr} with maximum backlog pending queue of ${options.backlog}, failed')!
|
2021-01-20 13:11:01 +03:00
|
|
|
|
return &TcpListener{
|
2020-08-21 00:01:37 +03:00
|
|
|
|
sock: s
|
|
|
|
|
accept_deadline: no_deadline
|
2020-11-15 23:54:47 +03:00
|
|
|
|
accept_timeout: infinite_timeout
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-02 02:24:33 +03:00
|
|
|
|
// accept a tcp connection from an external source to the listener `l`.
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut l TcpListener) accept() !&TcpConn {
|
2023-04-02 02:24:33 +03:00
|
|
|
|
mut res := l.accept_only()!
|
|
|
|
|
res.set_sock()!
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// accept_only accepts a tcp connection from an external source to the listener `l`.
|
|
|
|
|
// Unlike `accept`, `accept_only` *will not call* `.set_sock()!` on the result,
|
|
|
|
|
// and is thus faster.
|
|
|
|
|
//
|
|
|
|
|
// Note: you *need* to call `.set_sock()!` manually, before using the
|
|
|
|
|
// connection after calling `.accept_only()!`, but that does not have to happen
|
|
|
|
|
// in the same thread that called `.accept_only()!`.
|
|
|
|
|
// The intention of this API, is to have a more efficient way to accept
|
|
|
|
|
// connections, that are later processed by a thread pool, while the main
|
|
|
|
|
// thread remains active, so that it can accept other connections.
|
|
|
|
|
// See also vlib/vweb/vweb.v .
|
|
|
|
|
//
|
|
|
|
|
// If you do not need that, just call `.accept()!` instead, which will call
|
|
|
|
|
// `.set_sock()!` for you.
|
|
|
|
|
pub fn (mut l TcpListener) accept_only() !&TcpConn {
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' TcpListener.accept | l.sock.handle: ${l.sock.handle:6}')
|
|
|
|
|
}
|
2023-04-02 02:24:33 +03:00
|
|
|
|
|
2022-04-12 11:47:41 +03:00
|
|
|
|
mut new_handle := C.accept(l.sock.handle, 0, 0)
|
2020-08-21 00:01:37 +03:00
|
|
|
|
if new_handle <= 0 {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
l.wait_for_accept()!
|
2022-04-12 11:47:41 +03:00
|
|
|
|
new_handle = C.accept(l.sock.handle, 0, 0)
|
2020-08-21 00:01:37 +03:00
|
|
|
|
if new_handle == -1 || new_handle == 0 {
|
2021-06-13 23:53:38 +03:00
|
|
|
|
return error('accept failed')
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-02 02:24:33 +03:00
|
|
|
|
|
2021-01-20 13:11:01 +03:00
|
|
|
|
return &TcpConn{
|
2023-04-02 02:24:33 +03:00
|
|
|
|
handle: new_handle
|
2021-01-26 17:43:10 +03:00
|
|
|
|
read_timeout: net.tcp_default_read_timeout
|
|
|
|
|
write_timeout: net.tcp_default_write_timeout
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c &TcpListener) accept_deadline() !time.Time {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
if c.accept_deadline.unix != 0 {
|
|
|
|
|
return c.accept_deadline
|
|
|
|
|
}
|
2021-06-13 23:53:38 +03:00
|
|
|
|
return error('invalid deadline')
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) {
|
|
|
|
|
c.accept_deadline = deadline
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 13:11:01 +03:00
|
|
|
|
pub fn (c &TcpListener) accept_timeout() time.Duration {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return c.accept_timeout
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-15 18:39:11 +03:00
|
|
|
|
pub fn (mut c TcpListener) set_accept_timeout(t time.Duration) {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
c.accept_timeout = t
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpListener) wait_for_accept() ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut c TcpListener) close() ! {
|
|
|
|
|
c.sock.close()!
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (c &TcpListener) addr() !Addr {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return c.sock.address()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TcpSocket {
|
2022-02-22 11:34:38 +03:00
|
|
|
|
Socket
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
fn new_tcp_socket(family AddrFamily) !TcpSocket {
|
|
|
|
|
handle := socket_error(C.socket(family, SocketType.tcp, 0))!
|
2021-01-20 13:11:01 +03:00
|
|
|
|
mut s := TcpSocket{
|
2021-06-13 23:53:38 +03:00
|
|
|
|
handle: handle
|
2021-04-23 14:37:05 +03:00
|
|
|
|
}
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' new_tcp_socket | s.handle: ${s.handle:6}')
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 23:53:38 +03:00
|
|
|
|
// TODO(emily):
|
|
|
|
|
// we shouldnt be using ioctlsocket in the 21st century
|
|
|
|
|
// use the non-blocking socket option instead please :)
|
|
|
|
|
|
|
|
|
|
// TODO(emily):
|
|
|
|
|
// Move this to its own function on the socket
|
2022-10-16 09:28:57 +03:00
|
|
|
|
s.set_option_int(.reuse_addr, 1)!
|
2021-08-27 13:10:34 +03:00
|
|
|
|
|
|
|
|
|
$if !net_blocking_sockets ? {
|
|
|
|
|
$if windows {
|
|
|
|
|
t := u32(1) // true
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(C.ioctlsocket(handle, fionbio, &t))!
|
2021-08-27 13:10:34 +03:00
|
|
|
|
} $else {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(C.fcntl(handle, C.F_SETFL, C.fcntl(handle, C.F_GETFL) | C.O_NONBLOCK))!
|
2021-08-27 13:10:34 +03:00
|
|
|
|
}
|
2021-04-23 17:12:13 +03:00
|
|
|
|
}
|
2020-11-15 23:54:47 +03:00
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
fn tcp_socket_from_handle(sockfd int) !TcpSocket {
|
2021-01-20 13:11:01 +03:00
|
|
|
|
mut s := TcpSocket{
|
2020-11-15 23:54:47 +03:00
|
|
|
|
handle: sockfd
|
|
|
|
|
}
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' tcp_socket_from_handle | s.handle: ${s.handle:6}')
|
|
|
|
|
}
|
2021-04-23 17:12:13 +03:00
|
|
|
|
// s.set_option_bool(.reuse_addr, true)?
|
2022-10-16 09:28:57 +03:00
|
|
|
|
s.set_option_int(.reuse_addr, 1)!
|
2021-06-13 23:53:38 +03:00
|
|
|
|
s.set_dualstack(true) or {
|
|
|
|
|
// Not ipv6, we dont care
|
|
|
|
|
}
|
2021-08-27 13:10:34 +03:00
|
|
|
|
$if !net_blocking_sockets ? {
|
|
|
|
|
$if windows {
|
|
|
|
|
t := u32(1) // true
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(C.ioctlsocket(sockfd, fionbio, &t))!
|
2021-08-27 13:10:34 +03:00
|
|
|
|
} $else {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK))!
|
2021-08-27 13:10:34 +03:00
|
|
|
|
}
|
2021-04-23 17:12:13 +03:00
|
|
|
|
}
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-23 22:21:46 +03:00
|
|
|
|
// tcp_socket_from_handle_raw is similar to tcp_socket_from_handle, but it does not modify any socket options
|
|
|
|
|
pub fn tcp_socket_from_handle_raw(sockfd int) TcpSocket {
|
|
|
|
|
mut s := TcpSocket{
|
|
|
|
|
handle: sockfd
|
|
|
|
|
}
|
|
|
|
|
$if trace_tcp ? {
|
|
|
|
|
eprintln(' tcp_socket_from_handle_raw | s.handle: ${s.handle:6}')
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut s TcpSocket) set_option_bool(opt SocketOption, value bool) ! {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
// TODO reenable when this `in` operation works again
|
|
|
|
|
// if opt !in opts_can_set {
|
|
|
|
|
// return err_option_not_settable
|
|
|
|
|
// }
|
|
|
|
|
// if opt !in opts_bool {
|
|
|
|
|
// return err_option_wrong_type
|
|
|
|
|
// }
|
2021-03-18 17:23:29 +03:00
|
|
|
|
x := int(value)
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int)))!
|
2021-06-13 23:53:38 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut s TcpSocket) set_dualstack(on bool) ! {
|
2021-06-13 23:53:38 +03:00
|
|
|
|
x := int(!on)
|
|
|
|
|
socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x,
|
2022-10-16 09:28:57 +03:00
|
|
|
|
sizeof(int)))!
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
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)))!
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-15 12:38:17 +03:00
|
|
|
|
// bind a local rddress for TcpSocket
|
2022-10-16 09:28:57 +03:00
|
|
|
|
pub fn (mut s TcpSocket) bind(addr string) ! {
|
2022-07-15 12:38:17 +03:00
|
|
|
|
addrs := resolve_addrs(addr, AddrFamily.ip, .tcp) or {
|
2022-11-15 16:53:13 +03:00
|
|
|
|
return error('${err.msg()}; could not resolve address ${addr}')
|
2022-07-15 12:38:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(logic to pick here)
|
|
|
|
|
a := addrs[0]
|
|
|
|
|
|
|
|
|
|
// cast to the correct type
|
|
|
|
|
alen := a.len()
|
2022-11-15 16:53:13 +03:00
|
|
|
|
socket_error_message(C.bind(s.handle, voidptr(&a), alen), 'binding to ${addr} failed') or {
|
2022-07-15 12:38:17 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
fn (mut s TcpSocket) close() ! {
|
2023-01-25 13:32:05 +03:00
|
|
|
|
shutdown(s.handle)
|
|
|
|
|
return close(s.handle)
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
fn (mut s TcpSocket) @select(test Select, timeout time.Duration) !bool {
|
2020-08-21 00:01:37 +03:00
|
|
|
|
return @select(s.handle, test, timeout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
connect_timeout = 5 * time.second
|
|
|
|
|
)
|
|
|
|
|
|
2022-10-16 09:28:57 +03:00
|
|
|
|
fn (mut s TcpSocket) connect(a Addr) ! {
|
2022-06-27 11:01:55 +03:00
|
|
|
|
$if !net_blocking_sockets ? {
|
|
|
|
|
res := C.connect(s.handle, voidptr(&a), a.len())
|
|
|
|
|
if res == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-08-07 10:40:05 +03:00
|
|
|
|
ecode := error_code()
|
|
|
|
|
// On nix non-blocking sockets we expect einprogress
|
|
|
|
|
// On windows we expect res == -1 && error_code() == ewouldblock
|
|
|
|
|
if (is_windows && ecode == int(error_ewouldblock))
|
|
|
|
|
|| (!is_windows && res == -1 && ecode == int(error_einprogress)) {
|
|
|
|
|
// 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).
|
2022-10-16 09:28:57 +03:00
|
|
|
|
write_result := s.@select(.write, net.connect_timeout)!
|
2022-06-27 11:01:55 +03:00
|
|
|
|
err := 0
|
|
|
|
|
len := sizeof(err)
|
2022-08-07 10:40:05 +03:00
|
|
|
|
xyz := C.getsockopt(s.handle, C.SOL_SOCKET, C.SO_ERROR, &err, &len)
|
|
|
|
|
if xyz == 0 && err == 0 {
|
|
|
|
|
return
|
2022-06-27 11:01:55 +03:00
|
|
|
|
}
|
2022-08-07 10:40:05 +03:00
|
|
|
|
if write_result {
|
|
|
|
|
if xyz == 0 {
|
2022-10-16 09:28:57 +03:00
|
|
|
|
wrap_error(err)!
|
2022-08-07 10:40:05 +03:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
return err_timed_out
|
2021-06-13 23:53:38 +03:00
|
|
|
|
}
|
2022-10-16 09:28:57 +03:00
|
|
|
|
wrap_error(ecode)!
|
2022-08-07 10:40:05 +03:00
|
|
|
|
return
|
2022-06-27 11:01:55 +03:00
|
|
|
|
} $else {
|
2022-08-07 10:40:05 +03:00
|
|
|
|
x := C.connect(s.handle, voidptr(&a), a.len())
|
2022-10-16 09:28:57 +03:00
|
|
|
|
socket_error(x)!
|
2022-06-27 11:01:55 +03:00
|
|
|
|
}
|
2020-08-21 00:01:37 +03:00
|
|
|
|
}
|