module picoev import net import picohttpparser #include $if windows { #include #include } $else $if freebsd || macos { #include #include #include #include } $else { #include #include } @[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, b &u8, max_len int, idx int) int { // use `recv` instead of `read` for windows compatibility unsafe { return C.recv(fd, b + idx, max_len - idx, 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) assert fd != -1 $if trace_fd ? { eprintln('listen: ${fd}') } // Setting flags for socket flag := 1 assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0 $if linux { // epoll socket options assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)) == 0 assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)) == 0 assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT, &config.timeout_secs, sizeof(int)) == 0 queue_len := max_queue assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len, sizeof(int)) == 0 } // 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') or { panic(err) } net.socket_error_message(C.listen(fd, C.SOMAXCONN), 'listening on ${saddr} with maximum backlog pending queue of ${C.SOMAXCONN}, failed') or { panic(err) } setup_sock(fd) or { config.err_cb(config.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{}, err) } return fd }