-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathfetch.native.mbt
More file actions
117 lines (107 loc) · 2.77 KB
/
fetch.native.mbt
File metadata and controls
117 lines (107 loc) · 2.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
///|
#owned(url, http_method_c, body, headers, timeout_ms)
extern "c" fn fetch_ffi(
url : @native.CStr,
http_method_c : @native.CStr,
body : @native.CStr,
headers : @native.CStr,
timeout_ms : Int,
) -> Int = "mocket_fetch"
///|
extern "c" fn fetch_status_ffi() -> Int = "mocket_fetch_status"
///|
extern "c" fn fetch_headers_ffi() -> @native.CStr = "mocket_fetch_headers"
///|
extern "c" fn fetch_error_ffi() -> @native.CStr = "mocket_fetch_error"
///|
extern "c" fn fetch_body_len_ffi() -> Int = "mocket_fetch_body_len"
///|
#owned(dst, max_len)
extern "c" fn fetch_body_copy_ffi(dst : Bytes, max_len : Int) -> Int = "mocket_fetch_body_copy"
///|
fn headers_to_lines(headers : Map[String, String]) -> String {
let buf = @buffer.new()
headers.each(fn(k, v) {
buf.write_string(k)
buf.write_string(": ")
buf.write_string(v)
buf.write_string("\r\n")
})
buf.to_string()
}
///|
fn parse_headers_native(headers_text : String) -> Map[StringView, StringView] {
let headers : Map[StringView, StringView] = {}
headers_text
.split("\n")
.each(line => {
let line = line.trim()
if line != "" {
if line.find(":") is Some(idx) {
let key = line[:idx].trim()
let value = line[idx + 1:].trim()
if key != "" {
headers.set(key, value)
}
}
}
})
headers
}
///|
fn fetch_body_native() -> Bytes {
let body_len = fetch_body_len_ffi()
if body_len <= 0 {
return b""
}
let arr = Array::make(body_len, b'\x00')
let buf = Bytes::from_array(arr)
let copied_len = fetch_body_copy_ffi(buf, body_len)
buf[:copied_len].to_bytes()
}
///|
priv struct NativeFetchResult {
status_code : Int
headers : Map[StringView, StringView]
body : Bytes
}
///|
pub async fn fetch(
url : String,
body? : String,
http_method : HttpMethod,
data? : &Responder,
headers? : Map[String, String],
credentials? : FetchCredentials,
mode? : FetchMode,
) -> HttpResponse raise Error {
ignore(credentials)
ignore(mode)
let (req_body, req_headers) = prepare_fetch_payload(body, data, headers)
let method_str = http_method.to_string()
let body_str = req_body.unwrap_or("")
let headers_str = headers_to_lines(req_headers)
let result : NativeFetchResult = suspend(fn(ok_cb, reject) {
let ret = fetch_ffi(
to_cstr(url),
to_cstr(method_str),
to_cstr(body_str),
to_cstr(headers_str),
30000,
)
if ret != 0 {
reject(FetchError::RequestFailed(from_cstr(fetch_error_ffi())))
return
}
ok_cb({
status_code: fetch_status_ffi(),
headers: parse_headers_native(from_cstr(fetch_headers_ffi())),
body: fetch_body_native(),
})
})
HttpResponse::new(
StatusCode::from_int(result.status_code),
headers=result.headers,
raw_body=result.body,
)
}