use super::Response;
use crate::tarantool::tlua;
use crate::tarantool::tlua::AnyLuaValue::LuaAnyString;
use crate::tarantool::tlua::{AnyLuaString, AnyLuaValue};
use crate::transport::http::Request;
use std::collections::HashMap;
use std::str::from_utf8;
pub trait LuaHttpHandler {
fn handle(&self, req: Request) -> Response;
fn method(&self) -> &str;
fn path(&self) -> &str;
}
pub struct Server {}
#[allow(clippy::new_without_default)]
impl Server {
pub fn new() -> Self {
let lua = tarantool::lua_state();
lua.exec("
local function make_json_handler(fn)
return function(req)
local data = req:read()
local status, body, headers = fn(req.method, req.path, req.query, req.headers, req.tstash, req.proto, req.peer, data)
return {
status = status,
body = body,
headers = headers,
}
end
end
function register_handler(route, method, handler)
local httpd = assert(rawget(_G, 'pico') and rawget(_G, 'pico').httpd or require('cartridge').service_get('httpd'), 'Failed to get httpd service')
local metrics = require('metrics')
httpd:route(
{ method = method, path = route },
metrics.http_middleware.v1(
make_json_handler(handler),
metrics.http_middleware.get_default_collector()
)
)
end
").unwrap();
Self {}
}
pub fn register(&self, route: Box<dyn LuaHttpHandler>) {
let lua = tarantool::lua_state();
let path = route.path().to_string();
let method = route.method().to_string();
let lua_handler = tlua::function8(
move |method: String,
path: String,
query: String,
headers: HashMap<String, String>,
stash: HashMap<String, String>,
proto: Vec<i32>,
peer: HashMap<String, AnyLuaValue>,
body: AnyLuaString| {
let proto: [i32; 2] = proto.try_into().unwrap_or_default();
let lpeer: HashMap<String, String> = peer
.into_iter()
.filter_map(|(k, v)| match v {
AnyLuaValue::LuaString(s) => Some((k, s)),
AnyLuaValue::LuaAnyString(s) => {
Some((k, from_utf8(s.as_bytes()).ok().map(|s| s.to_owned())?))
}
AnyLuaValue::LuaNumber(f) => Some((k, format!("{}", f))),
_ => None,
})
.collect();
let res = route.handle(Request {
method,
path,
query,
headers,
stash,
body: body.as_bytes().to_vec(),
proto: proto.into(),
peer: lpeer.into(),
});
(
res.status,
LuaAnyString(AnyLuaString(res.body)),
res.headers,
)
},
);
lua.get::<tlua::LuaFunction<_>, _>("register_handler")
.unwrap()
.call_with_args::<(), _>((path, method, lua_handler))
.expect("register route fail");
}
}