mirror of
https://github.com/lxsang/luafcgi.git
synced 2024-12-26 13:18:22 +01:00
Working LUA base FastCGI server
This commit is contained in:
parent
4e122bd245
commit
88ec016405
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -70,15 +70,6 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastcgi"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4159a0f48bea0281602e508eb070d7d7ba1f6ac2480f9db1a60a39274aea1cc"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@ -108,7 +99,6 @@ name = "luad"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"fastcgi",
|
|
||||||
"libc",
|
"libc",
|
||||||
"mlua",
|
"mlua",
|
||||||
"nix",
|
"nix",
|
||||||
|
@ -6,7 +6,6 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fastcgi = "1.0.0"
|
|
||||||
mlua = { version = "0.8", features = ["lua54", "vendored"] }
|
mlua = { version = "0.8", features = ["lua54", "vendored"] }
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
nix = "0.26.1"
|
nix = "0.26.1"
|
||||||
@ -19,6 +18,5 @@ libc = "0.2"
|
|||||||
opt-level = 's'
|
opt-level = 's'
|
||||||
# 's' for size
|
# 's' for size
|
||||||
lto = true
|
lto = true
|
||||||
# this strategy doesnot work on 1.3.9
|
|
||||||
# panic = 'abort'
|
# panic = 'abort'
|
||||||
codegen-units = 1
|
codegen-units = 1
|
@ -1,10 +1,12 @@
|
|||||||
# TCP socket or Unix socket file
|
# TCP socket or Unix socket file
|
||||||
socket = "unix:/tmp/lua.sock"
|
socket = "unix:/tmp/lua1.sock"
|
||||||
|
|
||||||
# pid file
|
# pid file
|
||||||
pidfile = "/tmp/luad.pid"
|
pidfile = "/tmp/luad.pid"
|
||||||
|
|
||||||
# user name
|
# user name
|
||||||
# user = "demo"
|
user = "dany"
|
||||||
# group name
|
# group name
|
||||||
# group = "demo"
|
group = "dany"
|
||||||
|
|
||||||
|
debug = false
|
550
src/lib.rs
550
src/lib.rs
@ -1,12 +1,12 @@
|
|||||||
use mlua::prelude::*;
|
|
||||||
use libc;
|
use libc;
|
||||||
|
use mlua::prelude::*;
|
||||||
use nix;
|
use nix;
|
||||||
use std::io::{Error, ErrorKind, Read,Write};
|
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::os::fd::RawFd;
|
use std::os::fd::RawFd;
|
||||||
use std::os::unix::io::{AsRawFd};
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// app author
|
/// app author
|
||||||
pub const APP_AUTHOR: &str = "Dany LE <mrsang@iohub.dev>";
|
pub const APP_AUTHOR: &str = "Dany LE <mrsang@iohub.dev>";
|
||||||
@ -17,6 +17,14 @@ pub const APP_VERSION: &str = "0.1.0";
|
|||||||
/// Application name
|
/// Application name
|
||||||
pub const DAEMON_NAME: &str = "luad";
|
pub const DAEMON_NAME: &str = "luad";
|
||||||
|
|
||||||
|
fn is_debug_enable() -> bool {
|
||||||
|
match std::env::var("debug") {
|
||||||
|
Ok(value) => return value == "1" || value == "true",
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Drop user privileges
|
/// Drop user privileges
|
||||||
///
|
///
|
||||||
/// This function drop the privileges of the current user
|
/// This function drop the privileges of the current user
|
||||||
@ -32,14 +40,14 @@ pub const DAEMON_NAME: &str = "luad";
|
|||||||
///
|
///
|
||||||
/// * `nix::Error` - The error from the nix package
|
/// * `nix::Error` - The error from the nix package
|
||||||
pub fn privdrop(useropt: Option<&str>, groupopt: Option<&str>) -> Result<(), nix::Error> {
|
pub fn privdrop(useropt: Option<&str>, groupopt: Option<&str>) -> Result<(), nix::Error> {
|
||||||
match groupopt{
|
match groupopt {
|
||||||
Some(group) => {
|
Some(group) => {
|
||||||
INFO!("Dropping current process group to {}", group);
|
INFO!("Dropping current process group to {}", group);
|
||||||
match nix::unistd::Group::from_name(group)? {
|
match nix::unistd::Group::from_name(group)? {
|
||||||
Some(group) => nix::unistd::setgid(group.gid),
|
Some(group) => nix::unistd::setgid(group.gid),
|
||||||
None => Err(nix::Error::last()),
|
None => Err(nix::Error::last()),
|
||||||
}?;
|
}?;
|
||||||
},
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
match useropt {
|
match useropt {
|
||||||
@ -49,12 +57,24 @@ pub fn privdrop(useropt: Option<&str>, groupopt: Option<&str>) -> Result<(), nix
|
|||||||
Some(user) => nix::unistd::setuid(user.uid),
|
Some(user) => nix::unistd::setuid(user.uid),
|
||||||
None => Err(nix::Error::last()),
|
None => Err(nix::Error::last()),
|
||||||
}?
|
}?
|
||||||
},
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unix_socket(fd: libc::c_int) -> Result<bool, Error> {
|
||||||
|
unsafe {
|
||||||
|
let mut addr: libc::sockaddr_storage = std::mem::zeroed();
|
||||||
|
let mut addr_len: libc::socklen_t =
|
||||||
|
std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
|
||||||
|
let ret = libc::getsockname(fd, &mut addr as *mut _ as *mut _, &mut addr_len);
|
||||||
|
if ret != 0 {
|
||||||
|
return Err(ERR!(format!("Unable to check socket: {}", fd)));
|
||||||
|
}
|
||||||
|
Ok(i32::from(addr.ss_family) == libc::AF_UNIX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Utility function to catch common signal that
|
/// Utility function to catch common signal that
|
||||||
/// cause the program to exit
|
/// cause the program to exit
|
||||||
@ -162,9 +182,19 @@ impl LOG {
|
|||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// * `std error` - All errors related to formated and C string manipulation
|
/// * `std error` - All errors related to formatted and C string manipulation
|
||||||
pub fn log(prefix: &str, level: &LogLevel, args: Arguments<'_>) -> Result<(), Error> {
|
pub fn log(prefix: &str, level: &LogLevel, args: Arguments<'_>) -> Result<(), Error> {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
let sysloglevel = match level {
|
||||||
|
LogLevel::ERROR => libc::LOG_ERR,
|
||||||
|
LogLevel::WARN => libc::LOG_WARNING,
|
||||||
|
_ => {
|
||||||
|
if !is_debug_enable() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
libc::LOG_NOTICE
|
||||||
|
}
|
||||||
|
};
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
if output.write_fmt(args).is_err() {
|
if output.write_fmt(args).is_err() {
|
||||||
return Err(ERR!("Unable to create format string from arguments"));
|
return Err(ERR!("Unable to create format string from arguments"));
|
||||||
@ -172,11 +202,6 @@ impl LOG {
|
|||||||
let log_fmt = format!("{}(v{}){}%s\n", DAEMON_NAME, APP_VERSION, prefix);
|
let log_fmt = format!("{}(v{}){}%s\n", DAEMON_NAME, APP_VERSION, prefix);
|
||||||
let fmt = CString::new(log_fmt.as_bytes())?;
|
let fmt = CString::new(log_fmt.as_bytes())?;
|
||||||
let c_msg = CString::new(output.as_bytes())?;
|
let c_msg = CString::new(output.as_bytes())?;
|
||||||
let sysloglevel = match level {
|
|
||||||
LogLevel::ERROR => libc::LOG_ERR,
|
|
||||||
LogLevel::WARN => libc::LOG_WARNING,
|
|
||||||
_ => libc::LOG_NOTICE,
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::syslog(sysloglevel, fmt.as_ptr(), c_msg.as_ptr());
|
libc::syslog(sysloglevel, fmt.as_ptr(), c_msg.as_ptr());
|
||||||
}
|
}
|
||||||
@ -195,10 +220,9 @@ impl Drop for LOG {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Protocol goes here
|
/// Protocol goes here
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum FCGIHeaderType{
|
enum FCGIHeaderType {
|
||||||
BeginRequest,
|
BeginRequest,
|
||||||
AbortRequest,
|
AbortRequest,
|
||||||
EndRequest,
|
EndRequest,
|
||||||
@ -213,7 +237,6 @@ enum FCGIHeaderType{
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FCGIHeaderType {
|
impl FCGIHeaderType {
|
||||||
|
|
||||||
/// convert a u8 value to `FCGIHeaderType` value
|
/// convert a u8 value to `FCGIHeaderType` value
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -275,7 +298,7 @@ impl std::fmt::Display for FCGIHeaderType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EndRequestStatus{
|
enum EndRequestStatus {
|
||||||
Complete,
|
Complete,
|
||||||
// CantMaxMPXConn,
|
// CantMaxMPXConn,
|
||||||
// Overloaded,
|
// Overloaded,
|
||||||
@ -298,20 +321,18 @@ impl EndRequestStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FCGI_HEADER_LEN:usize = 8;
|
const FCGI_HEADER_LEN: usize = 8;
|
||||||
const FCGI_VERSION:u8 = 1;
|
const FCGI_VERSION: u8 = 1;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum FCGIRole {
|
enum FCGIRole {
|
||||||
Responder,
|
Responder,
|
||||||
Authorizer,
|
Authorizer,
|
||||||
Filter,
|
Filter,
|
||||||
Unknown
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FCGIRole {
|
impl FCGIRole {
|
||||||
|
|
||||||
/// convert a u8 value to `FCGIRole` value
|
/// convert a u8 value to `FCGIRole` value
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -339,14 +360,13 @@ impl std::fmt::Display for FCGIRole {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FCGIBeginRequestBody{
|
struct FCGIBeginRequestBody {
|
||||||
role: FCGIRole,
|
role: FCGIRole,
|
||||||
flags: u8,
|
flags: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FCGIBeginRequestBody {
|
impl FCGIBeginRequestBody {
|
||||||
pub fn from_bytes(data: &[u8]) -> Self
|
pub fn from_bytes(data: &[u8]) -> Self {
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
role: FCGIRole::from_u16(((data[0] as u16) << 8) | (data[1] as u16)),
|
role: FCGIRole::from_u16(((data[0] as u16) << 8) | (data[1] as u16)),
|
||||||
flags: data[2],
|
flags: data[2],
|
||||||
@ -361,30 +381,27 @@ impl std::fmt::Display for FCGIBeginRequestBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FcgiHeader {
|
struct FcgiHeader {
|
||||||
version: u8,
|
version: u8,
|
||||||
kind: FCGIHeaderType,
|
kind: FCGIHeaderType,
|
||||||
id: u16,
|
id: u16,
|
||||||
padding: u8,
|
padding: u8,
|
||||||
length:u16,
|
length: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FcgiHeader {
|
impl FcgiHeader {
|
||||||
pub fn from_bytes(data: &[u8]) -> Self
|
pub fn from_bytes(data: &[u8]) -> Self {
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
version: data[0],
|
version: data[0],
|
||||||
kind: FCGIHeaderType::from_u8(data[1]),
|
kind: FCGIHeaderType::from_u8(data[1]),
|
||||||
id: ((data[2] as u16) << 8) | (data[3] as u16),
|
id: ((data[2] as u16) << 8) | (data[3] as u16),
|
||||||
length: ((data[4] as u16) << 8) | (data[5]as u16),
|
length: ((data[4] as u16) << 8) | (data[5] as u16),
|
||||||
padding: data[6]
|
padding: data[6],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> Vec<u8>
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
{
|
|
||||||
vec![
|
vec![
|
||||||
self.version,
|
self.version,
|
||||||
self.kind.as_u8(),
|
self.kind.as_u8(),
|
||||||
@ -393,7 +410,7 @@ impl FcgiHeader {
|
|||||||
(self.length >> 8) as u8,
|
(self.length >> 8) as u8,
|
||||||
(self.length & 0xFF) as u8,
|
(self.length & 0xFF) as u8,
|
||||||
self.padding,
|
self.padding,
|
||||||
0
|
0,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,12 +425,11 @@ impl std::fmt::Display for FcgiHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum FCGIRequestState {
|
enum FCGIRequestState {
|
||||||
WaitForParams,
|
WaitForParams,
|
||||||
WaitForStdin,
|
WaitForStdin,
|
||||||
WaitForStdout
|
WaitForStdout,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for FCGIRequestState {
|
impl std::fmt::Display for FCGIRequestState {
|
||||||
@ -431,32 +447,71 @@ struct FGCIRequest {
|
|||||||
params: HashMap<String, String>,
|
params: HashMap<String, String>,
|
||||||
id: u16,
|
id: u16,
|
||||||
fd: RawFd,
|
fd: RawFd,
|
||||||
data: Vec<u8>,
|
data: Option<Vec<u8>>,
|
||||||
state: FCGIRequestState,
|
state: FCGIRequestState,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FCGIStdoutStream {
|
struct FCGIOStream {
|
||||||
fd: RawFd,
|
fd: RawFd,
|
||||||
id: u16
|
id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FCGIStdoutStream {
|
impl FCGIOStream {
|
||||||
fn _write(&self, buf: &[u8]) -> std::io::Result<usize>
|
fn write_record(&mut self, buf: Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
{
|
fcgi_send_stdout(self, self.id, Some(buf))?;
|
||||||
let ret = unsafe { libc::write(self.fd, buf.as_ptr() as *const libc::c_void, buf.len()) };
|
Ok(())
|
||||||
if ret != buf.len() as isize
|
}
|
||||||
{
|
fn write_variadic(
|
||||||
let msg = format!("Unable to write data to {}: only {} out of {} bytes have been written", self.fd, ret, buf.len());
|
&mut self,
|
||||||
return Err(ERR!(msg));
|
values: mlua::Variadic<LuaValue>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut output: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
match value {
|
||||||
|
LuaNil => {}
|
||||||
|
LuaValue::Boolean(v) => output.extend(v.to_string().as_bytes()),
|
||||||
|
LuaValue::Integer(v) => output.extend(v.to_string().as_bytes()),
|
||||||
|
LuaValue::Number(v) => output.extend(v.to_string().as_bytes()),
|
||||||
|
LuaValue::String(v) => output.extend(v.as_bytes()),
|
||||||
|
LuaValue::LightUserData(_)
|
||||||
|
| LuaValue::Table(_)
|
||||||
|
| LuaValue::Function(_)
|
||||||
|
| LuaValue::Thread(_) => {
|
||||||
|
return Err(Box::new(ERR!("Unsupported data type")));
|
||||||
|
}
|
||||||
|
LuaValue::UserData(v) => {
|
||||||
|
if !v.is::<LuabyteArray>() {
|
||||||
|
return Err(Box::new(ERR!("Unsupported data type")));
|
||||||
|
}
|
||||||
|
let arr = v.borrow::<LuabyteArray>()?;
|
||||||
|
output.extend(arr.0.clone());
|
||||||
|
}
|
||||||
|
LuaValue::Error(e) => {
|
||||||
|
fcgi_send_stderr(self, self.id, Some(e.to_string().into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(ret as usize)
|
if output.len() > 0 {
|
||||||
|
self.write_record(output)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for FCGIStdoutStream
|
impl Write for FCGIOStream {
|
||||||
{
|
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
self._write(buf)
|
let ret = unsafe { libc::write(self.fd, buf.as_ptr() as *const libc::c_void, buf.len()) };
|
||||||
|
if ret != buf.len() as isize {
|
||||||
|
let msg = format!(
|
||||||
|
"Unable to write data to {}: only {} out of {} bytes have been written",
|
||||||
|
self.fd,
|
||||||
|
ret,
|
||||||
|
buf.len()
|
||||||
|
);
|
||||||
|
return Err(ERR!(msg));
|
||||||
|
}
|
||||||
|
Ok(ret as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> {
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
@ -464,120 +519,104 @@ impl Write for FCGIStdoutStream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mlua::UserData for FCGIStdoutStream {
|
impl mlua::UserData for FCGIOStream {
|
||||||
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("write", |_, this:& FCGIStdoutStream, strings:mlua::Variadic<String>| {
|
methods.add_method_mut(
|
||||||
let mut stream = FCGIStdoutStream
|
"echo",
|
||||||
{
|
|_, this: &mut FCGIOStream, values: mlua::Variadic<_>| {
|
||||||
fd: this.fd,
|
this.write_variadic(values)
|
||||||
id: this.id
|
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))
|
||||||
};
|
},
|
||||||
let mut output = String::new();
|
);
|
||||||
for string in strings{
|
methods.add_method_mut(
|
||||||
output.push_str(&string);
|
"print",
|
||||||
}
|
|_, this: &mut FCGIOStream, values: mlua::Variadic<_>| {
|
||||||
fcgi_send_stdout(&mut stream, this.id, Some(output.as_bytes().to_vec()))
|
this.write_variadic(values)
|
||||||
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))?;
|
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))
|
||||||
Ok(())
|
},
|
||||||
});
|
);
|
||||||
|
methods.add_method("raw_fd", |_, this: &FCGIOStream, ()| Ok(this.fd));
|
||||||
|
methods.add_method("id", |_, this: &FCGIOStream, ()| Ok(this.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_define_print(lua: &Lua, strings: mlua::Variadic<String>) -> LuaResult<()> {
|
fn fcgi_execute_request_handle(rq: &mut FGCIRequest) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let global = lua.globals();
|
|
||||||
let server = global.get::<_, mlua::Table>("_SERVER")?;
|
|
||||||
let fd = server.get::<_,RawFd>("FD")?;
|
|
||||||
let id: u16 = server.get::<_,u16>("ID")?;
|
|
||||||
if fd <= 0 {
|
|
||||||
return Err(mlua::Error::external(ERR!("Invalid file descriptor")));
|
|
||||||
}
|
|
||||||
let mut data = String::new();
|
|
||||||
for string in strings{
|
|
||||||
data.push_str(&string);
|
|
||||||
}
|
|
||||||
// make the request
|
|
||||||
let mut stream = FCGIStdoutStream { fd, id};
|
|
||||||
let body = data.as_bytes().to_vec();
|
|
||||||
fcgi_send_stdout(&mut stream, id, Some(body))
|
|
||||||
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
//fn lua_define_io_write(lua: &Lua, strings: String) -> LuaResult<()> {
|
|
||||||
//}
|
|
||||||
|
|
||||||
fn fcgi_execute_request_handle(rq: & FGCIRequest) -> Result<(), Box<dyn std::error::Error>>
|
|
||||||
{
|
|
||||||
let lua = mlua::Lua::new();
|
let lua = mlua::Lua::new();
|
||||||
let global = lua.globals();
|
let global = lua.globals();
|
||||||
let request = lua.create_table()?;
|
let request = lua.create_table()?;
|
||||||
request.set("ID", rq.id)?;
|
|
||||||
request.set("FD", rq.fd)?;
|
for (k, v) in &rq.params {
|
||||||
for (k,v) in &rq.params{
|
request.set(String::from(k), String::from(v))?;
|
||||||
request.set(String::from(k),String::from(v))?;
|
}
|
||||||
|
if let Some(data) = rq.data.take() {
|
||||||
|
let data_arr = LuabyteArray(data);
|
||||||
|
request.set("RAW_DATA", data_arr)?;
|
||||||
}
|
}
|
||||||
// request params stored in _SERVER table
|
// request params stored in _SERVER table
|
||||||
global.set("_SERVER", request)?;
|
global.set("_SERVER", request)?;
|
||||||
// replace the print function
|
|
||||||
let func = lua.create_function(lua_define_print)?;
|
|
||||||
global.set("print", func)?;
|
|
||||||
|
|
||||||
// replace the io.stdout
|
// put the fcgio object
|
||||||
let stdout = FCGIStdoutStream {
|
let fcgio = FCGIOStream {
|
||||||
fd: rq.fd,
|
fd: rq.fd,
|
||||||
id: rq.id
|
id: rq.id,
|
||||||
};
|
};
|
||||||
let io = global.get::<_, mlua::Table>("io")?;
|
global.set("fcgio", fcgio)?;
|
||||||
io.set("stdout", stdout)?;
|
|
||||||
let func = lua.create_function(lua_define_print)?;
|
|
||||||
io.set("write",func)?;
|
|
||||||
|
|
||||||
let path = rq.params.get("SCRIPT_FILENAME").ok_or(ERR!("No SCRIPT_FILENAME found"))?;
|
// support for byte array
|
||||||
|
let bytes = lua.create_table()?;
|
||||||
|
bytes.set(
|
||||||
|
"from_string",
|
||||||
|
lua.create_function(lua_new_bytes_from_string)?,
|
||||||
|
)?;
|
||||||
|
bytes.set("new", lua.create_function(lua_new_bytes)?)?;
|
||||||
|
global.set("bytes", bytes)?;
|
||||||
|
|
||||||
|
let path = rq
|
||||||
|
.params
|
||||||
|
.get("SCRIPT_FILENAME")
|
||||||
|
.ok_or(ERR!("No SCRIPT_FILENAME found"))?;
|
||||||
let source = std::fs::read_to_string(path)?;
|
let source = std::fs::read_to_string(path)?;
|
||||||
INFO!("source: {}", &source);
|
|
||||||
lua.load(&source).exec()?;
|
lua.load(&source).exec()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
||||||
//global.set("hello", func)?;
|
|
||||||
//let source = std::fs::read_to_string(script).unwrap();
|
|
||||||
//lua.load(&source).exec().unwrap();
|
|
||||||
//lua.load("hello('world')").exec()?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_send_stderr<T: Read + Write + AsRawFd >(stream: &mut T,id: u16, eopt: Option<Box<dyn std::error::Error>>) -> Result<(), Box<dyn std::error::Error>>
|
fn fcgi_send_stderr<T: Write>(
|
||||||
{
|
stream: &mut T,
|
||||||
|
id: u16,
|
||||||
|
eopt: Option<Box<dyn std::error::Error>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut header = FcgiHeader {
|
let mut header = FcgiHeader {
|
||||||
version: FCGI_VERSION,
|
version: FCGI_VERSION,
|
||||||
kind: FCGIHeaderType::Stderr,
|
kind: FCGIHeaderType::Stderr,
|
||||||
id: id,
|
id: id,
|
||||||
length: 0,
|
length: 0,
|
||||||
padding: 0 ,
|
padding: 0,
|
||||||
};
|
};
|
||||||
if let Some(error) = eopt{
|
if let Some(error) = eopt {
|
||||||
let err_str = error.to_string();
|
let err_str = error.to_string();
|
||||||
let str_len = err_str.len();
|
let str_len = err_str.len();
|
||||||
let mut padding = (8 - str_len % 8) as u8;
|
let mut padding = (8 - str_len % 8) as u8;
|
||||||
if padding == 8
|
if padding == 8 {
|
||||||
{
|
|
||||||
padding = 0;
|
padding = 0;
|
||||||
}
|
}
|
||||||
let mut body = err_str.as_bytes().to_vec();
|
let mut body = err_str.as_bytes().to_vec();
|
||||||
let pad = vec![0;padding as usize];
|
let pad = vec![0; padding as usize];
|
||||||
header.length = str_len as u16;
|
header.length = str_len as u16;
|
||||||
header.padding = padding;
|
header.padding = padding;
|
||||||
body.extend(pad);
|
body.extend(pad);
|
||||||
stream.write_all(&header.as_bytes())?;
|
stream.write_all(&header.as_bytes())?;
|
||||||
stream.write_all(&body)?;
|
stream.write_all(&body)?;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
stream.write_all(&header.as_bytes())?;
|
stream.write_all(&header.as_bytes())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_send_stdout<T: Write >(stream: &mut T,id: u16, dopt: Option<Vec<u8>>) -> Result<(), Box<dyn std::error::Error>>
|
fn fcgi_send_stdout<T: Write>(
|
||||||
{
|
stream: &mut T,
|
||||||
|
id: u16,
|
||||||
|
dopt: Option<Vec<u8>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut header = FcgiHeader {
|
let mut header = FcgiHeader {
|
||||||
version: FCGI_VERSION,
|
version: FCGI_VERSION,
|
||||||
kind: FCGIHeaderType::Stdout,
|
kind: FCGIHeaderType::Stdout,
|
||||||
@ -585,115 +624,106 @@ fn fcgi_send_stdout<T: Write >(stream: &mut T,id: u16, dopt: Option<Vec<u8>>) ->
|
|||||||
length: 0,
|
length: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
};
|
};
|
||||||
if let Some(data) = dopt{
|
if let Some(data) = dopt {
|
||||||
header.length = data.len() as u16;
|
header.length = data.len() as u16;
|
||||||
header.padding = (8 - header.length % 8) as u8;
|
header.padding = (8 - header.length % 8) as u8;
|
||||||
if header.padding == 8
|
if header.padding == 8 {
|
||||||
{
|
|
||||||
header.padding = 0;
|
header.padding = 0;
|
||||||
}
|
}
|
||||||
let mut body = data;
|
let mut body = data;
|
||||||
let pad = vec![0;header.padding as usize];
|
let pad = vec![0; header.padding as usize];
|
||||||
body.extend(pad);
|
body.extend(pad);
|
||||||
stream.write_all(&header.as_bytes())?;
|
stream.write_all(&header.as_bytes())?;
|
||||||
stream.write_all(&body)?;
|
stream.write_all(&body)?;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
stream.write_all(&header.as_bytes())?;
|
stream.write_all(&header.as_bytes())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_send_end_request<T: Read + Write + AsRawFd >(stream: &mut T,id:u16, status: EndRequestStatus) -> Result<(), Box<dyn std::error::Error>>
|
fn fcgi_send_end_request<T: Read + Write + AsRawFd>(
|
||||||
{
|
stream: &mut T,
|
||||||
|
id: u16,
|
||||||
|
status: EndRequestStatus,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let header = FcgiHeader {
|
let header = FcgiHeader {
|
||||||
version: FCGI_VERSION,
|
version: FCGI_VERSION,
|
||||||
kind: FCGIHeaderType::EndRequest,
|
kind: FCGIHeaderType::EndRequest,
|
||||||
id: id,
|
id: id,
|
||||||
length: 8,
|
length: 8,
|
||||||
padding: 0 ,
|
padding: 0,
|
||||||
};
|
};
|
||||||
let body = vec![
|
let body = vec![0, 0, 0, 0, status.as_u8(), 0, 0, 0];
|
||||||
0,0,0,0,
|
|
||||||
status.as_u8(),
|
|
||||||
0,0,0
|
|
||||||
];
|
|
||||||
stream.write_all(&header.as_bytes())?;
|
stream.write_all(&header.as_bytes())?;
|
||||||
stream.write_all(&body)?;
|
stream.write_all(&body)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_request<T: Read + Write + AsRawFd >(stream:&mut T)-> Result<(), Box<dyn std::error::Error>>{
|
pub fn process_request<T: Read + Write + AsRawFd>(
|
||||||
|
stream: &mut T,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut requests: HashMap<u16, FGCIRequest> = HashMap::new();
|
let mut requests: HashMap<u16, FGCIRequest> = HashMap::new();
|
||||||
loop{
|
loop {
|
||||||
let header = fcgi_read_header(stream)?;
|
let header = fcgi_read_header(stream)?;
|
||||||
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);
|
INFO!("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")));
|
||||||
}
|
}
|
||||||
// check if we have already request of this kind
|
// check if we have already request of this kind
|
||||||
if let Some(_) = requests.get(&header.id)
|
if let Some(_) = requests.get(&header.id) {
|
||||||
{
|
|
||||||
WARN!("Request {} already exists, ignore this message", header.id);
|
WARN!("Request {} already exists, ignore this message", header.id);
|
||||||
}
|
} else {
|
||||||
else
|
let rq: FGCIRequest = FGCIRequest {
|
||||||
{
|
|
||||||
let rq:FGCIRequest = FGCIRequest {
|
|
||||||
id: header.id,
|
id: header.id,
|
||||||
params: HashMap::new(),
|
params: HashMap::new(),
|
||||||
data: vec![0;0],
|
data: None,
|
||||||
state: FCGIRequestState::WaitForParams,
|
state: FCGIRequestState::WaitForParams,
|
||||||
fd: stream.as_raw_fd(),
|
fd: stream.as_raw_fd(),
|
||||||
};
|
};
|
||||||
requests.insert(header.id, rq);
|
requests.insert(header.id, rq);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
FCGIHeaderType::Params => {
|
FCGIHeaderType::Params => {
|
||||||
if let Some(rq) = requests.get_mut(&header.id)
|
if let Some(rq) = requests.get_mut(&header.id) {
|
||||||
{
|
if rq.state != FCGIRequestState::WaitForParams {
|
||||||
if rq.state != FCGIRequestState::WaitForParams
|
WARN!(
|
||||||
{
|
"Should not receive a param record as the request is in {} state",
|
||||||
WARN!("Should not receive a param record as the request is in {} state", rq.state);
|
rq.state
|
||||||
}
|
);
|
||||||
else
|
} else {
|
||||||
{
|
if header.length == 0 {
|
||||||
if header.length == 0
|
INFO!(
|
||||||
{
|
"All param records read, now wait for stdin data on request: {}",
|
||||||
INFO!("All param records read, now wait for stdin data on request: {}", header.id);
|
header.id
|
||||||
|
);
|
||||||
rq.state = FCGIRequestState::WaitForStdin;
|
rq.state = FCGIRequestState::WaitForStdin;
|
||||||
}
|
} else {
|
||||||
else
|
fcgi_decode_params(rq, &fcgi_read_body(stream, &header)?)?;
|
||||||
{
|
|
||||||
fcgi_decode_params(rq,&fcgi_read_body(stream, &header)?)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
WARN!("Uknow request {}, ignore param record", header.id);
|
WARN!("Uknow request {}, ignore param record", header.id);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
FCGIHeaderType::Stdin => {
|
FCGIHeaderType::Stdin => {
|
||||||
if let Some(rq) = requests.get_mut(&header.id)
|
if let Some(rq) = requests.get_mut(&header.id) {
|
||||||
{
|
if rq.state != FCGIRequestState::WaitForStdin {
|
||||||
if rq.state != FCGIRequestState::WaitForStdin
|
WARN!(
|
||||||
{
|
"Should not receive a stdin record as the request is in {} state",
|
||||||
WARN!("Should not receive a stdin record as the request is in {} state", rq.state);
|
rq.state
|
||||||
}
|
);
|
||||||
else
|
} else {
|
||||||
{
|
if header.length == 0 {
|
||||||
if header.length == 0
|
INFO!(
|
||||||
{
|
"All stdin records read, now wait for stdout data on request: {}",
|
||||||
INFO!("All stdin records read, now wait for stdout data on request: {}", header.id);
|
header.id
|
||||||
|
);
|
||||||
rq.state = FCGIRequestState::WaitForStdout;
|
rq.state = FCGIRequestState::WaitForStdout;
|
||||||
if let Err(error) = fcgi_execute_request_handle(rq)
|
if let Err(error) = fcgi_execute_request_handle(rq) {
|
||||||
{
|
|
||||||
// send stderror
|
// send stderror
|
||||||
fcgi_send_stderr(stream, header.id, Some(error))?;
|
fcgi_send_stderr(stream, header.id, Some(error))?;
|
||||||
}
|
}
|
||||||
@ -702,37 +732,47 @@ pub fn process_request<T: Read + Write + AsRawFd >(stream:&mut T)-> Result<(), B
|
|||||||
// send end connection
|
// send end connection
|
||||||
fcgi_send_end_request(stream, header.id, EndRequestStatus::Complete)?;
|
fcgi_send_end_request(stream, header.id, EndRequestStatus::Complete)?;
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
let body = fcgi_read_body(stream, &header)?;
|
let body = fcgi_read_body(stream, &header)?;
|
||||||
rq.data.extend(body);
|
if let None = rq.data {
|
||||||
|
rq.data = Some(Vec::new())
|
||||||
|
}
|
||||||
|
match rq.data.take() {
|
||||||
|
Some(mut data) => {
|
||||||
|
data.extend(body);
|
||||||
|
rq.data = Some(data);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
WARN!("Uknow request {}, ignore stdin record", header.id);
|
WARN!("Uknow request {}, ignore stdin record", header.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
WARN!("Unsupported record type: {} on request {}", header.kind, header.id);
|
WARN!(
|
||||||
|
"Unsupported record type: {} on request {}",
|
||||||
|
header.kind,
|
||||||
|
header.id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_read_header<T: Read + Write + AsRawFd >(stream: &mut T) -> Result<FcgiHeader, Error>
|
fn fcgi_read_header<T: Read + Write + AsRawFd>(stream: &mut T) -> Result<FcgiHeader, Error> {
|
||||||
{
|
let mut buf = vec![0; FCGI_HEADER_LEN];
|
||||||
let mut buf = vec![0;FCGI_HEADER_LEN];
|
|
||||||
stream.read_exact(&mut buf)?;
|
stream.read_exact(&mut buf)?;
|
||||||
let header: FcgiHeader = FcgiHeader::from_bytes(&buf);
|
let header: FcgiHeader = FcgiHeader::from_bytes(&buf);
|
||||||
Ok(header)
|
Ok(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_read_body<T: Read + Write + AsRawFd >(stream: &mut T, header: & FcgiHeader) -> Result<Vec<u8>, Error>
|
fn fcgi_read_body<T: Read + Write + AsRawFd>(
|
||||||
{
|
stream: &mut T,
|
||||||
|
header: &FcgiHeader,
|
||||||
|
) -> Result<Vec<u8>, Error> {
|
||||||
let mut buf = vec![0; header.length as usize];
|
let mut buf = vec![0; header.length as usize];
|
||||||
stream.read_exact(&mut buf)?;
|
stream.read_exact(&mut buf)?;
|
||||||
let mut pad: Vec<u8> = vec![0; header.padding as usize];
|
let mut pad: Vec<u8> = vec![0; header.padding as usize];
|
||||||
@ -741,45 +781,117 @@ fn fcgi_read_body<T: Read + Write + AsRawFd >(stream: &mut T, header: & FcgiHead
|
|||||||
Ok(buf.to_vec())
|
Ok(buf.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_decode_strlen(data: &[u8]) -> usize
|
fn fcgi_decode_strlen(data: &[u8]) -> usize {
|
||||||
{
|
|
||||||
let b0 = data[0];
|
let b0 = data[0];
|
||||||
if b0 >> 7 == 0
|
if b0 >> 7 == 0 {
|
||||||
{
|
|
||||||
b0 as usize
|
b0 as usize
|
||||||
}
|
} else {
|
||||||
else
|
return (((data[0] as usize) & 0x7f) << 24)
|
||||||
{
|
+ ((data[1] as usize) << 16)
|
||||||
return (((data[0] as usize) & 0x7f) << 24) + ((data[1] as usize) << 16) + ((data[2] as usize) << 8) + (data[3] as usize)
|
+ ((data[2] as usize) << 8)
|
||||||
|
+ (data[3] as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcgi_decode_params(rq: &mut FGCIRequest, data:& Vec<u8>) -> Result<(), Box<dyn std::error::Error>>
|
fn fcgi_decode_params(
|
||||||
{
|
rq: &mut FGCIRequest,
|
||||||
|
data: &Vec<u8>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut index: usize = 1;
|
let mut index: usize = 1;
|
||||||
let key_len = fcgi_decode_strlen(data);
|
let key_len = fcgi_decode_strlen(data);
|
||||||
if key_len > 127
|
if key_len > 127 {
|
||||||
{
|
|
||||||
index = 4;
|
index = 4;
|
||||||
}
|
}
|
||||||
let value_len = fcgi_decode_strlen(&data[index..]);
|
let value_len = fcgi_decode_strlen(&data[index..]);
|
||||||
//INFO!("Key len {}, value len {}", key_len, value_len);
|
//INFO!("Key len {}, value len {}", key_len, value_len);
|
||||||
if value_len > 127
|
if value_len > 127 {
|
||||||
{
|
|
||||||
index += 4;
|
index += 4;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
//INFO!("data: {:?}", data);
|
//INFO!("data: {:?}", data);
|
||||||
//INFO!("key: {:?}", data[index..index + key_len].to_vec());
|
//INFO!("key: {:?}", data[index..index + key_len].to_vec());
|
||||||
//INFO!("Value: {:?}", data[index+key_len..index+key_len+value_len].to_vec());
|
//INFO!("Value: {:?}", data[index+key_len..index+key_len+value_len].to_vec());
|
||||||
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 = String::from_utf8(data[index+key_len..index+key_len+value_len].to_vec())?;
|
let value: String =
|
||||||
|
String::from_utf8(data[index + key_len..index + key_len + value_len].to_vec())?;
|
||||||
INFO!("PARAM: [{}] -> [{}]", key, value);
|
INFO!("PARAM: [{}] -> [{}]", key, value);
|
||||||
let _ = rq.params.insert(key, value);
|
let _ = rq.params.insert(key, value);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lua_new_bytes(_: &mlua::Lua, size: usize) -> LuaResult<LuabyteArray> {
|
||||||
|
let arr = LuabyteArray(vec![0; size]);
|
||||||
|
Ok(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lua_new_bytes_from_string(_: &mlua::Lua, string: String) -> LuaResult<LuabyteArray> {
|
||||||
|
let arr = LuabyteArray(string.as_bytes().to_vec());
|
||||||
|
Ok(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LuabyteArray(Vec<u8>);
|
||||||
|
|
||||||
|
impl mlua::UserData for LuabyteArray {
|
||||||
|
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("write", |_, this: &LuabyteArray, path: String| {
|
||||||
|
match std::fs::File::create(&path) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
if let Err(error) = file.write_all(&this.0) {
|
||||||
|
ERROR!("Unable to write byte array to file {}: {}", &path, error);
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
ERROR!("Unable open file {}: {}", path, error);
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(1)
|
||||||
|
});
|
||||||
|
methods.add_meta_method(mlua::MetaMethod::Index, |_, this, index: isize| {
|
||||||
|
if index < 1 || index > this.0.len() as isize {
|
||||||
|
let error = ERR!(format!(
|
||||||
|
"Index {} out of bound, array size is {}",
|
||||||
|
index,
|
||||||
|
this.0.len()
|
||||||
|
));
|
||||||
|
ERROR!("{}", error);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Ok(Some(this.0[index as usize - 1]))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(
|
||||||
|
mlua::MetaMethod::ToString,
|
||||||
|
|_, this, ()| match String::from_utf8(this.0.clone()) {
|
||||||
|
Err(error) => {
|
||||||
|
let err = format!("Unable to convert byte array to string: {}", error);
|
||||||
|
ERROR!("{}", err);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Ok(string) => Ok(Some(string)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
methods.add_meta_method_mut(
|
||||||
|
mlua::MetaMethod::NewIndex,
|
||||||
|
|_, this, (index, value): (isize, u8)| {
|
||||||
|
if index < 1 || index > this.0.len() as isize {
|
||||||
|
let error = ERR!(format!(
|
||||||
|
"Index {} out of bound, array size is {}",
|
||||||
|
index,
|
||||||
|
this.0.len()
|
||||||
|
));
|
||||||
|
ERROR!("{}", error);
|
||||||
|
} else {
|
||||||
|
this.0[index as usize - 1] = value;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
methods.add_meta_method_mut(mlua::MetaMethod::Len, |_, this, ()| Ok(this.0.len()));
|
||||||
|
}
|
||||||
}
|
}
|
106
src/main.rs
106
src/main.rs
@ -13,20 +13,19 @@
|
|||||||
clippy::pedantic,
|
clippy::pedantic,
|
||||||
clippy::missing_docs_in_private_items
|
clippy::missing_docs_in_private_items
|
||||||
)]
|
)]
|
||||||
|
use clap;
|
||||||
use serde;
|
use serde;
|
||||||
use toml;
|
use toml;
|
||||||
use clap;
|
|
||||||
//use std::fs::File;
|
//use std::fs::File;
|
||||||
|
use luad::*;
|
||||||
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
|
use std::os::fd::FromRawFd;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::os::unix::net::UnixListener;
|
use std::os::unix::net::UnixListener;
|
||||||
use std::panic;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::os::fd::FromRawFd;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::io::Read;
|
|
||||||
use luad::*;
|
|
||||||
|
|
||||||
/// Callback: clean up function
|
/// Callback: clean up function
|
||||||
///
|
///
|
||||||
@ -45,15 +44,16 @@ fn clean_up(n: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n != 0 {
|
if n != 0 {
|
||||||
panic!("{}", format!("The LUA fastCGI daemon is terminated by system signal: {}", n));
|
ERROR!(
|
||||||
|
"The LUA fastCGI daemon is terminated by system signal: {}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_request<T: Read + Write + AsRawFd>(stream: &mut T) {
|
||||||
|
if let Err(error) = process_request(stream) {
|
||||||
fn handle_request<T: Read + Write + AsRawFd >(stream: &mut T) {
|
|
||||||
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());
|
INFO!("Request on socket {} is processed", stream.as_raw_fd());
|
||||||
@ -64,21 +64,19 @@ fn handle_request<T: Read + Write + AsRawFd >(stream: &mut T) {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `socket_opt` - The socket string that the server listens on
|
/// * `socket_opt` - The socket string that the server listens on
|
||||||
fn serve(socket_opt: Option<&str>) {
|
fn serve(config: &Config) {
|
||||||
|
|
||||||
|
|
||||||
// bind to a socket if any
|
// bind to a socket if any
|
||||||
if let Some(socket_name) = socket_opt {
|
if let Some(socket_name) = config.socket.as_deref() {
|
||||||
// test if the socket name is an unix domain socket
|
// test if the socket name is an unix domain socket
|
||||||
if socket_name.starts_with("unix:") {
|
if socket_name.starts_with("unix:") {
|
||||||
// e.g unix:/var/run/lighttpd/maint/efcgi.socket
|
// e.g unix:/var/run/lighttpd/maint/efcgi.socket
|
||||||
INFO!("Use unix domain socket: {}", socket_name);
|
INFO!("Use unix domain socket: {}", socket_name);
|
||||||
std::env::set_var("socket", socket_name);
|
std::env::set_var("socket", socket_name);
|
||||||
|
clean_up(0);
|
||||||
let listener = UnixListener::bind(socket_name.replace("unix:", "")).unwrap();
|
let listener = UnixListener::bind(socket_name.replace("unix:", "")).unwrap();
|
||||||
on_exit(clean_up);
|
|
||||||
for client in listener.incoming() {
|
for client in listener.incoming() {
|
||||||
let mut stream = client.unwrap();
|
let mut stream = client.unwrap();
|
||||||
let _= std::thread::spawn(move || {
|
let _ = std::thread::spawn(move || {
|
||||||
handle_request(&mut stream);
|
handle_request(&mut stream);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -88,7 +86,7 @@ fn serve(socket_opt: Option<&str>) {
|
|||||||
let listener = TcpListener::bind(socket_name).unwrap();
|
let listener = TcpListener::bind(socket_name).unwrap();
|
||||||
for client in listener.incoming() {
|
for client in listener.incoming() {
|
||||||
let mut stream = client.unwrap();
|
let mut stream = client.unwrap();
|
||||||
let _= thread::spawn(move || {
|
let _ = thread::spawn(move || {
|
||||||
handle_request(&mut stream);
|
handle_request(&mut stream);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -98,13 +96,27 @@ fn serve(socket_opt: Option<&str>) {
|
|||||||
// 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 listenning socket");
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
let listener = unsafe{ UnixListener::from_raw_fd(stdin.as_raw_fd())};
|
let fd = stdin.as_raw_fd();
|
||||||
for client in listener.incoming() {
|
if is_unix_socket(fd).unwrap() {
|
||||||
let mut stream = client.unwrap();
|
INFO!("Stdin is used as Unix domain socket");
|
||||||
|
let listener = unsafe { UnixListener::from_raw_fd(stdin.as_raw_fd()) };
|
||||||
|
for client in listener.incoming() {
|
||||||
|
let mut stream = client.unwrap();
|
||||||
|
|
||||||
let _= thread::spawn(move || {
|
let _ = thread::spawn(move || {
|
||||||
handle_request(&mut stream);
|
handle_request(&mut stream);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
INFO!("Stdin is used as TCP Socket");
|
||||||
|
let listener = unsafe { TcpListener::from_raw_fd(stdin.as_raw_fd()) };
|
||||||
|
for client in listener.incoming() {
|
||||||
|
let mut stream = client.unwrap();
|
||||||
|
|
||||||
|
let _ = thread::spawn(move || {
|
||||||
|
handle_request(&mut stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,19 +127,18 @@ struct Config {
|
|||||||
pidfile: Option<String>,
|
pidfile: Option<String>,
|
||||||
user: Option<String>,
|
user: Option<String>,
|
||||||
group: Option<String>,
|
group: Option<String>,
|
||||||
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Main application entry
|
/// Main application entry
|
||||||
///
|
///
|
||||||
/// Run a `fastCGI` server
|
/// Run a `fastCGI` server
|
||||||
fn main() {
|
fn main() {
|
||||||
|
on_exit(clean_up);
|
||||||
let _log = LOG::init_log();
|
let _log = LOG::init_log();
|
||||||
|
|
||||||
let matches = clap::App::new(DAEMON_NAME)
|
let matches = clap::App::new(DAEMON_NAME)
|
||||||
.author(APP_AUTHOR)
|
.author(APP_AUTHOR)
|
||||||
.about("Lua general purpose socket handle daemon")
|
.about("Lua FastCGI daemon")
|
||||||
.version(APP_VERSION)
|
.version(APP_VERSION)
|
||||||
.arg(
|
.arg(
|
||||||
clap::Arg::with_name("file")
|
clap::Arg::with_name("file")
|
||||||
@ -139,29 +150,36 @@ fn main() {
|
|||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
let mut config = Config {
|
||||||
|
socket: None,
|
||||||
|
pidfile: None,
|
||||||
|
user: None,
|
||||||
|
group: None,
|
||||||
|
debug: false,
|
||||||
|
};
|
||||||
match matches.value_of("file") {
|
match matches.value_of("file") {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
INFO!("Configuration file: {}", path);
|
INFO!("Configuration file: {}", path);
|
||||||
let contents = std::fs::read_to_string(path).unwrap();
|
let contents = std::fs::read_to_string(path).unwrap();
|
||||||
let config: Config = toml::from_str(&contents).unwrap();
|
config = toml::from_str(&contents).unwrap();
|
||||||
|
if config.debug {
|
||||||
// write pid file
|
std::env::set_var("luad_debug", "true");
|
||||||
match config.pidfile {
|
|
||||||
Some(pidfile) => {
|
|
||||||
let mut f = std::fs::File::create(&pidfile).unwrap();
|
|
||||||
write!(f, "{}", std::process::id()).unwrap();
|
|
||||||
INFO!("PID file created at {}", pidfile);
|
|
||||||
},
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
// drop user privilege if only user and group available in
|
// drop user privilege if only user and group available in
|
||||||
// the configuration file, otherwise ignore
|
// the configuration file, otherwise ignore
|
||||||
privdrop(config.user.as_deref(), config.group.as_deref()).unwrap();
|
privdrop(config.user.as_deref(), config.group.as_deref()).unwrap();
|
||||||
serve(config.socket.as_deref());
|
|
||||||
},
|
// write pid file
|
||||||
None => {
|
match &config.pidfile {
|
||||||
serve(None);
|
Some(pidfile) => {
|
||||||
|
let mut f = std::fs::File::create(&pidfile).unwrap();
|
||||||
|
write!(f, "{}", std::process::id()).unwrap();
|
||||||
|
INFO!("PID file created at {}", pidfile);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
|
serve(&config);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user