@@ -57,6 +57,8 @@ unsafe extern "C" {
5757 method_size : usize ,
5858 body_ptr : * const u8 ,
5959 body_size : usize ,
60+ headers_ptr : * const u8 ,
61+ headers_size : usize ,
6062 result_ptr : * mut u8 ,
6163 result_max_size : usize ,
6264 ) -> i32 ;
@@ -95,7 +97,7 @@ fn debug_console_log_internal(message: &str) {
9597/// u32 LE body_size
9698/// body bytes
9799#[ cfg( feature = "enable-external" ) ]
98- fn cf_fetch ( url : & str , method : & str , body : & str ) -> Result < Vec < u8 > , String > {
100+ fn cf_fetch ( url : & str , method : & str , body : & str , headers : & [ u8 ] ) -> Result < Vec < u8 > , String > {
99101 const BUFFER_SIZE : usize = 65536 ;
100102 let mut buffer = vec ! [ 0u8 ; BUFFER_SIZE ] ;
101103
@@ -107,6 +109,8 @@ fn cf_fetch(url: &str, method: &str, body: &str) -> Result<Vec<u8>, String> {
107109 method. len ( ) ,
108110 body. as_ptr ( ) ,
109111 body. len ( ) ,
112+ headers. as_ptr ( ) ,
113+ headers. len ( ) ,
110114 buffer. as_mut_ptr ( ) ,
111115 BUFFER_SIZE ,
112116 ) ;
@@ -194,7 +198,7 @@ fn uzumibi_kernel_debug_console_log(
194198 Ok ( RObject :: nil ( ) . to_refcount_assigned ( ) )
195199}
196200
197- /// Fetch.fetch(url, method="GET", body="") -> Uzumibi::Response
201+ /// Fetch.fetch(url, method="GET", body="", headers={} ) -> Uzumibi::Response
198202#[ cfg( feature = "enable-external" ) ]
199203fn uzumibi_fetch_class_fetch (
200204 vm : & mut VM ,
@@ -220,13 +224,55 @@ fn uzumibi_fetch_class_fetch(
220224 String :: new ( )
221225 } ;
222226
223- let packed = cf_fetch ( & url, & method, & body)
227+ // Pack request headers from Hash (4th argument)
228+ let packed_headers = if args. len ( ) > 3 {
229+ pack_headers_from_hash ( vm, & args[ 3 ] ) ?
230+ } else {
231+ vec ! [ 0u8 ; 2 ] // u16 LE count = 0
232+ } ;
233+
234+ let packed = cf_fetch ( & url, & method, & body, & packed_headers)
224235 . map_err ( |e| mrubyedge:: Error :: RuntimeError ( format ! ( "Fetch failed: {}" , e) ) ) ?;
225236
226237 // Unpack the packed response into Uzumibi::Response
227238 unpack_response_to_robject ( vm, & packed)
228239}
229240
241+ /// Pack a mruby Hash into binary format for request headers:
242+ /// u16 LE headers_count
243+ /// (u16 LE key_size, key bytes, u16 LE value_size, value bytes) * count
244+ #[ cfg( feature = "enable-external" ) ]
245+ fn pack_headers_from_hash (
246+ vm : & mut VM ,
247+ hash_obj : & Rc < RObject > ,
248+ ) -> Result < Vec < u8 > , mrubyedge:: Error > {
249+ match & hash_obj. as_ref ( ) . value {
250+ RValue :: Hash ( h) => {
251+ let hash = h. borrow ( ) ;
252+ let mut buf = Vec :: new ( ) ;
253+ let count = hash. len ( ) as u16 ;
254+ buf. extend_from_slice ( & count. to_le_bytes ( ) ) ;
255+ for ( _, ( key_obj, value_obj) ) in hash. iter ( ) {
256+ let key = mrb_funcall ( vm, key_obj. clone ( ) . into ( ) , "to_s" , & [ ] ) ?;
257+ let key: String = key. as_ref ( ) . try_into ( ) ?;
258+ let value = mrb_funcall ( vm, value_obj. clone ( ) . into ( ) , "to_s" , & [ ] ) ?;
259+ let value: String = value. as_ref ( ) . try_into ( ) ?;
260+ buf. extend_from_slice ( & ( key. len ( ) as u16 ) . to_le_bytes ( ) ) ;
261+ buf. extend_from_slice ( key. as_bytes ( ) ) ;
262+ buf. extend_from_slice ( & ( value. len ( ) as u16 ) . to_le_bytes ( ) ) ;
263+ buf. extend_from_slice ( value. as_bytes ( ) ) ;
264+ }
265+ Ok ( buf)
266+ }
267+ RValue :: Nil => {
268+ Ok ( vec ! [ 0u8 ; 2 ] ) // u16 LE count = 0
269+ }
270+ _ => Err ( mrubyedge:: Error :: RuntimeError (
271+ "headers argument must be a Hash" . to_string ( ) ,
272+ ) ) ,
273+ }
274+ }
275+
230276/// Unpack packed binary response into Uzumibi::Response mruby object
231277#[ cfg( feature = "enable-external" ) ]
232278fn unpack_response_to_robject ( vm : & mut VM , buf : & [ u8 ] ) -> Result < Rc < RObject > , mrubyedge:: Error > {
0 commit comments