nginx 0.10.0

Rust bindings for NGINX API
use bindings::{
    ngx_http_headers_in_t, ngx_list_part_t, ngx_list_push, ngx_list_t, ngx_palloc, ngx_pool_t,
    ngx_str_t, ngx_table_elt_t, u_char,
};
use std::convert::{From, TryFrom};
use std::ffi::OsStr;
use std::fmt;
use std::ptr::copy_nonoverlapping;
use std::{slice, str};

pub struct Header(ngx_table_elt_t);

impl From<ngx_str_t> for &[u8] {
    fn from(s: ngx_str_t) -> Self {
        if s.len == 0 || s.data.is_null() {
            return Default::default();
        }
        unsafe { slice::from_raw_parts(s.data, s.len as usize) }
    }
}

#[cfg(any(unix, target_os = "redox"))]
impl From<ngx_str_t> for &OsStr {
    fn from(s: ngx_str_t) -> Self {
        std::os::unix::ffi::OsStrExt::from_bytes(s.into())
    }
}

impl fmt::Display for ngx_str_t {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", String::from_utf8_lossy((*self).into()))
    }
}

impl TryFrom<ngx_str_t> for &str {
    type Error = str::Utf8Error;

    fn try_from(s: ngx_str_t) -> Result<Self, Self::Error> {
        str::from_utf8(s.into())
    }
}

impl TryFrom<ngx_str_t> for String {
    type Error = std::string::FromUtf8Error;

    fn try_from(s: ngx_str_t) -> Result<Self, Self::Error> {
        let bytes: &[u8] = s.into();
        String::from_utf8(bytes.into())
    }
}

impl ngx_http_headers_in_t {
    pub fn host_str(&self) -> String {
        if self.host.is_null() {
            return Default::default();
        }
        unsafe { *self.host }.value.to_string()
    }

    pub fn add(&mut self, pool: *mut ngx_pool_t, key: &str, value: &str) -> Option<()> {
        let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.headers) as _ };
        unsafe { table.as_mut() }.map(|table| {
            table.hash = 1;
            table.key.len = key.len() as _;
            table.key.data = str_to_uchar(pool, key);
            table.value.len = value.len() as _;
            table.value.data = str_to_uchar(pool, value);
            table.lowcase_key = str_to_uchar(pool, String::from(key).to_ascii_lowercase().as_str());
        })
    }
}

impl IntoIterator for ngx_http_headers_in_t {
    type Item = Header;
    type IntoIter = ListIterator;

    fn into_iter(self) -> Self::IntoIter {
        ListIterator::from_ngx_list(self.headers)
    }
}

pub struct ListIterator {
    part: ngx_list_part_t,
    h: *mut ngx_table_elt_t,
    i: isize,
}

impl ListIterator {
    pub fn from_ngx_list(list: ngx_list_t) -> Self {
        let part = list.part;
        ListIterator {
            part: part,
            h: part.elts as _,
            i: 0,
        }
    }
}

impl Iterator for ListIterator {
    type Item = Header;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.part.nelts as _ {
            if let Some(next) = unsafe { self.part.next.as_ref() } {
                self.part = *next;
                self.h = self.part.elts as _;
                self.i = 0;
            } else {
                return None;
            }
        }
        let header = unsafe { *self.h.offset(self.i) };
        self.i += 1;
        Some(Header::from(header))
    }
}

impl From<ngx_table_elt_t> for Header {
    fn from(table: ngx_table_elt_t) -> Self {
        Self(table)
    }
}

impl Header {
    pub fn key(&self) -> String {
        self.0.key.to_string()
    }

    pub fn value(&self) -> String {
        self.0.value.to_string()
    }

    pub fn into_inner(self) -> ngx_table_elt_t {
        self.0
    }
}

fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char {
    let ptr: *mut u_char = unsafe { ngx_palloc(pool, data.len() as _) as _ };
    unsafe {
        copy_nonoverlapping(data.as_ptr(), ptr, data.len());
    }
    ptr
}