std.net.tls.Server
type pub Server[T: mut + Deadline + RawSocketOperations]A type that acts as the server in a TLS session.
Server values wrap existing sockets such as std.net.socket.TcpClient and
apply TLS encryption/decryption to IO operations.
Closing TLS connections
When a Client is dropped the TLS connection is closed by sending the TLS
close_notify message.
Examples
import std.crypto.x509 (Certificate, PrivateKey)
import std.net.ip (IpAddress)
import std.net.socket (TcpServer)
import std.net.tls (Server, ServerConfig)
let cert = Certificate.new(ByteArray.from_array([1, 2, 3]))
let key = PrivateKey.new(ByteArray.from_array([4, 5, 6]))
let conf = ServerConfig
.new(cert, key)
.or_panic_with('failed to create the server configuration')
let server = TcpServer
.new(ip: IpAddress.v4(0, 0, 0, 0), port: 9000)
.or_panic_with('failed to start the server')
let con = server
.accept
.map(fn (sock) { Server.new(sock, conf) })
.or_panic_with('failed to accept the new connection')
let bytes = ByteArray.new
con.read(into: bytes, size: 32).or_panic_with('failed to read the data')
Fields
socket
let pub @socket: T: mutThe socket wrapped by this Server.
Static methods
new
Show source codeHide source code
fn pub static new(socket: T, config: ref ServerConfig) -> Server[T] {
Server(socket, inko_tls_server_connection_new(config.raw))
}fn pub static new(socket: T: mut, config: ref ServerConfig) -> Server[T: mut]Returns a Server acting as the server in a TLS session.
The socket argument is the socket (e.g. std.net.socket.TcpClient) to
wrap. This can be either an owned socket or a mutable borrow of a socket.
The config argument is a ServerConfig instance to use for configuring
the TLS connection.
Examples
import std.crypto.x509 (Certificate, PrivateKey)
import std.net.ip (IpAddress)
import std.net.socket (TcpServer)
import std.net.tls (Server, ServerConfig)
let cert = Certificate.new(ByteArray.from_array([1, 2, 3]))
let key = PrivateKey.new(ByteArray.from_array([4, 5, 6]))
let conf = ServerConfig
.new(cert, key)
.or_panic_with('failed to create the server configuration')
let server = TcpServer
.new(ip: IpAddress.v4(0, 0, 0, 0), port: 9000)
.or_panic_with('failed to start the server')
server
.accept
.map(fn (sock) { Server.new(sock, conf) })
.or_panic_with('failed to accept the new connection')
Instance methods
flush
Show source codeHide source code
fn pub mut flush -> Result[Nil, Error] {
Result.Ok(nil)
}fn pub mut flush -> Result[Nil, Error]Flushes any pending writes to the file system.
Flushing writes is a potentially expensive operation, and unnecessarily calling this method may degrade performance.
When flushing data to disk it's important to remember that the actual behaviour may vary based on the type of file system, operating system and storage hardware that's used. In particular, it's possible for one of these components to say "Yup, I totally flushed the data, you're all good!" when in fact they have not fully flushed the data.
Show source codeHide source code
fn pub mut print[B: Bytes](bytes: ref B) -> Result[Nil, E] {
try write(bytes)
write('\n')
}fn pub mut print[B: Bytes](bytes: ref B) -> Result[Nil, E]Writes the entirety of bytes to the underlying stream, followed by
a Unix newline.
read
Show source codeHide source code
fn pub mut read(into: mut ByteArray, size: Int) -> Result[Int, Error] {
into.reserve_exact(size)
let len = into.size
let ptr = ptr.add(into.pointer, len)
match
inko_tls_server_read(
@socket.raw_socket,
@state,
ptr,
size,
@socket.raw_deadline,
mut read_callback,
mut write_callback,
)
{
case { @tag = OK, @value = v } -> {
into.size = len + v
Result.Ok(v)
}
case { @tag = _, @value = e } -> Result.Error(Error.from_os_error(e))
}
}fn pub mut read(into: mut ByteArray, size: Int) -> Result[Int, Error]Reads up to size bytes from self into the given ByteArray, returning
the number of bytes read.
The into argument is the ByteArray to read the bytes into. The capacity
of this ByteArray is increased automatically if necessary.
The size argument specifies how many bytes are to be read.
The return value is the number of bytes read.
The number of bytes read may be less than size. This can happen for
different reasons, such as when all input is consumed or not enough data is
available (yet).
read_all
Show source codeHide source code
fn pub mut read_all(bytes: mut ByteArray) -> Result[Int, E] {
let mut total = 0
let mut read_size = INITIAL_READ_ALL_SIZE
loop {
match read(into: bytes, size: read_size) {
case Ok(0) -> return Result.Ok(total)
case Ok(n) -> {
total += n
# To reduce the number of calls to `Reader.read` when there's lots of
# input to consume, we increase the read size if deemed beneficial.
if read_size < MAX_READ_ALL_SIZE and n == read_size { read_size *= 2 }
}
case Error(e) -> throw e
}
}
}fn pub mut read_all(bytes: mut ByteArray) -> Result[Int, E]Reads from self into the given ByteArray, returning when all input is
consumed.
The return value is the number of bytes read.
Errors
This method returns an Error if the underlying call to Read.read returns
an Error.
read_exact
Show source codeHide source code
fn pub mut read_exact(
into: mut ByteArray,
size: Int,
) -> Result[Nil, ReadExactError[E]] {
let mut pending = size
while pending > 0 {
match read(into, pending) {
case Ok(0) if pending > 0 -> throw ReadExactError.EndOfInput
case Ok(n) -> pending -= n
case Error(e) -> throw ReadExactError.Read(e)
}
}
Result.Ok(nil)
}fn pub mut read_exact(into: mut ByteArray, size: Int) -> Result[Nil, ReadExactError[E]]Reads exactly size bytes into into.
Whereas Read.read might return early if fewer bytes are available in the
input stream, Read.read_exact continues reading until the desired amount
of bytes is read.
Errors
If the end of the input stream is encountered before filling the buffer, an
Error.EndOfInput error is returned.
If an error is returned, no assumption can be made about the state of the
into buffer, i.e. there's no guarantee data read so far is in the buffer
in the event of an error.
reset_deadline
Show source codeHide source code
fn pub mut reset_deadline {
@socket.reset_deadline
}fn pub mut reset_deadlineResets the deadline.
Examples
import std.net.socket (Socket)
import std.time (Duration)
let socket = Socket.datagram(ipv6: false)
socket.timeout_after = Duration.from_secs(5)
socket.reset_deadline
send_file
Show source codeHide source code
fn pub mut send_file(file: mut ReadOnlyFile) -> Result[Int, Error] {
net.send_file_userspace(file, self)
}fn pub mut send_file(file: mut ReadOnlyFile) -> Result[Int, Error]Sends a read-only file to the other half of self, without the need for an
intermediate buffer.
Upon success the number of copied bytes is returned.
Upon returning from this method, the cursor/offset of the source file is the same as it was before calling this method.
Platform differences
The exact mechanism used for sending the file may vary per platform. For
example, on FreeBSD, Linux and macOS this method uses the sendfile system
call.
Fallbacks
Not all platforms or socket types support zero-copy sending of files. In such cases a userspace copy is performed. Such a fallback is applied to:
- TLS sockets provided by
std.net.tls UdpSocket.send_fileandUnixDatagram.send_fileon FreeBSD and macOS
The userspace implementation copies data in chunks of 32 KiB and resets the
cursor of file to its original position after copying the data. While
resetting the cursor incurs a small cost, it ensures behavior is consistent
with the kernel implementations of this method.
Examples
import std.fs.file (ReadOnlyFile)
import std.net.ip (IpAddress)
import std.net.socket (UdpSocket)
let file = ReadOnlyFile.new('README.md'.to_path).or_panic
let server = UdpSocket.new(IpAddress.v4(0, 0, 0, 0), port: 9999).or_panic
let client = UdpSocket.new(IpAddress.v4(0, 0, 0, 0), port: 0).or_panic
client.connect(IpAddress.v4(0, 0, 0, 0), port: 9999).or_panic
client.send_file(file).or_panic
let buf = ByteArray.new
server.read(buf, 1024).or_panic
buf.to_string # => '...'
timeout_after=
Show source codeHide source code
fn pub mut timeout_after=[I: ToInstant](deadline: ref I) {
@socket.timeout_after = deadline
}fn pub mut timeout_after=[I: ToInstant](deadline: ref I)Sets the point in time after which socket operations must time out, known as a "deadline".
Examples
Using a Duration results in this method calculating the absolute time
after which operations time out:
import std.net.socket (Socket)
import std.time (Duration)
let socket = Socket.datagram(ipv6: false)
socket.timeout_after = Duration.from_secs(5)
We can also use an Instant:
import std.net.socket (Socket)
import std.time (Duration, Instant)
let socket = Socket.datagram(ipv6: false)
socket.timeout_after = Instant.new + Duration.from_secs(5)
write
Show source codeHide source code
fn pub mut write[B: Bytes](bytes: ref B) -> Result[Nil, Error] {
write_all_internal(bytes.pointer, bytes.size)
}fn pub mut write[B: Bytes](bytes: ref B) -> Result[Nil, Error]Writes the entirety of bytes to the underlying stream.
Types implementing this method must guarantee that upon returning from this
method, either all of the data is written and a Ok(Nil) is returned, or
an Error is returned.
Implemented traits
Drop
impl Drop for ServerRead
impl Read[Error] for ServerWrite
impl Write[Error] for Server