1use crate::error::HttpErrorKind;
2use json::JsonValue;
3use std::{cmp::Ordering, collections::BTreeMap};
4
5#[cfg(not(feature = "mock-ffi"))]
6#[link(wasm_import_module = "blockless_http")]
7extern "C" {
8 #[link_name = "http_req"]
9 pub(crate) fn http_open(
10 url: *const u8,
11 url_len: u32,
12 opts: *const u8,
13 opts_len: u32,
14 fd: *mut u32,
15 status: *mut u32,
16 ) -> u32;
17
18 #[link_name = "http_read_header"]
19 pub(crate) fn http_read_header(
20 handle: u32,
21 header: *const u8,
22 header_len: u32,
23 buf: *mut u8,
24 buf_len: u32,
25 num: *mut u32,
26 ) -> u32;
27
28 #[link_name = "http_read_body"]
29 pub(crate) fn http_read_body(handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32;
30
31 #[link_name = "http_close"]
32 pub(crate) fn http_close(handle: u32) -> u32;
33}
34
35#[cfg(feature = "mock-ffi")]
36#[allow(unused_variables)]
37mod mock_ffi {
38
39 pub unsafe fn http_open(
40 _url: *const u8,
41 _url_len: u32,
42 _opts: *const u8,
43 _opts_len: u32,
44 fd: *mut u32,
45 status: *mut u32,
46 ) -> u32 {
47 unimplemented!()
48 }
49
50 pub unsafe fn http_read_header(
51 _handle: u32,
52 _header: *const u8,
53 _header_len: u32,
54 buf: *mut u8,
55 buf_len: u32,
56 num: *mut u32,
57 ) -> u32 {
58 unimplemented!()
59 }
60
61 pub unsafe fn http_read_body(_handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32 {
62 unimplemented!()
63 }
64
65 pub unsafe fn http_close(_handle: u32) -> u32 {
66 unimplemented!()
67 }
68}
69
70#[cfg(feature = "mock-ffi")]
71use mock_ffi::*;
72
73type Handle = u32;
74type ExitCode = u32;
75
76pub struct BlocklessHttp {
77 inner: Handle,
78 code: ExitCode,
79}
80
81pub struct HttpOptions {
82 pub method: String,
83 pub connect_timeout: u32,
84 pub read_timeout: u32,
85 pub body: Option<String>,
86 pub headers: Option<BTreeMap<String, String>>,
87}
88
89impl HttpOptions {
90 pub fn new(method: &str, connect_timeout: u32, read_timeout: u32) -> Self {
91 HttpOptions {
92 method: method.into(),
93 connect_timeout,
94 read_timeout,
95 body: None,
96 headers: None,
97 }
98 }
99
100 pub fn dump(&self) -> String {
101 let mut headers_str = self
103 .headers
104 .clone()
105 .unwrap_or_default()
106 .iter()
107 .map(|(k, v)| format!("\"{}\":\"{}\"", k, v))
108 .collect::<Vec<String>>()
109 .join(",");
110 headers_str = format!("{{{}}}", headers_str);
111
112 let mut json = JsonValue::new_object();
113 json["method"] = self.method.clone().into();
114 json["connectTimeout"] = self.connect_timeout.into();
115 json["readTimeout"] = self.read_timeout.into();
116 json["headers"] = headers_str.into();
117 json["body"] = self.body.clone().into();
118 json.dump()
119 }
120}
121
122impl BlocklessHttp {
123 pub fn open(url: &str, opts: &HttpOptions) -> Result<Self, HttpErrorKind> {
124 let opts = opts.dump();
125 let mut fd = 0;
126 let mut status = 0;
127 let rs = unsafe {
128 http_open(
129 url.as_ptr(),
130 url.len() as _,
131 opts.as_ptr(),
132 opts.len() as _,
133 &mut fd,
134 &mut status,
135 )
136 };
137 if rs != 0 {
138 return Err(HttpErrorKind::from(rs));
139 }
140 Ok(Self {
141 inner: fd,
142 code: status,
143 })
144 }
145
146 pub fn get_code(&self) -> ExitCode {
147 self.code
148 }
149
150 pub fn get_all_body(&self) -> Result<Vec<u8>, HttpErrorKind> {
151 let mut vec = Vec::new();
152 loop {
153 let mut buf = [0u8; 1024];
154 let mut num: u32 = 0;
155 let rs =
156 unsafe { http_read_body(self.inner, buf.as_mut_ptr(), buf.len() as _, &mut num) };
157 if rs != 0 {
158 return Err(HttpErrorKind::from(rs));
159 }
160
161 match num.cmp(&0) {
162 Ordering::Greater => vec.extend_from_slice(&buf[0..num as _]),
163 _ => break,
164 }
165 }
166 Ok(vec)
167 }
168
169 pub fn get_header(&self, header: &str) -> Result<String, HttpErrorKind> {
170 let mut vec = Vec::new();
171 loop {
172 let mut buf = [0u8; 1024];
173 let mut num: u32 = 0;
174 let rs = unsafe {
175 http_read_header(
176 self.inner,
177 header.as_ptr(),
178 header.len() as _,
179 buf.as_mut_ptr(),
180 buf.len() as _,
181 &mut num,
182 )
183 };
184 if rs != 0 {
185 return Err(HttpErrorKind::from(rs));
186 }
187 match num.cmp(&0) {
188 Ordering::Greater => vec.extend_from_slice(&buf[0..num as _]),
189 _ => break,
190 }
191 }
192 String::from_utf8(vec).map_err(|_| HttpErrorKind::Utf8Error)
193 }
194
195 pub fn close(self) {
196 unsafe {
197 http_close(self.inner);
198 }
199 }
200
201 pub fn read_body(&self, buf: &mut [u8]) -> Result<u32, HttpErrorKind> {
202 let mut num: u32 = 0;
203 let rs = unsafe { http_read_body(self.inner, buf.as_mut_ptr(), buf.len() as _, &mut num) };
204 if rs != 0 {
205 return Err(HttpErrorKind::from(rs));
206 }
207 Ok(num)
208 }
209}
210
211impl Drop for BlocklessHttp {
212 fn drop(&mut self) {
213 unsafe {
214 http_close(self.inner);
215 }
216 }
217}