fastly 0.2.0-alpha1

Fastly Compute@Edge API
Documentation
use crate::body::BodyHandle;
use crate::request::RequestHandle;
use crate::response::ResponseHandle;
use crate::Error;
use bytes::{Buf, Bytes, BytesMut};
pub use fastly_shared::{HttpVersion, XqdStatus, XQD_ABI_VERSION};

#[link(wasm_import_module = "env")]
extern "C" {
    #[no_mangle]
    pub fn xqd_body_append(dst_handle: BodyHandle, src_handle: BodyHandle) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_body_new(handle_out: *mut BodyHandle) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_body_read(
        body_handle: BodyHandle,
        buf: *mut u8,
        buf_len: usize,
        nread_out: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_body_write(
        body_handle: BodyHandle,
        buf: *const u8,
        buf_len: usize,
        end: fastly_shared::BodyWriteEnd,
        nwritten_out: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    /// Tell the runtime what ABI version this program is using (XQD_ABI_VERSION)
    pub fn xqd_init(abi_version: u64) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_body_downstream_get(
        req_handle_out: *mut RequestHandle,
        body_handle_out: *mut BodyHandle,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_header_append(
        req_handle: RequestHandle,
        name: *const u8,
        name_len: usize,
        value: *const u8,
        value_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_header_insert(
        req_handle: RequestHandle,
        name: *const u8,
        name_len: usize,
        value: *const u8,
        value_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_header_names_get(
        req_handle: RequestHandle,
        buf: *mut u8,
        buf_len: usize,
        cursor: u32,
        ending_cursor: *mut i64,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_header_values_get(
        req_handle: RequestHandle,
        name: *const u8,
        name_len: usize,
        buf: *mut u8,
        buf_len: usize,
        cursor: u32,
        ending_cursor: *mut i64,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_header_values_set(
        req_handle: RequestHandle,
        name: *const u8,
        name_len: usize,
        values: *const u8,
        values_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_method_get(
        req_handle: RequestHandle,
        method: *mut u8,
        method_max_len: usize,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_method_set(
        req_handle: RequestHandle,
        method: *const u8,
        method_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_new(req_handle_out: *mut RequestHandle) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_send(
        req_handle: RequestHandle,
        body_handle: BodyHandle,
        backend: *const u8,
        backend_len: usize,
        ttl: i32,
        resp_handle_out: *mut ResponseHandle,
        resp_body_handle_out: *mut BodyHandle,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_uri_get(
        req_handle: RequestHandle,
        uri: *mut u8,
        uri_max_len: usize,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_uri_set(req_handle: RequestHandle, uri: *const u8, uri_len: usize) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_version_get(req_handle: RequestHandle, version: *mut u32) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_req_version_set(req_handle: RequestHandle, version: u32) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_append(
        resp_handle: ResponseHandle,
        name: *const u8,
        name_len: usize,
        value: *const u8,
        value_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_insert(
        resp_handle: ResponseHandle,
        name: *const u8,
        name_len: usize,
        value: *const u8,
        value_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_names_get(
        resp_handle: ResponseHandle,
        buf: *mut u8,
        buf_len: usize,
        cursor: u32,
        ending_cursor: *mut i64,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_value_get(
        resp_handle: ResponseHandle,
        name: *const u8,
        name_len: usize,
        value: *mut u8,
        value_max_len: usize,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_values_get(
        resp_handle: ResponseHandle,
        name: *const u8,
        name_len: usize,
        buf: *mut u8,
        buf_len: usize,
        cursor: u32,
        ending_cursor: *mut i64,
        nwritten: *mut usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_header_values_set(
        resp_handle: ResponseHandle,
        name: *const u8,
        name_len: usize,
        values: *const u8,
        values_len: usize,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_new(resp_handle_out: *mut ResponseHandle) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_send_downstream(
        resp_handle: ResponseHandle,
        body_handle: BodyHandle,
    ) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_status_get(resp_handle: ResponseHandle, status: *mut u16) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_status_set(resp_handle: ResponseHandle, status: u16) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_version_get(resp_handle: ResponseHandle, version: *mut u32) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_resp_version_set(resp_handle: ResponseHandle, version: u32) -> XqdStatus;

    #[no_mangle]
    pub fn xqd_uap_parse(
        user_agent: *const u8,
        user_agent_max_len: usize,
        family: *mut u8,
        family_max_len: usize,
        family_written: *mut usize,
        major: *mut u8,
        major_max_len: usize,
        major_written: *mut usize,
        minor: *mut u8,
        minor_max_len: usize,
        minor_written: *mut usize,
        patch: *mut u8,
        patch_max_len: usize,
        patch_written: *mut usize
        ) -> XqdStatus;

}

pub(crate) struct MultiValueHostcall<F> {
    fill_buf: F,
    term: u8,
    buf: BytesMut,
    max_buf_size: usize,
    cursor: u32,
    is_done: bool,
}

impl<F> MultiValueHostcall<F> {
    pub(crate) fn new(term: u8, max_buf_size: usize, fill_buf: F) -> Self {
        Self {
            fill_buf,
            term,
            buf: BytesMut::with_capacity(max_buf_size),
            max_buf_size,
            cursor: 0,
            is_done: false,
        }
    }
}

impl<F> std::iter::Iterator for MultiValueHostcall<F>
where
    F: Fn(*mut u8, usize, u32, *mut i64, *mut usize) -> XqdStatus,
{
    type Item = Result<Bytes, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        // first fill the buffer, if it's empty
        if self.buf.is_empty() {
            if self.is_done {
                // if there are no more calls to make, and the buffer is empty, we're done
                return None;
            }
            self.buf.reserve(self.max_buf_size);
            let mut ending_cursor = 0;
            let mut nwritten = 0;
            let status = (self.fill_buf)(
                self.buf.as_mut_ptr(),
                self.buf.capacity(),
                self.cursor,
                &mut ending_cursor,
                &mut nwritten,
            );
            if status.is_err() {
                self.is_done = true;
                // TODO ACF 2019-12-02: make sure these implementation detail error messages don't
                // bubble out to the user
                return Some(Err(Error::new("MultiValueHostcall closure returned error")));
            }
            if nwritten == 0 {
                // if we get no bytes, we're definitely done; this only comes up if there are no
                // values at all, otherwise we see the ending cursor at -1 and stop
                self.is_done = true;
                return None;
            }
            unsafe {
                self.buf.set_len(nwritten);
            }
            if self.cursor == ending_cursor as u32 {
                self.is_done = true;
                // if the cursor doesn't advance at all, that means there's a value that's too big
                // to fit in our buffer
                return Some(Err(Error::new("MultiValueHostcall buffer too small")));
            }
            if ending_cursor < 0 {
                // no more calls necessary after this one
                self.is_done = true;
            } else {
                // otherwise adjust the cursor for the next fill
                self.cursor += ending_cursor as u32;
            }
        }
        // find the index of the first terminator byte in the buffer
        let first_term_ix = if let Some(ix) = self.buf.iter().position(|b| b == &self.term) {
            ix
        } else {
            // if the terminator is not found, that violates the protocol of these hostcalls, which
            // must always terminate each element with the terminator
            self.is_done = true;
            return Some(Err(Error::new(
                "MultiValueHostcall separator byte not found",
            )));
        };
        // split off the first element from the buffer
        let elt = self.buf.split_to(first_term_ix);
        // drop the terminator byte, which now remains in the buffer
        self.buf.advance(1);
        Some(Ok(elt.freeze()))
    }
}

impl<F> std::iter::FusedIterator for MultiValueHostcall<F> where
    F: Fn(*mut u8, usize, u32, *mut i64, *mut usize) -> XqdStatus
{
}