diff --git a/Cargo.lock b/Cargo.lock index 9c2bd430b..65508a939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3913,9 +3913,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wai-bindgen-gen-core" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd16a4dc5fb71faaa3d21530a1d004b7fc88f0bf70cda6f5c4099220254d947b" +checksum = "c43a21791697e899140bb82c27db1fac26ad28ef60eddca55f05e5ca365a6e4c" dependencies = [ "anyhow", "wai-parser", @@ -3923,9 +3923,9 @@ dependencies = [ [[package]] name = "wai-bindgen-gen-rust" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17625f823712dd6eff5656b4308842b7aeac9eca429f6eea36443d4305cadf7d" +checksum = "dd86fcd3046f63882137f46723c643b7cc33ea4c739c0f9999c6bd76f4ae21f2" dependencies = [ "heck 0.3.3", "wai-bindgen-gen-core", @@ -3933,9 +3933,20 @@ dependencies = [ [[package]] name = "wai-bindgen-gen-rust-wasm" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757fbff15d11d0578ee879cea209b111bd09f8b39346c7b212a7e7f52585d2f7" +checksum = "ab8850eeef5405252a368086f0b3e0eefef4ed47666f37ff9a321ecdcfc96376" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", +] + +[[package]] +name = "wai-bindgen-gen-wasmer" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f3b3d04d56cc09616cf8daab90b5601e0e894509f34c81a3829aab03f2b457" dependencies = [ "heck 0.3.3", "wai-bindgen-gen-core", @@ -3944,19 +3955,20 @@ dependencies = [ [[package]] name = "wai-bindgen-rust" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e6c91f681f714599326252f4ae8b9897293110055d25c576fa09ffb01661cd" +checksum = "de0eef889bfee6c2d1f21304b5e8c5786629827ae4f26c44e24d0e0ded80f6cd" dependencies = [ + "async-trait", "bitflags", "wai-bindgen-rust-impl", ] [[package]] name = "wai-bindgen-rust-impl" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb36c407339837a5e1fcd9b41641fbe36488fc53ab0664add14d5f7105f6fbbb" +checksum = "6d3c00bda01d3cf02e9a0a6c7e6b738a6da5cbdefa92f979486f7968b74f290d" dependencies = [ "proc-macro2", "syn", @@ -3965,10 +3977,37 @@ dependencies = [ ] [[package]] -name = "wai-parser" -version = "0.2.1" +name = "wai-bindgen-wasmer" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a24cfad91494925ba41c6a03bd7d092eb3c3cdbccf4ef680b06831030bd7a69" +checksum = "b69eca47105340af3e4dc5ba47951a23534d7139a62b12550cc6702a5cb6ab70" +dependencies = [ + "anyhow", + "bitflags", + "once_cell", + "thiserror", + "tracing", + "wai-bindgen-wasmer-impl", + "wasmer", +] + +[[package]] +name = "wai-bindgen-wasmer-impl" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaf3a72820d1708185cdd26eafe034e3f88d740bae27b5d852379d242d9ff68" +dependencies = [ + "proc-macro2", + "syn", + "wai-bindgen-gen-core", + "wai-bindgen-gen-wasmer", +] + +[[package]] +name = "wai-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2811f0b0c8883f92b9c616b6c38a1b462dc37d04cfc700be60e4990497ed1f7d" dependencies = [ "anyhow", "id-arena", @@ -4048,6 +4087,15 @@ dependencies = [ "wast 24.0.0", ] +[[package]] +name = "wasix_http_client" +version = "0.1.0" +dependencies = [ + "anyhow", + "http", + "wai-bindgen-rust", +] + [[package]] name = "wasm-bindgen" version = "0.2.83" @@ -4664,6 +4712,7 @@ dependencies = [ "generational-arena", "getrandom", "hex", + "http", "lazy_static", "libc", "linked_hash_set", @@ -4734,6 +4783,7 @@ dependencies = [ name = "wasmer-wasi-types" version = "3.0.2" dependencies = [ + "anyhow", "bitflags", "byteorder", "cfg-if 1.0.0", @@ -4745,6 +4795,7 @@ dependencies = [ "wai-bindgen-gen-rust", "wai-bindgen-gen-rust-wasm", "wai-bindgen-rust", + "wai-bindgen-wasmer", "wai-parser", "wasmer", "wasmer-derive", diff --git a/Cargo.toml b/Cargo.toml index 4530831e5..c6f527adc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ members = [ "lib/wasi-types", "lib/wasi-experimental-io-devices", "lib/wasi-local-networking", + "lib/wasix/wasix-http-client", "lib/c-api/tests/wasmer-c-api-test-runner", "lib/c-api/examples/wasmer-capi-examples-runner", "lib/types", diff --git a/lib/wasix/wasix-http-client/Cargo.toml b/lib/wasix/wasix-http-client/Cargo.toml new file mode 100644 index 000000000..bc89db42e --- /dev/null +++ b/lib/wasix/wasix-http-client/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasix_http_client" +description = "Wasix bindings library for Webassembly." +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.66" +http = "0.2.8" +wai-bindgen-rust = "0.2.2" diff --git a/lib/wasix/wasix-http-client/examples/http_client.rs b/lib/wasix/wasix-http-client/examples/http_client.rs new file mode 100644 index 000000000..ad2fb8886 --- /dev/null +++ b/lib/wasix/wasix-http-client/examples/http_client.rs @@ -0,0 +1,20 @@ +use wasix_http_client::{Body, HttpClient, RequestBuilder}; + +fn main() { + let c = HttpClient::new().unwrap(); + let r = RequestBuilder::new() + .uri("http://ferris2.christoph.app.wapm.dev/http-client-test") + .body(Body::empty()) + .unwrap(); + eprintln!("fetching: {r:?}"); + + let res = c.send(r).unwrap(); + dbg!(&res); + assert!(res.status().is_success()); + + let body = res.into_body().read_all().unwrap(); + let s = String::from_utf8(body).unwrap(); + eprintln!("Response body: {s}"); + + assert!(s.contains("http-client-test")); +} diff --git a/lib/wasix/wasix-http-client/src/lib.rs b/lib/wasix/wasix-http-client/src/lib.rs new file mode 100644 index 000000000..f7f5ac1da --- /dev/null +++ b/lib/wasix/wasix-http-client/src/lib.rs @@ -0,0 +1,140 @@ +// Auto-generated sys bindings. +#[allow(dead_code)] +mod wasix_http_client_v1; + +use anyhow::bail; + +use crate::wasix_http_client_v1 as sys; + +pub use http::{header, Method, StatusCode}; + +#[derive(Clone)] +pub struct HttpClient { + client: sys::Client, +} + +// TODO: use proper custom error type +impl HttpClient { + pub fn new() -> Result { + let client = sys::Client::new().map_err(anyhow::Error::msg)?; + Ok(Self { client }) + } + + pub fn send(&self, request: Request) -> Result { + let (parts, body) = request.into_parts(); + + let uri = parts.uri.to_string(); + let method = match &parts.method { + m if m == Method::GET => sys::Method::Get, + m if m == Method::HEAD => sys::Method::Head, + m if m == Method::POST => sys::Method::Post, + m if m == Method::PUT => sys::Method::Put, + m if m == Method::DELETE => sys::Method::Delete, + m if m == Method::CONNECT => sys::Method::Connect, + m if m == Method::OPTIONS => sys::Method::Options, + m if m == Method::TRACE => sys::Method::Trace, + m if m == Method::PATCH => sys::Method::Patch, + m => sys::Method::Other(m.as_str()), + }; + + let headers: Vec<_> = parts + .headers + .iter() + .map(|(key, val)| sys::HeaderParam { + key: key.as_str(), + value: val.as_bytes(), + }) + .collect(); + + let body = match &body.0 { + BodyInner::Data(d) => Some(sys::BodyParam::Data(&d)), + }; + + let req = sys::Request { + url: &uri, + method, + headers: &headers, + body, + timeout: None, + redirect_policy: None, + }; + + let res = self.client.send(req).map_err(anyhow::Error::msg)?; + + let body = match res.body { + sys::BodyResult::Data(d) => Body::new_data(d), + sys::BodyResult::Fd(_) => { + bail!("Received file descriptor response body, which is not suppported yet"); + } + }; + + let mut builder = http::Response::builder().status(res.status); + for param in res.headers { + builder = builder.header(param.key, param.value); + } + builder.body(body).map_err(anyhow::Error::from) + } +} + +// TODO: use custom request which can also hold timeout config, etc. +// #[derive(Clone, Debug)] +// pub struct Request { +// pub method: Method, +// pub headers: http::HeaderMap, +// } + +pub type Request = http::Request; +pub type Response = http::Response; + +#[derive(Debug)] +pub struct Body(BodyInner); + +impl Body { + pub fn empty() -> Self { + Self::new_data(Vec::::new()) + } + + pub fn new_data(data: I) -> Self + where + I: Into>, + { + Self(BodyInner::Data(data.into())) + } + + pub fn read_all(self) -> Result, anyhow::Error> { + match self.0 { + BodyInner::Data(d) => Ok(d), + } + } +} + +#[derive(Debug)] +pub enum BodyInner { + Data(Vec), +} + +impl From> for Body { + fn from(value: Vec) -> Self { + Self(BodyInner::Data(value)) + } +} + +impl<'a> From<&'a [u8]> for Body { + fn from(value: &'a [u8]) -> Self { + Self(BodyInner::Data(value.to_vec())) + } +} + +impl From for Body { + fn from(value: String) -> Self { + Self(BodyInner::Data(value.into_bytes())) + } +} + +impl<'a> From<&'a str> for Body { + fn from(value: &'a str) -> Self { + Self(BodyInner::Data(value.as_bytes().to_vec())) + } +} + +pub type RequestBuilder = http::request::Builder; diff --git a/lib/wasix/wasix-http-client/src/wasix_http_client_v1.rs b/lib/wasix/wasix-http-client/src/wasix_http_client_v1.rs new file mode 100644 index 000000000..59a5b029e --- /dev/null +++ b/lib/wasix/wasix-http-client/src/wasix_http_client_v1.rs @@ -0,0 +1,514 @@ +#[derive(Clone)] +pub enum Method<'a> { + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch, + Other(&'a str), +} +impl<'a> core::fmt::Debug for Method<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Method::Get => f.debug_tuple("Method::Get").finish(), + Method::Head => f.debug_tuple("Method::Head").finish(), + Method::Post => f.debug_tuple("Method::Post").finish(), + Method::Put => f.debug_tuple("Method::Put").finish(), + Method::Delete => f.debug_tuple("Method::Delete").finish(), + Method::Connect => f.debug_tuple("Method::Connect").finish(), + Method::Options => f.debug_tuple("Method::Options").finish(), + Method::Trace => f.debug_tuple("Method::Trace").finish(), + Method::Patch => f.debug_tuple("Method::Patch").finish(), + Method::Other(e) => f.debug_tuple("Method::Other").field(e).finish(), + } + } +} +#[derive(Clone)] +pub struct HeaderParam<'a> { + pub key: &'a str, + pub value: &'a [u8], +} +impl<'a> core::fmt::Debug for HeaderParam<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("HeaderParam") + .field("key", &self.key) + .field("value", &self.value) + .finish() + } +} +#[derive(Clone)] +pub struct HeaderResult { + pub key: String, + pub value: Vec, +} +impl core::fmt::Debug for HeaderResult { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("HeaderResult") + .field("key", &self.key) + .field("value", &self.value) + .finish() + } +} +pub type HeaderListParam<'a> = &'a [HeaderParam<'a>]; +pub type HeaderListResult = Vec; +pub type Fd = u32; +pub type TimeoutMs = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RedirectFollow { + pub max: u32, +} +impl core::fmt::Debug for RedirectFollow { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("RedirectFollow") + .field("max", &self.max) + .finish() + } +} +#[derive(Clone, Copy)] +pub enum RedirectPolicy { + NoFollow, + Follow(RedirectFollow), +} +impl core::fmt::Debug for RedirectPolicy { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + RedirectPolicy::NoFollow => f.debug_tuple("RedirectPolicy::NoFollow").finish(), + RedirectPolicy::Follow(e) => f.debug_tuple("RedirectPolicy::Follow").field(e).finish(), + } + } +} +#[derive(Clone)] +pub enum BodyParam<'a> { + Data(&'a [u8]), + Fd(Fd), +} +impl<'a> core::fmt::Debug for BodyParam<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BodyParam::Data(e) => f.debug_tuple("BodyParam::Data").field(e).finish(), + BodyParam::Fd(e) => f.debug_tuple("BodyParam::Fd").field(e).finish(), + } + } +} +#[derive(Clone)] +pub enum BodyResult { + Data(Vec), + Fd(Fd), +} +impl core::fmt::Debug for BodyResult { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BodyResult::Data(e) => f.debug_tuple("BodyResult::Data").field(e).finish(), + BodyResult::Fd(e) => f.debug_tuple("BodyResult::Fd").field(e).finish(), + } + } +} +#[derive(Clone)] +pub struct Request<'a> { + pub url: &'a str, + pub method: Method<'a>, + pub headers: HeaderListParam<'a>, + pub body: Option>, + pub timeout: Option, + pub redirect_policy: Option, +} +impl<'a> core::fmt::Debug for Request<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Request") + .field("url", &self.url) + .field("method", &self.method) + .field("headers", &self.headers) + .field("body", &self.body) + .field("timeout", &self.timeout) + .field("redirect-policy", &self.redirect_policy) + .finish() + } +} +#[derive(Clone)] +pub struct Response { + pub status: u16, + pub headers: HeaderListResult, + pub body: BodyResult, + pub redirect_urls: Option>, +} +impl core::fmt::Debug for Response { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Response") + .field("status", &self.status) + .field("headers", &self.headers) + .field("body", &self.body) + .field("redirect-urls", &self.redirect_urls) + .finish() + } +} +#[derive(Debug)] +#[repr(transparent)] +pub struct Client(i32); +impl Client { + pub unsafe fn from_raw(raw: i32) -> Self { + Self(raw) + } + + pub fn into_raw(self) -> i32 { + let ret = self.0; + core::mem::forget(self); + return ret; + } + + pub fn as_raw(&self) -> i32 { + self.0 + } +} +impl Drop for Client { + fn drop(&mut self) { + #[link(wasm_import_module = "canonical_abi")] + extern "C" { + #[link_name = "resource_drop_client"] + fn close(fd: i32); + } + unsafe { + close(self.0); + } + } +} +impl Clone for Client { + fn clone(&self) -> Self { + #[link(wasm_import_module = "canonical_abi")] + extern "C" { + #[link_name = "resource_clone_client"] + fn clone(val: i32) -> i32; + } + unsafe { Self(clone(self.0)) } + } +} +impl Client { + pub fn new() -> Result { + unsafe { + let ptr0 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32; + #[link(wasm_import_module = "wasix_http_client_v1")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "client::new")] + #[cfg_attr( + not(target_arch = "wasm32"), + link_name = "wasix_http_client_v1_client::new" + )] + fn wai_import(_: i32); + } + wai_import(ptr0); + match i32::from(*((ptr0 + 0) as *const u8)) { + 0 => Ok(Client(*((ptr0 + 4) as *const i32))), + 1 => Err({ + let len1 = *((ptr0 + 8) as *const i32) as usize; + + String::from_utf8(Vec::from_raw_parts( + *((ptr0 + 4) as *const i32) as *mut _, + len1, + len1, + )) + .unwrap() + }), + _ => panic!("invalid enum discriminant"), + } + } + } +} +impl Client { + pub fn send(&self, request: Request<'_>) -> Result { + unsafe { + let ptr0 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32; + *((ptr0 + 0) as *mut i32) = self.0; + let Request { + url: url1, + method: method1, + headers: headers1, + body: body1, + timeout: timeout1, + redirect_policy: redirect_policy1, + } = request; + let vec2 = url1; + let ptr2 = vec2.as_ptr() as i32; + let len2 = vec2.len() as i32; + *((ptr0 + 8) as *mut i32) = len2; + *((ptr0 + 4) as *mut i32) = ptr2; + match method1 { + Method::Get => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (0i32) as u8; + let () = e; + } + } + Method::Head => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (1i32) as u8; + let () = e; + } + } + Method::Post => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (2i32) as u8; + let () = e; + } + } + Method::Put => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (3i32) as u8; + let () = e; + } + } + Method::Delete => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (4i32) as u8; + let () = e; + } + } + Method::Connect => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (5i32) as u8; + let () = e; + } + } + Method::Options => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (6i32) as u8; + let () = e; + } + } + Method::Trace => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (7i32) as u8; + let () = e; + } + } + Method::Patch => { + let e = (); + { + *((ptr0 + 12) as *mut u8) = (8i32) as u8; + let () = e; + } + } + Method::Other(e) => { + *((ptr0 + 12) as *mut u8) = (9i32) as u8; + let vec3 = e; + let ptr3 = vec3.as_ptr() as i32; + let len3 = vec3.len() as i32; + *((ptr0 + 20) as *mut i32) = len3; + *((ptr0 + 16) as *mut i32) = ptr3; + } + }; + let vec7 = headers1; + let len7 = vec7.len() as i32; + let layout7 = core::alloc::Layout::from_size_align_unchecked(vec7.len() * 16, 4); + let result7 = std::alloc::alloc(layout7); + if result7.is_null() { + std::alloc::handle_alloc_error(layout7); + } + for (i, e) in vec7.into_iter().enumerate() { + let base = result7 as i32 + (i as i32) * 16; + { + let HeaderParam { + key: key4, + value: value4, + } = e; + let vec5 = key4; + let ptr5 = vec5.as_ptr() as i32; + let len5 = vec5.len() as i32; + *((base + 4) as *mut i32) = len5; + *((base + 0) as *mut i32) = ptr5; + let vec6 = value4; + let ptr6 = vec6.as_ptr() as i32; + let len6 = vec6.len() as i32; + *((base + 12) as *mut i32) = len6; + *((base + 8) as *mut i32) = ptr6; + } + } + *((ptr0 + 28) as *mut i32) = len7; + *((ptr0 + 24) as *mut i32) = result7 as i32; + match body1 { + Some(e) => { + *((ptr0 + 32) as *mut u8) = (1i32) as u8; + match e { + BodyParam::Data(e) => { + *((ptr0 + 36) as *mut u8) = (0i32) as u8; + let vec8 = e; + let ptr8 = vec8.as_ptr() as i32; + let len8 = vec8.len() as i32; + *((ptr0 + 44) as *mut i32) = len8; + *((ptr0 + 40) as *mut i32) = ptr8; + } + BodyParam::Fd(e) => { + *((ptr0 + 36) as *mut u8) = (1i32) as u8; + *((ptr0 + 40) as *mut i32) = wai_bindgen_rust::rt::as_i32(e); + } + }; + } + None => { + let e = (); + { + *((ptr0 + 32) as *mut u8) = (0i32) as u8; + let () = e; + } + } + }; + match timeout1 { + Some(e) => { + *((ptr0 + 48) as *mut u8) = (1i32) as u8; + *((ptr0 + 52) as *mut i32) = wai_bindgen_rust::rt::as_i32(e); + } + None => { + let e = (); + { + *((ptr0 + 48) as *mut u8) = (0i32) as u8; + let () = e; + } + } + }; + match redirect_policy1 { + Some(e) => { + *((ptr0 + 56) as *mut u8) = (1i32) as u8; + match e { + RedirectPolicy::NoFollow => { + let e = (); + { + *((ptr0 + 60) as *mut u8) = (0i32) as u8; + let () = e; + } + } + RedirectPolicy::Follow(e) => { + *((ptr0 + 60) as *mut u8) = (1i32) as u8; + let RedirectFollow { max: max9 } = e; + *((ptr0 + 64) as *mut i32) = wai_bindgen_rust::rt::as_i32(max9); + } + }; + } + None => { + let e = (); + { + *((ptr0 + 56) as *mut u8) = (0i32) as u8; + let () = e; + } + } + }; + let ptr10 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32; + #[link(wasm_import_module = "wasix_http_client_v1")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "client::send")] + #[cfg_attr( + not(target_arch = "wasm32"), + link_name = "wasix_http_client_v1_client::send" + )] + fn wai_import(_: i32, _: i32); + } + wai_import(ptr0, ptr10); + std::alloc::dealloc(result7, layout7); + match i32::from(*((ptr10 + 0) as *const u8)) { + 0 => Ok({ + let base13 = *((ptr10 + 8) as *const i32); + let len13 = *((ptr10 + 12) as *const i32); + let mut result13 = Vec::with_capacity(len13 as usize); + for i in 0..len13 { + let base = base13 + i * 16; + result13.push({ + let len11 = *((base + 4) as *const i32) as usize; + let len12 = *((base + 12) as *const i32) as usize; + + HeaderResult { + key: String::from_utf8(Vec::from_raw_parts( + *((base + 0) as *const i32) as *mut _, + len11, + len11, + )) + .unwrap(), + value: Vec::from_raw_parts( + *((base + 8) as *const i32) as *mut _, + len12, + len12, + ), + } + }); + } + std::alloc::dealloc( + base13 as *mut _, + std::alloc::Layout::from_size_align_unchecked((len13 as usize) * 16, 4), + ); + + Response { + status: i32::from(*((ptr10 + 4) as *const u16)) as u16, + headers: result13, + body: match i32::from(*((ptr10 + 16) as *const u8)) { + 0 => BodyResult::Data({ + let len14 = *((ptr10 + 24) as *const i32) as usize; + + Vec::from_raw_parts( + *((ptr10 + 20) as *const i32) as *mut _, + len14, + len14, + ) + }), + 1 => BodyResult::Fd(*((ptr10 + 20) as *const i32) as u32), + _ => panic!("invalid enum discriminant"), + }, + redirect_urls: match i32::from(*((ptr10 + 28) as *const u8)) { + 0 => None, + 1 => Some({ + let base16 = *((ptr10 + 32) as *const i32); + let len16 = *((ptr10 + 36) as *const i32); + let mut result16 = Vec::with_capacity(len16 as usize); + for i in 0..len16 { + let base = base16 + i * 8; + result16.push({ + let len15 = *((base + 4) as *const i32) as usize; + + String::from_utf8(Vec::from_raw_parts( + *((base + 0) as *const i32) as *mut _, + len15, + len15, + )) + .unwrap() + }); + } + std::alloc::dealloc( + base16 as *mut _, + std::alloc::Layout::from_size_align_unchecked( + (len16 as usize) * 8, + 4, + ), + ); + + result16 + }), + _ => panic!("invalid enum discriminant"), + }, + } + }), + 1 => Err({ + let len17 = *((ptr10 + 8) as *const i32) as usize; + + String::from_utf8(Vec::from_raw_parts( + *((ptr10 + 4) as *const i32) as *mut _, + len17, + len17, + )) + .unwrap() + }), + _ => panic!("invalid enum discriminant"), + } + } + } +} + +#[repr(align(4))] +struct RetArea([u8; 68]); +static mut WASIX_HTTP_CLIENT_V1_RET_AREA: RetArea = RetArea([0; 68]);