118 lines
2.9 KiB
Gleam
118 lines
2.9 KiB
Gleam
import gleam/bit_array
|
|
import gleam/erlang/atom
|
|
import gleam/result
|
|
|
|
import tcp/reason.{type Reason}
|
|
|
|
pub type Socket
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/inet#t:address_family/0
|
|
// local socket
|
|
type Local {
|
|
Local(socket_path: String)
|
|
}
|
|
|
|
type ModeValue {
|
|
Binary
|
|
}
|
|
|
|
type TCPOption {
|
|
Active(Bool)
|
|
Mode(ModeValue)
|
|
Reuseaddr(Bool)
|
|
Ifaddr(Local)
|
|
ExitOnClose(Bool)
|
|
}
|
|
|
|
pub fn listen(socket_path: String) -> Result(Socket, Reason) {
|
|
let options = [
|
|
Mode(Binary),
|
|
Active(False),
|
|
Reuseaddr(True),
|
|
Ifaddr(Local(socket_path)),
|
|
ExitOnClose(False),
|
|
]
|
|
// port zero with `local` address
|
|
let port = 0
|
|
|
|
gen_tcp_listen(port, options)
|
|
}
|
|
|
|
pub fn accept(listen_socket: Socket) -> Result(Socket, Reason) {
|
|
gen_tcp_accept(listen_socket)
|
|
}
|
|
|
|
pub fn receive(socket: Socket, timeout: Int) -> Result(String, Reason) {
|
|
// Get all bytes
|
|
let length = 0
|
|
|
|
use bits <- result.try(gen_tcp_recv(socket, length, timeout))
|
|
case bits |> bit_array.to_string {
|
|
// TODO what error is best?
|
|
Error(_) -> Error(reason.Ebadmsg)
|
|
Ok(s) -> Ok(s)
|
|
}
|
|
}
|
|
|
|
pub fn connect(socket_path: String) -> Result(Socket, Reason) {
|
|
let options = [Mode(Binary), Active(False)]
|
|
|
|
// port zero with `local` address
|
|
let port = 0
|
|
|
|
// timeout in ms
|
|
let timeout = 1000
|
|
|
|
gen_tcp_connect(Local(socket_path), port, options, timeout)
|
|
}
|
|
|
|
pub fn send(socket: Socket, message: String) -> Result(Nil, Reason) {
|
|
gen_tcp_send(socket, bit_array.from_string(message))
|
|
}
|
|
|
|
pub fn close(socket: Socket) -> Nil {
|
|
gen_tcp_close(socket)
|
|
}
|
|
|
|
pub fn shutdown(socket: Socket) -> Result(Nil, Reason) {
|
|
let how = atom.create("write")
|
|
gen_tcp_shutdown(socket, how)
|
|
}
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#listen/2
|
|
@external(erlang, "gen_tcp", "listen")
|
|
fn gen_tcp_listen(port: Int, option: List(TCPOption)) -> Result(Socket, Reason)
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#accept/1
|
|
@external(erlang, "gen_tcp", "accept")
|
|
fn gen_tcp_accept(listen_socket: Socket) -> Result(Socket, Reason)
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#recv/3
|
|
@external(erlang, "gen_tcp", "recv")
|
|
fn gen_tcp_recv(
|
|
socket: Socket,
|
|
length: Int,
|
|
timeout: Int,
|
|
) -> Result(BitArray, Reason)
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#connect/4
|
|
@external(erlang, "gen_tcp", "connect")
|
|
fn gen_tcp_connect(
|
|
address: Local,
|
|
port: Int,
|
|
options: List(TCPOption),
|
|
timeout: Int,
|
|
) -> Result(Socket, Reason)
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#send/2
|
|
@external(erlang, "tcp_ffi", "send")
|
|
fn gen_tcp_send(socket: Socket, packet: BitArray) -> Result(Nil, Reason)
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#close/1
|
|
@external(erlang, "gen_tcp", "close")
|
|
fn gen_tcp_close(socket: Socket) -> Nil
|
|
|
|
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#shutdown/2
|
|
@external(erlang, "tcp_ffi", "shutdown")
|
|
fn gen_tcp_shutdown(socket: Socket, how: atom.Atom) -> Result(Nil, Reason)
|