std.net

Raw TCP and TLS sockets. Needs net.

import std.net

c = net.connect("example.com", 80)
net.send(c, bytes("GET / HTTP/1.0\r\n\r\n"))
data = net.recv(c)                    # bytes
net.close(c)

c = net.connect_tls("example.com", 443)
c = net.starttls(c)                   # upgrade an open connection

Reading

call
net.recv(c)whatever is available, as bytes
net.recv_text(c)the same, decoded as UTF-8
net.recv_exact(c, n)exactly n bytes
net.recv_until(c, delim)up to and including a delimiter

recv returns whatever arrived, not a whole message. TCP is a byte stream with no message boundaries, so a single recv may give you half a response or two responses. This is the classic source of protocol bugs.

Use recv_exact for a length-prefixed protocol and recv_until for a line-delimited one - those two cover almost everything, and both handle the framing you would otherwise get wrong.

starttls

Upgrades an established plaintext connection, which is what SMTP, IMAP and PostgreSQL do. connect_tls is for a connection that is encrypted from the start.

Why this exists

So that a protocol client can be written in Ecko, with no native code. The official MySQL, PostgreSQL, Redis and SMTP clients are all built on this module - real wire protocols, authentication scrambles and all, in the language itself.

That is the three-layer policy working: rather than adding a database driver to the runtime, the runtime provides sockets and the driver is an ordinary, capability-gated, replaceable package.

Use a client, not this

For talking to a database or a mail server, use the package:

Reach for net when you are implementing a protocol that does not have a client yet.

Errors and timeouts

Failures raise { kind: "net", host }. A read on a peer that never responds waits - so bound your own protocol logic, and do not expose an unbounded socket loop to untrusted peers.