TCP echo server
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import gleam/bytes_tree
|
||||
import gleam/bit_array
|
||||
import gleam/erlang/atom
|
||||
import gleam/result
|
||||
|
||||
import tcp/reason.{type Reason}
|
||||
|
||||
@@ -17,25 +19,82 @@ type ModeValue {
|
||||
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), 0, options, timeout)
|
||||
gen_tcp_connect(Local(socket_path), port, options, timeout)
|
||||
}
|
||||
|
||||
pub fn send(socket: Socket, message: String) -> Result(Nil, Reason) {
|
||||
gen_tcp_send(socket, bytes_tree.from_string(message))
|
||||
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(
|
||||
@@ -47,11 +106,12 @@ fn gen_tcp_connect(
|
||||
|
||||
// https://www.erlang.org/doc/apps/kernel/gen_tcp.html#send/2
|
||||
@external(erlang, "tcp_ffi", "send")
|
||||
fn gen_tcp_send(
|
||||
socket: Socket,
|
||||
packet: bytes_tree.BytesTree,
|
||||
) -> Result(Nil, Reason)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user