mlua/examples/async_tcp_server.rs

127 lines
3.8 KiB
Rust
Raw Normal View History

2020-05-05 20:32:05 -05:00
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
2020-05-05 20:32:05 -05:00
use tokio::sync::Mutex;
use tokio::task;
2021-06-03 17:52:29 -05:00
use mlua::{Function, Lua, Result, String as LuaString, UserData, UserDataMethods};
2020-04-18 19:23:42 -05:00
struct LuaTcp;
#[derive(Clone)]
2020-05-05 20:32:05 -05:00
struct LuaTcpListener(Arc<Mutex<TcpListener>>);
2020-04-18 19:23:42 -05:00
#[derive(Clone)]
2020-05-05 20:32:05 -05:00
struct LuaTcpStream(Arc<Mutex<TcpStream>>);
2020-04-18 19:23:42 -05:00
impl UserData for LuaTcp {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
2020-04-18 19:23:42 -05:00
methods.add_async_function("bind", |_, addr: String| async move {
let listener = TcpListener::bind(addr).await?;
2020-05-05 20:32:05 -05:00
Ok(LuaTcpListener(Arc::new(Mutex::new(listener))))
});
2020-04-18 19:23:42 -05:00
}
}
2020-04-18 19:23:42 -05:00
impl UserData for LuaTcpListener {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("accept", |_, listener, ()| async move {
2020-05-05 20:32:05 -05:00
let (stream, _) = listener.0.lock().await.accept().await?;
Ok(LuaTcpStream(Arc::new(Mutex::new(stream))))
});
}
}
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("peer_addr", |_, stream, ()| async move {
2020-05-05 20:32:05 -05:00
Ok(stream.0.lock().await.peer_addr()?.to_string())
});
2021-06-03 17:52:29 -05:00
methods.add_async_method("read", |lua, stream, size: usize| async move {
let mut buf = vec![0; size];
2020-05-05 20:32:05 -05:00
let n = stream.0.lock().await.read(&mut buf).await?;
buf.truncate(n);
2021-06-03 17:52:29 -05:00
lua.create_string(&buf)
});
2021-06-03 17:52:29 -05:00
methods.add_async_method("write", |_, stream, data: LuaString| async move {
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
Ok(n)
});
2020-05-05 20:32:05 -05:00
methods.add_async_method("close", |_, stream, ()| async move {
2021-01-16 08:07:26 -06:00
stream.0.lock().await.shutdown().await?;
Ok(())
});
}
}
async fn run_server(lua: &'static Lua) -> Result<()> {
2020-04-18 19:23:42 -05:00
let spawn = lua.create_function(move |_, func: Function| {
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
2020-04-18 19:23:42 -05:00
Ok(())
})?;
2020-04-18 19:23:42 -05:00
let globals = lua.globals();
globals.set("tcp", LuaTcp)?;
globals.set("spawn", spawn)?;
2020-04-18 19:23:42 -05:00
let server = lua
.load(
r#"
2020-04-18 19:23:42 -05:00
local addr = ...
local listener = tcp.bind(addr)
print("listening on "..addr)
local accept_new = true
2020-04-18 19:23:42 -05:00
while true do
local stream = listener:accept()
local peer_addr = stream:peer_addr()
print("connected from "..peer_addr)
if not accept_new then
return
end
2020-04-18 19:23:42 -05:00
spawn(function()
while true do
local data = stream:read(100)
data = data:match("^%s*(.-)%s*$") -- trim
print("["..peer_addr.."] "..data)
if data == "bye" then
stream:write("bye bye\n")
stream:close()
return
end
2020-04-18 19:23:42 -05:00
if data == "exit" then
stream:close()
accept_new = false
return
end
stream:write("echo: "..data.."\n")
2020-04-18 19:23:42 -05:00
end
end)
end
"#,
)
2020-04-18 19:23:42 -05:00
.into_function()?;
2020-04-18 19:23:42 -05:00
task::LocalSet::new()
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
.await
}
#[tokio::main]
async fn main() {
let lua = Lua::new().into_static();
run_server(lua).await.unwrap();
// Consume the static reference and drop it.
// This is safe as long as we don't hold any other references to Lua
// or alive resources.
unsafe { Lua::from_static(lua) };
}