blockless_sdk/
http.rs

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