Skip to content

std/tls ​

The TLS server transport: listen, accept, and run the server handshake. The client half (TlsStream, fetch) is in std/fetch, and std/https is the HTTP server built on this module. Importing it pulls in OpenSSL; std/net and std/http stay free of it.

milo
from "std/tls" import { TlsListener, TlsConn }

API reference ​

TlsConn ​

milo
pub struct TlsConn

One accepted TLS connection. The server-side counterpart of std/fetch's TlsStream; separate from it because the two differ where it matters — a client owns its SSL_CTX and verifies the peer, a server's ctx belongs to the listener and is shared.

LIFETIME: ctx is deliberately absent. The SSL_CTX comes from the TlsListener and is shared by every connection it accepts, so freeing it here would pull it out from under the next accept(). The listener must outlive the connections it hands out.

TlsConn.readMoreBy ​

milo
fn TlsConn.readMoreBy(self: &TlsConn, raw: &mut string, by: i64): i64

recvOnce that gives up at by (epoch ms, 0 = never), appending to raw: the byte count, 0 once the peer has closed or the read failed, -1 at the deadline. That is std/http's request-reader contract (see readMoreBy there), so a TLS client that stalls mid-request is dropped like a plaintext one. Needs the fd nonblocking, which acceptWithin leaves it.

TlsConn.recv ​

milo
fn TlsConn.recv(self: &TlsConn): Result<string, NetError>

Read to EOF, as one string. For a request framed by the peer closing its write half; an HTTP server wants recvOnce().

TlsConn.recvOnce ​

milo
fn TlsConn.recvOnce(self: &TlsConn): string

A single SSL_read of whatever has arrived. A server must answer while the client still holds the connection open, so a read-to-EOF loop would deadlock; this returns after one record — the TLS analogue of one read(2) on the socket.

TlsConn.send ​

milo
fn TlsConn.send(self: &TlsConn, data: &string): Result<i64, NetError>

Write data through the TLS record layer. Returns the byte count OpenSSL accepted.

TlsConn.sendWithin ​

milo
fn TlsConn.sendWithin(self: &TlsConn, data: &string, ms: i64): Result<i64, NetError>

send that stops waiting on a peer that is not reading once ms has passed (0 = no limit).

TlsListener ​

milo
pub struct TlsListener

Listening TLS socket: a TCP listener plus the server SSL_CTX holding the certificate chain and private key. Frees the ctx and closes the fd on drop.

TlsListener.accept ​

milo
fn TlsListener.accept(self: &TlsListener): Result<TlsConn, NetError>

Accept one connection and run the server handshake on it.

TlsListener.acceptWithin ​

milo
fn TlsListener.acceptWithin(self: &TlsListener, ms: i64): Result<TlsConn, NetError>

accept() whose handshake must finish within ms of the TCP accept (0 = no limit). Without it a client that connects and sends nothing parks the handshake forever, and a server accepting one connection at a time (std/https) stops answering everyone. The connection comes back nonblocking, for readMoreBy and sendWithin; recvOnce/recv/send assume a blocking fd outside a green task, so use those on a connection from accept().

TlsListener.addr4 ​

milo
fn TlsListener.addr4(self: &TlsListener): Option<u32>

The bound IPv4 address, or None for an IPv6 socket (or a failed getsockname).

TlsListener.bind ​

milo
fn TlsListener.bind(port: u16, certPath: &string, keyPath: &string): Result<TlsListener, NetError>

Bind 0.0.0.0:port and serve certPath (PEM chain, leaf first) with keyPath. Port 0 lets the OS pick — recover it with port(). Binds exactly that; started under systemd socket activation for this process it errs naming inheritedOr.

TlsListener.handshakeWithin ​

milo
fn TlsListener.handshakeWithin(self: &TlsListener, clientFd: i32, ms: i64): Result<TlsConn, NetError>

The server handshake on clientFd, an accepted TCP connection this call takes over (closed on failure, owned by the TlsConn on success), finishing within ms (0 = no limit). acceptWithin's second half, apart so a concurrent server can accept on one task and handshake on the connection's own: a handshake on the accept task is one stalled client stopping every other accept. @unsafe: the caller vouches that clientFd is an accepted socket it owns and hands over here.

TlsListener.inheritedOr ​

milo
fn TlsListener.inheritedOr(port: u16, certPath: &string, keyPath: &string): Result<TlsListener, NetError>

The socket systemd socket activation passed this process, serving certPath with keyPath; or, when it was not started that way, a fresh bind of 0.0.0.0:port like bind. The TLS twin of TcpListener.inheritedOr; isInherited() says which happened.

TlsListener.isInherited ​

milo
fn TlsListener.isInherited(self: &TlsListener): bool

Whether this listener came from socket activation (inheritedOr) rather than a bind.

TlsListener.open ​

milo
fn TlsListener.open(port: u16, certPath: &string, keyPath: &string, inherit: bool): Result<TlsListener, NetError>

bind and inheritedOr: the inherited socket when inherit and there is one, else a fresh bind of 0.0.0.0:port; then the certificate chain and key. Use those two.

TlsListener.port ​

milo
fn TlsListener.port(self: &TlsListener): i32

The bound port — how to learn what the OS picked after bind(0). -1 if getsockname fails, mirroring getSockPort rather than reporting a plausible-looking port number.

TlsListener.releaseFd ​

milo
fn TlsListener.releaseFd(self: &mut TlsListener): i32

Hand the listening fd to a caller that will close it (std/https's accept loop does when draining); this listener then only frees the SSL_CTX on drop. @unsafe: the number is the caller's to close exactly once.