extern crate libc;
pub mod ffi;
use ffi::*;
use std::ffi::CStr;
use std::io;
use std::marker::PhantomData;
use std::os::unix::io::AsRawFd;
use std::sync::{ONCE_INIT, Once};
static FCGX_INIT: Once = ONCE_INIT;
#[derive(Debug)]
pub struct Exchange {
request: *mut FCGX_Request,
}
impl Exchange {
pub unsafe fn new(request: *mut FCGX_Request) -> Self {
Exchange{request}
}
pub fn accept<L>(listener: &L) -> io::Result<Self>
where L: AsRawFd {
unsafe {
FCGX_INIT.call_once(|| {
let status = FCGX_Init();
assert!(status == 0);
});
let request = FCGX_ABICompat_MallocRequest();
if request.is_null() {
return Err(io::Error::last_os_error());
}
let file_descriptor = listener.as_raw_fd();
let status = FCGX_InitRequest(request, file_descriptor, 0);
if status != 0 {
libc::free(request);
return Err(io::Error::last_os_error());
}
let status = FCGX_Accept_r(request);
if status == -1 {
FCGX_Free(request, 0);
libc::free(request);
return Err(io::Error::last_os_error());
}
Ok(Self::new(request))
}
}
pub fn environment(&self) -> Environment {
Environment::new(self)
}
pub fn request_body(&self) -> RequestBody {
RequestBody::new(self)
}
pub fn response(&self) -> Response {
Response::new(self)
}
}
impl Drop for Exchange {
fn drop(&mut self) {
unsafe {
FCGX_Finish_r(self.request);
libc::free(self.request);
}
}
}
unsafe impl Send for Exchange {
}
#[derive(Clone, Debug)]
pub struct Environment<'a> {
envp: FCGX_ParamArray,
phantom: PhantomData<&'a ()>,
}
impl<'a> Environment<'a> {
fn new(exchange: &Exchange) -> Self {
unsafe {
let envp = FCGX_ABICompat_RequestEnvp(exchange.request);
Environment{envp, phantom: PhantomData}
}
}
}
impl<'a> Iterator for Environment<'a> {
type Item = &'a CStr;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if (*self.envp).is_null() {
return None;
}
let entry = CStr::from_ptr(*self.envp);
self.envp = self.envp.offset(1);
Some(entry)
}
}
}
#[derive(Debug)]
pub struct RequestBody<'a> {
stream: *mut FCGX_Stream,
phantom: PhantomData<&'a ()>,
}
impl<'a> RequestBody<'a> {
fn new(exchange: &Exchange) -> Self {
unsafe {
let stream = FCGX_ABICompat_RequestIn(exchange.request);
RequestBody{stream, phantom: PhantomData}
}
}
}
impl<'a> io::Read for RequestBody<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let actual_read = FCGX_GetStr(buf.as_mut_ptr() as *mut i8,
buf.len() as i32, self.stream);
if FCGX_GetError(self.stream) != 0 {
return Err(io::Error::last_os_error());
}
Ok(actual_read as usize)
}
}
}
#[derive(Debug)]
pub struct Response<'a> {
stream: *mut FCGX_Stream,
phantom: PhantomData<&'a ()>,
}
impl<'a> Response<'a> {
fn new(exchange: &Exchange) -> Self {
unsafe {
let stream = FCGX_ABICompat_RequestOut(exchange.request);
Response{stream, phantom: PhantomData}
}
}
}
impl<'a> io::Write for Response<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let actual_written = FCGX_PutStr(buf.as_ptr() as *const i8,
buf.len() as i32, self.stream);
if actual_written == -1 {
return Err(io::Error::last_os_error());
}
Ok(actual_written as usize)
}
}
fn flush(&mut self) -> io::Result<()> {
unsafe {
let status = FCGX_FFlush(self.stream);
if status == -1 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
}