nomen-lang 0.4.0 → 0.5.0

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.
@@ -5,18 +5,19 @@
5
5
  // An `async` block names its nursery (`async pool { }`), binding a Nursery
6
6
  // variable in scope. That variable is passed to helper functions with `ref`
7
7
  // and is the receiver of `pool.spawn(fn(args))`. The Nursery wraps the
8
- // per-invocation tracking state of the `async { }` block — a pointer to the
9
- // futures array and a pointer to the count slot, both on the async block's
10
- // stack frame — so it is only valid for the lifetime of its enclosing block
11
- // (which is exactly the structured-concurrency invariant: the block cannot
12
- // exit until every spawned task, including those spawned through a passed
13
- // Nursery, has finished).
8
+ // per-invocation tracking state of the `async { }` block — pointers to the
9
+ // futures list's storage slot, the registered count, and the capacity (the
10
+ // list starts empty and grows by realloc; the compiler's registration helper
11
+ // writes through these pointers) — so it is only valid for the lifetime of
12
+ // its enclosing block (which is exactly the structured-concurrency
13
+ // invariant: the block cannot exit until every spawned task, including those
14
+ // spawned through a passed Nursery, has finished).
14
15
  //
15
16
  // Config rides on the declaration: `async pool = Nursery(timeout: 2000) { }`.
16
17
  //
17
18
  // `name.spawn(fn(args))` is special-cased by the compiler (it needs the spawn
18
- // trampoline machinery), so it is NOT a regular method declared here. The two
19
- // fields are the full runtime contract.
19
+ // trampoline machinery), so it is NOT a regular method declared here. The
20
+ // three fields are the full runtime contract.
20
21
 
21
22
  /**
22
23
  * The capability for spawning into a caller's `async` block; owns and tracks its tasks
@@ -24,4 +25,5 @@
24
25
  pub struct Nursery: Sendable {
25
26
  pub var uint64 futures_ptr = 0
26
27
  pub var uint64 count_ptr = 0
28
+ pub var uint64 cap_ptr = 0
27
29
  }
@@ -1,12 +1,16 @@
1
1
  /**
2
- * A minimal blocking HTTP/1.1 client.
2
+ * A minimal HTTP/1.1 client built on Tcp.
3
3
  *
4
4
  * Plain `http://` only — no TLS (an `https://` URL cannot be served; use a
5
5
  * TLS proxy or wait for a TLS-capable transport). Construct an instance,
6
- * call `get`/`post` (which block until the full response is received), then
7
- * read the `status` field for the response's status code. Fallible calls
8
- * return a `Result` whose error side is an `HttpError`; `status` is 0 when
9
- * DNS resolution or the connection fails.
6
+ * call `get`/`post`, then read the `status` field for the response's status
7
+ * code. Fallible calls return a `Result` whose error side is an `HttpError`;
8
+ * `status` is 0 when DNS resolution or the connection fails.
9
+ *
10
+ * Every wait rides Tcp's netpoller hook: inside a Fiber the request parks
11
+ * (freeing its worker) instead of blocking — the same `Http` code scales
12
+ * from one call to thousands of concurrent fibers. See ASYNC.md
13
+ * Phase 3.
10
14
  *
11
15
  * URLs may carry an explicit port (`http://host:8080/path`); the default is
12
16
  * 80 and the default path is `/`. `post` sends `body` with a
@@ -27,8 +31,9 @@ pub struct Http {
27
31
  pub var int status = 0
28
32
 
29
33
  // Internal failure code from the most recent request: 0 = success,
30
- // 1 = DNS resolution, 2 = socket creation, 3 = connect. Raw #arch bodies
31
- // set it, the Result-returning wrappers map it to HttpError.
34
+ // 1 = DNS resolution, 2 = socket creation, 3 = connect, 6 = send failed.
35
+ // Mirrors Tcp's codes on the connect path; the Result-returning wrappers
36
+ // map it to HttpError.
32
37
  var int error = 0
33
38
 
34
39
  // Maps the raw failure code to its HttpError case.
@@ -53,449 +58,93 @@ pub struct Http {
53
58
  // method string ("GET" / "POST"); a leading 'P' selects POST semantics
54
59
  // (sends `body` and the Content-* headers). Sets self->status (0 on
55
60
  // failure) and self->error, and returns the response body (empty string
56
- // on failure). The raw body mallocs the response — `move out string`
57
- // transfers ownership to the caller.
61
+ // on failure).
58
62
  func raw_exchange = (ref self, string method, string url, string body, move out string) {
59
- ```
60
- #arch: c
61
- #scope: file
62
- #include <sys/socket.h>
63
- #include <netdb.h>
64
- #include <unistd.h>
65
- #include <string.h>
66
- #include <stdlib.h>
67
- ```
68
- ```
69
- #arch: c
70
- // Fat-string ABI: params are (ptr, len) pairs; consume the .ptr
71
- // halves at each FFI boundary.
72
- const char *scheme = strstr(url.ptr, "://");
73
- const char *hp = scheme ? scheme + 3 : url.ptr;
74
- const char *colon = strchr(hp, ':');
75
- const char *slash = strchr(hp, '/');
76
- char host[256];
77
- int port = 80;
78
- const char *path = "/";
79
-
80
- if (colon && (!slash || colon < slash)) {
81
- int hlen = (int)(colon - hp);
82
- if (hlen > 255) hlen = 255;
83
- memcpy(host, hp, hlen);
84
- host[hlen] = 0;
85
- port = atoi(colon + 1);
86
- } else {
87
- const char *end = slash ? slash : hp + strlen(hp);
88
- int hlen = (int)(end - hp);
89
- if (hlen > 255) hlen = 255;
90
- memcpy(host, hp, hlen);
91
- host[hlen] = 0;
63
+ // Parse the URL: [http://]host[:port][/path]
64
+ var int host_start = 0
65
+ if url.starts_with("http://") {
66
+ host_start = 7
92
67
  }
93
- if (slash) path = slash;
94
-
95
- struct addrinfo hints, *res;
96
- memset(&hints, 0, sizeof(hints));
97
- hints.ai_family = AF_INET;
98
- hints.ai_socktype = SOCK_STREAM;
99
- char portstr[16];
100
- snprintf(portstr, sizeof(portstr), "%d", port);
101
- if (getaddrinfo(host, portstr, &hints, &res) != 0) {
102
- self->status = 0;
103
- self->error = 1;
104
- return (nomen_string){ strdup(""), 0 };
68
+ var int slash = url.index_of_from("/", host_start)
69
+ var int colon = url.index_of_from(":", host_start)
70
+ var int host_end = url.length
71
+ if slash >= 0 {
72
+ host_end = slash
73
+ }
74
+ var int name_end = host_end
75
+ if colon >= 0 {
76
+ if colon < host_end {
77
+ name_end = colon
78
+ }
105
79
  }
106
- int sock = socket(AF_INET, SOCK_STREAM, 0);
107
- if (sock < 0) {
108
- freeaddrinfo(res);
109
- self->status = 0;
110
- self->error = 2;
111
- return (nomen_string){ strdup(""), 0 };
80
+ var int port = 80
81
+ if name_end != host_end {
82
+ var string port_text = url.substring(name_end + 1, host_end)
83
+ port = int.parse(port_text)
112
84
  }
113
- if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
114
- freeaddrinfo(res);
115
- close(sock);
116
- self->status = 0;
117
- self->error = 3;
118
- return (nomen_string){ strdup(""), 0 };
85
+ var string host = url.substring(host_start, name_end)
86
+ var string path = "/"
87
+ if slash >= 0 {
88
+ path = url.substring(slash, url.length)
119
89
  }
120
- freeaddrinfo(res);
121
90
 
122
- char req[1600];
123
- int off = snprintf(req, sizeof(req),
124
- "%s %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n",
125
- method.ptr, path, host);
126
- if (method.ptr[0] == 'P') {
127
- int blen = (int)body.len;
128
- off += snprintf(req + off, sizeof(req) - off,
129
- "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n",
130
- blen);
131
- send(sock, req, off, 0);
132
- send(sock, body.ptr, blen, 0);
91
+ // Connect — parks a fiber while the handshake completes.
92
+ var Tcp conn = Tcp.connect(host, port)
93
+ if conn.fd < 0 {
94
+ self.status = 0
95
+ self.error = conn.error
96
+ return ""
97
+ }
98
+
99
+ // Build the request head.
100
+ var string request = method + " " + path + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n"
101
+ if method.starts_with("P") {
102
+ request = request + "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: \{body.length}\r\n\r\n"
133
103
  } else {
134
- off += snprintf(req + off, sizeof(req) - off, "\r\n");
135
- send(sock, req, off, 0);
104
+ request = request + "\r\n"
136
105
  }
137
106
 
138
- int cap = 4096, len = 0;
139
- char *buf = (char*)malloc(cap);
140
- int n;
141
- while ((n = recv(sock, buf + len, cap - len - 1, 0)) > 0) {
142
- len += n;
143
- if (len + 1 >= cap) {
144
- cap *= 2;
145
- buf = (char*)realloc(buf, cap);
107
+ // Send — parks while the socket back-pressures.
108
+ var bool sent = conn.send(request)
109
+ if sent {
110
+ if method.starts_with("P") {
111
+ sent = conn.send(body)
146
112
  }
147
113
  }
148
- buf[len] = 0;
149
- close(sock);
150
-
151
- int code = 0;
152
- if (len >= 12 && strncmp(buf, "HTTP/", 5) == 0) {
153
- code = atoi(buf + 9);
114
+ if !sent {
115
+ conn.close()
116
+ self.status = 0
117
+ self.error = 6
118
+ return ""
154
119
  }
155
- self->status = code;
156
- self->error = 0;
157
120
 
158
- char *body_start = strstr(buf, "\r\n\r\n");
159
- if (body_start) {
160
- char *result = strdup(body_start + 4);
161
- long rlen = (long)len - (long)(body_start + 4 - buf);
162
- free(buf);
163
- return (nomen_string){ result, rlen };
121
+ // Receive until the peer closes (how Connection: close HTTP works).
122
+ // (Named `wire`, not `response`: the aarch64 build-global moved-set is
123
+ // name-keyed, and a bare `return response` here would suppress the
124
+ // return-path free of get/post's own `response` local — see the
125
+ // FOLLOWUP.md entry.)
126
+ var string wire = conn.recv_all()
127
+ conn.close()
128
+
129
+ self.error = 0
130
+ // Status line: "HTTP/1.x NNN ..." — the code lives at bytes 9..12.
131
+ if wire.starts_with("HTTP/") && wire.length >= 12 {
132
+ var string code_text = wire.substring(9, 12)
133
+ self.status = int.parse(code_text)
134
+ } else {
135
+ self.status = 0
164
136
  }
165
- return (nomen_string){ buf, len };
166
- ```
167
- ```
168
- #arch: aarch64
169
- // Fat-string ABI: x0=self(->x19); method=(x1,x2), url=(x3,x4),
170
- // body=(x5,x6) — only the ptr halves are consumed below, plus
171
- // body.len (stashed at #200) for Content-Length/send.
172
- // Struct layout: self->status at [x19, #8], self->error at
173
- // [x19, #16] (1 = dns, 2 = socket, 3 = connect; cleared on success).
174
- // macOS arm64 passes variadic args on the caller stack at [sp+0..], so
175
- // [sp+0..47] is reserved scratch for snprintf variadic args and the
176
- // addrinfo hints live above it. Stack frame (2048 bytes):
177
- // #0 variadic scratch (snprintf args)
178
- // #48 hints (struct addrinfo, 48 bytes)
179
- // #96 res* #104 sock fd
180
- // #112 port #120 hp (host start)
181
- // #128 method #136 url
182
- // #144 body #152 path
183
- // #160 colon/blen/buf #168 slash/cap
184
- // #176 hostlen/req_off #184 portstr (16)
185
- // #208 host buffer (256) #464 request buffer (1584)
186
- sub sp, sp, #2048
187
- str x1, [sp, #128]
188
- str x3, [sp, #136]
189
- str x5, [sp, #144]
190
- str x6, [sp, #200] // body len
191
- mov x0, #80
192
- str x0, [sp, #112]
193
- adr x0, .Lhttp_slash
194
- str x0, [sp, #152]
195
-
196
- // hp = (strstr(url, "://") ?: url) + 3 when scheme present
197
- ldr x0, [sp, #136]
198
- adr x1, .Lhttp_scheme
199
- bl _strstr
200
- cbz x0, .Lhttp_sethp
201
- add x0, x0, #3
202
- .Lhttp_sethp:
203
- str x0, [sp, #120]
204
-
205
- // colon = strchr(hp, ':'); slash = strchr(hp, '/')
206
- ldr x0, [sp, #120]
207
- mov x1, #0x3A
208
- bl _strchr
209
- str x0, [sp, #160]
210
- ldr x0, [sp, #120]
211
- mov x1, #0x2F
212
- bl _strchr
213
- str x0, [sp, #168]
214
137
 
215
- // if (colon && (!slash || colon < slash)) -> port branch
216
- ldr x0, [sp, #160]
217
- cbz x0, .Lhttp_np
218
- ldr x1, [sp, #168]
219
- cbz x1, .Lhttp_port
220
- cmp x0, x1
221
- b.lo .Lhttp_port
222
- .Lhttp_np:
223
- // hostlen = slash ? (slash - hp) : strlen(hp); path = slash
224
- ldr x0, [sp, #168]
225
- cbz x0, .Lhttp_np_strlen
226
- ldr x1, [sp, #120]
227
- sub x0, x0, x1
228
- str x0, [sp, #176]
229
- ldr x0, [sp, #168]
230
- str x0, [sp, #152]
231
- b .Lhttp_cph
232
- .Lhttp_np_strlen:
233
- ldr x0, [sp, #120]
234
- bl _strlen
235
- str x0, [sp, #176]
236
- b .Lhttp_cph
237
- .Lhttp_port:
238
- // hostlen = colon - hp; port = atoi(colon + 1)
239
- ldr x0, [sp, #120]
240
- ldr x1, [sp, #160]
241
- sub x0, x1, x0
242
- str x0, [sp, #176]
243
- ldr x0, [sp, #160]
244
- add x0, x0, #1
245
- bl _atoi
246
- str x0, [sp, #112]
247
- ldr x0, [sp, #168]
248
- cbz x0, .Lhttp_cph
249
- str x0, [sp, #152]
250
-
251
- // hostlen = min(hostlen, 255); memcpy(hostbuf, hp, hostlen); hostbuf[hostlen]=0
252
- .Lhttp_cph:
253
- ldr x0, [sp, #176]
254
- mov x9, #255
255
- cmp x0, x9
256
- csel x0, x0, x9, ls
257
- str x0, [sp, #176]
258
- add x0, sp, #208
259
- ldr x1, [sp, #120]
260
- ldr x2, [sp, #176]
261
- bl _memcpy
262
- add x0, sp, #208
263
- ldr x1, [sp, #176]
264
- strb wzr, [x0, x1]
265
-
266
- // memset(&hints, 0, 48); hints.ai_family=AF_INET(2); hints.ai_socktype=SOCK_STREAM(1)
267
- add x0, sp, #48
268
- mov x1, xzr
269
- mov x2, #48
270
- bl _memset
271
- mov w0, #2
272
- str w0, [sp, #52]
273
- mov w0, #1
274
- str w0, [sp, #56]
275
- // snprintf(portstr, 16, "%ld", port) — variadic arg on stack at [sp+0]
276
- ldr x9, [sp, #112]
277
- str x9, [sp, #0]
278
- add x0, sp, #184
279
- mov x1, #16
280
- adr x2, .Lhttp_fmt_ld
281
- bl _snprintf
282
- // getaddrinfo(host, portstr, &hints, &res)
283
- add x0, sp, #208
284
- add x1, sp, #184
285
- add x2, sp, #48
286
- add x3, sp, #96
287
- bl _getaddrinfo
288
- cbnz w0, .Lhttp_dnsfail
289
-
290
- // sock = socket(AF_INET, SOCK_STREAM, 0)
291
- mov x0, #2
292
- mov x1, #1
293
- mov x2, xzr
294
- bl _socket
295
- cmp w0, #0
296
- b.ge .Lhttp_sockok
297
- ldr x0, [sp, #96]
298
- bl _freeaddrinfo
299
- mov x9, #2
300
- str x9, [x19, #16]
301
- b .Lhttp_fail
302
- .Lhttp_sockok:
303
- str w0, [sp, #104]
304
- // connect(sock, res->ai_addr (off 32), res->ai_addrlen (off 16))
305
- ldr x9, [sp, #96]
306
- ldr x1, [x9, #32]
307
- ldr w2, [x9, #16]
308
- ldr w0, [sp, #104]
309
- bl _connect
310
- cmp w0, #0
311
- b.ge .Lhttp_connected
312
- ldr x0, [sp, #96]
313
- bl _freeaddrinfo
314
- ldr w0, [sp, #104]
315
- bl _close
316
- mov x9, #3
317
- str x9, [x19, #16]
318
- b .Lhttp_fail
319
- .Lhttp_connected:
320
- ldr x0, [sp, #96]
321
- bl _freeaddrinfo
322
-
323
- // off = snprintf(req, 1584, common_fmt, method, path, host)
324
- // variadic args on stack: method@[0], path@[8], host@[16]
325
- ldr x9, [sp, #128]
326
- str x9, [sp, #0]
327
- ldr x9, [sp, #152]
328
- str x9, [sp, #8]
329
- add x9, sp, #208
330
- str x9, [sp, #16]
331
- add x0, sp, #464
332
- mov x1, #1584
333
- adr x2, .Lhttp_reqcommon
334
- bl _snprintf
335
- sxtw x0, w0
336
- str x0, [sp, #176]
337
-
338
- // if method[0] == 'P' (0x50) -> POST
339
- ldr x0, [sp, #128]
340
- ldrb w0, [x0]
341
- cmp w0, #0x50
342
- b.ne .Lhttp_getsend
343
-
344
- // POST: blen = body.len (fat half); append Content-Length; send both
345
- ldr x9, [sp, #200]
346
- str x9, [sp, #160]
347
- // snprintf(req+off, 1584-off, reqpost, blen) — blen on stack at [sp+0]
348
- ldr x9, [sp, #160]
349
- str x9, [sp, #0]
350
- add x0, sp, #464
351
- ldr x1, [sp, #176]
352
- add x0, x0, x1
353
- mov x1, #1584
354
- ldr x2, [sp, #176]
355
- sub x1, x1, x2
356
- adr x2, .Lhttp_reqpost
357
- bl _snprintf
358
- sxtw x0, w0
359
- ldr x1, [sp, #176]
360
- add x1, x1, x0
361
- str x1, [sp, #176]
362
- ldr w0, [sp, #104]
363
- add x1, sp, #464
364
- ldr x2, [sp, #176]
365
- mov x3, xzr
366
- bl _send
367
- ldr w0, [sp, #104]
368
- ldr x1, [sp, #144]
369
- ldr x2, [sp, #200]
370
- mov x3, xzr
371
- bl _send
372
- b .Lhttp_recv
373
-
374
- .Lhttp_getsend:
375
- // GET: append trailing "\r\n" (no variadic) and send
376
- add x0, sp, #464
377
- ldr x1, [sp, #176]
378
- add x0, x0, x1
379
- mov x1, #1584
380
- ldr x2, [sp, #176]
381
- sub x1, x1, x2
382
- adr x2, .Lhttp_crlf
383
- bl _snprintf
384
- sxtw x0, w0
385
- ldr x1, [sp, #176]
386
- add x1, x1, x0
387
- str x1, [sp, #176]
388
- ldr w0, [sp, #104]
389
- add x1, sp, #464
390
- ldr x2, [sp, #176]
391
- mov x3, xzr
392
- bl _send
393
-
394
- // recv loop: buf = malloc(4096); grow as needed
395
- .Lhttp_recv:
396
- mov x0, #4096
397
- bl _malloc
398
- str x0, [sp, #160]
399
- mov x0, #4096
400
- str x0, [sp, #168]
401
- str xzr, [sp, #96]
402
- .Lhttp_recvloop:
403
- ldr w0, [sp, #104]
404
- ldr x1, [sp, #160]
405
- ldr x2, [sp, #96]
406
- add x1, x1, x2
407
- ldr x2, [sp, #168]
408
- ldr x3, [sp, #96]
409
- sub x2, x2, x3
410
- sub x2, x2, #1
411
- mov x3, xzr
412
- bl _recv
413
- cmp x0, #0
414
- ble .Lhttp_recvdone
415
- ldr x1, [sp, #96]
416
- add x1, x1, x0
417
- str x1, [sp, #96]
418
- add x2, x1, #1
419
- ldr x3, [sp, #168]
420
- cmp x2, x3
421
- blt .Lhttp_recvloop
422
- ldr x0, [sp, #168]
423
- lsl x0, x0, #1
424
- str x0, [sp, #168]
425
- mov x1, x0
426
- ldr x0, [sp, #160]
427
- bl _realloc
428
- str x0, [sp, #160]
429
- b .Lhttp_recvloop
430
-
431
- .Lhttp_recvdone:
432
- // buf[len] = 0; close(sock)
433
- ldr x0, [sp, #160]
434
- ldr x1, [sp, #96]
435
- strb wzr, [x0, x1]
436
- ldr w0, [sp, #104]
437
- bl _close
438
- // success: clear error before storing the status code
439
- str xzr, [x19, #16]
440
- // self->status = atoi(buf + 9)
441
- ldr x0, [sp, #160]
442
- add x0, x0, #9
443
- bl _atoi
444
- sxtw x0, w0
445
- str x0, [x19, #8]
446
- // body_start = strstr(buf, "\r\n\r\n"); fat returns carry lens.
447
- ldr x0, [sp, #160]
448
- adr x1, .Lhttp_hdrsep
449
- bl _strstr
450
- cbz x0, .Lhttp_retbuf
451
- // result = strdup(body_start + 4); rlen = len - (body_start+4-buf)
452
- add x9, x0, #4
453
- ldr x10, [sp, #160]
454
- sub x9, x9, x10
455
- ldr x10, [sp, #96]
456
- sub x9, x10, x9 // rlen
457
- str x9, [sp, #168]
458
- add x0, x0, #4
459
- bl _strdup
460
- ldr x1, [sp, #168]
461
- str x0, [sp, #112]
462
- ldr x0, [sp, #160]
463
- bl _free
464
- ldr x0, [sp, #112]
465
- add sp, sp, #2048
466
- b .return_Http_raw_exchange
467
- .Lhttp_retbuf:
468
- ldr x0, [sp, #160]
469
- ldr x1, [sp, #96]
470
- add sp, sp, #2048
471
- b .return_Http_raw_exchange
472
- .Lhttp_dnsfail:
473
- mov x9, #1
474
- str x9, [x19, #16]
475
- b .Lhttp_fail
476
- .Lhttp_fail:
477
- str xzr, [x19, #8]
478
- adr x0, .Lhttp_empty
479
- bl _strdup
480
- mov x1, #0
481
- add sp, sp, #2048
482
- b .return_Http_raw_exchange
483
-
484
- .Lhttp_scheme: .asciz "://"
485
- .Lhttp_slash: .asciz "/"
486
- .Lhttp_fmt_ld: .asciz "%ld"
487
- .Lhttp_crlf: .asciz "\r\n"
488
- .Lhttp_hdrsep: .asciz "\r\n\r\n"
489
- .Lhttp_empty: .asciz ""
490
- .Lhttp_reqcommon: .asciz "%s %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n"
491
- .Lhttp_reqpost: .asciz "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: %ld\r\n\r\n"
492
- .p2align 2
493
- ```
494
- return ""
138
+ // Body starts after the blank header/body separator line.
139
+ var int sep = wire.index_of("\r\n\r\n")
140
+ if sep >= 0 {
141
+ return wire.substring(sep + 4, wire.length)
142
+ }
143
+ return wire
495
144
  }
496
145
 
497
- // Issue a blocking GET request to `url`. `.ok(body)` with the response
498
- // body and `self.status` set to the response's status code, or
146
+ // Issue a GET request to `url`. `.ok(body)` with the response body and
147
+ // `self.status` set to the response's status code, or
499
148
  // `.error(HttpError)` (`status` is 0).
500
149
  pub func get = (ref self, string url, out Result<string, HttpError>) {
501
150
  const string response = self.raw_exchange("GET", url, "")
@@ -505,7 +154,7 @@ pub struct Http {
505
154
  return Result.error(Http.error_of(self.error))
506
155
  }
507
156
 
508
- // Issue a blocking POST request to `url` with `body` as the payload.
157
+ // Issue a POST request to `url` with `body` as the payload.
509
158
  // `.ok(body)` with the response body and `self.status` set to the
510
159
  // response's status code, or `.error(HttpError)` (`status` is 0).
511
160
  pub func post = (ref self, string url, string body, out Result<string, HttpError>) {