redevplugin-worker-sdk 2.0.8

Rust SDK for ReDevPlugin WASM workers
Documentation
use crate::api;
use crate::error::{Error, Result};
use crate::resource::{Handle, IO_FLAG_EOF, MAX_IO_CHUNK_BYTES};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Header {
    pub name: String,
    pub value: String,
}

#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RedirectMode {
    #[default]
    Follow,
    Manual,
    Error,
}

#[derive(Debug, Clone, Serialize)]
pub struct HttpRequest {
    pub method: String,
    pub url: String,
    #[serde(default)]
    pub headers: Vec<Header>,
    #[serde(default)]
    pub redirect: RedirectMode,
    #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeout_ms: Option<u32>,
}

pub type RequestOptions = HttpRequest;

#[derive(Deserialize)]
struct BeginResult {
    upload_handle: u64,
}

#[derive(Deserialize)]
struct FinishResult {
    status: u16,
    #[serde(default)]
    headers: Vec<Header>,
    final_url: String,
    body_handle: u64,
}

pub struct RequestBody {
    handle: Handle,
}

impl RequestBody {
    pub fn begin(options: HttpRequest) -> Result<Self> {
        let opened: BeginResult = api::call("net.http.begin", &options)?;
        Ok(Self {
            handle: Handle::new(opened.upload_handle)?,
        })
    }

    pub fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
        for chunk in bytes.chunks(MAX_IO_CHUNK_BYTES) {
            self.handle.write(chunk, 0)?;
        }
        Ok(())
    }

    pub fn finish(mut self) -> Result<Response> {
        #[derive(Serialize)]
        struct Arguments {
            handle: u64,
        }
        let finished: FinishResult = api::call(
            "net.http.finish",
            &Arguments {
                handle: self.handle.id(),
            },
        )?;
        self.handle.disarm();
        Ok(Response {
            status: finished.status,
            headers: finished.headers,
            final_url: finished.final_url,
            body: ResponseBody {
                handle: Handle::new(finished.body_handle)?,
            },
        })
    }

    pub fn abort(mut self) -> Result<()> {
        #[derive(Serialize)]
        struct Arguments {
            handle: u64,
        }
        let _: serde_json::Value = api::call(
            "net.http.abort",
            &Arguments {
                handle: self.handle.id(),
            },
        )?;
        self.handle.disarm();
        Ok(())
    }
}

pub struct HttpResponse {
    pub status: u16,
    pub headers: Vec<Header>,
    pub final_url: String,
    pub body: ResponseBody,
}

pub type Response = HttpResponse;

pub struct ResponseBody {
    handle: Handle,
}

impl ResponseBody {
    pub fn read(&mut self, capacity: usize) -> Result<(Vec<u8>, u32)> {
        self.handle.read(capacity)
    }

    pub fn read_all(mut self) -> Result<Vec<u8>> {
        let mut result = Vec::new();
        loop {
            let (chunk, flags) = self.handle.read(MAX_IO_CHUNK_BYTES)?;
            if chunk.is_empty() && flags & IO_FLAG_EOF == 0 {
                return Err(Error::internal("HTTP response body made no progress"));
            }
            result.extend_from_slice(&chunk);
            if flags & IO_FLAG_EOF != 0 {
                self.handle.close()?;
                return Ok(result);
            }
        }
    }

    pub fn close(mut self) -> Result<()> {
        self.handle.close()
    }
}