Skip to content

Commit

Permalink
Updated fetch_io APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
uditdc committed Dec 14, 2023
1 parent 30262a2 commit 77268ae
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 41 deletions.
33 changes: 9 additions & 24 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions crates/apis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ categories = ["wasm"]
[features]
console = []
crypto = ["dep:rand"]
fetch_io = ["dep:blockless-sdk", "dep:serde_json"]
fetch_io = ["dep:serde", "dep:serde_json"]
random = ["dep:fastrand"]
stream_io = []
text_encoding = []

[dependencies]
javy = { workspace = true }
anyhow = { workspace = true }
fastrand = { version = "2.0.1", optional = true }
rand = { version = "0.8.5", optional = true }
javy = { workspace = true }
blockless-sdk = { version = "0.1.3", optional = true }
serde_json = { version = "1.0.108", optional = true }
serde_json = { version = "1.0.108", optional = true }
serde = { version = "1.0.193", optional = true, features = ["derive"] }
4 changes: 1 addition & 3 deletions crates/apis/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@ impl JSApiSet for Crypto {

fn get_random_values() -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> Result<JSValue> {
move |_ctx: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]| {
let mut rng = rand::rngs::OsRng::default();

let buffer = args[0].as_bytes_mut()?;
let byte_offset: usize = args[1].try_into()?;
let byte_length: usize = args[2].try_into()?;

let buffer = &mut buffer[byte_offset..(byte_offset + byte_length)];

// Fill the buffer with random values.
rng.fill_bytes(buffer);
rand::rngs::OsRng.fill_bytes(buffer);

Ok(JSValue::ArrayBuffer(buffer.to_vec()))
}
Expand Down
222 changes: 222 additions & 0 deletions crates/apis/src/fetch_io/blockless.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
use std::cmp::Ordering;
use serde_json::{json, Value};
use crate::fetch_io::FetchOptions;

pub type Handle = u32;

pub type CodeStatus = u32;

pub struct BlocklessHttp {
inner: Handle,
code: CodeStatus,
}

pub struct HttpOptions {
method: String,
connect_timeout: u32,
read_timeout: u32,
body: Option<String>,
}

impl HttpOptions {
pub fn new(method: &str, connect_timeout: u32, read_timeout: u32) -> Self {
HttpOptions {
method: method.into(),
connect_timeout,
read_timeout,
body: None,
}
}

pub fn to_json(&self) -> Value {
json!({
"method": self.method,
"connectTimeout": self.connect_timeout,
"readTimeout": self.read_timeout,
"headers": "{}",
"body": self.body,
})
}
}

impl BlocklessHttp {
pub fn open(url: &str, opts: &FetchOptions) -> Result<Self, HttpErrorKind> {
let http_opts = HttpOptions::new(&opts.method, 30, 10);
let http_opts_str = serde_json::to_string(&http_opts.to_json()).unwrap();

let mut fd = 0;
let mut status = 0;
let rs = unsafe {
http_open(
url.as_ptr(),
url.len() as _,
http_opts_str.as_ptr(),
http_opts_str.len() as _,
&mut fd,
&mut status,
)
};
if rs != 0 {
return Err(HttpErrorKind::from(rs));
}
Ok(Self {
inner: fd,
code: status,
})
}

pub fn get_code(&self) -> CodeStatus {
self.code
}

pub fn get_all_body(&self) -> Result<Vec<u8>, HttpErrorKind> {
let mut vec = Vec::new();
loop {
let mut buf = [0u8; 1024];
let mut num: u32 = 0;
let rs =
unsafe { http_read_body(self.inner, buf.as_mut_ptr(), buf.len() as _, &mut num) };
if rs != 0 {
return Err(HttpErrorKind::from(rs));
}

match num.cmp(&0) {
Ordering::Greater => vec.extend_from_slice(&buf[0..num as _]),
_ => break,
}
}
Ok(vec)
}

pub fn get_header(&self, header: &str) -> Result<String, HttpErrorKind> {
let mut vec = Vec::new();
loop {
let mut buf = [0u8; 1024];
let mut num: u32 = 0;
let rs = unsafe {
http_read_header(
self.inner,
header.as_ptr(),
header.len() as _,
buf.as_mut_ptr(),
buf.len() as _,
&mut num,
)
};
if rs != 0 {
return Err(HttpErrorKind::from(rs));
}
match num.cmp(&0) {
Ordering::Greater => vec.extend_from_slice(&buf[0..num as _]),
_ => break,
}
}
String::from_utf8(vec).map_err(|_| HttpErrorKind::Utf8Error)
}

pub fn close(self) {
unsafe {
http_close(self.inner);
}
}

pub fn read_body(&self, buf: &mut [u8]) -> Result<u32, HttpErrorKind> {
let mut num: u32 = 0;
let rs = unsafe { http_read_body(self.inner, buf.as_mut_ptr(), buf.len() as _, &mut num) };
if rs != 0 {
return Err(HttpErrorKind::from(rs));
}
Ok(num)
}
}

#[derive(Debug)]
pub enum HttpErrorKind {
InvalidDriver,
InvalidHandle,
MemoryAccessError,
BufferTooSmall,
HeaderNotFound,
Utf8Error,
DestinationNotAllowed,
InvalidMethod,
InvalidEncoding,
InvalidUrl,
RequestError,
RuntimeError,
TooManySessions,
PermissionDeny,
}

impl std::error::Error for HttpErrorKind {}

impl std::fmt::Display for HttpErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::InvalidDriver => write!(f, "Invalid Driver"),
Self::InvalidHandle => write!(f, "Invalid Error"),
Self::MemoryAccessError => write!(f, "Memory Access Error"),
Self::BufferTooSmall => write!(f, "Buffer too small"),
Self::HeaderNotFound => write!(f, "Header not found"),
Self::Utf8Error => write!(f, "Utf8 error"),
Self::DestinationNotAllowed => write!(f, "Destination not allowed"),
Self::InvalidMethod => write!(f, "Invalid method"),
Self::InvalidEncoding => write!(f, "Invalid encoding"),
Self::InvalidUrl => write!(f, "Invalid url"),
Self::RequestError => write!(f, "Request url"),
Self::RuntimeError => write!(f, "Runtime error"),
Self::TooManySessions => write!(f, "Too many sessions"),
Self::PermissionDeny => write!(f, "Permission deny."),
}
}
}

impl From<u32> for HttpErrorKind {
fn from(i: u32) -> HttpErrorKind {
match i {
1 => HttpErrorKind::InvalidHandle,
2 => HttpErrorKind::MemoryAccessError,
3 => HttpErrorKind::BufferTooSmall,
4 => HttpErrorKind::HeaderNotFound,
5 => HttpErrorKind::Utf8Error,
6 => HttpErrorKind::DestinationNotAllowed,
7 => HttpErrorKind::InvalidMethod,
8 => HttpErrorKind::InvalidEncoding,
9 => HttpErrorKind::InvalidUrl,
10 => HttpErrorKind::RequestError,
11 => HttpErrorKind::RuntimeError,
12 => HttpErrorKind::TooManySessions,
13 => HttpErrorKind::PermissionDeny,
_ => HttpErrorKind::RuntimeError,
}
}
}

#[link(wasm_import_module = "blockless_http")]
extern "C" {
#[link_name = "http_req"]
pub(crate) fn http_open(
url: *const u8,
url_len: u32,
opts: *const u8,
opts_len: u32,
fd: *mut u32,
status: *mut u32,
) -> u32;

#[link_name = "http_read_header"]
pub(crate) fn http_read_header(
handle: u32,
header: *const u8,
header_len: u32,
buf: *mut u8,
buf_len: u32,
num: *mut u32,
) -> u32;

#[link_name = "http_read_body"]
pub(crate) fn http_read_body(handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32;

#[link_name = "http_close"]
pub(crate) fn http_close(handle: u32) -> u32;
}
9 changes: 5 additions & 4 deletions crates/apis/src/fetch_io/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
const responseObj = __javy_fetchio_request(url, data.buffer, data.byteOffset, data.byteLength);

// @TODO: Capture all response data from response object
const responseOk = true;
const responseOk = responseObj.ok;
const responseHeaders = {};
const responseBody = responseObj.body;

return new Promise((resolve, reject) => {
const response = {
url,
headers: responseHeaders,
ok: responseOk,
type: typeof responseObj === 'string' ? 'text' : 'json',
text: () => typeof responseObj === 'string' ? responseObj : JSON.stringify(responseObj),
json: () => typeof responseObj !== 'string' ? responseObj : JSON.parse(responseObj),
type: typeof responseBody === 'string' ? 'text' : 'json',
text: () => typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody),
json: () => typeof responseBody !== 'string' ? responseBody : JSON.parse(responseBody),
};

resolve(response);
Expand Down
Loading

0 comments on commit 77268ae

Please sign in to comment.