///|
typealias Int64 as RawFd
///|
pub(all) struct Socket {
inner : RawFd
} derive(Show)
///|
extern "C" fn socket_init_ffi() -> Int = "moonbit_socket_init"
///|
extern "C" fn socket_create_ffi(domain : Int, t : Int, protocol : Int) -> RawFd = "moonbit_socket_create"
///|
extern "C" fn socket_close_ffi(sockfd : RawFd) -> Int = "moonbit_socket_close"
///| Shutdown a socket.
/// Returns 0 on success, -1 on error.
extern "C" fn socket_shutdown_ffi(sockfd : RawFd, how : Int) -> Int = "moonbit_socket_shutdown"
///|
extern "C" fn socket_setsockopt(
sockfd : RawFd,
level : Int,
optname : Int,
optval : Int
) -> Int = "moonbit_socket_setsockopt_int"
///| Bind a socket to an address.
/// Success return a non-negative integer, -1 on error.
extern "C" fn socket_bind_ipv4_ffi(
sockfd : RawFd,
host : Bytes,
port : Int
) -> Int = "moonbit_socket_bind_ipv4"
///| Bind a socket to an IPv6 address.
/// Success return a non-negative integer, -1 on error.
extern "C" fn socket_bind_ipv6_ffi(
sockfd : RawFd,
host : Bytes,
port : Int
) -> Int = "moonbit_socket_bind_ipv6"
///| Listen for incoming connections on a socket.
/// Returns 0 on success, -1 on error.
/// The `sockfd` parameter is the file descriptor of the socket to listen on.
/// The `backlog` parameter specifies the maximum length of the queue of pending connections.
extern "C" fn socket_listen_ffi(sockfd : RawFd, backlog : Int) -> Int = "moonbit_socket_listen"
///|
extern "C" fn socket_accept_ffi(sockfd : RawFd, peeraddr : Ipaddr) -> RawFd = "moonbit_socket_accept"
///|
extern "C" fn socket_connect_ipv4_ffi(
sockfd : RawFd,
host : Bytes,
port : Int
) -> Int = "moonbit_socket_connect_ipv4"
///|
extern "C" fn socket_connect_ipv6_ffi(
sockfd : RawFd,
host : Bytes,
port : Int
) -> Int = "moonbit_socket_connect_ipv6"
///| Send data to a socket.
/// Returns the number of bytes sent, or -1 on error, or 0 if the remote side has closed the connection.
extern "C" fn socket_send_ffi(
sockfd : RawFd,
data : FixedArray[Byte],
len : Int,
flags : Int
) -> Int = "moonbit_socket_send"
///| Receive data from a socket.
/// Returns the number of bytes received, or -1 on error.
/// The `buf` parameter is a mutable byte array where the received data will be stored.
/// The `len` parameter specifies the maximum number of bytes to read.
/// The `flags` parameter can be used to specify additional options for the receive operation.
extern "C" fn socket_recv_ffi(
sockfd : RawFd,
buf : FixedArray[Byte],
len : Int,
flags : Int
) -> Int = "moonbit_socket_recv"
///|
extern "C" fn get_net_error_message_ffi() -> Bytes = "moonbit_get_error_message"
///|
fn socket_create_internal(
domain : Domain,
ty : Type,
protocol~ : Protocol? = None
) -> RawFd raise NetError {
let domain : Int = domain.inner()
let ty : Int = ty.inner()
let protocol : Int = protocol.map_or(0, fn(p) { p.inner() })
guard socket_init_ffi() == 0 else { raise NetError(get_net_error_message()) }
let sockfd = socket_create_ffi(domain, ty, protocol)
guard sockfd >= 0 else { raise NetError(get_net_error_message()) }
return sockfd
}
///|
fn socket_close_internal(sockfd : RawFd) -> Unit raise NetError {
guard socket_close_ffi(sockfd) == 0 else {
raise NetError(get_net_error_message())
}
}
///| Shutdown a socket.
/// Returns 0 on success, -1 on error.
fn socket_shutdown_internal(
sockfd : RawFd,
how : Shutdown
) -> Unit raise NetError {
guard socket_shutdown_ffi(sockfd, how.inner()) == 0 else {
raise NetError(get_net_error_message())
}
}
///| Set socket options.
/// Returns 0 on success, -1 on error.
fn socket_setsockopt_internal(
sockfd : RawFd,
level : Int,
optname : Int,
optval : Int
) -> Unit raise NetError {
guard socket_setsockopt(sockfd, level, optname, optval) >= 0 else {
raise NetError(get_net_error_message())
}
}
///|
fn[S : ToSocketAddrs] socket_bind_internal(
sockfd : RawFd,
addr : S
) -> Unit raise NetError {
let mut lastErro = ""
for ad in addr.to_socket_addr() {
let res = match ad {
V4(host, port) =>
socket_bind_ipv4_ffi(
sockfd,
mbt_string_to_utf8_bytes(host, true),
port.to_int(),
)
V6(host, port, ..) =>
socket_bind_ipv6_ffi(
sockfd,
mbt_string_to_utf8_bytes(host, true),
port.to_int(),
)
}
if res >= 0 {
return
} else {
lastErro = get_net_error_message()
}
}
raise NetError(
"Failed to bind socket to any of the provided addresses: " + lastErro,
)
}
///|
fn socket_listen_internal(sockfd : RawFd, backlog : Int) -> Unit raise NetError {
guard socket_listen_ffi(sockfd, backlog) >= 0 else {
raise NetError(get_net_error_message())
}
}
///|
fn socket_accept_internal(sockfd : RawFd) -> (RawFd, SocketAddr) raise NetError {
let peeraddr = create_sockaddr_storage_ffi()
let clientfd = socket_accept_ffi(sockfd, peeraddr)
guard clientfd >= 0 else { raise NetError(get_net_error_message()) }
return (clientfd, peeraddr.to_socket_addr())
}
///|
fn[S : ToSocketAddrs] socket_connect_internal(
sockfd : RawFd,
addr : S
) -> Unit raise NetError {
let mut lastErro = ""
for ad in addr.to_socket_addr() {
let res = match ad {
V4(host, port) =>
socket_connect_ipv4_ffi(
sockfd,
mbt_string_to_utf8_bytes(host, true),
port.to_int(),
)
V6(host, port, ..) =>
socket_connect_ipv6_ffi(
sockfd,
mbt_string_to_utf8_bytes(host, true),
port.to_int(),
)
}
if res >= 0 {
return
} else {
lastErro = get_net_error_message()
}
}
raise NetError(
"Failed to connect socket to any of the provided addresses: " + lastErro,
)
}
///|
fn socket_send_internal(
sockfd : RawFd,
data : FixedArray[Byte],
len : Int,
flags : Int
) -> Int raise NetError {
let bytes_sent = socket_send_ffi(sockfd, data, len, flags)
guard bytes_sent >= 0 else { raise NetError(get_net_error_message()) }
return bytes_sent
}
///|
fn socket_recv_internal(
sockfd : RawFd,
buf : FixedArray[Byte],
len : Int,
flags : Int
) -> Int raise NetError {
let bytes_received = socket_recv_ffi(sockfd, buf, len, flags)
guard bytes_received >= 0 else { raise NetError(get_net_error_message()) }
return bytes_received
}
///|
fn get_net_error_message() -> String {
let res = try? utf8_bytes_to_mbt_string(get_net_error_message_ffi())
match res {
Ok(msg) => msg
Err(_) => "Unknown error".to_string()
}
}