///|
#external
priv type JsPromise[T]
///|
fn[T, U] JsPromise::cast(self : JsPromise[T]) -> @js_async.Promise[U] = "%identity"
///|
priv struct JsUdpAddr {
host : String
port : Int
family : Int
}
///|
priv struct JsUdpSocketResult {
id : Int
host : String
port : Int
family : Int
}
///|
priv struct JsUdpRecvResult {
data : Bytes
host : String
port : Int
family : Int
}
///|
async fn[T] run_js_promise(promise : JsPromise[T]) -> T {
@js_async.run_promise(fn(_signal) { promise.cast() })
}
///|
fn addr_from_js(host : String, port : Int, family : Int) -> Addr {
if family == 6 {
Addr::{ host, port, ip: 0U, is_ipv6: true, }
} else {
Addr::parse("\{host}:\{port}") catch {
_ => Addr::new(0U, port)
}
}
}
///|
fn addr_to_js(addr : Addr) -> JsUdpAddr {
JsUdpAddr::{
host: addr.host,
port: addr.port,
family: if addr.is_ipv6 {
6
} else {
4
},
}
}
///|
extern "js" fn js_udp_resolve(
host : String,
port : Int,
protocol : Int,
) -> JsPromise[JsUdpSocketResult] =
#| async (host, port, protocol) => {
#| const dns = await import('node:dns/promises');
#| const family = protocol === 4 ? 4 : protocol === 6 ? 6 : 0;
#| const order = protocol === 46 ? 'ipv4first' : protocol === 64 ? 'ipv6first' : 'verbatim';
#| const result = await dns.lookup(host, { family, order });
#| return { id: 0, host: result.address, port: port | 0, family: result.family | 0 };
#| }
///|
extern "js" fn js_udp_client_new(
addr : JsUdpAddr,
) -> JsPromise[JsUdpSocketResult] =
#| async (addr) => {
#| const dgram = await import('node:dgram');
#| const state = globalThis.__xudp ??= { nextId: 1, sockets: new Map() };
#| const socket = dgram.createSocket(addr.family === 6 ? 'udp6' : 'udp4');
#| const handle = { socket, q: [], waiters: [], closed: false };
#| socket.on('message', (msg, rinfo) => {
#| const item = { data: new Uint8Array(msg), host: rinfo.address, port: rinfo.port | 0, family: rinfo.family === 'IPv6' ? 6 : 4 };
#| const waiter = handle.waiters.shift();
#| if (waiter) waiter(item);
#| else handle.q.push(item);
#| });
#| await new Promise((resolve, reject) => {
#| const onError = (err) => { socket.off('connect', onConnect); reject(err); };
#| const onConnect = () => { socket.off('error', onError); resolve(); };
#| socket.once('error', onError);
#| socket.connect(addr.port, addr.host, onConnect);
#| });
#| const id = state.nextId++;
#| state.sockets.set(id, handle);
#| const local = socket.address();
#| return { id, host: local.address, port: local.port | 0, family: local.family === 'IPv6' ? 6 : 4 };
#| }
///|
extern "js" fn js_udp_server_new(
addr : JsUdpAddr,
dual_stack : Bool,
) -> JsPromise[JsUdpSocketResult] =
#| async (addr, dualStack) => {
#| const dgram = await import('node:dgram');
#| const state = globalThis.__xudp ??= { nextId: 1, sockets: new Map() };
#| const socket = dgram.createSocket({
#| type: addr.family === 6 ? 'udp6' : 'udp4',
#| ipv6Only: addr.family === 6 ? !dualStack : false,
#| reuseAddr: true,
#| });
#| const handle = { socket, q: [], waiters: [], closed: false };
#| socket.on('message', (msg, rinfo) => {
#| const item = { data: new Uint8Array(msg), host: rinfo.address, port: rinfo.port | 0, family: rinfo.family === 'IPv6' ? 6 : 4 };
#| const waiter = handle.waiters.shift();
#| if (waiter) waiter(item);
#| else handle.q.push(item);
#| });
#| await new Promise((resolve, reject) => {
#| const onError = (err) => { socket.off('listening', onListening); reject(err); };
#| const onListening = () => { socket.off('error', onError); resolve(); };
#| socket.once('error', onError);
#| socket.once('listening', onListening);
#| socket.bind(addr.port, addr.host);
#| });
#| const id = state.nextId++;
#| state.sockets.set(id, handle);
#| const local = socket.address();
#| return { id, host: local.address, port: local.port | 0, family: local.family === 'IPv6' ? 6 : 4 };
#| }
///|
extern "js" fn js_udp_close(id : Int) -> Unit =
#| (id) => {
#| const state = globalThis.__xudp;
#| const handle = state?.sockets?.get(id);
#| if (!handle || handle.closed) return;
#| handle.closed = true;
#| state.sockets.delete(id);
#| const empty = { data: new Uint8Array(0), host: '', port: 0, family: 4 };
#| for (const waiter of handle.waiters.splice(0)) waiter(empty);
#| try { handle.socket.close(); } catch {}
#| }
///|
extern "js" fn js_udp_recv(id : Int) -> JsPromise[JsUdpRecvResult] =
#| async (id) => {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| if (handle.q.length > 0) return handle.q.shift();
#| if (handle.closed) return { data: new Uint8Array(0), host: '', port: 0, family: 4 };
#| return await new Promise(resolve => handle.waiters.push(resolve));
#| }
///|
extern "js" fn js_udp_send(
id : Int,
data : Bytes,
offset : Int,
len : Int,
) -> JsPromise[Unit] =
#| async (id, data, offset, len) => {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| const payload = Buffer.from(data).subarray(offset, offset + len);
#| await new Promise((resolve, reject) => handle.socket.send(payload, (err) => err ? reject(err) : resolve()));
#| }
///|
extern "js" fn js_udp_sendto(
id : Int,
data : Bytes,
addr : JsUdpAddr,
offset : Int,
len : Int,
) -> JsPromise[Unit] =
#| async (id, data, addr, offset, len) => {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| const payload = Buffer.from(data).subarray(offset, offset + len);
#| await new Promise((resolve, reject) => handle.socket.send(payload, addr.port, addr.host, (err) => err ? reject(err) : resolve()));
#| }
///|
extern "js" fn js_udp_connect(id : Int, addr : JsUdpAddr) -> String =
#| (id, addr) => {
#| try {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| handle.socket.connect(addr.port, addr.host);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
extern "js" fn js_udp_set_multicast_ttl(id : Int, ttl : Int) -> String =
#| (id, ttl) => {
#| try {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| handle.socket.setMulticastTTL(ttl);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
extern "js" fn js_udp_set_multicast_loopback(
id : Int,
loopback : Bool,
) -> String =
#| (id, loopback) => {
#| try {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| handle.socket.setMulticastLoopback(loopback);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
extern "js" fn js_udp_set_multicast_interface(
id : Int,
address : String,
) -> String =
#| (id, address) => {
#| try {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| handle.socket.setMulticastInterface(address);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
extern "js" fn js_udp_join_multicast_group(
id : Int,
multi_host : String,
interface_host : String,
) -> String =
#| (id, multiHost, interfaceHost) => {
#| try {
#| const handle = globalThis.__xudp?.sockets?.get(id);
#| if (!handle) throw new Error('udp socket not found');
#| handle.socket.addMembership(multiHost, interfaceHost || undefined);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
priv struct JsTcpAcceptResult {
conn_id : Int
conn_host : String
conn_port : Int
conn_family : Int
peer_host : String
peer_port : Int
peer_family : Int
}
///|
extern "js" fn js_tcp_connect(addr : JsUdpAddr) -> JsPromise[JsUdpSocketResult] =
#| async (addr) => {
#| const net = await import('node:net');
#| const state = globalThis.__xtcp ??= { nextId: 1, conns: new Map(), servers: new Map() };
#| const registerConn = globalThis.__xtcpRegisterConn ??= (socket) => {
#| socket.setNoDelay(true);
#| const id = state.nextId++;
#| const handle = { socket, q: [], waiters: [], closed: false };
#| const resolveBytes = (waiter, chunk) => {
#| const maxLen = Math.max(0, waiter.maxLen | 0);
#| if (maxLen === 0 || chunk.length <= maxLen) {
#| waiter.resolve(new Uint8Array(chunk));
#| } else {
#| waiter.resolve(new Uint8Array(chunk.subarray(0, maxLen)));
#| handle.q.unshift(chunk.subarray(maxLen));
#| }
#| };
#| socket.on('data', (chunk) => {
#| const waiter = handle.waiters.shift();
#| if (waiter) resolveBytes(waiter, Buffer.from(chunk));
#| else handle.q.push(Buffer.from(chunk));
#| });
#| const close = () => {
#| if (handle.closed) return;
#| handle.closed = true;
#| for (const waiter of handle.waiters.splice(0)) waiter.resolve(new Uint8Array(0));
#| };
#| socket.once('end', close);
#| socket.once('close', close);
#| socket.once('error', close);
#| state.conns.set(id, handle);
#| return id;
#| };
#| const socket = net.createConnection({ host: addr.host, port: addr.port, family: addr.family || undefined });
#| await new Promise((resolve, reject) => {
#| const onError = (err) => { socket.off('connect', onConnect); reject(err); };
#| const onConnect = () => { socket.off('error', onError); resolve(); };
#| socket.once('error', onError);
#| socket.once('connect', onConnect);
#| });
#| const id = registerConn(socket);
#| return {
#| id,
#| host: socket.localAddress || addr.host,
#| port: socket.localPort | 0,
#| family: socket.localFamily === 'IPv6' ? 6 : 4,
#| };
#| }
///|
extern "js" fn js_tcp_server_new(
addr : JsUdpAddr,
dual_stack : Bool,
reuse_addr : Bool,
) -> JsPromise[JsUdpSocketResult] =
#| async (addr, dualStack, reuseAddr) => {
#| const net = await import('node:net');
#| const state = globalThis.__xtcp ??= { nextId: 1, conns: new Map(), servers: new Map() };
#| if (!globalThis.__xtcpRegisterConn) {
#| globalThis.__xtcpRegisterConn = (socket) => {
#| socket.setNoDelay(true);
#| const id = state.nextId++;
#| const handle = { socket, q: [], waiters: [], closed: false };
#| const resolveBytes = (waiter, chunk) => {
#| const maxLen = Math.max(0, waiter.maxLen | 0);
#| if (maxLen === 0 || chunk.length <= maxLen) {
#| waiter.resolve(new Uint8Array(chunk));
#| } else {
#| waiter.resolve(new Uint8Array(chunk.subarray(0, maxLen)));
#| handle.q.unshift(chunk.subarray(maxLen));
#| }
#| };
#| socket.on('data', (chunk) => {
#| const waiter = handle.waiters.shift();
#| if (waiter) resolveBytes(waiter, Buffer.from(chunk));
#| else handle.q.push(Buffer.from(chunk));
#| });
#| const close = () => {
#| if (handle.closed) return;
#| handle.closed = true;
#| for (const waiter of handle.waiters.splice(0)) waiter.resolve(new Uint8Array(0));
#| };
#| socket.once('end', close);
#| socket.once('close', close);
#| socket.once('error', close);
#| state.conns.set(id, handle);
#| return id;
#| };
#| }
#| const server = net.createServer();
#| const id = state.nextId++;
#| const handle = { server, q: [], waiters: [], closed: false };
#| server.on('connection', (socket) => {
#| const connId = globalThis.__xtcpRegisterConn(socket);
#| const item = {
#| conn_id: connId,
#| conn_host: socket.localAddress || addr.host,
#| conn_port: socket.localPort | 0,
#| conn_family: socket.localFamily === 'IPv6' ? 6 : 4,
#| peer_host: socket.remoteAddress || '',
#| peer_port: socket.remotePort | 0,
#| peer_family: socket.remoteFamily === 'IPv6' ? 6 : 4,
#| };
#| const waiter = handle.waiters.shift();
#| if (waiter) waiter.resolve(item);
#| else handle.q.push(item);
#| });
#| await new Promise((resolve, reject) => {
#| const onError = (err) => { server.off('listening', onListening); reject(err); };
#| const onListening = () => { server.off('error', onError); resolve(); };
#| server.once('error', onError);
#| server.once('listening', onListening);
#| server.listen({
#| host: addr.host,
#| port: addr.port,
#| ipv6Only: addr.family === 6 ? !dualStack : false,
#| exclusive: !reuseAddr,
#| });
#| });
#| state.servers.set(id, handle);
#| const local = server.address();
#| return { id, host: local.address, port: local.port | 0, family: local.family === 'IPv6' ? 6 : 4 };
#| }
///|
extern "js" fn js_tcp_accept(id : Int) -> JsPromise[JsTcpAcceptResult] =
#| async (id) => {
#| const handle = globalThis.__xtcp?.servers?.get(id);
#| if (!handle) throw new Error('tcp server not found');
#| if (handle.q.length > 0) return handle.q.shift();
#| if (handle.closed) throw new Error('tcp server closed');
#| return await new Promise((resolve, reject) => handle.waiters.push({ resolve, reject }));
#| }
///|
extern "js" fn js_tcp_close(id : Int) -> Unit =
#| (id) => {
#| const state = globalThis.__xtcp;
#| const handle = state?.conns?.get(id);
#| if (!handle || handle.closed) return;
#| handle.closed = true;
#| state.conns.delete(id);
#| for (const waiter of handle.waiters.splice(0)) waiter.resolve(new Uint8Array(0));
#| try { handle.socket.destroy(); } catch {}
#| }
///|
extern "js" fn js_tcp_server_close(id : Int) -> Unit =
#| (id) => {
#| const state = globalThis.__xtcp;
#| const handle = state?.servers?.get(id);
#| if (!handle || handle.closed) return;
#| handle.closed = true;
#| state.servers.delete(id);
#| for (const waiter of handle.waiters.splice(0)) waiter.reject(new Error('tcp server closed'));
#| try { handle.server.close(); } catch {}
#| }
///|
extern "js" fn js_tcp_read(id : Int, max_len : Int) -> JsPromise[Bytes] =
#| async (id, maxLen) => {
#| const handle = globalThis.__xtcp?.conns?.get(id);
#| if (!handle) throw new Error('tcp connection not found');
#| const take = (chunk) => {
#| maxLen = Math.max(0, maxLen | 0);
#| if (maxLen === 0 || chunk.length <= maxLen) return new Uint8Array(chunk);
#| handle.q.unshift(chunk.subarray(maxLen));
#| return new Uint8Array(chunk.subarray(0, maxLen));
#| };
#| if (handle.q.length > 0) return take(handle.q.shift());
#| if (handle.closed) return new Uint8Array(0);
#| return await new Promise((resolve, reject) => handle.waiters.push({ maxLen, resolve, reject }));
#| }
///|
extern "js" fn js_tcp_write(
id : Int,
data : Bytes,
offset : Int,
len : Int,
) -> JsPromise[Int] =
#| async (id, data, offset, len) => {
#| const handle = globalThis.__xtcp?.conns?.get(id);
#| if (!handle) throw new Error('tcp connection not found');
#| const payload = Buffer.from(data).subarray(offset, offset + len);
#| if (payload.length === 0) return 0;
#| await new Promise((resolve, reject) => handle.socket.write(payload, (err) => err ? reject(err) : resolve()));
#| return payload.length | 0;
#| }
///|
extern "js" fn js_tcp_enable_keepalive(
id : Int,
idle_before_keep_alive : Int,
) -> String =
#| (id, idleBeforeKeepAlive) => {
#| try {
#| const handle = globalThis.__xtcp?.conns?.get(id);
#| if (!handle) throw new Error('tcp connection not found');
#| const initialDelay = Math.max(0, Number(idleBeforeKeepAlive || 0)) * 1000;
#| handle.socket.setKeepAlive(true, initialDelay);
#| return '';
#| } catch (err) {
#| return String(err?.message ?? err);
#| }
#| }
///|
fn protocol_to_js(protocol : IpProtocolPreference) -> Int {
match protocol {
OnlyV4 => 4
OnlyV6 => 6
FavorV4 => 46
FavorV6 => 64
NoPreference => 0
}
}
///|
pub async fn Addr::resolve(
host : String,
port~ : Int,
protocol? : IpProtocolPreference,
) -> Addr raise ResolveHostnameError {
let result = run_js_promise(
js_udp_resolve(host, port, protocol_to_js(protocol.unwrap_or(NoPreference))),
) catch {
err => raise ResolveHostnameError::ResolveHostnameError(err.to_string())
}
addr_from_js(result.host, result.port, result.family)
}
///|
pub struct Tcp {
addr : Addr
priv id : Int
priv read_buf : @io.ReaderBuffer
}
///|
pub fn Tcp::close(self : Tcp) -> Unit {
js_tcp_close(self.id)
}
///|
pub fn Tcp::fd(_self : Tcp) -> Int {
-1
}
///|
pub fn Tcp::addr(self : Tcp) -> Addr {
self.addr
}
///|
pub async fn Tcp::connect(addr : Addr) -> Tcp {
let result = run_js_promise(js_tcp_connect(addr_to_js(addr)))
Tcp::{
id: result.id,
addr: addr_from_js(result.host, result.port, result.family),
read_buf: @io.ReaderBuffer::new(),
}
}
///|
pub async fn Tcp::connect_to_host(
host : StringView,
port~ : Int,
protocol? : IpProtocolPreference,
) -> Tcp {
let addr = Addr::resolve(host.to_owned(), port~, protocol?)
Tcp::connect(addr)
}
///|
pub fn Tcp::enable_keepalive(
self : Tcp,
idle_before_keep_alive? : Int,
keep_alive_count? : Int,
keep_alive_interval? : Int,
) -> Unit raise {
ignore((keep_alive_count, keep_alive_interval))
let err = js_tcp_enable_keepalive(
self.id,
idle_before_keep_alive.unwrap_or(0),
)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub impl @io.Reader for Tcp with fn _get_internal_buffer(self) {
self.read_buf
}
///|
pub impl @io.Reader for Tcp with fn _direct_read(self, buf, offset~, max_len~) {
if max_len <= 0 {
0
} else {
let data = run_js_promise(js_tcp_read(self.id, max_len)) catch {
_ => return 0
}
let len = data.length()
if len <= 0 {
0
} else {
let n = if len < max_len { len } else { max_len }
buf.blit_from_bytes(offset, data, 0, n)
n
}
}
}
///|
pub impl @io.Writer for Tcp with fn write_once(self, buf, offset~, len~) {
let available = buf.length() - offset
let n = if len < available { len } else { available }
if n <= 0 {
0
} else {
run_js_promise(js_tcp_write(self.id, buf, offset, n))
}
}
///|
pub struct TcpServer {
addr : Addr
priv id : Int
}
///|
#alias(new, deprecated)
pub async fn TcpServer::TcpServer(
addr : Addr,
dual_stack? : Bool,
reuse_addr? : Bool,
) -> TcpServer {
let result = run_js_promise(
js_tcp_server_new(
addr_to_js(addr),
dual_stack.unwrap_or(true),
reuse_addr.unwrap_or(false),
),
)
TcpServer::{
id: result.id,
addr: addr_from_js(result.host, result.port, result.family),
}
}
///|
pub fn TcpServer::close(self : TcpServer) -> Unit {
js_tcp_server_close(self.id)
}
///|
pub fn TcpServer::fd(_self : TcpServer) -> Int {
-1
}
///|
#deprecated("use `.addr` instead")
pub fn TcpServer::addr(self : TcpServer) -> Addr {
self.addr
}
///|
pub async fn TcpServer::accept(self : TcpServer) -> (Tcp, Addr) {
let result = run_js_promise(js_tcp_accept(self.id))
(
Tcp::{
id: result.conn_id,
addr: addr_from_js(result.conn_host, result.conn_port, result.conn_family),
read_buf: @io.ReaderBuffer::new(),
},
addr_from_js(result.peer_host, result.peer_port, result.peer_family),
)
}
///|
pub async fn TcpServer::run_forever(
self : TcpServer,
f : async (Tcp, Addr) -> Unit,
allow_failure? : Bool,
max_connections? : Int,
) -> Unit {
defer self.close()
let conn_limit = match max_connections {
Some(n) => Some(@async.Semaphore(n))
None => None
}
@async.with_task_group(group => {
for ;; {
let (conn, peer) = self.accept()
if conn_limit is Some(limit) {
limit.acquire()
}
group.spawn_bg(allow_failure=allow_failure.unwrap_or(true), () => {
defer conn.close()
if conn_limit is Some(limit) {
defer limit.release()
f(conn, peer)
} else {
f(conn, peer)
}
})
}
})
}
///|
fn copy_recv(
result : JsUdpRecvResult,
buf : FixedArray[Byte],
offset : Int,
max_len : Int,
) -> Int {
let n = if result.data.length() < max_len {
result.data.length()
} else {
max_len
}
buf.blit_from_bytes(offset, result.data, 0, n)
n
}
///|
pub struct UdpClient {
addr : Addr
priv id : Int
}
///|
#alias(new, deprecated)
pub async fn UdpClient::UdpClient(addr : Addr) -> UdpClient {
let result = run_js_promise(js_udp_client_new(addr_to_js(addr)))
UdpClient::{
id: result.id,
addr: addr_from_js(result.host, result.port, result.family),
}
}
///|
pub fn UdpClient::close(self : UdpClient) -> Unit {
js_udp_close(self.id)
}
///|
pub fn UdpClient::fd(_self : UdpClient) -> Int {
-1
}
///|
#deprecated("use `.addr` instead")
pub fn UdpClient::addr(self : UdpClient) -> Addr {
self.addr
}
///|
pub fn UdpClient::connect(self : UdpClient, addr : Addr) -> Unit raise {
let err = js_udp_connect(self.id, addr_to_js(addr))
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub async fn UdpClient::recv(
self : UdpClient,
buf : FixedArray[Byte],
offset? : Int,
max_len? : Int,
) -> Int {
let offset = offset.unwrap_or(0)
let max_len = max_len.unwrap_or(buf.length() - offset)
let result = run_js_promise(js_udp_recv(self.id))
copy_recv(result, buf, offset, max_len)
}
///|
pub async fn UdpClient::recvfrom(
self : UdpClient,
buf : FixedArray[Byte],
offset? : Int,
max_len? : Int,
) -> (Int, Addr) {
let offset = offset.unwrap_or(0)
let max_len = max_len.unwrap_or(buf.length() - offset)
let result = run_js_promise(js_udp_recv(self.id))
let n = copy_recv(result, buf, offset, max_len)
(n, addr_from_js(result.host, result.port, result.family))
}
///|
pub async fn UdpClient::send(
self : UdpClient,
buf : Bytes,
offset? : Int,
len? : Int,
) -> Unit {
let offset = offset.unwrap_or(0)
let len = len.unwrap_or(buf.length() - offset)
run_js_promise(js_udp_send(self.id, buf, offset, len))
}
///|
pub fn UdpClient::set_multicast_interface(
self : UdpClient,
addr : Addr,
) -> Unit raise {
let err = js_udp_set_multicast_interface(self.id, addr.host)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpClient::set_multicast_interface_v6(
self : UdpClient,
interface : String,
) -> Unit raise {
let err = js_udp_set_multicast_interface(self.id, interface)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpClient::set_multicast_loopback(
self : UdpClient,
loopback : Bool,
) -> Unit raise {
let err = js_udp_set_multicast_loopback(self.id, loopback)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpClient::set_multicast_ttl(self : UdpClient, ttl : Int) -> Unit raise {
let err = js_udp_set_multicast_ttl(self.id, ttl)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub struct UdpServer {
addr : Addr
priv id : Int
}
///|
#alias(new, deprecated)
pub async fn UdpServer::UdpServer(addr : Addr, dual_stack? : Bool) -> UdpServer {
let result = run_js_promise(
js_udp_server_new(addr_to_js(addr), dual_stack.unwrap_or(true)),
)
UdpServer::{
id: result.id,
addr: addr_from_js(result.host, result.port, result.family),
}
}
///|
pub async fn UdpServer::multicast(
multi_addr : Addr,
interface_addr? : Addr,
) -> UdpServer {
let server = UdpServer(Addr::new(0U, multi_addr.port))
errdefer server.close()
server.join_multicast_group(multi_addr, interface_addr?)
server
}
///|
pub fn UdpServer::close(self : UdpServer) -> Unit {
js_udp_close(self.id)
}
///|
pub fn UdpServer::fd(_self : UdpServer) -> Int {
-1
}
///|
#deprecated("use `.addr` instead")
pub fn UdpServer::addr(self : UdpServer) -> Addr {
self.addr
}
///|
pub async fn UdpServer::recvfrom(
self : UdpServer,
buf : FixedArray[Byte],
offset? : Int,
max_len? : Int,
) -> (Int, Addr) {
let offset = offset.unwrap_or(0)
let max_len = max_len.unwrap_or(buf.length() - offset)
let result = run_js_promise(js_udp_recv(self.id))
let n = copy_recv(result, buf, offset, max_len)
(n, addr_from_js(result.host, result.port, result.family))
}
///|
pub async fn UdpServer::sendto(
self : UdpServer,
buf : Bytes,
addr : Addr,
offset? : Int,
len? : Int,
) -> Unit {
let offset = offset.unwrap_or(0)
let len = len.unwrap_or(buf.length() - offset)
run_js_promise(js_udp_sendto(self.id, buf, addr_to_js(addr), offset, len))
}
///|
pub fn UdpServer::join_multicast_group(
self : UdpServer,
multi_addr : Addr,
interface_addr? : Addr,
) -> Unit raise {
let err = js_udp_join_multicast_group(
self.id,
multi_addr.host,
interface_addr.map(fn(addr) { addr.host }).unwrap_or(""),
)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpServer::join_multicast_group_v6(
self : UdpServer,
multi_addr : Addr,
interface? : String,
) -> Unit raise {
let err = js_udp_join_multicast_group(
self.id,
multi_addr.host,
interface.unwrap_or(""),
)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpServer::set_multicast_interface(
self : UdpServer,
addr : Addr,
) -> Unit raise {
let err = js_udp_set_multicast_interface(self.id, addr.host)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpServer::set_multicast_interface_v6(
self : UdpServer,
interface : String,
) -> Unit raise {
let err = js_udp_set_multicast_interface(self.id, interface)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpServer::set_multicast_loopback(
self : UdpServer,
loopback : Bool,
) -> Unit raise {
let err = js_udp_set_multicast_loopback(self.id, loopback)
if err != "" {
raise Failure::Failure(err)
}
}
///|
pub fn UdpServer::set_multicast_ttl(self : UdpServer, ttl : Int) -> Unit raise {
let err = js_udp_set_multicast_ttl(self.id, ttl)
if err != "" {
raise Failure::Failure(err)
}
}