support for user defined bytes slice

This commit is contained in:
DanyLE 2023-01-26 02:22:05 +01:00
parent 74dff76282
commit b5acb19bc7
2 changed files with 113 additions and 22 deletions

View File

@ -1,10 +1,9 @@
use libc;
use mlua::prelude::*; use mlua::prelude::*;
use nix; use nix;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CString; use std::ffi::CString;
use std::fmt::Arguments; use std::fmt::Arguments;
use std::io::{Error, ErrorKind, Read, Write}; use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write};
use std::os::fd::RawFd; use std::os::fd::RawFd;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
@ -17,6 +16,8 @@ pub const APP_VERSION: &str = "0.1.0";
/// Application name /// Application name
pub const DAEMON_NAME: &str = "luad"; pub const DAEMON_NAME: &str = "luad";
const LUA_SLICE_MAGIC: usize = 0x8AD73B9F;
fn is_debug_enable() -> bool { fn is_debug_enable() -> bool {
match std::env::var("debug") { match std::env::var("debug") {
Ok(value) => return value == "1" || value == "true", Ok(value) => return value == "1" || value == "true",
@ -136,6 +137,16 @@ macro_rules! ERROR {
}) })
} }
/// Macro for info log debug
///
#[macro_export]
macro_rules! DEBUG {
($($args:tt)*) => ({
let prefix = format!(":debug@[{}:{}]: ",file!(), line!());
let _ = LOG::log(&prefix[..], &LogLevel::DEBUG, format_args!($($args)*));
})
}
/// Different Logging levels for `LOG` /// Different Logging levels for `LOG`
pub enum LogLevel { pub enum LogLevel {
/// Error conditions /// Error conditions
@ -144,6 +155,8 @@ pub enum LogLevel {
INFO, INFO,
/// Warning conditions /// Warning conditions
WARN, WARN,
/// Debugs message
DEBUG,
} }
/// Log struct wrapper /// Log struct wrapper
@ -188,11 +201,12 @@ impl LOG {
let sysloglevel = match level { let sysloglevel = match level {
LogLevel::ERROR => libc::LOG_ERR, LogLevel::ERROR => libc::LOG_ERR,
LogLevel::WARN => libc::LOG_WARNING, LogLevel::WARN => libc::LOG_WARNING,
LogLevel::INFO => libc::LOG_NOTICE,
_ => { _ => {
if !is_debug_enable() { if !is_debug_enable() {
return Ok(()); return Ok(());
} }
libc::LOG_NOTICE libc::LOG_INFO
} }
}; };
let mut output = String::new(); let mut output = String::new();
@ -450,7 +464,7 @@ struct FGCIRequest {
data: Option<Vec<u8>>, data: Option<Vec<u8>>,
state: FCGIRequestState, state: FCGIRequestState,
} }
#[repr(C)]
struct FCGIOStream { struct FCGIOStream {
fd: RawFd, fd: RawFd,
id: u16, id: u16,
@ -468,7 +482,7 @@ impl FCGIOStream {
let mut output: Vec<u8> = Vec::new(); let mut output: Vec<u8> = Vec::new();
for value in values { for value in values {
match value { match &value {
LuaNil => {} LuaNil => {}
LuaValue::Boolean(v) => output.extend(v.to_string().as_bytes()), LuaValue::Boolean(v) => output.extend(v.to_string().as_bytes()),
LuaValue::Integer(v) => output.extend(v.to_string().as_bytes()), LuaValue::Integer(v) => output.extend(v.to_string().as_bytes()),
@ -481,11 +495,18 @@ impl FCGIOStream {
return Err(Box::new(ERR!("Unsupported data type"))); return Err(Box::new(ERR!("Unsupported data type")));
} }
LuaValue::UserData(v) => { LuaValue::UserData(v) => {
if !v.is::<LuabyteArray>() { if v.is::<LuabyteArray>() {
return Err(Box::new(ERR!("Unsupported data type"))); let arr = v.borrow::<LuabyteArray>()?;
output.extend(&arr.0);
} else {
let st = value.to_pointer() as *const LuaSlice;
if unsafe { (*st).magic } != LUA_SLICE_MAGIC {
return Err(Box::new(ERR!("Unsupported data type")));
}
let data_slice =
unsafe { std::slice::from_raw_parts((*st).data, (*st).len) };
output.extend(data_slice);
} }
let arr = v.borrow::<LuabyteArray>()?;
output.extend(arr.0.clone());
} }
LuaValue::Error(e) => { LuaValue::Error(e) => {
fcgi_send_stderr(self, self.id, Some(e.to_string().into()))?; fcgi_send_stderr(self, self.id, Some(e.to_string().into()))?;
@ -528,6 +549,24 @@ impl mlua::UserData for FCGIOStream {
.map_err(|e| mlua::Error::external(ERR!(e.to_string()))) .map_err(|e| mlua::Error::external(ERR!(e.to_string())))
}, },
); );
methods.add_method_mut("send_file", |_, this: &mut FCGIOStream, path: String| {
let file = std::fs::File::open(path)?;
let mut buf_reader = BufReader::with_capacity(2048, file);
loop {
let length = {
let buffer = buf_reader.fill_buf()?;
fcgi_send_stdout(this, this.id, Some(buffer.to_vec()))
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))?;
buffer.len()
};
if length == 0 {
break;
}
buf_reader.consume(length);
}
Ok(())
});
methods.add_method_mut( methods.add_method_mut(
"print", "print",
|_, this: &mut FCGIOStream, values: mlua::Variadic<_>| { |_, this: &mut FCGIOStream, values: mlua::Variadic<_>| {
@ -535,8 +574,24 @@ impl mlua::UserData for FCGIOStream {
.map_err(|e| mlua::Error::external(ERR!(e.to_string()))) .map_err(|e| mlua::Error::external(ERR!(e.to_string())))
}, },
); );
methods.add_method("raw_fd", |_, this: &FCGIOStream, ()| Ok(this.fd)); methods.add_method("fd", |_, this: &FCGIOStream, ()| Ok(this.fd));
methods.add_method("id", |_, this: &FCGIOStream, ()| Ok(this.id)); methods.add_method("id", |_, this: &FCGIOStream, ()| Ok(this.id));
methods.add_method("log_info", |_, _: &FCGIOStream, string: String| {
INFO!("{}", string);
Ok(())
});
methods.add_method("log_error", |_, _: &FCGIOStream, string: String| {
ERROR!("{}", string);
Ok(())
});
methods.add_method("log_debug", |_, _: &FCGIOStream, string: String| {
DEBUG!("{}", string);
Ok(())
});
methods.add_method("log_warn", |_, _: &FCGIOStream, string: String| {
WARN!("{}", string);
Ok(())
});
} }
} }
@ -569,7 +624,9 @@ fn fcgi_execute_request_handle(rq: &mut FGCIRequest) -> Result<(), Box<dyn std::
"from_string", "from_string",
lua.create_function(lua_new_bytes_from_string)?, lua.create_function(lua_new_bytes_from_string)?,
)?; )?;
bytes.set("new", lua.create_function(lua_new_bytes)?)?; bytes.set("new", lua.create_function(lua_new_bytes)?)?;
bytes.set("from_slice", lua.create_function(lua_new_from_slice)?)?;
global.set("bytes", bytes)?; global.set("bytes", bytes)?;
let path = rq let path = rq
@ -669,7 +726,7 @@ pub fn process_request<T: Read + Write + AsRawFd>(
match header.kind { match header.kind {
FCGIHeaderType::BeginRequest => { FCGIHeaderType::BeginRequest => {
let body = FCGIBeginRequestBody::from_bytes(&fcgi_read_body(stream, &header)?); let body = FCGIBeginRequestBody::from_bytes(&fcgi_read_body(stream, &header)?);
INFO!("Begin Request: {:?}, with body {:?}", header, body); DEBUG!("Begin Request: {:?}, with body {:?}", header, body);
if body.role != FCGIRole::Responder { if body.role != FCGIRole::Responder {
fcgi_send_end_request(stream, header.id, EndRequestStatus::UnknownRole)?; fcgi_send_end_request(stream, header.id, EndRequestStatus::UnknownRole)?;
return Err(Box::new(ERR!("Only Responder role is supported"))); return Err(Box::new(ERR!("Only Responder role is supported")));
@ -697,7 +754,7 @@ pub fn process_request<T: Read + Write + AsRawFd>(
); );
} else { } else {
if header.length == 0 { if header.length == 0 {
INFO!( DEBUG!(
"All param records read, now wait for stdin data on request: {}", "All param records read, now wait for stdin data on request: {}",
header.id header.id
); );
@ -719,7 +776,7 @@ pub fn process_request<T: Read + Write + AsRawFd>(
); );
} else { } else {
if header.length == 0 { if header.length == 0 {
INFO!( DEBUG!(
"All stdin records read, now wait for stdout data on request: {}", "All stdin records read, now wait for stdout data on request: {}",
header.id header.id
); );
@ -816,7 +873,7 @@ fn fcgi_decode_params(
let key = String::from_utf8(data[index..index + key_len].to_vec())?; let key = String::from_utf8(data[index..index + key_len].to_vec())?;
let value: String = let value: String =
String::from_utf8(data[index + key_len..index + key_len + value_len].to_vec())?; String::from_utf8(data[index + key_len..index + key_len + value_len].to_vec())?;
INFO!("PARAM: [{}] -> [{}]", key, value); DEBUG!("PARAM: [{}] -> [{}]", key, value);
let _ = rq.params.insert(key, value); let _ = rq.params.insert(key, value);
Ok(()) Ok(())
} }
@ -826,6 +883,15 @@ fn lua_new_bytes(_: &mlua::Lua, size: usize) -> LuaResult<LuabyteArray> {
Ok(arr) Ok(arr)
} }
fn lua_new_from_slice(_: &mlua::Lua, value: mlua::Value) -> LuaResult<LuabyteArray> {
let st = value.to_pointer() as *const LuaSlice;
if unsafe { (*st).magic } != LUA_SLICE_MAGIC {
return Err(mlua::Error::external(ERR!("Unsupported data type")));
}
let data_slice = unsafe { std::slice::from_raw_parts((*st).data, (*st).len) };
Ok(LuabyteArray(data_slice.to_vec()))
}
fn lua_new_bytes_from_string(_: &mlua::Lua, string: String) -> LuaResult<LuabyteArray> { fn lua_new_bytes_from_string(_: &mlua::Lua, string: String) -> LuaResult<LuabyteArray> {
let arr = LuabyteArray(string.as_bytes().to_vec()); let arr = LuabyteArray(string.as_bytes().to_vec());
Ok(arr) Ok(arr)
@ -837,9 +903,11 @@ impl mlua::UserData for LuabyteArray {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("size", |_, this: &LuabyteArray, ()| Ok(this.0.len())); methods.add_method("size", |_, this: &LuabyteArray, ()| Ok(this.0.len()));
methods.add_method("ptr", |_, this:&LuabyteArray, ()| Ok(this.0.as_ptr() as usize)); methods.add_method("ptr", |_, this: &LuabyteArray, ()| {
Ok(this.0.as_ptr() as usize)
});
methods.add_method("write", |_, this: &LuabyteArray, path: String| { methods.add_method("fileout", |_, this: &LuabyteArray, path: String| {
match std::fs::File::create(&path) { match std::fs::File::create(&path) {
Ok(mut file) => { Ok(mut file) => {
if let Err(error) = file.write_all(&this.0) { if let Err(error) = file.write_all(&this.0) {
@ -878,7 +946,6 @@ impl mlua::UserData for LuabyteArray {
Ok(string) => Ok(Some(string)), Ok(string) => Ok(Some(string)),
}, },
); );
methods.add_meta_method_mut( methods.add_meta_method_mut(
mlua::MetaMethod::NewIndex, mlua::MetaMethod::NewIndex,
|_, this, (index, value): (isize, u8)| { |_, this, (index, value): (isize, u8)| {
@ -898,3 +965,27 @@ impl mlua::UserData for LuabyteArray {
methods.add_meta_method(mlua::MetaMethod::Len, |_, this, ()| Ok(this.0.len())); methods.add_meta_method(mlua::MetaMethod::Len, |_, this, ()| Ok(this.0.len()));
} }
} }
#[repr(C)]
pub struct LuaSlice {
magic: usize,
len: usize,
data: *const u8,
}
#[no_mangle]
pub extern "C" fn fcgi_send_slice(fd: RawFd, id: u16, ptr: *const u8, size: usize) -> isize {
let data_slice = unsafe { std::slice::from_raw_parts(ptr, size) }.to_vec();
let mut stream = FCGIOStream { fd, id };
if let Err(error) = fcgi_send_stdout(&mut stream, id, Some(data_slice)) {
ERROR!("Unable to send data slice: {}", error);
return -1;
}
return 0;
}
#[no_mangle]
pub extern "C" fn lua_slice_magic() -> usize {
return LUA_SLICE_MAGIC;
}

View File

@ -56,7 +56,7 @@ fn handle_request<T: Read + Write + AsRawFd>(stream: &mut T) {
if let Err(error) = process_request(stream) { if let Err(error) = process_request(stream) {
ERROR!("Unable to process request: {}", error); ERROR!("Unable to process request: {}", error);
} }
INFO!("Request on socket {} is processed", stream.as_raw_fd()); DEBUG!("Request on socket {} is processed", stream.as_raw_fd());
} }
/// Start the `fastCGI` server /// Start the `fastCGI` server
@ -94,11 +94,11 @@ fn serve(config: &Config) {
} else { } else {
// if there is no socket configuration, assume that the stdio is already mapped // if there is no socket configuration, assume that the stdio is already mapped
// to a socket. This is usually done by by the parent process (e.g. webserver) that launches efcgi // to a socket. This is usually done by by the parent process (e.g. webserver) that launches efcgi
INFO!("No socket specified! use stdin as listenning socket"); INFO!("No socket specified! use stdin as listening socket");
let stdin = std::io::stdin(); let stdin = std::io::stdin();
let fd = stdin.as_raw_fd(); let fd = stdin.as_raw_fd();
if is_unix_socket(fd).unwrap() { if is_unix_socket(fd).unwrap() {
INFO!("Stdin is used as Unix domain socket"); DEBUG!("Stdin is used as Unix domain socket");
let listener = unsafe { UnixListener::from_raw_fd(stdin.as_raw_fd()) }; let listener = unsafe { UnixListener::from_raw_fd(stdin.as_raw_fd()) };
for client in listener.incoming() { for client in listener.incoming() {
let mut stream = client.unwrap(); let mut stream = client.unwrap();
@ -108,7 +108,7 @@ fn serve(config: &Config) {
}); });
} }
} else { } else {
INFO!("Stdin is used as TCP Socket"); DEBUG!("Stdin is used as TCP Socket");
let listener = unsafe { TcpListener::from_raw_fd(stdin.as_raw_fd()) }; let listener = unsafe { TcpListener::from_raw_fd(stdin.as_raw_fd()) };
for client in listener.incoming() { for client in listener.incoming() {
let mut stream = client.unwrap(); let mut stream = client.unwrap();
@ -174,7 +174,7 @@ fn main() {
Some(pidfile) => { Some(pidfile) => {
let mut f = std::fs::File::create(&pidfile).unwrap(); let mut f = std::fs::File::create(&pidfile).unwrap();
write!(f, "{}", std::process::id()).unwrap(); write!(f, "{}", std::process::id()).unwrap();
INFO!("PID file created at {}", pidfile); DEBUG!("PID file created at {}", pidfile);
} }
None => {} None => {}
} }