@shd101wyy/yo 0.1.26 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/yo-async-effects/SKILL.md +4 -4
- package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
- package/.github/skills/yo-core-patterns/SKILL.md +1 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +26 -26
- package/.github/skills/yo-project-workflow/SKILL.md +6 -3
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
- package/.github/skills/yo-syntax/SKILL.md +7 -6
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +73 -60
- package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
- package/README.md +10 -8
- package/out/cjs/index.cjs +456 -438
- package/out/cjs/yo-cli.cjs +576 -543
- package/out/cjs/yo-lsp.cjs +559 -532
- package/out/esm/index.mjs +281 -263
- package/out/types/src/formatter.d.ts +11 -0
- package/out/types/src/lsp/formatting.d.ts +2 -0
- package/out/types/src/tests/formatter.test.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/alg/hash.yo +13 -21
- package/std/allocator.yo +25 -40
- package/std/async.yo +3 -7
- package/std/build.yo +105 -151
- package/std/cli/arg_parser.yo +184 -169
- package/std/collections/array_list.yo +350 -314
- package/std/collections/btree_map.yo +142 -131
- package/std/collections/deque.yo +132 -128
- package/std/collections/hash_map.yo +542 -566
- package/std/collections/hash_set.yo +623 -687
- package/std/collections/linked_list.yo +275 -293
- package/std/collections/ordered_map.yo +113 -85
- package/std/collections/priority_queue.yo +73 -73
- package/std/crypto/md5.yo +191 -95
- package/std/crypto/random.yo +56 -64
- package/std/crypto/sha256.yo +151 -107
- package/std/encoding/base64.yo +87 -81
- package/std/encoding/hex.yo +43 -50
- package/std/encoding/html.yo +56 -81
- package/std/encoding/html_char_utils.yo +7 -13
- package/std/encoding/html_entities.yo +2248 -2253
- package/std/encoding/json.yo +316 -224
- package/std/encoding/punycode.yo +86 -116
- package/std/encoding/toml.yo +67 -66
- package/std/encoding/utf16.yo +37 -44
- package/std/env.yo +62 -91
- package/std/error.yo +7 -15
- package/std/fmt/display.yo +5 -9
- package/std/fmt/index.yo +8 -14
- package/std/fmt/to_string.yo +330 -315
- package/std/fmt/writer.yo +58 -87
- package/std/fs/dir.yo +83 -102
- package/std/fs/file.yo +147 -180
- package/std/fs/metadata.yo +45 -78
- package/std/fs/temp.yo +55 -65
- package/std/fs/types.yo +27 -40
- package/std/fs/walker.yo +53 -68
- package/std/gc.yo +5 -8
- package/std/glob.yo +30 -43
- package/std/http/client.yo +107 -120
- package/std/http/http.yo +106 -96
- package/std/http/index.yo +4 -6
- package/std/imm/list.yo +88 -93
- package/std/imm/map.yo +528 -464
- package/std/imm/set.yo +52 -57
- package/std/imm/sorted_map.yo +340 -286
- package/std/imm/sorted_set.yo +57 -63
- package/std/imm/string.yo +404 -345
- package/std/imm/vec.yo +173 -181
- package/std/io/reader.yo +3 -6
- package/std/io/writer.yo +4 -8
- package/std/libc/assert.yo +5 -9
- package/std/libc/ctype.yo +32 -22
- package/std/libc/dirent.yo +26 -25
- package/std/libc/errno.yo +164 -90
- package/std/libc/fcntl.yo +52 -45
- package/std/libc/float.yo +66 -44
- package/std/libc/limits.yo +42 -33
- package/std/libc/math.yo +53 -82
- package/std/libc/signal.yo +72 -47
- package/std/libc/stdatomic.yo +217 -188
- package/std/libc/stdint.yo +5 -29
- package/std/libc/stdio.yo +5 -29
- package/std/libc/stdlib.yo +32 -39
- package/std/libc/string.yo +5 -23
- package/std/libc/sys/stat.yo +58 -56
- package/std/libc/time.yo +5 -19
- package/std/libc/unistd.yo +5 -20
- package/std/libc/wctype.yo +6 -9
- package/std/libc/windows.yo +26 -30
- package/std/log.yo +41 -55
- package/std/net/addr.yo +102 -97
- package/std/net/dns.yo +27 -28
- package/std/net/errors.yo +50 -49
- package/std/net/tcp.yo +113 -124
- package/std/net/udp.yo +55 -66
- package/std/os/env.yo +35 -33
- package/std/os/signal.yo +15 -25
- package/std/path.yo +276 -311
- package/std/prelude.yo +6304 -4315
- package/std/process/command.yo +87 -103
- package/std/process/index.yo +12 -31
- package/std/regex/compiler.yo +196 -95
- package/std/regex/flags.yo +58 -39
- package/std/regex/index.yo +157 -173
- package/std/regex/match.yo +20 -31
- package/std/regex/node.yo +134 -152
- package/std/regex/parser.yo +283 -259
- package/std/regex/unicode.yo +172 -202
- package/std/regex/vm.yo +155 -171
- package/std/string/index.yo +5 -7
- package/std/string/rune.yo +45 -55
- package/std/string/string.yo +937 -964
- package/std/string/string_builder.yo +94 -104
- package/std/string/unicode.yo +46 -64
- package/std/sync/channel.yo +72 -73
- package/std/sync/cond.yo +31 -36
- package/std/sync/mutex.yo +30 -32
- package/std/sync/once.yo +13 -16
- package/std/sync/rwlock.yo +26 -31
- package/std/sync/waitgroup.yo +20 -25
- package/std/sys/advise.yo +16 -24
- package/std/sys/bufio/buf_reader.yo +77 -93
- package/std/sys/bufio/buf_writer.yo +52 -65
- package/std/sys/clock.yo +4 -9
- package/std/sys/constants.yo +77 -61
- package/std/sys/copy.yo +4 -10
- package/std/sys/dir.yo +26 -43
- package/std/sys/dns.yo +41 -61
- package/std/sys/errors.yo +95 -103
- package/std/sys/events.yo +45 -57
- package/std/sys/externs.yo +319 -267
- package/std/sys/fallocate.yo +7 -11
- package/std/sys/fcntl.yo +14 -22
- package/std/sys/file.yo +26 -40
- package/std/sys/future.yo +5 -8
- package/std/sys/iov.yo +12 -25
- package/std/sys/lock.yo +12 -13
- package/std/sys/mmap.yo +38 -43
- package/std/sys/path.yo +3 -8
- package/std/sys/perm.yo +7 -21
- package/std/sys/pipe.yo +5 -12
- package/std/sys/process.yo +23 -29
- package/std/sys/seek.yo +10 -12
- package/std/sys/signal.yo +7 -13
- package/std/sys/signals.yo +52 -35
- package/std/sys/socket.yo +63 -58
- package/std/sys/socketpair.yo +3 -6
- package/std/sys/sockinfo.yo +11 -20
- package/std/sys/statfs.yo +11 -34
- package/std/sys/statx.yo +25 -52
- package/std/sys/sysinfo.yo +15 -20
- package/std/sys/tcp.yo +62 -92
- package/std/sys/temp.yo +5 -9
- package/std/sys/time.yo +5 -15
- package/std/sys/timer.yo +6 -11
- package/std/sys/tty.yo +10 -18
- package/std/sys/udp.yo +22 -39
- package/std/sys/umask.yo +3 -6
- package/std/sys/unix.yo +33 -52
- package/std/testing/bench.yo +49 -52
- package/std/thread.yo +10 -15
- package/std/time/datetime.yo +105 -89
- package/std/time/duration.yo +43 -56
- package/std/time/instant.yo +13 -18
- package/std/time/sleep.yo +5 -9
- package/std/url/index.yo +184 -209
- package/std/worker.yo +6 -10
package/std/http/client.yo
CHANGED
|
@@ -16,180 +16,176 @@
|
|
|
16
16
|
// resp := io.await(fetch(`http://example.com/api/data`, using(io)));
|
|
17
17
|
// println(resp.body);
|
|
18
18
|
// });
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
{ GlobalAllocator } :: import "../allocator";
|
|
19
|
+
open(import("../string"));
|
|
20
|
+
{ ArrayList } :: import("../collections/array_list");
|
|
21
|
+
{ GlobalAllocator } :: import("../allocator");
|
|
23
22
|
{ malloc, free } :: GlobalAllocator;
|
|
24
|
-
open
|
|
25
|
-
{ Error, AnyError, Exception } :: import
|
|
26
|
-
{ TcpStream } :: import
|
|
27
|
-
{ IpAddr, SocketAddr } :: import
|
|
28
|
-
{ lookup_host } :: import
|
|
29
|
-
{ Url, UrlError } :: import
|
|
30
|
-
{
|
|
31
|
-
HttpMethod, HttpHeader, HttpRequest, HttpResponse,
|
|
32
|
-
parse_response, http_status_text
|
|
33
|
-
} :: import "./http.yo";
|
|
34
|
-
|
|
23
|
+
open(import("../fmt"));
|
|
24
|
+
{ Error, AnyError, Exception } :: import("../error");
|
|
25
|
+
{ TcpStream } :: import("../net/tcp");
|
|
26
|
+
{ IpAddr, SocketAddr } :: import("../net/addr");
|
|
27
|
+
{ lookup_host } :: import("../net/dns");
|
|
28
|
+
{ Url, UrlError } :: import("../url");
|
|
29
|
+
{ HttpMethod, HttpHeader, HttpRequest, HttpResponse, parse_response, http_status_text } :: import("./http.yo");
|
|
35
30
|
// ============================================================================
|
|
36
31
|
// HttpError
|
|
37
32
|
// ============================================================================
|
|
38
|
-
|
|
39
33
|
/// HTTP client error variants.
|
|
40
34
|
HttpError :: enum(
|
|
41
35
|
/// Failed to connect to the remote host.
|
|
42
|
-
ConnectionFailed(msg: String),
|
|
36
|
+
ConnectionFailed(msg : String),
|
|
43
37
|
/// The URL could not be parsed.
|
|
44
|
-
InvalidUrl(msg: String),
|
|
38
|
+
InvalidUrl(msg : String),
|
|
45
39
|
/// The request timed out.
|
|
46
40
|
Timeout,
|
|
47
41
|
/// Too many HTTP redirects.
|
|
48
42
|
TooManyRedirects,
|
|
49
43
|
/// The URL scheme is not supported (only http/https).
|
|
50
|
-
UnsupportedScheme(scheme: String),
|
|
44
|
+
UnsupportedScheme(scheme : String),
|
|
51
45
|
/// The response body exceeds the size limit.
|
|
52
46
|
ResponseTooLarge,
|
|
53
47
|
/// An unclassified HTTP error.
|
|
54
|
-
Other(msg: String)
|
|
48
|
+
Other(msg : String)
|
|
55
49
|
);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
50
|
+
impl(
|
|
51
|
+
HttpError,
|
|
52
|
+
ToString(
|
|
53
|
+
to_string : (
|
|
54
|
+
self ->
|
|
55
|
+
match(
|
|
56
|
+
self,
|
|
57
|
+
.ConnectionFailed(msg) => `HTTP connection failed: ${msg}`,
|
|
58
|
+
.InvalidUrl(msg) => `Invalid URL: ${msg}`,
|
|
59
|
+
.Timeout => `HTTP request timed out`,
|
|
60
|
+
.TooManyRedirects => `Too many redirects`,
|
|
61
|
+
.UnsupportedScheme(scheme) => `Unsupported scheme: ${scheme}`,
|
|
62
|
+
.ResponseTooLarge => `Response too large`,
|
|
63
|
+
.Other(msg) => `HTTP error: ${msg}`
|
|
64
|
+
)
|
|
67
65
|
)
|
|
68
66
|
)
|
|
69
|
-
)
|
|
70
|
-
|
|
67
|
+
);
|
|
71
68
|
impl(HttpError, Error());
|
|
72
|
-
|
|
73
|
-
export HttpError;
|
|
74
|
-
|
|
69
|
+
export(HttpError);
|
|
75
70
|
// ============================================================================
|
|
76
71
|
// FetchOptions
|
|
77
72
|
// ============================================================================
|
|
78
|
-
|
|
79
73
|
/// Options for configuring an HTTP request.
|
|
80
74
|
FetchOptions :: object(
|
|
81
|
-
method
|
|
75
|
+
method : HttpMethod,
|
|
82
76
|
headers : ArrayList(HttpHeader),
|
|
83
|
-
body
|
|
77
|
+
body : String
|
|
84
78
|
);
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
impl(
|
|
80
|
+
FetchOptions,
|
|
87
81
|
/// Create default options (GET, no headers, empty body).
|
|
88
82
|
new : (fn() -> Self)(
|
|
89
83
|
Self(
|
|
90
|
-
method
|
|
91
|
-
headers: ArrayList(HttpHeader).new(),
|
|
92
|
-
body: ``
|
|
84
|
+
method :.GET,
|
|
85
|
+
headers : ArrayList(HttpHeader).new(),
|
|
86
|
+
body : ``
|
|
93
87
|
)
|
|
94
88
|
),
|
|
95
|
-
|
|
96
89
|
/// Set the HTTP method.
|
|
97
|
-
with_method : (fn(self: Self, method: HttpMethod) -> Self)({
|
|
90
|
+
with_method : (fn(self : Self, method : HttpMethod) -> Self)({
|
|
98
91
|
self.method = method;
|
|
99
92
|
self
|
|
100
93
|
}),
|
|
101
|
-
|
|
102
94
|
/// Add a request header.
|
|
103
|
-
with_header : (fn(self: Self, name: String, value: String) -> Self)({
|
|
95
|
+
with_header : (fn(self : Self, name : String, value : String) -> Self)({
|
|
104
96
|
self.headers.push(HttpHeader.new(name, value));
|
|
105
97
|
self
|
|
106
98
|
}),
|
|
107
|
-
|
|
108
99
|
/// Set the request body.
|
|
109
|
-
with_body : (fn(self: Self, body: String) -> Self)({
|
|
100
|
+
with_body : (fn(self : Self, body : String) -> Self)({
|
|
110
101
|
self.body = body;
|
|
111
102
|
self
|
|
112
103
|
})
|
|
113
104
|
);
|
|
114
|
-
|
|
115
|
-
export FetchOptions;
|
|
116
|
-
|
|
105
|
+
export(FetchOptions);
|
|
117
106
|
// ============================================================================
|
|
118
107
|
// Internal helpers
|
|
119
108
|
// ============================================================================
|
|
120
|
-
|
|
121
109
|
// Find the position of \r\n\r\n (end of HTTP headers)
|
|
122
|
-
_find_header_end :: (fn(data: ArrayList(u8)) -> usize)({
|
|
110
|
+
_find_header_end :: (fn(data : ArrayList(u8)) -> usize)({
|
|
123
111
|
len := data.len();
|
|
124
112
|
cond(
|
|
125
|
-
(len < usize(4)) => {
|
|
113
|
+
(len < usize(4)) => {
|
|
114
|
+
return(usize(0));
|
|
115
|
+
},
|
|
126
116
|
true => ()
|
|
127
117
|
);
|
|
128
118
|
i := usize(0);
|
|
129
119
|
limit := (len - usize(3));
|
|
130
|
-
while
|
|
120
|
+
while(runtime(i < limit), {
|
|
131
121
|
cond(
|
|
132
|
-
(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
(
|
|
123
|
+
(data.get(i).unwrap() == u8(13)) &&
|
|
124
|
+
(
|
|
125
|
+
(data.get(i + usize(1)).unwrap() == u8(10)) &&
|
|
126
|
+
(
|
|
127
|
+
(data.get(i + usize(2)).unwrap() == u8(13)) &&
|
|
128
|
+
(data.get(i + usize(3)).unwrap() == u8(10))
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
) => {
|
|
132
|
+
return(i);
|
|
137
133
|
},
|
|
138
134
|
true => ()
|
|
139
135
|
);
|
|
140
136
|
i = (i + usize(1));
|
|
141
|
-
};
|
|
137
|
+
});
|
|
142
138
|
usize(0)
|
|
143
139
|
});
|
|
144
|
-
|
|
145
140
|
// Parse Content-Length from headers; returns -1 if not found.
|
|
146
|
-
_find_content_length :: (fn(data: ArrayList(u8), header_end: usize) -> i32)({
|
|
141
|
+
_find_content_length :: (fn(data : ArrayList(u8), header_end : usize) -> i32)({
|
|
147
142
|
// Build header string up to header_end
|
|
148
143
|
header_bytes := ArrayList(u8).new();
|
|
149
144
|
i := usize(0);
|
|
150
|
-
while
|
|
145
|
+
while(runtime(i < header_end), {
|
|
151
146
|
header_bytes.push(data.get(i).unwrap());
|
|
152
147
|
i = (i + usize(1));
|
|
153
|
-
};
|
|
148
|
+
});
|
|
154
149
|
header_str := String.from_bytes(header_bytes).to_lowercase();
|
|
155
|
-
match(
|
|
150
|
+
match(
|
|
151
|
+
header_str.index_of(`content-length:`),
|
|
156
152
|
.Some(pos) => {
|
|
157
153
|
val_start := (pos + usize(16));
|
|
158
154
|
// Find end of value by looking for \r\n after val_start
|
|
159
155
|
rest := header_str.substring(val_start, header_str.len());
|
|
160
|
-
match(
|
|
156
|
+
match(
|
|
157
|
+
rest.index_of(`\r\n`),
|
|
161
158
|
.Some(eol) => {
|
|
162
159
|
val_str := rest.substring(usize(0), eol).trim();
|
|
163
|
-
match(
|
|
160
|
+
match(
|
|
161
|
+
val_str.parse_i32(),
|
|
164
162
|
.Some(n) => n,
|
|
165
|
-
.None => i32(-1)
|
|
163
|
+
.None => i32(-(1))
|
|
166
164
|
)
|
|
167
165
|
},
|
|
168
166
|
.None => {
|
|
169
167
|
val_str := rest.trim();
|
|
170
|
-
match(
|
|
168
|
+
match(
|
|
169
|
+
val_str.parse_i32(),
|
|
171
170
|
.Some(n) => n,
|
|
172
|
-
.None => i32(-1)
|
|
171
|
+
.None => i32(-(1))
|
|
173
172
|
)
|
|
174
173
|
}
|
|
175
174
|
)
|
|
176
175
|
},
|
|
177
|
-
.None => i32(-1)
|
|
176
|
+
.None => i32(-(1))
|
|
178
177
|
)
|
|
179
178
|
});
|
|
180
|
-
|
|
181
179
|
// ============================================================================
|
|
182
180
|
// Internal: read full HTTP response from a TCP stream
|
|
183
181
|
// ============================================================================
|
|
184
|
-
|
|
185
|
-
_read_http_response :: (fn(stream: TcpStream, using(io : IO, exn : Exception)) -> String)({
|
|
182
|
+
_read_http_response :: (fn(stream : TcpStream, using(io : IO, exn : Exception)) -> String)({
|
|
186
183
|
buf_size := usize(8192);
|
|
187
184
|
buf := *(u8)(malloc(buf_size).unwrap());
|
|
188
185
|
result := ArrayList(u8).new();
|
|
189
|
-
|
|
190
186
|
// Read until connection closes or we've read the full response
|
|
191
187
|
done := false;
|
|
192
|
-
while
|
|
188
|
+
while(runtime(!(done)), {
|
|
193
189
|
n := io.await(stream.read(buf, buf_size, using(io)));
|
|
194
190
|
cond(
|
|
195
191
|
(n <= i32(0)) => {
|
|
@@ -197,10 +193,10 @@ _read_http_response :: (fn(stream: TcpStream, using(io : IO, exn : Exception)) -
|
|
|
197
193
|
},
|
|
198
194
|
true => {
|
|
199
195
|
i := usize(0);
|
|
200
|
-
while
|
|
196
|
+
while(runtime(i < usize(n)), {
|
|
201
197
|
result.push((buf &+ i).*);
|
|
202
198
|
i = (i + usize(1));
|
|
203
|
-
};
|
|
199
|
+
});
|
|
204
200
|
// Check if we've received the full response by looking for
|
|
205
201
|
// Content-Length or end of chunked transfer
|
|
206
202
|
// For simplicity, check if the header section is complete
|
|
@@ -234,15 +230,13 @@ _read_http_response :: (fn(stream: TcpStream, using(io : IO, exn : Exception)) -
|
|
|
234
230
|
);
|
|
235
231
|
}
|
|
236
232
|
);
|
|
237
|
-
};
|
|
233
|
+
});
|
|
238
234
|
free(.Some(*(void)(buf)));
|
|
239
235
|
String.from_bytes(result)
|
|
240
236
|
});
|
|
241
|
-
|
|
242
237
|
// ============================================================================
|
|
243
238
|
// fetch — High-level async HTTP request (like JavaScript's fetch)
|
|
244
239
|
// ============================================================================
|
|
245
|
-
|
|
246
240
|
/// Perform an HTTP request with custom options.
|
|
247
241
|
///
|
|
248
242
|
/// # Example
|
|
@@ -251,13 +245,13 @@ _read_http_response :: (fn(stream: TcpStream, using(io : IO, exn : Exception)) -
|
|
|
251
245
|
/// opts := FetchOptions.new().with_method(.POST).with_body(`{"key": "value"}`);
|
|
252
246
|
/// resp := io.await(fetch_with(`http://example.com/api`, opts, using(io)));
|
|
253
247
|
/// ```
|
|
254
|
-
fetch_with :: (
|
|
255
|
-
|
|
248
|
+
fetch_with :: (
|
|
249
|
+
fn(url_str : String, opts : FetchOptions, using(io : IO)) ->
|
|
250
|
+
Impl(Future(HttpResponse, IO, Exception))
|
|
256
251
|
)(
|
|
257
252
|
io.async((using(io : IO, exn : Exception)) => {
|
|
258
253
|
// Parse URL
|
|
259
254
|
url := Url.parse(url_str.as_str(), using(exn));
|
|
260
|
-
|
|
261
255
|
// Validate scheme
|
|
262
256
|
scheme := url.scheme();
|
|
263
257
|
is_http := (scheme == `http`);
|
|
@@ -265,65 +259,64 @@ fetch_with :: (fn(url_str: String, opts: FetchOptions, using(io : IO)) ->
|
|
|
265
259
|
cond(
|
|
266
260
|
(is_http || is_https) => (),
|
|
267
261
|
true => {
|
|
268
|
-
exn.throw(dyn
|
|
262
|
+
exn.throw(dyn(HttpError.UnsupportedScheme(scheme)));
|
|
269
263
|
}
|
|
270
264
|
);
|
|
271
|
-
|
|
272
265
|
// Extract host
|
|
273
|
-
host := match(
|
|
266
|
+
host := match(
|
|
267
|
+
url.host(),
|
|
274
268
|
.Some(h) => h,
|
|
275
269
|
.None => {
|
|
276
|
-
exn.throw(dyn
|
|
270
|
+
exn.throw(dyn(HttpError.InvalidUrl(`missing host`)));
|
|
277
271
|
`_` // unreachable
|
|
278
272
|
}
|
|
279
273
|
);
|
|
280
|
-
|
|
281
274
|
// Determine port (default: 80 for http, 443 for https)
|
|
282
|
-
port := match(
|
|
275
|
+
port := match(
|
|
276
|
+
url.port(),
|
|
283
277
|
.Some(p) => p,
|
|
284
278
|
.None => cond(
|
|
285
279
|
(scheme == `https`) => u16(443),
|
|
286
280
|
true => u16(80)
|
|
287
281
|
)
|
|
288
282
|
);
|
|
289
|
-
|
|
290
283
|
// Resolve hostname to IP
|
|
291
284
|
addrs := io.await(lookup_host(host, using(io)));
|
|
292
285
|
cond(
|
|
293
286
|
(addrs.len() == usize(0)) => {
|
|
294
|
-
exn.throw(dyn
|
|
287
|
+
exn.throw(dyn(HttpError.ConnectionFailed(`DNS resolution failed for ${host}`)));
|
|
295
288
|
},
|
|
296
289
|
true => ()
|
|
297
290
|
);
|
|
298
291
|
ip := addrs.get(usize(0)).unwrap();
|
|
299
292
|
addr := SocketAddr.new(ip, port);
|
|
300
|
-
|
|
301
293
|
// Connect
|
|
302
294
|
stream := io.await(TcpStream.connect(addr, using(io)));
|
|
303
|
-
|
|
304
295
|
// Build the request path (include query string if present)
|
|
305
296
|
req_path := url.path();
|
|
306
297
|
cond(
|
|
307
|
-
req_path.is_empty() => {
|
|
298
|
+
req_path.is_empty() => {
|
|
299
|
+
req_path = `/`;
|
|
300
|
+
},
|
|
308
301
|
true => ()
|
|
309
302
|
);
|
|
310
|
-
match(
|
|
311
|
-
.
|
|
303
|
+
match(
|
|
304
|
+
url.query(),
|
|
305
|
+
.Some(q) => {
|
|
306
|
+
req_path = `${req_path}?${q}`;
|
|
307
|
+
},
|
|
312
308
|
.None => ()
|
|
313
309
|
);
|
|
314
|
-
|
|
315
310
|
// Build HTTP request
|
|
316
311
|
req := HttpRequest.new(opts.method, req_path);
|
|
317
312
|
req.set_host(host);
|
|
318
|
-
|
|
319
313
|
// Add user-provided headers
|
|
320
314
|
hi := usize(0);
|
|
321
|
-
while
|
|
315
|
+
while(runtime(hi < opts.headers.len()), {
|
|
322
316
|
h := opts.headers.get(hi).unwrap();
|
|
323
317
|
req.set_header(h.name, h.value);
|
|
324
318
|
hi = (hi + usize(1));
|
|
325
|
-
};
|
|
326
|
-
|
|
319
|
+
});
|
|
327
320
|
// Set Content-Length if body is provided
|
|
328
321
|
cond(
|
|
329
322
|
(!(opts.body.is_empty())) => {
|
|
@@ -332,33 +325,27 @@ fetch_with :: (fn(url_str: String, opts: FetchOptions, using(io : IO)) ->
|
|
|
332
325
|
},
|
|
333
326
|
true => ()
|
|
334
327
|
);
|
|
335
|
-
|
|
336
328
|
// Set Connection: close to ensure the server closes after response
|
|
337
329
|
req.set_header(`Connection`, `close`);
|
|
338
|
-
|
|
339
330
|
// Send request
|
|
340
331
|
req_str := req.to_string();
|
|
341
332
|
io.await(stream.write_string(req_str, using(io)));
|
|
342
|
-
|
|
343
333
|
// Read response
|
|
344
334
|
raw_response := _read_http_response(stream, using(io, exn));
|
|
345
|
-
|
|
346
335
|
// Close connection
|
|
347
336
|
io.await(stream.close(using(io)));
|
|
348
|
-
|
|
349
337
|
// Parse response
|
|
350
|
-
match(
|
|
338
|
+
match(
|
|
339
|
+
parse_response(raw_response),
|
|
351
340
|
.Ok(resp) => resp,
|
|
352
341
|
.Err(e) => {
|
|
353
|
-
exn.throw(dyn
|
|
342
|
+
exn.throw(dyn(HttpError.Other(e)));
|
|
354
343
|
HttpResponse.new(i32(0), ``) // unreachable
|
|
355
344
|
}
|
|
356
345
|
)
|
|
357
346
|
})
|
|
358
347
|
);
|
|
359
|
-
|
|
360
|
-
export fetch_with;
|
|
361
|
-
|
|
348
|
+
export(fetch_with);
|
|
362
349
|
/// Perform an HTTP GET request to the given URL string.
|
|
363
350
|
/// Returns the `HttpResponse` on success.
|
|
364
351
|
///
|
|
@@ -368,12 +355,12 @@ export fetch_with;
|
|
|
368
355
|
/// resp := io.await(fetch(`http://example.com`, using(io)));
|
|
369
356
|
/// cond(resp.is_ok() => println(resp.body), true => println(`Error`));
|
|
370
357
|
/// ```
|
|
371
|
-
fetch :: (
|
|
372
|
-
|
|
358
|
+
fetch :: (
|
|
359
|
+
fn(url_str : String, using(io : IO)) ->
|
|
360
|
+
Impl(Future(HttpResponse, IO, Exception))
|
|
373
361
|
)(
|
|
374
362
|
io.async((using(io : IO, exn : Exception)) => {
|
|
375
|
-
return
|
|
363
|
+
return(io.await(fetch_with(url_str, FetchOptions.new(), using(io))));
|
|
376
364
|
})
|
|
377
365
|
);
|
|
378
|
-
|
|
379
|
-
export fetch;
|
|
366
|
+
export(fetch);
|