std/net
TCP and UDP sockets, and TLS on both sides of the connection. Import with
import "std/net";. Native backend only.
Calls block by default. Failure is reported as none, false, or a negative
byte count, never as an exception. Sockets are plain OS handles: call close
when you are done with one.
The module defines five types: Socket (a TCP or UDP socket), TlsSocket (a
TLS connection over TCP), TlsConfig (a reusable TLS setup: role,
certificates, ALPN, verification policy), Addr (a resolved network address),
and Datagram (bytes received over UDP together with their sender).
Connecting and listening
Section titled “Connecting and listening”connect
Section titled “connect”Socket? connect(host: string, port: int)Resolves host, creates a TCP socket, and connects it to host:port. Returns
the connected socket, or none if any step fails.
let sock = net.connect("example.com", 80);if (sock == none) { return 1; }listen
Section titled “listen”Socket? listen(host: string, port: int, backlog: int)Creates a TCP server socket bound to host:port (with address reuse enabled)
and starts listening. backlog is the pending-connection queue length.
Returns none on failure. Use "0.0.0.0" as the host to accept from any
interface.
bind_udp
Section titled “bind_udp”Socket? bind_udp(host: string, port: int)Creates a UDP socket bound to host:port, ready for recv_from and
send_to. Returns none on failure.
resolve
Section titled “resolve”Addr? resolve(host: string, port: int)Resolves host:port into an address suitable as a connect or send target.
host can be a name or a numeric IP. Returns none if resolution fails.
resolve_bind
Section titled “resolve_bind”Addr? resolve_bind(host: string, port: int)Like resolve, but produces a wildcard-capable address suitable for binding a
server socket.
tcp_socket, udp_socket
Section titled “tcp_socket, udp_socket”Socket? tcp_socket(family: int)Socket? udp_socket(family: int)Create an unbound, unconnected socket. family is net.IPV4 or net.IPV6.
These are the low-level building blocks. Most programs use connect,
listen, or bind_udp instead.
Methods on Socket
Section titled “Methods on Socket”accept
Section titled “accept”Socket? accept(self)Waits for the next incoming connection on a listening socket and returns it,
or none on error. Close the returned socket separately.
int send(self, data: string)Sends data on a connected socket. Returns the number of bytes actually sent,
which may be fewer than data.len(), or -1 on error.
send_all
Section titled “send_all”bool send_all(self, data: string)Sends every byte of data, retrying partial sends. Returns false on error.
Prefer this over send unless you handle partial sends yourself.
string? recv(self, max: int)Receives up to max bytes. Returns "" when the peer has closed the
connection, or none on error.
let chunk = sock.recv(4096);while (chunk != none && chunk!.len() > 0) { io.write(chunk!); chunk = sock.recv(4096);}send_to
Section titled “send_to”int send_to(self, data: string, addr: Addr)Sends data as a UDP datagram to addr. Returns bytes sent, or -1 on
error.
recv_from
Section titled “recv_from”Datagram? recv_from(self, max: int)Receives one UDP datagram of up to max bytes. Returns the data and the
sender’s address, or none on error. A Datagram is a struct
{ data: string, from: Addr }.
local, peer
Section titled “local, peer”Addr? local(self) # this socket's own addressAddr? peer(self) # the connected peer's addressReturn none on error, for example peer on an unconnected socket.
Options and lifetime
Section titled “Options and lifetime”bool set_blocking(self, blocking: bool) # non-blocking calls fail fast instead of waitingbool set_timeout(self, ms: int) # send/receive timeout in ms; 0 disablesbool set_reuse(self, on: bool) # SO_REUSEADDRbool set_keepalive(self, on: bool) # SO_KEEPALIVEbool set_broadcast(self, on: bool) # SO_BROADCAST, needed to send UDP broadcastsbool set_nodelay(self, on: bool) # TCP_NODELAY, disable Nagle batchingbool set_option(self, option: int, value: int) # generic form, see the constants belowbool shutdown(self, how: int) # stop one direction, see the constants belowvoid close(self) # release the OS handleAll the setters return true on success. The module provides constants for
the numeric codes:
let IPV4 = 4; # address families, for tcp_socket and udp_socketlet IPV6 = 6;
let OPT_REUSE = 0; # option codes, for set_optionlet OPT_KEEPALIVE = 1;let OPT_BROADCAST = 2;let OPT_NODELAY = 3;
let SHUT_READ = 0; # directions, for shutdownlet SHUT_WRITE = 1;let SHUT_BOTH = 2;let sock = net.tcp_socket(net.IPV6)!;sock.shutdown(net.SHUT_WRITE);The native runtime bundles TLS clients and servers (Mbed TLS 3.6). By default,
certificates are verified against the system trust store. This includes the Windows
certificate store, or the standard CA bundle locations on Linux and macOS.
Point the KORA_CA_BUNDLE environment variable at a PEM file to supply your
own roots. Set KORA_TLS_DEBUG=1 to print handshake failures to stderr.
For the common client case, use tls_connect or tls_client below and skip
the rest. Everything else (servers, mutual TLS, ALPN, custom roots) goes
through a TlsConfig.
tls_connect
Section titled “tls_connect”TlsSocket? tls_connect(host: string, port: int)Resolves host, connects over TCP, and performs a TLS handshake, verifying
the server’s certificate and hostname. Returns none if any step fails,
including verification.
let sock = net.tls_connect("example.com", 443);tls_client
Section titled “tls_client”TlsSocket? tls_client(sock: Socket, host: string)Upgrades an already-connected TCP socket to TLS, verifying the certificate
against host. Use this when you need socket options (like timeouts) set
before the handshake. The returned TlsSocket owns the connection. Do not
read or write sock directly afterwards.
tls_client_insecure
Section titled “tls_client_insecure”TlsSocket? tls_client_insecure(sock: Socket, host: string)Like tls_client, but skips certificate verification. The connection is
encrypted yet you have no proof of who is on the other end. Only for local
endpoints and self-signed setups.
tls_config
Section titled “tls_config”TlsConfig? tls_config()A client configuration: verification on, system roots loaded. Adjust it with
the methods below, then upgrade connected sockets with handshake. One config
serves any number of connections and is safe to share across threads.
tls_server_config
Section titled “tls_server_config”TlsConfig? tls_server_config(cert_pem: string, key_pem: string)A server configuration presenting the given certificate chain and private key,
both PEM text. Returns none if either fails to parse. By default it does not
request client certificates. See verify for mutual TLS.
tls_accept
Section titled “tls_accept”TlsSocket? tls_accept(listener: Socket, config: TlsConfig)Waits for the next TCP connection on a listening socket and performs the
server side of the handshake. Returns none on error, with the raw connection
closed.
Methods on TlsConfig
Section titled “Methods on TlsConfig”bool own_cert(self, cert_pem: string, key_pem: string) # certificate to present; # on a client config this is mutual TLSbool ca(self, ca_pem: string) # trust these roots instead of the system'sbool alpn(self, protos: [string]) # offer application protocols, e.g. ["h2", "http/1.1"]bool verify(self, on: bool) # require and check the peer's certificateTlsSocket? handshake(self, sock: Socket, host: string) # upgrade sock; "" as host on serversConfigure first, then hand out sessions: alpn can be set once, and a config
should not be modified while connections made from it are live. On a server
config, verify(true) plus ca(...) demands and checks client certificates,
which is mutual TLS.
Methods on TlsSocket
Section titled “Methods on TlsSocket”int send(self, data: string) # bytes written, or -1bool send_all(self, data: string) # write every byte; false on errorstring? recv(self, max: int) # up to max bytes; "" at clean close, none on errorvoid close(self) # send close-notify and close the socket
bool verified(self) # the peer's certificate chain checked outstring version(self) # negotiated protocol, e.g. "TLSv1.3"string? alpn(self) # negotiated ALPN protocol, or nonestring? peer_cert(self) # the peer's certificate as DER bytes, or noneThe I/O methods have the same shapes as their Socket counterparts, carried
over TLS.
Methods on Addr
Section titled “Methods on Addr”string host(self) # numeric IP, e.g. "93.184.216.34"int port(self)int family(self) # 4 for IPv4, 6 for IPv6Example: HTTPS client
Section titled “Example: HTTPS client”An HTTPS GET, start to finish. For plain HTTP, swap net.tls_connect(host, 443)
for net.connect(host, 80). Everything else is identical.
import "std/net";import "std/io";
int main() { let opened = net.tls_connect("example.com", 443); if (opened == none) { io.print("could not connect"); return 1; } let sock = opened!;
let request = "GET / HTTP/1.0\r\nHost: example.com\r\nConnection: close\r\n\r\n"; if (!sock.send_all(request)) { sock.close(); return 1; }
let chunk = sock.recv(4096); while (chunk != none && chunk!.len() > 0) { io.write(chunk!); chunk = sock.recv(4096); } sock.close(); return 0;}Example: TCP echo server
Section titled “Example: TCP echo server”import "std/net";import "std/io";
int main() { let server = net.listen("0.0.0.0", 7777, 16); if (server == none) { io.print("could not listen on 7777"); return 1; } while (true) { let conn = server!.accept(); if (conn == none) { continue; } let c = conn!; let data = c.recv(1024); if (data != none) { c.send_all(data!); } c.close(); } return 0;}Example: TLS echo server
Section titled “Example: TLS echo server”The same server behind TLS. The certificate and key are PEM files, for local
testing generated with
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout server.key -out server.crt -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost".
import "std/net";import "std/io";import "std/fs";
int main() { let cert = fs.open("server.crt", "r")!.read_all(); let key = fs.open("server.key", "r")!.read_all(); let made = net.tls_server_config(cert, key); if (made == none) { io.print("bad certificate or key"); return 1; } let config = made!;
let server = net.listen("0.0.0.0", 8443, 16); if (server == none) { io.print("could not listen on 8443"); return 1; } while (true) { let conn = net.tls_accept(server!, config); if (conn == none) { continue; } let c = conn!; let data = c.recv(1024); if (data != none) { c.send_all(data!); } c.close(); } return 0;}A client connects with its own roots pointed at the server’s certificate:
let config = net.tls_config()!;config.ca(cert_pem); # trust this server's certlet sock = config.handshake(net.connect("127.0.0.1", 8443)!, "localhost");Example: UDP
Section titled “Example: UDP”import "std/net";
int main() { # receiver let sock = net.bind_udp("127.0.0.1", 9999)!; let dgram = sock.recv_from(1024); if (dgram != none) { # reply to whoever sent it sock.send_to("pong", dgram!.from); } sock.close(); return 0;}