Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ export default {
return 0;
},

// Fetch.fetch(url, method, body) -> packed Uzumibi::Response
// Fetch.fetch(url, method, body, headers) -> packed Uzumibi::Response
// Format: u16 status | u16 headers_count | (u16 key_size, key, u16 value_size, value)... | u32 body_size | body
uzumibi_cf_fetch: async (
urlPtr, urlSize,
methodPtr, methodSize,
bodyPtr, bodySize,
headersPtr, headersSize,
resultPtr, resultMaxSize
) => {
const memory = exports.memory;
Expand All @@ -64,6 +65,28 @@ export default {
fetchOptions.body = body;
}

// Unpack request headers: u16 LE count, then (u16 LE key_size, key, u16 LE value_size, value) * count
if (headersSize >= 2) {
const hView = new DataView(memory.buffer, headersPtr, headersSize);
const hCount = hView.getUint16(0, true);
if (hCount > 0) {
const reqHeaders = {};
let hPos = 2;
for (let i = 0; i < hCount; i++) {
const kLen = hView.getUint16(hPos, true);
hPos += 2;
const k = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, kLen));
hPos += kLen;
const vLen = hView.getUint16(hPos, true);
hPos += 2;
const v = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, vLen));
hPos += vLen;
reqHeaders[k] = v;
}
fetchOptions.headers = reqHeaders;
}
}

const response = await fetch(url, fetchOptions);
const responseBody = await response.text();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ unsafe extern "C" {
method_size: usize,
body_ptr: *const u8,
body_size: usize,
headers_ptr: *const u8,
headers_size: usize,
result_ptr: *mut u8,
result_max_size: usize,
) -> i32;
Expand Down Expand Up @@ -82,7 +84,7 @@ fn debug_console_log_internal(message: &str) {
/// u32 LE body_size
/// body bytes
#[cfg(feature = "enable-external")]
fn cf_fetch(url: &str, method: &str, body: &str) -> Result<Vec<u8>, String> {
fn cf_fetch(url: &str, method: &str, body: &str, headers: &[u8]) -> Result<Vec<u8>, String> {
const BUFFER_SIZE: usize = 65536;
let mut buffer = vec![0u8; BUFFER_SIZE];

Expand All @@ -94,6 +96,8 @@ fn cf_fetch(url: &str, method: &str, body: &str) -> Result<Vec<u8>, String> {
method.len(),
body.as_ptr(),
body.len(),
headers.as_ptr(),
headers.len(),
buffer.as_mut_ptr(),
BUFFER_SIZE,
);
Expand Down Expand Up @@ -181,7 +185,7 @@ fn uzumibi_kernel_debug_console_log(
Ok(RObject::nil().to_refcount_assigned())
}

/// Fetch.fetch(url, method="GET", body="") -> Uzumibi::Response
/// Fetch.fetch(url, method="GET", body="", headers={}) -> Uzumibi::Response
#[cfg(feature = "enable-external")]
fn uzumibi_fetch_class_fetch(
vm: &mut VM,
Expand All @@ -207,13 +211,55 @@ fn uzumibi_fetch_class_fetch(
String::new()
};

let packed = cf_fetch(&url, &method, &body)
// Pack request headers from Hash (4th argument)
let packed_headers = if args.len() > 3 {
pack_headers_from_hash(vm, &args[3])?
} else {
vec![0u8; 2] // u16 LE count = 0
};

let packed = cf_fetch(&url, &method, &body, &packed_headers)
.map_err(|e| mrubyedge::Error::RuntimeError(format!("Fetch failed: {}", e)))?;

// Unpack the packed response into Uzumibi::Response
unpack_response_to_robject(vm, &packed)
}

/// Pack a mruby Hash into binary format for request headers:
/// u16 LE headers_count
/// (u16 LE key_size, key bytes, u16 LE value_size, value bytes) * count
#[cfg(feature = "enable-external")]
fn pack_headers_from_hash(
vm: &mut VM,
hash_obj: &Rc<RObject>,
) -> Result<Vec<u8>, mrubyedge::Error> {
match &hash_obj.as_ref().value {
RValue::Hash(h) => {
let hash = h.borrow();
let mut buf = Vec::new();
let count = hash.len() as u16;
buf.extend_from_slice(&count.to_le_bytes());
for (_, (key_obj, value_obj)) in hash.iter() {
let key = mrb_funcall(vm, key_obj.clone().into(), "to_s", &[])?;
let key: String = key.as_ref().try_into()?;
let value = mrb_funcall(vm, value_obj.clone().into(), "to_s", &[])?;
let value: String = value.as_ref().try_into()?;
buf.extend_from_slice(&(key.len() as u16).to_le_bytes());
buf.extend_from_slice(key.as_bytes());
buf.extend_from_slice(&(value.len() as u16).to_le_bytes());
buf.extend_from_slice(value.as_bytes());
}
Ok(buf)
}
RValue::Nil => {
Ok(vec![0u8; 2]) // u16 LE count = 0
}
_ => Err(mrubyedge::Error::RuntimeError(
"headers argument must be a Hash".to_string(),
)),
}
}

/// Unpack packed binary response into Uzumibi::Response mruby object
#[cfg(feature = "enable-external")]
fn unpack_response_to_robject(vm: &mut VM, buf: &[u8]) -> Result<Rc<RObject>, mrubyedge::Error> {
Expand Down
25 changes: 24 additions & 1 deletion uzumibi-cli/templates/cloudflare/__features__/queue/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ export default {
return 0;
},

// Fetch.fetch(url, method, body) -> packed Uzumibi::Response
// Fetch.fetch(url, method, body, headers) -> packed Uzumibi::Response
uzumibi_cf_fetch: async (
urlPtr, urlSize,
methodPtr, methodSize,
bodyPtr, bodySize,
headersPtr, headersSize,
resultPtr, resultMaxSize,
) => {
const memory = exports.memory;
Expand All @@ -67,6 +68,28 @@ export default {
fetchOptions.body = body;
}

// Unpack request headers: u16 LE count, then (u16 LE key_size, key, u16 LE value_size, value) * count
if (headersSize >= 2) {
const hView = new DataView(memory.buffer, headersPtr, headersSize);
const hCount = hView.getUint16(0, true);
if (hCount > 0) {
const reqHeaders = {};
let hPos = 2;
for (let i = 0; i < hCount; i++) {
const kLen = hView.getUint16(hPos, true);
hPos += 2;
const k = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, kLen));
hPos += kLen;
const vLen = hView.getUint16(hPos, true);
hPos += 2;
const v = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, vLen));
hPos += vLen;
reqHeaders[k] = v;
}
fetchOptions.headers = reqHeaders;
}
}

const response = await fetch(url, fetchOptions);
const responseBody = await response.text();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ unsafe extern "C" {
method_size: usize,
body_ptr: *const u8,
body_size: usize,
headers_ptr: *const u8,
headers_size: usize,
result_ptr: *mut u8,
result_max_size: usize,
) -> i32;
Expand Down Expand Up @@ -95,7 +97,7 @@ fn debug_console_log_internal(message: &str) {
/// u32 LE body_size
/// body bytes
#[cfg(feature = "enable-external")]
fn cf_fetch(url: &str, method: &str, body: &str) -> Result<Vec<u8>, String> {
fn cf_fetch(url: &str, method: &str, body: &str, headers: &[u8]) -> Result<Vec<u8>, String> {
const BUFFER_SIZE: usize = 65536;
let mut buffer = vec![0u8; BUFFER_SIZE];

Expand All @@ -107,6 +109,8 @@ fn cf_fetch(url: &str, method: &str, body: &str) -> Result<Vec<u8>, String> {
method.len(),
body.as_ptr(),
body.len(),
headers.as_ptr(),
headers.len(),
buffer.as_mut_ptr(),
BUFFER_SIZE,
);
Expand Down Expand Up @@ -194,7 +198,7 @@ fn uzumibi_kernel_debug_console_log(
Ok(RObject::nil().to_refcount_assigned())
}

/// Fetch.fetch(url, method="GET", body="") -> Uzumibi::Response
/// Fetch.fetch(url, method="GET", body="", headers={}) -> Uzumibi::Response
#[cfg(feature = "enable-external")]
fn uzumibi_fetch_class_fetch(
vm: &mut VM,
Expand All @@ -220,13 +224,55 @@ fn uzumibi_fetch_class_fetch(
String::new()
};

let packed = cf_fetch(&url, &method, &body)
// Pack request headers from Hash (4th argument)
let packed_headers = if args.len() > 3 {
pack_headers_from_hash(vm, &args[3])?
} else {
vec![0u8; 2] // u16 LE count = 0
};

let packed = cf_fetch(&url, &method, &body, &packed_headers)
.map_err(|e| mrubyedge::Error::RuntimeError(format!("Fetch failed: {}", e)))?;

// Unpack the packed response into Uzumibi::Response
unpack_response_to_robject(vm, &packed)
}

/// Pack a mruby Hash into binary format for request headers:
/// u16 LE headers_count
/// (u16 LE key_size, key bytes, u16 LE value_size, value bytes) * count
#[cfg(feature = "enable-external")]
fn pack_headers_from_hash(
vm: &mut VM,
hash_obj: &Rc<RObject>,
) -> Result<Vec<u8>, mrubyedge::Error> {
match &hash_obj.as_ref().value {
RValue::Hash(h) => {
let hash = h.borrow();
let mut buf = Vec::new();
let count = hash.len() as u16;
buf.extend_from_slice(&count.to_le_bytes());
for (_, (key_obj, value_obj)) in hash.iter() {
let key = mrb_funcall(vm, key_obj.clone().into(), "to_s", &[])?;
let key: String = key.as_ref().try_into()?;
let value = mrb_funcall(vm, value_obj.clone().into(), "to_s", &[])?;
let value: String = value.as_ref().try_into()?;
buf.extend_from_slice(&(key.len() as u16).to_le_bytes());
buf.extend_from_slice(key.as_bytes());
buf.extend_from_slice(&(value.len() as u16).to_le_bytes());
buf.extend_from_slice(value.as_bytes());
}
Ok(buf)
}
RValue::Nil => {
Ok(vec![0u8; 2]) // u16 LE count = 0
}
_ => Err(mrubyedge::Error::RuntimeError(
"headers argument must be a Hash".to_string(),
)),
}
}

/// Unpack packed binary response into Uzumibi::Response mruby object
#[cfg(feature = "enable-external")]
fn unpack_response_to_robject(vm: &mut VM, buf: &[u8]) -> Result<Rc<RObject>, mrubyedge::Error> {
Expand Down
3 changes: 1 addition & 2 deletions uzumibi-cli/tests/runn/help.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
desc: Test uzumibi CLI help command
vars:
binary: ${UZUMIBI_TEST_BINARY:-../target/release/uzumibi}
version: 0.6.0-rc2
steps:
build:
desc: Build release binary
Expand All @@ -28,7 +27,7 @@ steps:
version:
desc: Show version
exec:
command: "{{ vars.binary }} --version | grep {{ vars.version }}"
command: "{{ vars.binary }} --version"
test: |
current.exit_code == 0 &&
current.stdout contains "uzumibi"
25 changes: 24 additions & 1 deletion uzumibi-on-cloudflare-spike/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ export default {
return 0;
},

// Fetch.fetch(url, method, body) -> packed Uzumibi::Response
// Fetch.fetch(url, method, body, headers) -> packed Uzumibi::Response
// Format: u16 status | u16 headers_count | (u16 key_size, key, u16 value_size, value)... | u32 body_size | body
uzumibi_cf_fetch: async (
urlPtr, urlSize,
methodPtr, methodSize,
bodyPtr, bodySize,
headersPtr, headersSize,
resultPtr, resultMaxSize
) => {
const memory = exports.memory;
Expand All @@ -64,6 +65,28 @@ export default {
fetchOptions.body = body;
}

// Unpack request headers: u16 LE count, then (u16 LE key_size, key, u16 LE value_size, value) * count
if (headersSize >= 2) {
const hView = new DataView(memory.buffer, headersPtr, headersSize);
const hCount = hView.getUint16(0, true);
if (hCount > 0) {
const reqHeaders = {};
let hPos = 2;
for (let i = 0; i < hCount; i++) {
const kLen = hView.getUint16(hPos, true);
hPos += 2;
const k = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, kLen));
hPos += kLen;
const vLen = hView.getUint16(hPos, true);
hPos += 2;
const v = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, vLen));
hPos += vLen;
reqHeaders[k] = v;
}
fetchOptions.headers = reqHeaders;
}
}

const response = await fetch(url, fetchOptions);
const responseBody = await response.text();

Expand Down
25 changes: 24 additions & 1 deletion uzumibi-on-cloudflare-spike/src/index.queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ export default {
return 0;
},

// Fetch.fetch(url, method, body) -> packed Uzumibi::Response
// Fetch.fetch(url, method, body, headers) -> packed Uzumibi::Response
uzumibi_cf_fetch: async (
urlPtr, urlSize,
methodPtr, methodSize,
bodyPtr, bodySize,
headersPtr, headersSize,
resultPtr, resultMaxSize,
) => {
const memory = exports.memory;
Expand All @@ -67,6 +68,28 @@ export default {
fetchOptions.body = body;
}

// Unpack request headers: u16 LE count, then (u16 LE key_size, key, u16 LE value_size, value) * count
if (headersSize >= 2) {
const hView = new DataView(memory.buffer, headersPtr, headersSize);
const hCount = hView.getUint16(0, true);
if (hCount > 0) {
const reqHeaders = {};
let hPos = 2;
for (let i = 0; i < hCount; i++) {
const kLen = hView.getUint16(hPos, true);
hPos += 2;
const k = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, kLen));
hPos += kLen;
const vLen = hView.getUint16(hPos, true);
hPos += 2;
const v = decoder.decode(new Uint8Array(memory.buffer, headersPtr + hPos, vLen));
hPos += vLen;
reqHeaders[k] = v;
}
fetchOptions.headers = reqHeaders;
}
}

const response = await fetch(url, fetchOptions);
const responseBody = await response.text();

Expand Down
2 changes: 1 addition & 1 deletion uzumibi-on-cloudflare-spike/wasm-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ uzumibi-art-router = ">= 0.3.1"
mruby-compiler2-sys = ">= 0.3.0"

[features]
default = []
default = ["enable-external"]
enable-external = []
queue = ["enable-external"]
Loading
Loading