http.bend
bend-kit-http@0.38.2.0 · 7,122 lines · 973 definitions
1
2import Base
3import bend-kit-http@0.38.2.0/admission.bend as Admission
4import bend-kit-http@0.38.2.0/completion.bend as Completion
5import bend-kit-wire@0.4.7.0/wire.bend as Wire
6import bend-kit-http2@0.4.0.0/http2.bend as H2
7import bend-kit-http2@0.4.0.0/hpack.bend as Hpack
8import bend-kit-bytes@0.3.3.0/bytes.bend as HBytes
9import bend-kit-time@0.3.0.0/time.bend as Time
10import bend-kit-int@0.2.0.0/int.bend as Int
11import bend-kit-concurrency@0.2.0.0/concurrency.bend as Conc
12import bend-kit-url@0.4.1.0/url.bend as Url
13import bend-kit-encoding@0.5.0.0/encoding.bend as Enc
14import bend-kit-json@0.7.0.0/json.bend as Json
15import bend-kit-bytes@0.3.3.0/bytes.bend as Bytes
16import bend-kit-dns@0.7.0.0/dns.bend as Dns
17import bend-kit-zlib@0.5.0.0/zlib.bend as Zlib
18
19
20
21
22
23
24
25
26type Req is Type:
27 Req{method: String, path: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes}
28
29type Res is Type:
30 Res{status: U32, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes}
31
32
33type Reply is Type:
34 Reply{response: Res, receipt: Completion.Receipt}
35
36def reply.complete(~D: Data, ~done: Completion.Completion<D> -> IO(Unit), payload: D, response: Res) -> Reply:
37 Reply{response, Completion.receipt(~D, ~done, payload)}
38
39
40
41def Body() -> Type:
42 Bytes.Bytes
43
44
45def from_string(+s: String) -> Bytes.Bytes:
46 Bytes.from_string(s)
47
48def to_string(b: Bytes.Bytes) -> String:
49 Bytes.to_string(b)
50
51def length(b: Bytes.Bytes) -> Bytes.Bytes & U32:
52 Bytes.length(b)
53
54
55
56type Err is Data:
57 ErrUrl{}
58 ErrDns{}
59 ErrConnect{code: U32, why: String}
60 ErrTls{code: U32, why: String}
61 ErrRead{code: U32, why: String}
62 ErrWrite{code: U32, why: String}
63 ErrTimeout{}
64 ErrRedirect{}
65 ErrBad{}
66
67def err.late(+code: U32) -> Bool:
68 Bool.or(U32.is_eq(code, 60), U32.is_eq(code, 110))
69
70def err.pick(e: Err, late: Bool) -> Err:
71 match late:
72 case True{}:
73 ErrTimeout{}
74 case False{}:
75 e
76
77def err.or_late(code: U32, e: Err) -> Err:
78 err.pick(e, err.late(code))
79
80type Fields is Data:
81 FieldsBad{}
82 FieldsOk{m: Map<&2, List<&2, String>>}
83
84
85
86def empty() -> Map<&2, List<&2, String>>:
87 Map.new(&2, List<&2, String>)
88
89def fields.of(r: Map<&2, List<&2, String>> & List<&2, String>) -> List<&2, String>:
90 (h, xs) = r
91 xs
92
93def fields(+h: Map<&2, List<&2, String>>, k: String) -> List<&2, String>:
94 fields.of(Map.get(List<&2, String>, Nil{}, h, k))
95
96def header.first(xs: List<&2, String>) -> String:
97 match xs:
98 case Nil{}:
99 ""
100 case Con{v, t}:
101 v
102
103def header(+h: Map<&2, List<&2, String>>, k: String) -> String:
104 header.first(fields(h, k))
105
106def field.last(xs: List<&2, String>) -> String:
107 match xs:
108 case Nil{}:
109 ""
110 case Con{v, Nil{}}:
111 v
112 case Con{v, t}:
113 field.last(t)
114
115
116def header.last(+h: Map<&2, List<&2, String>>, k: String) -> String:
117 field.last(fields(h, k))
118
119def snoc(xs: List<&2, String>, v: String) -> List<&2, String>:
120 match xs:
121 case Nil{}:
122 [v]
123 case Con{h, t}:
124 Con{h, snoc(t, v)}
125
126def set(m: Map<&2, List<&2, String>>, k: String, v: String) -> Map<&2, List<&2, String>>:
127 Map.set(&2, List<&2, String>, m, k, [v])
128
129def add(+m: Map<&2, List<&2, String>>, +k: String, v: String) -> Map<&2, List<&2, String>>:
130 Map.set(&2, List<&2, String>, m, k, snoc(fields(m, k), v))
131
132def sanitize.cons(ch: Char, rest: String, drop: Bool) -> String:
133 match drop:
134 case True{}:
135 rest
136 case False{}:
137 SCon{ch, rest}
138
139def sanitize(s: String) -> String:
140 match s:
141 case SNil{}:
142 SNil{}
143 case SCon{Chr{+c}, t}:
144 sanitize.cons(Chr{c}, sanitize(t), Bool.or(U32.is_eq(c, 13), U32.is_eq(c, 10)))
145
146def drop_cr.last.if(cr: Bool, c: U32) -> String:
147 match cr:
148 case True{}:
149 SNil{}
150 case False{}:
151 SCon{Chr{c}, SNil{}}
152
153def drop_cr.last(+c: U32) -> String:
154 drop_cr.last.if(U32.is_eq(c, 13), c)
155
156
157def drop_cr.go(s: String, prev: U32) -> String:
158 match s:
159 case SNil{}:
160 drop_cr.last(prev)
161 case SCon{Chr{c}, t}:
162 SCon{Chr{prev}, drop_cr.go(t, c)}
163
164def drop_cr(s: String) -> String:
165 match s:
166 case SNil{}:
167 SNil{}
168 case SCon{Chr{c}, t}:
169 drop_cr.go(t, c)
170
171
172def blank_end.go(s: String, w: U32) -> Bool:
173 match s:
174 case SNil{}:
175 U32.is_eq(w, 218762506)
176 case SCon{Chr{+c}, t}:
177 blank_end.go(t, Bool.pick(U32, U32.is_lt(c, 256), (w * 256 + c : U32), 0))
178
179
180def blank_end(s: String) -> Bool:
181 blank_end.go(s, 0)
182
183def trim_end.cons(h: Char, r: String, space: Bool) -> String:
184 match r:
185 case SNil{}:
186 match space:
187 case True{}:
188 SNil{}
189 case False{}:
190 SCon{h, SNil{}}
191 case SCon{a, t}:
192 SCon{h, SCon{a, t}}
193
194
195def trim_end(s: String) -> String:
196 match s:
197 case SNil{}:
198 SNil{}
199 case SCon{+h, t}:
200 trim_end.cons(h, trim_end(t), Char.is_space(h))
201
202def trim(s: String) -> String:
203 trim_end(String.trim_start(s))
204
205
206def take.cut(acc: String, rest: String) -> String & String:
207 (String.reverse(String.drop(acc, 1n)), rest)
208
209
210def take_sp.go(s: String, acc: String, hit: Bool) -> String & String:
211 match s:
212 case SNil{}:
213 match hit:
214 case True{}:
215 take.cut(acc, SNil{})
216 case False{}:
217 (String.reverse(acc), SNil{})
218 case SCon{Chr{+c}, t}:
219 match hit:
220 case True{}:
221 take.cut(acc, SCon{Chr{c}, t})
222 case False{}:
223 take_sp.go(t, SCon{Chr{c}, acc}, U32.is_eq(c, 32))
224
225def take_sp(s: String) -> String & String:
226 take_sp.go(s, SNil{}, False{})
227
228def start_line.ver(m: String, pv: String & String) -> String & String & String:
229 (p, v) = pv
230 (m, p, drop_cr(v))
231
232def start_line.rest(mr: String & String) -> String & String & String:
233 (m, r) = mr
234 start_line.ver(m, take_sp(r))
235
236def start_line(s: String) -> String & String & String:
237 start_line.rest(take_sp(s))
238
239def split_at_blank.go(s: String, e: Bool, w: U32, acc: String) -> String & String:
240 match s:
241 case SNil{}:
242 (String.reverse(acc), SNil{})
243 case SCon{Chr{+a}, t}:
244 match e:
245 case True{}:
246 (String.reverse(acc), SCon{Chr{a}, t})
247 case False{}:
248 +w2 = (w * 256 + a : U32)
249 split_at_blank.go(t, U32.is_eq(w2, 218762506), w2, SCon{Chr{a}, acc})
250
251
252def split_at_blank.flag.go(+raw: String, s: String, off: Nat, w: U32, e: Bool) -> String & String & Bool:
253 match s:
254 case SNil{}:
255 (String.take(raw, off), SNil{}, e)
256 case SCon{Chr{+a}, t}:
257 match e:
258 case True{}:
259 (String.take(raw, off), SCon{Chr{a}, t}, True{})
260 case False{}:
261 +w2 = (w * 256 + a : U32)
262 +off1 = (1n + off : Nat)
263 split_at_blank.flag.go(raw, t, off1, w2, U32.is_eq(w2, 218762506))
264
265def split_at_blank.pair(r: String & String & Bool) -> String & String:
266 (head, rest, whole) = r
267 (head, rest)
268
269def split_at_blank(+s: String) -> String & String:
270 split_at_blank.pair(split_at_blank.flag.go(s, s, 0n, 0, False{}))
271
272def split_colon.go(s: String, acc: String, hit: Bool) -> String & String:
273 match s:
274 case SNil{}:
275 match hit:
276 case True{}:
277 take.cut(acc, SNil{})
278 case False{}:
279 (String.reverse(acc), SNil{})
280 case SCon{Chr{+c}, t}:
281 match hit:
282 case True{}:
283 take.cut(acc, SCon{Chr{c}, t})
284 case False{}:
285 split_colon.go(t, SCon{Char.to_lower(Chr{c}), acc}, U32.is_eq(c, 58))
286
287def split_colon(+s: String) -> String & String:
288 split_colon.go(s, SNil{}, False{})
289
290def has_header.of(r: Map<&2, List<&2, String>> & Bool) -> Bool:
291 (h, b) = r
292 b
293
294def has_header(+h: Map<&2, List<&2, String>>, k: String) -> Bool:
295 has_header.of(Map.has(&2, List<&2, String>, h, k))
296
297def folded(+line: String) -> Bool:
298 Bool.or(String.starts_with(line, " "), String.starts_with(line, "\t"))
299
300def tracked(+k: String) -> Bool:
301 Bool.or(String.eq(k, "host"), String.eq(k, "content-length"))
302
303def fields_put.dup(m: Map<&2, List<&2, String>>, k: String, v: String, bad: Bool) -> Fields:
304 match bad:
305 case True{}:
306 FieldsBad{}
307 case False{}:
308 FieldsOk{add(m, k, v)}
309
310def fields_put.tracked(+m: Map<&2, List<&2, String>>, +k: String, v: String, t: Bool) -> Fields:
311 match t:
312 case True{}:
313 fields_put.dup(m, k, v, has_header(m, k))
314 case False{}:
315 FieldsOk{add(m, k, v)}
316
317def fields_put.key(+m: Map<&2, List<&2, String>>, +k: String, v: String) -> Fields:
318 fields_put.tracked(m, k, v, tracked(k))
319
320def fields_put.kv(m: Map<&2, List<&2, String>>, kv: String & String) -> Fields:
321 (k, v) = kv
322 fields_put.key(m, k, trim(v))
323
324def fields_put.fold(m: Map<&2, List<&2, String>>, line: String, fold: Bool) -> Fields:
325 match fold:
326 case True{}:
327 FieldsBad{}
328 case False{}:
329 fields_put.kv(m, split_colon(line))
330
331def fields_put.empty(m: Map<&2, List<&2, String>>, +line: String, e: Bool) -> Fields:
332 match e:
333 case True{}:
334 FieldsOk{m}
335 case False{}:
336 fields_put.fold(m, line, folded(line))
337
338def fields_put.ok(m: Map<&2, List<&2, String>>, +line: String) -> Fields:
339 fields_put.empty(m, line, String.is_empty(line))
340
341def fields_put(acc: Fields, line: String) -> Fields:
342 match acc:
343 case FieldsBad{}:
344 FieldsBad{}
345 case FieldsOk{m}:
346 fields_put.ok(m, line)
347
348def parse.headers.done(acc: Fields) -> Maybe<&2, Map<&2, List<&2, String>>>:
349 match acc:
350 case FieldsBad{}:
351 None{}
352 case FieldsOk{m}:
353 Some{m}
354
355
356def parse.headers.scan.line(acc: String, fields: Fields) -> Fields:
357 match acc:
358 case SCon{Chr{13}, t}:
359 fields_put(fields, String.reverse(t))
360 case other:
361 fields_put(fields, String.reverse(other))
362
363def parse.headers.scan(s: String, acc: String, fields: Fields) -> Fields:
364 match s:
365 case SNil{}:
366 parse.headers.scan.line(acc, fields)
367 case SCon{Chr{10}, t}:
368 parse.headers.scan(t, SNil{}, parse.headers.scan.line(acc, fields))
369 case SCon{c, t}:
370 parse.headers.scan(t, SCon{c, acc}, fields)
371
372def parse.headers.raw(s: String) -> Maybe<&2, Map<&2, List<&2, String>>>:
373 parse.headers.done(parse.headers.scan(s, SNil{}, FieldsOk{empty()}))
374
375def parse.first(s: String, acc: String) -> String & String:
376 match s:
377 case SNil{}:
378 (String.reverse(acc), SNil{})
379 case SCon{Chr{10}, t}:
380 (String.reverse(acc), t)
381 case SCon{c, t}:
382 parse.first(t, SCon{c, acc})
383
384def parse_u32.digit(+c: U32) -> Bool:
385 Bool.and(U32.is_le(48, c), U32.is_le(c, 57))
386
387def parse_u32.add(acc: U32, c: U32) -> U32:
388 (acc * 10 + (c - 48 : U32) : U32)
389
390def parse_u32.done(acc: U32, bad: Bool) -> Maybe<&2, U32>:
391 match bad:
392 case True{}:
393 None{}
394 case False{}:
395 Some{acc}
396
397def parse_u32.go(s: String, acc: U32, bad: Bool) -> Maybe<&2, U32>:
398 match s:
399 case SNil{}:
400 parse_u32.done(acc, bad)
401 case SCon{Chr{+c}, t}:
402 parse_u32.go(t, parse_u32.add(acc, c), Bool.or(bad, Bool.not(parse_u32.digit(c))))
403
404def parse_u32(s: String) -> Maybe<&2, U32>:
405 match s:
406 case SNil{}:
407 None{}
408 case SCon{Chr{+c}, t}:
409 parse_u32.go(t, parse_u32.add(0, c), Bool.not(parse_u32.digit(c)))
410
411
412type Hold is Data:
413 HoldMore{}
414 HoldBad{}
415 HoldBody{b: String}
416 HoldUntil{b: String}
417
418def is_hex(+c: U32) -> Bool:
419 Bool.or(parse_u32.digit(c), Bool.or(Bool.and(U32.is_le(97, c), U32.is_le(c, 102)), Bool.and(U32.is_le(65, c), U32.is_le(c, 70))))
420
421def hexv(+c: U32) -> U32:
422 Bool.pick(U32, parse_u32.digit(c), (c - 48 : U32), Bool.pick(U32, U32.is_le(c, 70), (c - 55 : U32), (c - 87 : U32)))
423
424
425
426type Dc is Data:
427 DcSize{acc: U32, seen: Bool}
428 DcExt{n: U32}
429 DcSizeLf{n: U32}
430 DcData{k: U32}
431 DcDataCr{}
432 DcDataLf{}
433 DcTr{j: U32}
434 DcDone{}
435 DcBad{}
436
437
438type Dco is Data:
439 Dco{st: Dc, racc: String, rest: String}
440
441def dc.start() -> Dc:
442 DcSize{0, False{}}
443
444def dc.size.semi(+n: U32, semi: Bool) -> Dc:
445 match semi:
446 case True{}:
447 DcExt{n}
448 case False{}:
449 DcBad{}
450
451def dc.size.cr(+n: U32, +c: U32, cr: Bool) -> Dc:
452 match cr:
453 case True{}:
454 DcSizeLf{n}
455 case False{}:
456 dc.size.semi(n, U32.is_eq(c, 59))
457
458def dc.size.end(+n: U32, +c: U32, seen: Bool) -> Dc:
459 match seen:
460 case False{}:
461 DcBad{}
462 case True{}:
463 dc.size.cr(n, c, U32.is_eq(c, 13))
464
465
466def dc.size(+acc: U32, seen: Bool, +c: U32, digit: Bool) -> Dc:
467 match digit:
468 case True{}:
469 DcSize{(acc * 16 + hexv(c) : U32), True{}}
470 case False{}:
471 dc.size.end(acc, c, seen)
472
473
474def dc.ext.lf(+n: U32, lf: Bool) -> Dc:
475 match lf:
476 case True{}:
477 DcBad{}
478 case False{}:
479 DcExt{n}
480
481def dc.ext(+n: U32, +c: U32, cr: Bool) -> Dc:
482 match cr:
483 case True{}:
484 DcSizeLf{n}
485 case False{}:
486 dc.ext.lf(n, U32.is_eq(c, 10))
487
488def dc.sized(+n: U32, zero: Bool) -> Dc:
489 match zero:
490 case True{}:
491 DcTr{2}
492 case False{}:
493 DcData{n}
494
495def dc.size.lf(+n: U32, lf: Bool) -> Dc:
496 match lf:
497 case True{}:
498 dc.sized(n, U32.is_eq(n, 0))
499 case False{}:
500 DcBad{}
501
502
503def dc.data(+k: U32, +m: U32) -> Dc:
504 Bool.pick(Dc, U32.is_le(k, m), DcDataCr{}, DcData{(k - m : U32)})
505
506def dc.pick(ok: Bool, next: Dc) -> Dc:
507 match ok:
508 case True{}:
509 next
510 case False{}:
511 DcBad{}
512
513
514def dc.tr.hit(+j: U32) -> Dc:
515 Bool.pick(Dc, U32.is_eq(j, 3), DcDone{}, DcTr{(j + 1 : U32)})
516
517def dc.tr(+j: U32, +c: U32, hit: Bool) -> Dc:
518 match hit:
519 case True{}:
520 dc.tr.hit(j)
521 case False{}:
522 Bool.pick(Dc, U32.is_eq(c, 13), DcTr{1}, DcTr{0})
523
524def dc.tr.want(+j: U32) -> U32:
525 Bool.pick(U32, U32.is_eq(U32.mod(j, 2), 0), 13, 10)
526
527def dc.byte(h: Char) -> U32:
528 Chr{c} = h
529 c
530
531
532def dc.step(st: Dc, +c: U32) -> Dc:
533 match st:
534 case DcBad{}:
535 DcBad{}
536 case DcSize{+acc, seen}:
537 dc.size(acc, seen, c, Bool.and(is_hex(c), U32.is_le(acc, 268435455)))
538 case DcExt{+n}:
539 dc.ext(n, c, U32.is_eq(c, 13))
540 case DcSizeLf{+n}:
541 dc.size.lf(n, U32.is_eq(c, 10))
542 case DcDataCr{}:
543 dc.pick(U32.is_eq(c, 13), DcDataLf{})
544 case DcDataLf{}:
545 dc.pick(U32.is_eq(c, 10), dc.start())
546 case DcTr{+j}:
547 dc.tr(j, c, U32.is_eq(c, dc.tr.want(j)))
548 case DcData{+k}:
549 dc.data(k, 1)
550 case DcDone{}:
551 DcDone{}
552
553def dc.live(st: Dc) -> Bool:
554 match st:
555 case DcDone{}:
556 False{}
557 case DcBad{}:
558 False{}
559 case DcSize{a, b}:
560 True{}
561 case DcExt{n}:
562 True{}
563 case DcSizeLf{n}:
564 True{}
565 case DcData{k}:
566 True{}
567 case DcDataCr{}:
568 True{}
569 case DcDataLf{}:
570 True{}
571 case DcTr{j}:
572 True{}
573
574def dc.feed(s: String, st: Dc, racc: String) -> Dco:
575 match s:
576 case SNil{}:
577 Dco{st, racc, ""}
578 case SCon{h, t}:
579 match st:
580 case DcBad{}:
581 Dco{DcBad{}, racc, ""}
582 case DcDone{}:
583 Dco{DcDone{}, racc, SCon{h, t}}
584 case DcData{+k}:
585 dc.feed(t, dc.data(k, 1), SCon{h, racc})
586 case DcSize{+acc, seen}:
587 dc.feed(t, dc.step(DcSize{acc, seen}, dc.byte(h)), racc)
588 case DcExt{+n}:
589 dc.feed(t, dc.step(DcExt{n}, dc.byte(h)), racc)
590 case DcSizeLf{+n}:
591 dc.feed(t, dc.step(DcSizeLf{n}, dc.byte(h)), racc)
592 case DcDataCr{}:
593 dc.feed(t, dc.step(DcDataCr{}, dc.byte(h)), racc)
594 case DcDataLf{}:
595 dc.feed(t, dc.step(DcDataLf{}, dc.byte(h)), racc)
596 case DcTr{+j}:
597 dc.feed(t, dc.step(DcTr{j}, dc.byte(h)), racc)
598
599def chunk.hold(o: Dco) -> Hold:
600 Dco{st, racc, rest} = o
601 match st:
602 case DcDone{}:
603 HoldBody{String.reverse(racc)}
604 case DcBad{}:
605 HoldBad{}
606 case DcSize{a, b}:
607 HoldMore{}
608 case DcExt{n}:
609 HoldMore{}
610 case DcSizeLf{n}:
611 HoldMore{}
612 case DcData{k}:
613 HoldMore{}
614 case DcDataCr{}:
615 HoldMore{}
616 case DcDataLf{}:
617 HoldMore{}
618 case DcTr{j}:
619 HoldMore{}
620
621def chunk.live(s: String) -> Hold:
622 chunk.hold(dc.feed(s, dc.start(), ""))
623
624def chunk.rest(o: Dco) -> Maybe<&2, String>:
625 Dco{st, racc, rest} = o
626 match st:
627 case DcDone{}:
628 Some{rest}
629 case DcBad{}:
630 None{}
631 case DcSize{a, b}:
632 None{}
633 case DcExt{n}:
634 None{}
635 case DcSizeLf{n}:
636 None{}
637 case DcData{k}:
638 None{}
639 case DcDataCr{}:
640 None{}
641 case DcDataLf{}:
642 None{}
643 case DcTr{j}:
644 None{}
645
646def chunk.suffix(s: String) -> Maybe<&2, String>:
647 chunk.rest(dc.feed(s, dc.start(), ""))
648
649def chunk.decode.hold(h: Hold) -> Maybe<&2, String>:
650 match h:
651 case HoldMore{}:
652 None{}
653 case HoldBad{}:
654 None{}
655 case HoldUntil{b}:
656 None{}
657 case HoldBody{b}:
658 Some{b}
659
660def chunk.decode(s: String) -> Maybe<&2, String>:
661 chunk.decode.hold(chunk.live(s))
662
663def bytes.snd(r: Bytes.Bytes & Bytes.Bytes) -> Bytes.Bytes:
664 (a, b) = r
665 b
666
667def found.at(+from: U32, m: Maybe<&2, U32>) -> Maybe<&2, U32>:
668 match m:
669 case None{}:
670 None{}
671 case Some{+j}:
672 Some{(from + j : U32)}
673
674
675type Cs is Type:
676 Cs{piece: Bytes.Bytes, i: U32, st: Dc, parts: List<&1, Bytes.Bytes>}
677
678
679type Ck is Type:
680 Ck{st: Dc, parts: List<&1, Bytes.Bytes>, rest: Bytes.Bytes}
681
682def ck.next(+len: U32, +i: U32, +st: Dc, piece: Bytes.Bytes, parts: List<&1, Bytes.Bytes>) -> Bool & Cs:
683 (Bool.and(U32.is_lt(i, len), dc.live(st)), Cs{piece, i, st, parts})
684
685def ck.data(+len: U32, +i: U32, +k: U32, +m: U32, parts: List<&1, Bytes.Bytes>, r: Bytes.Bytes & Bytes.Bytes) -> Bool & Cs:
686 (piece, run) = r
687 ck.next(len, (i + m : U32), dc.data(k, m), piece, Con{run, parts})
688
689def ck.byte(+len: U32, +i: U32, st: Dc, parts: List<&1, Bytes.Bytes>, r: Bytes.Bytes & Maybe<&2, U32>) -> Bool & Cs:
690 (piece, m) = r
691 match m:
692 case None{}:
693 ck.next(len, i, DcBad{}, piece, parts)
694 case Some{+c}:
695 ck.next(len, (i + 1 : U32), dc.step(st, c), piece, parts)
696
697def ck.step(+len: U32, cs: Cs) -> Bool & Cs:
698 Cs{piece, +i, st, parts} = cs
699 match st:
700 case DcData{+k}:
701 +m = U32.min(k, (len - i : U32))
702 ck.data(len, i, k, m, parts, Bytes.slice(piece, i, m))
703 case DcDone{}:
704 (False{}, Cs{piece, i, DcDone{}, parts})
705 case DcBad{}:
706 (False{}, Cs{piece, i, DcBad{}, parts})
707 case DcSize{+acc, seen}:
708 ck.byte(len, i, DcSize{acc, seen}, parts, Bytes.get(piece, i))
709 case DcExt{+n}:
710 ck.byte(len, i, DcExt{n}, parts, Bytes.get(piece, i))
711 case DcSizeLf{+n}:
712 ck.byte(len, i, DcSizeLf{n}, parts, Bytes.get(piece, i))
713 case DcDataCr{}:
714 ck.byte(len, i, DcDataCr{}, parts, Bytes.get(piece, i))
715 case DcDataLf{}:
716 ck.byte(len, i, DcDataLf{}, parts, Bytes.get(piece, i))
717 case DcTr{+j}:
718 ck.byte(len, i, DcTr{j}, parts, Bytes.get(piece, i))
719
720def ck.rest(st: Dc, parts: List<&1, Bytes.Bytes>, r: Bytes.Bytes & Bytes.Bytes) -> Ck:
721 (piece, rest) = r
722 Ck{st, parts, rest}
723
724def ck.end(+len: U32, cs: Cs) -> Ck:
725 Cs{piece, +i, st, parts} = cs
726 ck.rest(st, parts, Bytes.slice(piece, i, (len - i : U32)))
727
728
729def ck.feed(f: Nat, +len: U32, r: Bool & Cs) -> Ck:
730 match f:
731 case 0n:
732 (go, cs) = r
733 ck.end(len, cs)
734 case 1n+p:
735 (go, cs) = r
736 match go:
737 case False{}:
738 ck.end(len, cs)
739 case True{}:
740 ck.feed(p, len, ck.step(len, cs))
741
742def ck(piece: Bytes.Bytes, +st: Dc, parts: List<&1, Bytes.Bytes>) -> Ck:
743 Bytes.Bytes{+len, buf} = piece
744 ck.feed(U32.to_nat(len), len, ck.next(len, 0, st, Bytes.Bytes{len, buf}, parts))
745
746def parse.res.cl.have(+rest: String, +k: Nat, short: Bool) -> Hold:
747 match short:
748 case True{}:
749 HoldMore{}
750 case False{}:
751 HoldBody{String.take(rest, k)}
752
753def parse.res.cl.k(+rest: String, +k: Nat) -> Hold:
754 parse.res.cl.have(rest, k, Nat.is_lt(String.length(rest), k))
755
756def parse.res.cl.n(rest: String, n: Maybe<&2, U32>) -> Hold:
757 match n:
758 case None{}:
759 HoldBad{}
760 case Some{k}:
761 parse.res.cl.k(rest, U32.to_nat(k))
762
763def parse.body.cl(+h: Map<&2, List<&2, String>>, rest: String, hascl: Bool) -> Hold:
764 match hascl:
765 case False{}:
766 HoldBody{""}
767 case True{}:
768 parse.res.cl.n(rest, parse_u32(header(h, "content-length")))
769
770
771def parse.body.chunked.eq(rest: String, ok: Bool) -> Hold:
772 match ok:
773 case False{}:
774 HoldBad{}
775 case True{}:
776 chunk.live(rest)
777
778def parse.body.chunked.val(te: String, rest: String) -> Hold:
779 parse.body.chunked.eq(rest, String.eq(String.to_lower(te), "chunked"))
780
781def parse.body.chunked(+h: Map<&2, List<&2, String>>, rest: String, hascl: Bool) -> Hold:
782 match hascl:
783 case True{}:
784 HoldBad{}
785 case False{}:
786 parse.body.chunked.val(header.last(h, "transfer-encoding"), rest)
787
788def parse.body.te(+h: Map<&2, List<&2, String>>, rest: String, te: Bool) -> Hold:
789 match te:
790 case True{}:
791 parse.body.chunked(h, rest, has_header(h, "content-length"))
792 case False{}:
793 parse.body.cl(h, rest, has_header(h, "content-length"))
794
795def parse.body(+h: Map<&2, List<&2, String>>, rest: String) -> Hold:
796 parse.body.te(h, rest, has_header(h, "transfer-encoding"))
797
798
799
800type Got is Type:
801 GotBad{}
802 GotMore{}
803 GotHead{req: Req}
804 GotReq{req: Req}
805
806def parse.finish(method: String, path: String, h: Map<&2, List<&2, String>>, body: Hold) -> Got:
807 match body:
808 case HoldBad{}:
809 GotBad{}
810 case HoldMore{}:
811 GotHead{Req{method, path, h, Bytes.new(0)}}
812 case HoldUntil{b}:
813 GotBad{}
814 case HoldBody{b}:
815 GotReq{Req{method, path, h, Bytes.from_string(b)}}
816
817def parse.host(method: String, path: String, +h: Map<&2, List<&2, String>>, rest: String, has: Bool) -> Got:
818 match has:
819 case False{}:
820 GotBad{}
821 case True{}:
822 parse.finish(method, path, h, parse.body(h, rest))
823
824
825def parse.fields(method: String, path: String, rest: String, v11: Bool, fs: Maybe<&2, Map<&2, List<&2, String>>>) -> Got:
826 match fs:
827 case None{}:
828 GotBad{}
829 case Some{+h}:
830 parse.host(method, path, h, rest, Bool.or(Bool.not(v11), has_header(h, "host")))
831
832def target_ok(+p: String) -> Bool:
833 Bool.or(String.starts_with(p, "/"), Bool.or(String.eq(p, "*"), String.starts_with(p, "http://")))
834
835def parse.target(method: String, +path: String, headers: String, rest: String, v11: Bool, ok: Bool) -> Got:
836 match ok:
837 case False{}:
838 GotBad{}
839 case True{}:
840 parse.fields(method, path, rest, v11, parse.headers.raw(headers))
841
842def parse.ver(method: String, +path: String, +v: String, headers: String, rest: String, ok: Bool) -> Got:
843 match ok:
844 case False{}:
845 GotBad{}
846 case True{}:
847 parse.target(method, path, headers, rest, String.eq(v, "HTTP/1.1"), target_ok(path))
848
849def parse.empty_method(method: String, path: String, +v: String, headers: String, rest: String, empty: Bool) -> Got:
850 match empty:
851 case True{}:
852 GotBad{}
853 case False{}:
854 parse.ver(method, path, v, headers, rest, Bool.or(String.eq(v, "HTTP/1.1"), String.eq(v, "HTTP/1.0")))
855
856def parse.start3(mpv: String & String & String, headers: String, rest: String) -> Got:
857 (+method, path, v) = mpv
858 parse.empty_method(method, path, v, headers, rest, String.is_empty(method))
859
860def parse.lines(lr: String & String, rest: String) -> Got:
861 (line, headers) = lr
862 parse.start3(start_line(line), headers, rest)
863
864
865def parse.head(+head: String, rest: String, whole: Bool) -> Got:
866 match whole:
867 case False{}:
868 GotMore{}
869 case True{}:
870 parse.lines(parse.first(head, SNil{}), rest)
871
872def parse.of(hb: String & String & Bool) -> Got:
873 (head, rest, whole) = hb
874 parse.head(head, rest, whole)
875
876
877def parse.got(+raw: String) -> Got:
878 parse.of(split_at_blank.flag.go(raw, raw, 0n, 0, False{}))
879
880
881def parse.req(g: Got) -> Maybe<&1, Req>:
882 match g:
883 case GotReq{req}:
884 Some{req}
885 case GotBad{}:
886 None{}
887 case GotMore{}:
888 None{}
889 case GotHead{req}:
890 None{}
891
892def parse(raw: String) -> Maybe<&1, Req>:
893 parse.req(parse.got(raw))
894
895def parse.res.cl(+h: Map<&2, List<&2, String>>, rest: String, hascl: Bool) -> Hold:
896 match hascl:
897 case False{}:
898 HoldBody{rest}
899 case True{}:
900 parse.res.cl.n(rest, parse_u32(header(h, "content-length")))
901
902def parse.res.chunked.eq(rest: String, ok: Bool) -> Hold:
903 match ok:
904 case False{}:
905 HoldUntil{rest}
906 case True{}:
907 chunk.live(rest)
908
909def parse.res.chunked.val(te: String, rest: String) -> Hold:
910 parse.res.chunked.eq(rest, String.eq(String.to_lower(te), "chunked"))
911
912def parse.res.chunked(+h: Map<&2, List<&2, String>>, rest: String, hascl: Bool) -> Hold:
913 match hascl:
914 case True{}:
915 HoldBad{}
916 case False{}:
917 parse.res.chunked.val(header.last(h, "transfer-encoding"), rest)
918
919def parse.res.te(+h: Map<&2, List<&2, String>>, rest: String, te: Bool) -> Hold:
920 match te:
921 case True{}:
922 parse.res.chunked(h, rest, has_header(h, "content-length"))
923 case False{}:
924 parse.res.cl(h, rest, has_header(h, "content-length"))
925
926def parse.res.body(+h: Map<&2, List<&2, String>>, rest: String) -> Hold:
927 parse.res.te(h, rest, has_header(h, "transfer-encoding"))
928
929
930type Frame is Type:
931 FrameBad{}
932 FrameMore{}
933 FrameDone{res: Res}
934
935
936def frame.cut(closed: Bool) -> Frame:
937 match closed:
938 case True{}:
939 FrameBad{}
940 case False{}:
941 FrameMore{}
942
943def frame.wait(res: Res, wait: Bool) -> Frame:
944 match wait:
945 case True{}:
946 FrameMore{}
947 case False{}:
948 FrameDone{res}
949
950def frame.done(status: U32, h: Map<&2, List<&2, String>>, body: Hold, +closed: Bool, open: Bool) -> Frame:
951 match body:
952 case HoldBad{}:
953 FrameBad{}
954 case HoldMore{}:
955 frame.cut(closed)
956 case HoldUntil{b}:
957 frame.wait(Res{status, h, Bytes.from_string(b)}, Bool.not(closed))
958 case HoldBody{b}:
959 frame.wait(Res{status, h, Bytes.from_string(b)}, Bool.and(open, Bool.not(closed)))
960
961
962def frame.open(+h: Map<&2, List<&2, String>>) -> Bool:
963 Bool.not(Bool.or(has_header(h, "transfer-encoding"), has_header(h, "content-length")))
964
965
966def frame.nobody(+status: U32, head: Bool) -> Bool:
967 Bool.or(head, Bool.or(U32.is_lt(status, 200), Bool.or(U32.is_eq(status, 204), U32.is_eq(status, 304))))
968
969
970def frame.interim(+status: U32) -> Bool:
971 Bool.and(U32.is_lt(status, 200), Bool.not(U32.is_eq(status, 101)))
972
973def frame.status(status: U32, +h: Map<&2, List<&2, String>>, rest: String, closed: Bool, nobody: Bool) -> Frame:
974 match nobody:
975 case True{}:
976 FrameDone{Res{status, h, Bytes.new(0)}}
977 case False{}:
978 frame.done(status, h, parse.res.body(h, rest), closed, frame.open(h))
979
980def parse.res.fields(+status: U32, rest: String, fs: Maybe<&2, Map<&2, List<&2, String>>>, closed: Bool, head: Bool) -> Frame:
981 match fs:
982 case None{}:
983 FrameBad{}
984 case Some{h}:
985 frame.status(status, h, rest, closed, frame.nobody(status, head))
986
987def parse.res.num(code: Maybe<&2, U32>, headers: String, rest: String, closed: Bool, head: Bool) -> Frame:
988 match code:
989 case None{}:
990 FrameBad{}
991 case Some{n}:
992 parse.res.fields(n, rest, parse.headers.raw(headers), closed, head)
993
994def parse.res.ver(code: String, headers: String, rest: String, closed: Bool, head: Bool, ok: Bool) -> Frame:
995 match ok:
996 case False{}:
997 FrameBad{}
998 case True{}:
999 parse.res.num(parse_u32(code), headers, rest, closed, head)
1000
1001def parse.res.start3(mpv: String & String & String, headers: String, rest: String, closed: Bool, head: Bool) -> Frame:
1002 (+ver, code, reason) = mpv
1003 parse.res.ver(code, headers, rest, closed, head, Bool.or(String.eq(ver, "HTTP/1.1"), String.eq(ver, "HTTP/1.0")))
1004
1005def parse.res.lines(lr: String & String, rest: String, closed: Bool, head: Bool) -> Frame:
1006 (line, headers) = lr
1007 parse.res.start3(start_line(line), headers, rest, closed, head)
1008
1009def parse.res.blank(hd: String, rest: String, +closed: Bool, head: Bool, ok: Bool) -> Frame:
1010 match ok:
1011 case False{}:
1012 frame.cut(closed)
1013 case True{}:
1014 parse.res.lines(parse.first(hd, SNil{}), rest, closed, head)
1015
1016def parse.res.of(hb: String & String, closed: Bool, head: Bool) -> Frame:
1017 (+hd, rest) = hb
1018 parse.res.blank(hd, rest, closed, head, blank_end(hd))
1019
1020type Lead is Data:
1021 LeadCut{}
1022 LeadFinal{}
1023 LeadSkip{+rest: String}
1024
1025def frame.lead.if(rest: String, interim: Bool) -> Lead:
1026 match interim:
1027 case True{}:
1028 LeadSkip{rest}
1029 case False{}:
1030 LeadFinal{}
1031
1032def frame.lead.num(n: Maybe<&2, U32>, rest: String) -> Lead:
1033 match n:
1034 case None{}:
1035 LeadFinal{}
1036 case Some{s}:
1037 frame.lead.if(rest, frame.interim(s))
1038
1039def frame.lead.code(mpv: String & String & String, rest: String) -> Lead:
1040 (ver, code, reason) = mpv
1041 frame.lead.num(parse_u32(code), rest)
1042
1043def frame.lead.lines(xs: List<&2, String>, rest: String) -> Lead:
1044 match xs:
1045 case Nil{}:
1046 LeadFinal{}
1047 case Con{line, headers}:
1048 frame.lead.code(start_line(line), rest)
1049
1050def frame.lead.blank(+hd: String, rest: String, ok: Bool) -> Lead:
1051 match ok:
1052 case False{}:
1053 LeadCut{}
1054 case True{}:
1055 frame.lead.lines(String.lines(hd), rest)
1056
1057def frame.lead(hb: String & String) -> Lead:
1058 (+hd, rest) = hb
1059 frame.lead.blank(hd, rest, blank_end(hd))
1060
1061def frame.at.cont(+raw: String, lead: Lead) -> String:
1062 match lead:
1063 case LeadCut{}:
1064 raw
1065 case LeadFinal{}:
1066 raw
1067 case LeadSkip{rest}:
1068 rest
1069
1070def frame.at.zero.go(+raw: String, closed: Bool, head: Bool, lead: Lead) -> Frame:
1071 match lead:
1072 case LeadCut{}:
1073 frame.cut(closed)
1074 case LeadFinal{}:
1075 parse.res.of(split_at_blank(raw), closed, head)
1076 case LeadSkip{rest}:
1077 FrameBad{}
1078
1079def frame.at.zero.of(+raw: String, closed: Bool, head: Bool) -> Frame:
1080 frame.at.zero.go(raw, closed, head, frame.lead(split_at_blank(raw)))
1081
1082def frame.at.pos.do(f: Nat, +raw: String, closed: Bool, head: Bool, hb: String & String, lead: Lead) -> Frame:
1083 match f:
1084 case 0n:
1085 match lead:
1086 case LeadCut{}:
1087 frame.cut(closed)
1088 case LeadFinal{}:
1089 parse.res.of(hb, closed, head)
1090 case LeadSkip{+rest}:
1091 frame.at.zero.of(rest, closed, head)
1092 case 1n+g:
1093 match lead:
1094 case LeadCut{}:
1095 frame.cut(closed)
1096 case LeadFinal{}:
1097 parse.res.of(hb, closed, head)
1098 case LeadSkip{+rest}:
1099 frame.at.pos.do(g, rest, closed, head, split_at_blank(rest), frame.lead(split_at_blank(rest)))
1100
1101def frame.at.zero(+raw: String, closed: Bool, head: Bool) -> Frame:
1102 frame.at.zero.of(raw, closed, head)
1103
1104
1105def frame.at(fuel: Nat, +raw: String, closed: Bool, head: Bool) -> Frame:
1106 match fuel:
1107 case 0n:
1108 frame.at.zero(raw, closed, head)
1109 case 1n+f:
1110 frame.at.pos.do(f, raw, closed, head, split_at_blank(raw), frame.lead(split_at_blank(raw)))
1111
1112
1113def frame(raw: String, closed: Bool, head: Bool) -> Frame:
1114 frame.at(8n, raw, closed, head)
1115
1116def frame.res(f: Frame) -> Maybe<&1, Res>:
1117 match f:
1118 case FrameDone{res}:
1119 Some{res}
1120 case FrameBad{}:
1121 None{}
1122 case FrameMore{}:
1123 None{}
1124
1125def parse_res(raw: String) -> Maybe<&1, Res>:
1126 frame.res(frame(raw, True{}, False{}))
1127
1128def res_fields.res(res: Res, k: String) -> List<&2, String>:
1129 Res{status, headers, body} = res
1130 fields(headers, k)
1131
1132def res_fields(m: Maybe<&1, Res>, k: String) -> List<&2, String>:
1133 match m:
1134 case None{}:
1135 Nil{}
1136 case Some{res}:
1137 res_fields.res(res, k)
1138
1139
1140
1141type Rb is Data:
1142 RbLen{left: U32}
1143 RbChunk{st: Dc}
1144 RbClose{}
1145 RbDone{}
1146 RbBad{}
1147
1148def rb.cl(n: Maybe<&2, U32>) -> Maybe<&2, Rb>:
1149 match n:
1150 case None{}:
1151 None{}
1152 case Some{+k}:
1153 Some{Bool.pick(Rb, U32.is_zero(k), RbDone{}, RbLen{k})}
1154
1155def rb.te(+h: Map<&2, List<&2, String>>, cl: Bool) -> Maybe<&2, Rb>:
1156 match cl:
1157 case True{}:
1158 None{}
1159 case False{}:
1160 Some{Bool.pick(Rb, String.eq(String.to_lower(header.last(h, "transfer-encoding")), "chunked"), RbChunk{dc.start()}, RbClose{})}
1161
1162def rb.len(+h: Map<&2, List<&2, String>>, te: Bool) -> Maybe<&2, Rb>:
1163 match te:
1164 case True{}:
1165 rb.te(h, has_header(h, "content-length"))
1166 case False{}:
1167 Bool.pick(Maybe<&2, Rb>, has_header(h, "content-length"), rb.cl(parse_u32(header(h, "content-length"))), Some{RbClose{}})
1168
1169
1170def rb.of(+h: Map<&2, List<&2, String>>, nobody: Bool) -> Maybe<&2, Rb>:
1171 match nobody:
1172 case True{}:
1173 Some{RbDone{}}
1174 case False{}:
1175 rb.len(h, has_header(h, "transfer-encoding"))
1176
1177
1178type Bf is Type:
1179 Bf{rb: Rb, parts: List<&1, Bytes.Bytes>, rest: Bytes.Bytes}
1180
1181def rb.len.cut(parts: List<&1, Bytes.Bytes>, +len: U32, +left: U32, r: Bytes.Bytes & Bytes.Bytes) -> Bf:
1182 (piece, data) = r
1183 Bf{RbDone{}, Con{data, parts}, bytes.snd(Bytes.slice(piece, left, (len - left : U32)))}
1184
1185def rb.len.have(short: Bool, +left: U32, parts: List<&1, Bytes.Bytes>, +len: U32, piece: Bytes.Bytes) -> Bf:
1186 match short:
1187 case True{}:
1188 Bf{RbLen{(left - len : U32)}, Con{piece, parts}, Bytes.new(0)}
1189 case False{}:
1190 rb.len.cut(parts, len, left, Bytes.slice(piece, 0, left))
1191
1192def rb.chunk(c: Ck) -> Bf:
1193 Ck{st, parts, rest} = c
1194 match st:
1195 case DcDone{}:
1196 Bf{RbDone{}, parts, rest}
1197 case DcBad{}:
1198 Bf{RbBad{}, parts, rest}
1199 case DcSize{a, b}:
1200 Bf{RbChunk{DcSize{a, b}}, parts, rest}
1201 case DcExt{x}:
1202 Bf{RbChunk{DcExt{x}}, parts, rest}
1203 case DcSizeLf{x}:
1204 Bf{RbChunk{DcSizeLf{x}}, parts, rest}
1205 case DcData{k}:
1206 Bf{RbChunk{DcData{k}}, parts, rest}
1207 case DcDataCr{}:
1208 Bf{RbChunk{DcDataCr{}}, parts, rest}
1209 case DcDataLf{}:
1210 Bf{RbChunk{DcDataLf{}}, parts, rest}
1211 case DcTr{j}:
1212 Bf{RbChunk{DcTr{j}}, parts, rest}
1213
1214
1215def rb.feed(rb: Rb, parts: List<&1, Bytes.Bytes>, piece: Bytes.Bytes) -> Bf:
1216 match rb:
1217 case RbLen{+left}:
1218 Bytes.Bytes{+len, buf} = piece
1219 rb.len.have(U32.is_lt(len, left), left, parts, len, Bytes.Bytes{len, buf})
1220 case RbChunk{st}:
1221 rb.chunk(ck(piece, st, parts))
1222 case RbClose{}:
1223 Bf{RbClose{}, Con{piece, parts}, Bytes.new(0)}
1224 case RbDone{}:
1225 Bf{RbDone{}, parts, piece}
1226 case RbBad{}:
1227 Bf{RbBad{}, parts, piece}
1228
1229
1230def rb.eof(rb: Rb) -> Bool:
1231 match rb:
1232 case RbClose{}:
1233 True{}
1234 case RbDone{}:
1235 True{}
1236 case RbLen{n}:
1237 False{}
1238 case RbChunk{st}:
1239 False{}
1240 case RbBad{}:
1241 False{}
1242
1243def rb.body(parts: List<&1, Bytes.Bytes>) -> Bytes.Bytes:
1244 Bytes.concat(List.reverse(&1, Bytes.Bytes, parts))
1245
1246
1247
1248type Hs is Type:
1249 HsSeek{buf: Bytes.Bytes, from: U32, k: U32}
1250 HsWait{buf: Bytes.Bytes, k: U32}
1251 HsBad{}
1252 HsRes{res: Res, rb: Rb, rest: Bytes.Bytes}
1253
1254def hs.final(+status: U32, +h: Map<&2, List<&2, String>>, rest: Bytes.Bytes, m: Maybe<&2, Rb>) -> Hs:
1255 match m:
1256 case None{}:
1257 HsBad{}
1258 case Some{rb}:
1259 HsRes{Res{status, h, Bytes.new(0)}, rb, rest}
1260
1261def hs.skip(more: Bool, +k: U32, rest: Bytes.Bytes) -> Hs:
1262 match more:
1263 case True{}:
1264 HsSeek{rest, 0, (k + 1 : U32)}
1265 case False{}:
1266 HsBad{}
1267
1268def hs.pick(interim: Bool, +head: Bool, +k: U32, +status: U32, +h: Map<&2, List<&2, String>>, rest: Bytes.Bytes) -> Hs:
1269 match interim:
1270 case True{}:
1271 hs.skip(U32.is_lt(k, 8), k, rest)
1272 case False{}:
1273 hs.final(status, h, rest, rb.of(h, frame.nobody(status, head)))
1274
1275def hs.res(+head: Bool, +k: U32, rest: Bytes.Bytes, res: Res) -> Hs:
1276 Res{+status, +h, body} = res
1277 hs.pick(frame.interim(status), head, k, status, h, rest)
1278
1279
1280def hs.parsed(+head: Bool, +k: U32, rest: Bytes.Bytes, f: Frame) -> Hs:
1281 match f:
1282 case FrameDone{res}:
1283 hs.res(head, k, rest, res)
1284 case FrameBad{}:
1285 HsBad{}
1286 case FrameMore{}:
1287 HsBad{}
1288
1289def hs.split(+head: Bool, +k: U32, +hlen: U32, +len: U32, r: Bytes.Bytes & Bytes.Bytes) -> Hs:
1290 (buf, hb) = r
1291 hs.parsed(head, k, bytes.snd(Bytes.slice(buf, hlen, (len - hlen : U32))), parse.res.of(split_at_blank(Bytes.to_string(hb)), False{}, True{}))
1292
1293def hs.hit(+head: Bool, +k: U32, +len: U32, buf: Bytes.Bytes, at: Maybe<&2, U32>) -> Hs:
1294 match at:
1295 case None{}:
1296 HsWait{buf, k}
1297 case Some{+i}:
1298 +hlen = (i + 4 : U32)
1299 hs.split(head, k, hlen, len, Bytes.slice(buf, 0, hlen))
1300
1301def hs.seek.of(+head: Bool, +k: U32, +len: U32, +from: U32, buf: Bytes.Bytes, r: Bytes.Bytes & Maybe<&2, U32>) -> Hs:
1302 (tail, m) = r
1303 hs.hit(head, k, len, buf, found.at(from, m))
1304
1305def hs.seek(+head: Bool, +k: U32, +len: U32, +from: U32, r: Bytes.Bytes & Bytes.Bytes) -> Hs:
1306 (buf, tail) = r
1307 hs.seek.of(head, k, len, from, buf, Bytes.find(tail, "\r\n\r\n"))
1308
1309def hs.step(+head: Bool, st: Hs) -> Hs:
1310 match st:
1311 case HsSeek{buf, +from, +k}:
1312 Bytes.Bytes{+len, b} = buf
1313 hs.seek(head, k, len, from, Bytes.slice(Bytes.Bytes{len, b}, from, (len - from : U32)))
1314 case HsWait{buf, k}:
1315 HsWait{buf, k}
1316 case HsBad{}:
1317 HsBad{}
1318 case HsRes{res, rb, rest}:
1319 HsRes{res, rb, rest}
1320
1321
1322def hs.scan(fuel: Nat, +head: Bool, st: Hs) -> Hs:
1323 match fuel:
1324 case 0n:
1325 match st:
1326 case HsSeek{buf, from, k}:
1327 HsBad{}
1328 case HsWait{buf, k}:
1329 HsWait{buf, k}
1330 case HsBad{}:
1331 HsBad{}
1332 case HsRes{res, rb, rest}:
1333 HsRes{res, rb, rest}
1334 case 1n+f:
1335 hs.scan(f, head, hs.step(head, st))
1336
1337
1338def hs.more(+head: Bool, +k: U32, buf: Bytes.Bytes, piece: Bytes.Bytes) -> Hs:
1339 Bytes.Bytes{+ol, ob} = buf
1340 hs.scan(10n, head, HsSeek{Bytes.append(Bytes.Bytes{ol, ob}, piece), Bool.pick(U32, U32.is_lt(ol, 3), 0, (ol - 3 : U32)), k})
1341
1342
1343def fetch.max() -> Nat:
1344 U32.to_nat(16777216)
1345
1346
1347
1348type Rv is Type:
1349 RvHead{buf: Bytes.Bytes, k: U32}
1350 RvBody{res: Res, rb: Rb, parts: List<&1, Bytes.Bytes>}
1351 RvLen{res: Res, left: U32, body: Bytes.Bytes}
1352
1353
1354type Rt is Type:
1355 RtMore{rv: Rv}
1356 RtBad{}
1357 RtDone{res: Res, rest: Maybe<&1, Bytes.Bytes>}
1358
1359def rv.start() -> Rv:
1360 RvHead{Bytes.new(0), 0}
1361
1362def rv.whole(res: Res, b: Bytes.Bytes, rest: Maybe<&1, Bytes.Bytes>) -> Rt:
1363 Res{status, headers, body} = res
1364 RtDone{Res{status, headers, b}, rest}
1365
1366def rv.done(res: Res, parts: List<&1, Bytes.Bytes>, rest: Maybe<&1, Bytes.Bytes>) -> Rt:
1367 rv.whole(res, rb.body(parts), rest)
1368
1369def rl.put.of(+total: U32, +len: U32, r: Array<U32> & Array<U32>) -> Bytes.Bytes & Bytes.Bytes:
1370 (src, dst) = r
1371 (Bytes.Bytes{total, dst}, Bytes.Bytes{len, src})
1372
1373
1374def rl.put(+m: U32, +left: U32, body: Bytes.Bytes, piece: Bytes.Bytes) -> Bytes.Bytes & Bytes.Bytes:
1375 Bytes.Bytes{+total, dst} = body
1376 Bytes.Bytes{+len, src} = piece
1377 rl.put.of(total, len, Bytes.copy(m, src, dst, 0, (total - left : U32)))
1378
1379def rl.fed(short: Bool, res: Res, +left: U32, +len: U32, r: Bytes.Bytes & Bytes.Bytes) -> Rt:
1380 match short:
1381 case True{}:
1382 (body, piece) = r
1383 RtMore{RvLen{res, (left - len : U32), body}}
1384 case False{}:
1385 (body, piece) = r
1386 rv.whole(res, body, Some{bytes.snd(Bytes.slice(piece, left, (len - left : U32)))})
1387
1388def rl.feed(res: Res, +left: U32, body: Bytes.Bytes, piece: Bytes.Bytes) -> Rt:
1389 Bytes.Bytes{+len, buf} = piece
1390 rl.fed(U32.is_lt(len, left), res, left, len, rl.put(U32.min(len, left), left, body, Bytes.Bytes{len, buf}))
1391
1392def rv.fed(res: Res, bf: Bf) -> Rt:
1393 Bf{rb, parts, rest} = bf
1394 match rb:
1395 case RbDone{}:
1396 rv.done(res, parts, Some{rest})
1397 case RbBad{}:
1398 RtBad{}
1399 case RbLen{n}:
1400 RtMore{RvBody{res, RbLen{n}, parts}}
1401 case RbChunk{st}:
1402 RtMore{RvBody{res, RbChunk{st}, parts}}
1403 case RbClose{}:
1404 RtMore{RvBody{res, RbClose{}, parts}}
1405
1406
1407def rb.buf(rb: Rb) -> Maybe<&2, U32>:
1408 match rb:
1409 case RbLen{+left}:
1410 Bool.pick(Maybe<&2, U32>, Nat.is_lt(fetch.max(), U32.to_nat(left)), None{}, Some{left})
1411 case RbChunk{st}:
1412 None{}
1413 case RbClose{}:
1414 None{}
1415 case RbDone{}:
1416 None{}
1417 case RbBad{}:
1418 None{}
1419
1420def rv.body(m: Maybe<&2, U32>, res: Res, rb: Rb, rest: Bytes.Bytes) -> Rt:
1421 match m:
1422 case None{}:
1423 rv.fed(res, rb.feed(rb, Nil{}, rest))
1424 case Some{+left}:
1425 rl.feed(res, left, Bytes.new(left), rest)
1426
1427def rv.head(hs: Hs) -> Rt:
1428 match hs:
1429 case HsWait{buf, k}:
1430 RtMore{RvHead{buf, k}}
1431 case HsBad{}:
1432 RtBad{}
1433 case HsSeek{buf, from, k}:
1434 RtBad{}
1435 case HsRes{res, +rb, rest}:
1436 rv.body(rb.buf(rb), res, rb, rest)
1437
1438def rv.eof(res: Res, parts: List<&1, Bytes.Bytes>, ok: Bool) -> Rt:
1439 match ok:
1440 case True{}:
1441 rv.done(res, parts, None{})
1442 case False{}:
1443 RtBad{}
1444
1445
1446def rv.step(closed: Bool, +head: Bool, rv: Rv, piece: Bytes.Bytes) -> Rt:
1447 match closed:
1448 case True{}:
1449 match rv:
1450 case RvHead{buf, k}:
1451 RtBad{}
1452 case RvBody{res, rb, parts}:
1453 rv.eof(res, parts, rb.eof(rb))
1454 case RvLen{res, left, body}:
1455 RtBad{}
1456 case False{}:
1457 match rv:
1458 case RvHead{buf, +k}:
1459 rv.head(hs.more(head, k, buf, piece))
1460 case RvBody{res, rb, parts}:
1461 rv.fed(res, rb.feed(rb, parts, piece))
1462 case RvLen{res, +left, body}:
1463 rl.feed(res, left, body, piece)
1464
1465def rv.end(closed: Bool, +head: Bool, t: Rt) -> Rt:
1466 match closed:
1467 case False{}:
1468 t
1469 case True{}:
1470 match t:
1471 case RtMore{rv}:
1472 rv.step(True{}, head, rv, Bytes.new(0))
1473 case RtBad{}:
1474 RtBad{}
1475 case RtDone{res, rest}:
1476 RtDone{res, rest}
1477
1478def rv.go(xs: List<&2, String>, closed: Bool, +head: Bool, t: Rt) -> Rt:
1479 match xs:
1480 case Nil{}:
1481 rv.end(closed, head, t)
1482 case Con{+x, rest}:
1483 match t:
1484 case RtMore{rv}:
1485 rv.go(rest, closed, head, rv.step(False{}, head, rv, Bytes.from_string(x)))
1486 case RtBad{}:
1487 RtBad{}
1488 case RtDone{res, r}:
1489 RtDone{res, r}
1490
1491def rt.frame(t: Rt) -> Frame:
1492 match t:
1493 case RtMore{rv}:
1494 FrameMore{}
1495 case RtBad{}:
1496 FrameBad{}
1497 case RtDone{res, rest}:
1498 FrameDone{res}
1499
1500
1501def res.frame(xs: List<&2, String>, closed: Bool, +head: Bool) -> Frame:
1502 rt.frame(rv.go(xs, closed, head, RtMore{rv.start()}))
1503
1504def rt.rest(t: Rt) -> Maybe<&1, Bytes.Bytes>:
1505 match t:
1506 case RtMore{rv}:
1507 None{}
1508 case RtBad{}:
1509 None{}
1510 case RtDone{res, rest}:
1511 rest
1512
1513def after.of(m: Maybe<&1, Bytes.Bytes>) -> Maybe<&2, String>:
1514 match m:
1515 case None{}:
1516 None{}
1517 case Some{b}:
1518 Some{Bytes.to_string(b)}
1519
1520
1521def after(+raw: String, +head: Bool) -> Maybe<&2, String>:
1522 after.of(rt.rest(rv.go([raw], False{}, head, RtMore{rv.start()})))
1523
1524def req.v11.ver(mpv: String & String & String) -> Bool:
1525 (+method, path, v) = mpv
1526 String.eq(v, "HTTP/1.1")
1527
1528def req.v11.line(+line: String) -> Bool:
1529 req.v11.ver(start_line(line))
1530
1531def req.v11.lines(xs: List<&2, String>) -> Bool:
1532 match xs:
1533 case Nil{}:
1534 False{}
1535 case Con{line, headers}:
1536 req.v11.line(line)
1537
1538def req.v11.pick(+head: String, ok: Bool) -> Bool:
1539 match ok:
1540 case False{}:
1541 False{}
1542 case True{}:
1543 req.v11.lines(String.lines(head))
1544
1545def req.v11.of(hb: String & String) -> Bool:
1546 (+head, rest) = hb
1547 req.v11.pick(head, blank_end(head))
1548
1549def req.v11(+raw: String) -> Bool:
1550 req.v11.of(split_at_blank(raw))
1551
1552def req.conn_close(+headers: Map<&2, List<&2, String>>) -> Bool:
1553 String.eq(String.to_lower(header(headers, "connection")), "close")
1554
1555def res.conn_close(+headers: Map<&2, List<&2, String>>) -> Bool:
1556 String.eq(String.to_lower(header(headers, "connection")), "close")
1557
1558def serve.fail_close(+status: U32) -> Bool:
1559 Bool.or(U32.is_eq(status, 400), Bool.or(U32.is_eq(status, 413), U32.is_eq(status, 431)))
1560
1561def serve.keep.ok(fail: Bool, res_close: Bool) -> Bool:
1562 match fail:
1563 case True{}:
1564 False{}
1565 case False{}:
1566 Bool.not(res_close)
1567
1568def serve.keep.v11(v11: Bool, +req_close: Bool, +status: U32, res_close: Bool) -> Bool:
1569 match v11:
1570 case False{}:
1571 False{}
1572 case True{}:
1573 match req_close:
1574 case True{}:
1575 False{}
1576 case False{}:
1577 serve.keep.ok(serve.fail_close(status), res_close)
1578
1579def serve.keep(+headers: Map<&2, List<&2, String>>, v11: Bool, +status: U32, +res: Map<&2, List<&2, String>>) -> Bool:
1580 serve.keep.v11(v11, req.conn_close(headers), status, res.conn_close(res))
1581
1582
1583def again.keep(close: Bool, x: Bytes.Bytes) -> Maybe<&1, Bytes.Bytes>:
1584 match close:
1585 case True{}:
1586 None{}
1587 case False{}:
1588 Some{x}
1589
1590def again.rest(close: Bool, rest: Maybe<&1, Bytes.Bytes>) -> Maybe<&1, Bytes.Bytes>:
1591 match rest:
1592 case None{}:
1593 None{}
1594 case Some{x}:
1595 again.keep(close, x)
1596
1597
1598
1599def again(close: Bool, +h: Map<&2, List<&2, String>>, rest: Maybe<&1, Bytes.Bytes>) -> Maybe<&1, Bytes.Bytes>:
1600 match close:
1601 case True{}:
1602 None{}
1603 case False{}:
1604 again.rest(String.eq(String.to_lower(header(h, "connection")), "close"), rest)
1605
1606def res_body.res(res: Res) -> Bytes.Bytes:
1607 Res{status, headers, body} = res
1608 body
1609
1610def res_body(m: Maybe<&1, Res>) -> Bytes.Bytes:
1611 match m:
1612 case None{}:
1613 Bytes.new(0)
1614 case Some{res}:
1615 res_body.res(res)
1616
1617def text(res: Res) -> String:
1618 Enc.utf8.decode(res_body.res(res))
1619
1620
1621def json(res: Res) -> Maybe<&1, Json.Val>:
1622 Json.parse.bytes(res_body.res(res))
1623
1624def got_body.req(req: Req) -> Bytes.Bytes:
1625 Req{method, path, headers, body} = req
1626 body
1627
1628def got_body(m: Maybe<&1, Req>) -> Bytes.Bytes:
1629 match m:
1630 case None{}:
1631 Bytes.new(0)
1632 case Some{req}:
1633 got_body.req(req)
1634
1635def body.of(hb: String & String) -> String:
1636 (h, b) = hb
1637 b
1638
1639def body(s: String) -> String:
1640 body.of(split_at_blank.go(s, False{}, 0, SNil{}))
1641
1642def headers_lines(+k: String, vs: List<&2, String>, acc: String) -> String:
1643 match vs:
1644 case Nil{}:
1645 acc
1646 case Con{v, t}:
1647 headers_lines(k, t, acc ++ ("\r\n" ++ sanitize(k) ++ ": " ++ sanitize(v)))
1648
1649def headers_block.go(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>, acc: String) -> String:
1650 match xs:
1651 case Nil{}:
1652 acc
1653 case (k, vs) <> t:
1654 headers_block.go(t, headers_lines(k, vs, acc))
1655
1656def headers_block(h: Map<&2, List<&2, String>>) -> String:
1657 headers_block.go(Map.to_list(&2, List<&2, String>, h), "")
1658
1659
1660type Reason is Data:
1661 Reason{code: U32, text: String}
1662
1663def reasons() -> List<&2, Reason>:
1664 [Reason{100, "Continue"}, Reason{101, "Switching Protocols"}, Reason{103, "Early Hints"},
1665 Reason{200, "OK"}, Reason{201, "Created"}, Reason{202, "Accepted"}, Reason{203, "Non-Authoritative Information"}, Reason{204, "No Content"}, Reason{205, "Reset Content"}, Reason{206, "Partial Content"},
1666 Reason{300, "Multiple Choices"}, Reason{301, "Moved Permanently"}, Reason{302, "Found"}, Reason{303, "See Other"}, Reason{304, "Not Modified"}, Reason{307, "Temporary Redirect"}, Reason{308, "Permanent Redirect"},
1667 Reason{400, "Bad Request"}, Reason{401, "Unauthorized"}, Reason{402, "Payment Required"}, Reason{403, "Forbidden"}, Reason{404, "Not Found"}, Reason{405, "Method Not Allowed"}, Reason{406, "Not Acceptable"}, Reason{407, "Proxy Authentication Required"}, Reason{408, "Request Timeout"}, Reason{409, "Conflict"}, Reason{410, "Gone"}, Reason{411, "Length Required"}, Reason{412, "Precondition Failed"}, Reason{413, "Content Too Large"}, Reason{414, "URI Too Long"}, Reason{415, "Unsupported Media Type"}, Reason{416, "Range Not Satisfiable"}, Reason{417, "Expectation Failed"}, Reason{421, "Misdirected Request"}, Reason{422, "Unprocessable Content"}, Reason{426, "Upgrade Required"}, Reason{428, "Precondition Required"}, Reason{429, "Too Many Requests"}, Reason{431, "Request Header Fields Too Large"},
1668 Reason{500, "Internal Server Error"}, Reason{501, "Not Implemented"}, Reason{502, "Bad Gateway"}, Reason{503, "Service Unavailable"}, Reason{504, "Gateway Timeout"}, Reason{505, "HTTP Version Not Supported"}]
1669
1670def reason.go(xs: List<&2, Reason>, +status: U32) -> String:
1671 match xs:
1672 case Nil{}:
1673 ""
1674 case Con{Reason{k, v}, t}:
1675 Bool.pick(String, U32.is_eq(k, status), v, reason.go(t, status))
1676
1677def reason(+status: U32) -> String:
1678 reason.go(reasons(), status)
1679
1680def response(+status: U32, headers: Map<&2, List<&2, String>>, body: String) -> String:
1681 ("HTTP/1.1 " ++ U32.show(status) ++ " " ++ reason(status) ++ headers_block(headers)) ++ ("\r\n\r\n" ++ body)
1682
1683def encode.body(head: Bool, top: Bytes.Bytes, body: Bytes.Bytes) -> Bytes.Bytes:
1684 match head:
1685 case True{}:
1686 top
1687 case False{}:
1688 Bytes.append(top, body)
1689
1690def encode.len(+status: U32, headers: Map<&2, List<&2, String>>, head: Bool, body: Bytes.Bytes) -> Bytes.Bytes:
1691 Bytes.Bytes{+len, buf} = body
1692 encode.body(head, Bytes.from_string(response(status, set(headers, "content-length", U32.show(len)), "")), Bytes.Bytes{len, buf})
1693
1694
1695def encode.pick(+status: U32, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, head: Bool, nobody: Bool) -> Bytes.Bytes:
1696 match nobody:
1697 case True{}:
1698 Bytes.from_string(response(status, headers, ""))
1699 case False{}:
1700 encode.len(status, headers, head, body)
1701
1702def encode.on(r: Res, head: Bool) -> Bytes.Bytes:
1703 Res{+status, headers, body} = r
1704 encode.pick(status, headers, body, head, frame.nobody(status, False{}))
1705
1706def encode(r: Res) -> Bytes.Bytes:
1707 encode.on(r, False{})
1708
1709def request(method: String, path: String, headers: Map<&2, List<&2, String>>, body: String) -> String:
1710 (method ++ " " ++ path ++ " HTTP/1.1" ++ headers_block(headers)) ++ ("\r\n\r\n" ++ body)
1711
1712def req.reserved(+k: String) -> Bool:
1713 Bool.or(Bool.or(String.eq(k, "host"), String.eq(k, "connection")), Bool.or(String.eq(k, "content-length"), String.eq(k, "transfer-encoding")))
1714
1715def req.put.one(m: Map<&2, List<&2, String>>, +k: String, vs: List<&2, String>, skip: Bool) -> Map<&2, List<&2, String>>:
1716 match skip:
1717 case True{}:
1718 m
1719 case False{}:
1720 Map.set(&2, List<&2, String>, m, k, vs)
1721
1722def req.put(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>, m: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
1723 match xs:
1724 case Nil{}:
1725 m
1726 case (k, vs) <> t:
1727 +lk = String.to_lower(k)
1728 req.put(t, req.put.one(m, lk, vs, req.reserved(lk)))
1729
1730
1731def req.cl(+m: Map<&2, List<&2, String>>, +method: String, +len: U32) -> Map<&2, List<&2, String>>:
1732 +bodied = Bool.or(Bool.not(U32.is_zero(len)), Bool.or(String.eq(method, "POST"), Bool.or(String.eq(method, "PUT"), String.eq(method, "PATCH"))))
1733 Bool.pick(Map<&2, List<&2, String>>, bodied, set(m, "content-length", U32.show(len)), m)
1734
1735
1736
1737def encode_req.word(close: Bool) -> String:
1738 match close:
1739 case True{}:
1740 "close"
1741 case False{}:
1742 "keep-alive"
1743
1744def encode_req.on(+method: String, target: String, host: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, close: Bool) -> Bytes.Bytes:
1745 Bytes.Bytes{+len, buf} = body
1746 +h = set(set(req.put(Map.to_list(&2, List<&2, String>, headers), empty()), "host", host), "connection", encode_req.word(close))
1747 Bytes.append(Bytes.from_string(request(method, target, req.cl(h, method, len), "")), Bytes.Bytes{len, buf})
1748
1749def encode_req(+method: String, target: String, host: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> Bytes.Bytes:
1750 encode_req.on(method, target, host, headers, body, True{})
1751
1752
1753
1754
1755
1756type Hop is Type:
1757 Hop{method: String, url: Url.Abs, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes}
1758
1759def redirect.code(+s: U32) -> Bool:
1760 Bool.or(Bool.or(U32.is_eq(s, 301), U32.is_eq(s, 302)), Bool.or(U32.is_eq(s, 303), Bool.or(U32.is_eq(s, 307), U32.is_eq(s, 308))))
1761
1762def redirect.origin(a: Url.Abs) -> String:
1763 Url.Abs{scheme, host, port, target} = a
1764 scheme ++ "://" ++ host ++ ":" ++ U32.show(port)
1765
1766def redirect.del(ks: List<&2, String>, m: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
1767 match ks:
1768 case Nil{}:
1769 m
1770 case Con{k, t}:
1771 redirect.del(t, Map.del(&2, List<&2, String>, m, k))
1772
1773def redirect.hdrs(+h: Map<&2, List<&2, String>>, drop: Bool, cross: Bool) -> Map<&2, List<&2, String>>:
1774 +h2 = Bool.pick(Map<&2, List<&2, String>>, drop, redirect.del(["content-type", "content-encoding", "content-language", "content-location"], h), h)
1775 Bool.pick(Map<&2, List<&2, String>>, cross, redirect.del(["authorization", "cookie", "proxy-authorization"], h2), h2)
1776
1777
1778
1779
1780def redirect.body(drop: Bool, body: Bytes.Bytes) -> Bytes.Bytes:
1781 match drop:
1782 case True{}:
1783 Bytes.new(0)
1784 case False{}:
1785 body
1786
1787def redirect.to(+status: U32, +method: String, +from: Url.Abs, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, next: Maybe<&2, Url.Abs>) -> Maybe<&1, Hop>:
1788 match next:
1789 case None{}:
1790 None{}
1791 case Some{+to}:
1792 +drop = Bool.or(Bool.and(U32.is_eq(status, 303), Bool.not(String.eq(method, "HEAD"))), Bool.and(Bool.or(U32.is_eq(status, 301), U32.is_eq(status, 302)), String.eq(method, "POST")))
1793 Some{Hop{Bool.pick(String, drop, "GET", method), to, redirect.hdrs(headers, drop, Bool.not(String.eq(redirect.origin(from), redirect.origin(to)))), redirect.body(drop, body)}}
1794
1795def redirect.hop(status: U32, h: Hop, loc: String) -> Maybe<&1, Hop>:
1796 Hop{method, +url, headers, body} = h
1797 redirect.to(status, method, url, headers, body, Url.resolve(url, loc))
1798
1799def redirect.if(status: U32, h: Hop, loc: String, ok: Bool) -> Maybe<&1, Hop>:
1800 match ok:
1801 case False{}:
1802 None{}
1803 case True{}:
1804 redirect.hop(status, h, loc)
1805
1806
1807def redirect(+status: U32, +headers: Map<&2, List<&2, String>>, h: Hop) -> Maybe<&1, Hop>:
1808 redirect.if(status, h, header(headers, "location"), Bool.and(redirect.code(status), has_header(headers, "location")))
1809
1810
1811
1812
1813
1814
1815
1816type Cookie is Data:
1817 Cookie{name: String, value: String, domain: String, host_only: Bool, path: String, secure: Bool, http_only: Bool, same_site: String, expiry: Maybe<&2, Time.Instant>}
1818
1819
1820type Jar is Data:
1821 Jar{now: Time.Instant, cookies: List<&2, Cookie>}
1822
1823def jar.new(now: Time.Instant) -> Jar:
1824 Jar{now, Nil{}}
1825
1826def jar.cookies(j: Jar) -> List<&2, Cookie>:
1827 Jar{now, cs} = j
1828 cs
1829
1830def cookie.live.at(+now: Time.Instant, e: Maybe<&2, Time.Instant>) -> Bool:
1831 match e:
1832 case None{}:
1833 True{}
1834 case Some{t}:
1835 Cmp.is_lt(Time.Instant.cmp(now, t))
1836
1837
1838def cookie.live(+now: Time.Instant, c: Cookie) -> Bool:
1839 Cookie{name, value, domain, host_only, path, secure, http_only, same_site, expiry} = c
1840 cookie.live.at(now, expiry)
1841
1842def jar.live(cs: List<&2, Cookie>, +now: Time.Instant) -> List<&2, Cookie>:
1843 match cs:
1844 case Nil{}:
1845 Nil{}
1846 case Con{+c, t}:
1847 List.filter.put(Cookie, c, jar.live(t, now), cookie.live(now, c))
1848
1849
1850def jar.at(j: Jar, +now: Time.Instant) -> Jar:
1851 Jar{old, cs} = j
1852 Jar{now, jar.live(cs, now)}
1853
1854def cookie.cut.found(r: String & String) -> String & String & Bool:
1855 (a, b) = r
1856 (a, b, True{})
1857
1858
1859def cookie.cut.go(s: String, +c: U32, acc: String, hit: Bool) -> String & String & Bool:
1860 match s:
1861 case SNil{}:
1862 match hit:
1863 case True{}:
1864 cookie.cut.found(take.cut(acc, SNil{}))
1865 case False{}:
1866 (String.reverse(acc), SNil{}, False{})
1867 case SCon{Chr{+x}, t}:
1868 match hit:
1869 case True{}:
1870 cookie.cut.found(take.cut(acc, SCon{Chr{x}, t}))
1871 case False{}:
1872 cookie.cut.go(t, c, SCon{Chr{x}, acc}, U32.is_eq(x, c))
1873
1874
1875def cookie.cut(s: String, +c: U32) -> String & String & Bool:
1876 cookie.cut.go(s, c, SNil{}, False{})
1877
1878def cookie.fst(r: String & String & Bool) -> String:
1879 (a, b, found) = r
1880 a
1881
1882
1883def cookie.target_path(t: String) -> String:
1884 cookie.fst(cookie.cut(t, 63))
1885
1886
1887def cookie.over(s: String, +n: U32) -> Bool:
1888 match s:
1889 case SNil{}:
1890 False{}
1891 case SCon{c, t}:
1892 Bool.or(U32.is_eq(n, 0), cookie.over(t, (n - 1 : U32)))
1893
1894
1895def cookie.ctl(s: String) -> Bool:
1896 match s:
1897 case SNil{}:
1898 False{}
1899 case SCon{Chr{+c}, t}:
1900 Bool.or(Bool.or(U32.is_lt(c, 9), Bool.and(U32.is_gt(c, 9), U32.is_lt(c, 32))), Bool.or(U32.is_eq(c, 127), cookie.ctl(t)))
1901
1902def cookie.ascii(s: String) -> Bool:
1903 match s:
1904 case SNil{}:
1905 True{}
1906 case SCon{Chr{+c}, t}:
1907 Bool.and(U32.is_lt(c, 128), cookie.ascii(t))
1908
1909def cookie.ip4(s: String) -> Bool:
1910 match s:
1911 case SNil{}:
1912 True{}
1913 case SCon{Chr{+c}, t}:
1914 Bool.and(Bool.or(parse_u32.digit(c), U32.is_eq(c, 46)), cookie.ip4(t))
1915
1916
1917def cookie.ip(+h: String) -> Bool:
1918 Bool.or(String.contains(h, ":"), cookie.ip4(h))
1919
1920
1921def cookie.domain_match(+s: String, +d: String) -> Bool:
1922 Bool.or(String.eq(s, d), Bool.and(String.ends_with(s, "." ++ d), Bool.not(cookie.ip(s))))
1923
1924
1925def cookie.up(rs: String) -> String:
1926 match rs:
1927 case SNil{}:
1928 SNil{}
1929 case SCon{Chr{47}, t}:
1930 t
1931 case SCon{Chr{c}, t}:
1932 cookie.up(t)
1933
1934
1935def cookie.default_path(+p: String) -> String:
1936 +up = String.reverse(cookie.up(String.reverse(p)))
1937 Bool.pick(String, Bool.or(Bool.not(String.starts_with(p, "/")), String.is_empty(up)), "/", up)
1938
1939
1940def cookie.path_match(+p: String, +c: String) -> Bool:
1941 Bool.or(String.eq(p, c), Bool.and(String.starts_with(p, c), Bool.or(String.ends_with(c, "/"), String.starts_with(String.drop(p, String.length(c)), "/"))))
1942
1943type Hms is Data:
1944 Hms{h: U32, m: U32, s: U32}
1945
1946
1947type Cdate is Data:
1948 Cdate{time: Maybe<&2, Hms>, day: Maybe<&2, U32>, month: Maybe<&2, U32>, year: Maybe<&2, U32>}
1949
1950def cookie.date.delim(+c: U32) -> Bool:
1951 Bool.or(U32.is_eq(c, 9), Bool.or(Bool.and(U32.is_ge(c, 32), U32.is_le(c, 47)), Bool.or(Bool.and(U32.is_ge(c, 59), U32.is_le(c, 64)), Bool.or(Bool.and(U32.is_ge(c, 91), U32.is_le(c, 96)), Bool.and(U32.is_ge(c, 123), U32.is_le(c, 126))))))
1952
1953def cookie.date.push(acc: String, ts: List<&2, String>) -> List<&2, String>:
1954 match acc:
1955 case SNil{}:
1956 ts
1957 case SCon{h, t}:
1958 Con{String.reverse(SCon{h, t}), ts}
1959
1960def cookie.date.step(d: Bool, +c: U32, acc: String, ts: List<&2, String>) -> String & List<&2, String>:
1961 match d:
1962 case True{}:
1963 (SNil{}, cookie.date.push(acc, ts))
1964 case False{}:
1965 (SCon{Chr{c}, acc}, ts)
1966
1967
1968def cookie.date.tokens(s: String, st: String & List<&2, String>) -> List<&2, String>:
1969 match s:
1970 case SNil{}:
1971 (acc, ts) = st
1972 cookie.date.push(acc, ts)
1973 case SCon{Chr{+c}, t}:
1974 (acc, ts) = st
1975 cookie.date.tokens(t, cookie.date.step(cookie.date.delim(c), c, acc, ts))
1976
1977def cookie.date.head(+s: String) -> Bool:
1978 match s:
1979 case SNil{}:
1980 False{}
1981 case SCon{Chr{+c}, t}:
1982 parse_u32.digit(c)
1983
1984
1985def cookie.date.run(s: String, d: Bool, +n: U32, +acc: U32) -> U32 & U32 & String:
1986 match s:
1987 case SNil{}:
1988 (n, acc, SNil{})
1989 case SCon{Chr{+c}, +t}:
1990 match d:
1991 case True{}:
1992 cookie.date.run(t, cookie.date.head(t), (n + 1 : U32), (acc * 10 + (c - 48 : U32) : U32))
1993 case False{}:
1994 (n, acc, SCon{Chr{c}, t})
1995
1996def cookie.date.within(r: U32 & U32 & String, +lo: U32, +hi: U32) -> Maybe<&1, U32 & String>:
1997 (+n, v, rest) = r
1998 Bool.pick(Maybe<&1, U32 & String>, Bool.and(U32.is_ge(n, lo), U32.is_le(n, hi)), Some{(v, rest)}, None{})
1999
2000
2001def cookie.date.num(+s: String, +lo: U32, +hi: U32) -> Maybe<&1, U32 & String>:
2002 cookie.date.within(cookie.date.run(s, cookie.date.head(s), 0, 0), lo, hi)
2003
2004def cookie.date.val(m: Maybe<&1, U32 & String>) -> Maybe<&2, U32>:
2005 match m:
2006 case None{}:
2007 None{}
2008 case Some{(v, t)}:
2009 Some{v}
2010
2011def cookie.date.colon(s: String) -> Maybe<&2, String>:
2012 match s:
2013 case SNil{}:
2014 None{}
2015 case SCon{Chr{58}, t}:
2016 Some{t}
2017 case SCon{Chr{c}, t}:
2018 None{}
2019
2020def cookie.date.sec(+h: U32, +m: U32, r: Maybe<&1, U32 & String>) -> Maybe<&2, Hms>:
2021 match r:
2022 case None{}:
2023 None{}
2024 case Some{(s, t)}:
2025 Some{Hms{h, m, s}}
2026
2027def cookie.date.min.c(+h: U32, +m: U32, c: Maybe<&2, String>) -> Maybe<&2, Hms>:
2028 match c:
2029 case None{}:
2030 None{}
2031 case Some{+t}:
2032 cookie.date.sec(h, m, cookie.date.num(t, 1, 2))
2033
2034def cookie.date.min(+h: U32, r: Maybe<&1, U32 & String>) -> Maybe<&2, Hms>:
2035 match r:
2036 case None{}:
2037 None{}
2038 case Some{(m, t)}:
2039 cookie.date.min.c(h, m, cookie.date.colon(t))
2040
2041def cookie.date.hour.c(+h: U32, c: Maybe<&2, String>) -> Maybe<&2, Hms>:
2042 match c:
2043 case None{}:
2044 None{}
2045 case Some{+t}:
2046 cookie.date.min(h, cookie.date.num(t, 1, 2))
2047
2048def cookie.date.hour(r: Maybe<&1, U32 & String>) -> Maybe<&2, Hms>:
2049 match r:
2050 case None{}:
2051 None{}
2052 case Some{(h, t)}:
2053 cookie.date.hour.c(h, cookie.date.colon(t))
2054
2055
2056def cookie.date.time(+tok: String) -> Maybe<&2, Hms>:
2057 cookie.date.hour(cookie.date.num(tok, 1, 2))
2058
2059def cookie.date.month.go(xs: List<&2, String>, +w: String, +i: U32) -> Maybe<&2, U32>:
2060 match xs:
2061 case Nil{}:
2062 None{}
2063 case Con{x, t}:
2064 Bool.pick(Maybe<&2, U32>, String.eq(x, w), Some{i}, cookie.date.month.go(t, w, (i + 1 : U32)))
2065
2066
2067def cookie.date.month(+tok: String) -> Maybe<&2, U32>:
2068 cookie.date.month.go(["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"], String.to_lower(String.take(tok, 3n)), 1)
2069
2070
2071def cookie.date.year(y: Maybe<&2, U32>, +tok: String, t: Maybe<&2, Hms>, d: Maybe<&2, U32>, m: Maybe<&2, U32>) -> Cdate:
2072 match y:
2073 case None{}:
2074 Cdate{t, d, m, cookie.date.val(cookie.date.num(tok, 2, 4))}
2075 case Some{v}:
2076 Cdate{t, d, m, Some{v}}
2077
2078def cookie.date.month.got(g: Maybe<&2, U32>, +tok: String, t: Maybe<&2, Hms>, d: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2079 match g:
2080 case None{}:
2081 cookie.date.year(y, tok, t, d, None{})
2082 case Some{v}:
2083 Cdate{t, d, Some{v}, y}
2084
2085def cookie.date.month.try(m: Maybe<&2, U32>, +tok: String, t: Maybe<&2, Hms>, d: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2086 match m:
2087 case None{}:
2088 cookie.date.month.got(cookie.date.month(tok), tok, t, d, y)
2089 case Some{v}:
2090 cookie.date.year(y, tok, t, d, Some{v})
2091
2092def cookie.date.day.got(g: Maybe<&2, U32>, +tok: String, t: Maybe<&2, Hms>, m: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2093 match g:
2094 case None{}:
2095 cookie.date.month.try(m, tok, t, None{}, y)
2096 case Some{v}:
2097 Cdate{t, Some{v}, m, y}
2098
2099def cookie.date.day.try(d: Maybe<&2, U32>, +tok: String, t: Maybe<&2, Hms>, m: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2100 match d:
2101 case None{}:
2102 cookie.date.day.got(cookie.date.val(cookie.date.num(tok, 1, 2)), tok, t, m, y)
2103 case Some{v}:
2104 cookie.date.month.try(m, tok, t, Some{v}, y)
2105
2106def cookie.date.time.got(g: Maybe<&2, Hms>, +tok: String, d: Maybe<&2, U32>, m: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2107 match g:
2108 case None{}:
2109 cookie.date.day.try(d, tok, None{}, m, y)
2110 case Some{v}:
2111 Cdate{Some{v}, d, m, y}
2112
2113def cookie.date.time.try(t: Maybe<&2, Hms>, +tok: String, d: Maybe<&2, U32>, m: Maybe<&2, U32>, y: Maybe<&2, U32>) -> Cdate:
2114 match t:
2115 case None{}:
2116 cookie.date.time.got(cookie.date.time(tok), tok, d, m, y)
2117 case Some{v}:
2118 cookie.date.day.try(d, tok, Some{v}, m, y)
2119
2120def cookie.date.fold(ts: List<&2, String>, st: Cdate) -> Cdate:
2121 match ts:
2122 case Nil{}:
2123 st
2124 case Con{+tok, rest}:
2125 Cdate{t, d, m, y} = st
2126 cookie.date.fold(rest, cookie.date.time.try(t, tok, d, m, y))
2127
2128
2129def cookie.date.fix(+y: U32) -> U32:
2130 Bool.pick(U32, U32.is_le(y, 69), (y + 2000 : U32), Bool.pick(U32, U32.is_le(y, 99), (y + 1900 : U32), y))
2131
2132def cookie.date.at(+y: U32, +d: U32, mo: U32, h: U32, mi: U32, s: U32) -> Maybe<&2, Time.Instant>:
2133 Bool.pick(Maybe<&2, Time.Instant>, Bool.and(U32.is_ge(y, 1601), Bool.and(U32.is_ge(d, 1), U32.is_le(d, 31))), Time.mk(Time.DateTime{Time.Date{y, mo, d}, h, mi, s, 0}), None{})
2134
2135def cookie.date.fin(st: Cdate) -> Maybe<&2, Time.Instant>:
2136 match st:
2137 case Cdate{Some{Hms{h, mi, s}}, Some{d}, Some{mo}, Some{y}}:
2138 cookie.date.at(cookie.date.fix(y), d, mo, h, mi, s)
2139 case _:
2140 None{}
2141
2142
2143
2144def cookie.date(s: String) -> Maybe<&2, Time.Instant>:
2145 cookie.date.fin(cookie.date.fold(List.reverse(&2, String, cookie.date.tokens(s, (SNil{}, Nil{}))), Cdate{None{}, None{}, None{}, None{}}))
2146
2147
2148
2149type Cav is Data:
2150 Cav{expires: Maybe<&2, Time.Instant>, max_age: Maybe<&2, U32>, domain: String, path: Maybe<&2, String>, secure: Bool, http_only: Bool, same_site: String}
2151
2152def cookie.cav() -> Cav:
2153 Cav{None{}, None{}, "", None{}, False{}, False{}, "default"}
2154
2155
2156def cookie.limit() -> U32:
2157 34560000
2158
2159def cookie.later(+now: Time.Instant, +secs: U32) -> Time.Instant:
2160 Time.Instant.add(now, Time.Duration.of_secs(Int.I64.from_u32(secs)))
2161
2162def cookie.expires.cap(+now: Time.Instant, m: Maybe<&2, Time.Instant>) -> Maybe<&2, Time.Instant>:
2163 match m:
2164 case None{}:
2165 None{}
2166 case Some{+t}:
2167 +lim = cookie.later(now, cookie.limit())
2168 Some{Bool.pick(Time.Instant, Cmp.is_lt(Time.Instant.cmp(lim, t)), lim, t)}
2169
2170def cookie.max_age.zero(m: Maybe<&2, U32>) -> Maybe<&2, U32>:
2171 match m:
2172 case None{}:
2173 None{}
2174 case Some{s}:
2175 Some{0}
2176
2177def cookie.max_age.nz(s: String) -> String:
2178 match s:
2179 case SNil{}:
2180 SNil{}
2181 case SCon{Chr{48}, t}:
2182 cookie.max_age.nz(t)
2183 case SCon{Chr{c}, t}:
2184 SCon{Chr{c}, t}
2185
2186
2187def cookie.max_age.cap(long: Bool, m: Maybe<&2, U32>) -> Maybe<&2, U32>:
2188 match m:
2189 case None{}:
2190 None{}
2191 case Some{s}:
2192 Some{Bool.pick(U32, long, cookie.limit(), s)}
2193
2194def cookie.max_age.secs(+v: String, neg: Bool) -> Maybe<&2, U32>:
2195 match neg:
2196 case True{}:
2197 cookie.max_age.zero(parse_u32(String.drop(v, 1n)))
2198 case False{}:
2199 cookie.max_age.cap(cookie.over(cookie.max_age.nz(v), 9), parse_u32(v))
2200
2201
2202def cookie.max_age.at(+now: Time.Instant, m: Maybe<&2, U32>) -> Maybe<&2, Time.Instant>:
2203 match m:
2204 case None{}:
2205 None{}
2206 case Some{+s}:
2207 Some{Bool.pick(Time.Instant, U32.is_eq(s, 0), Time.Instant.epoch(), cookie.later(now, U32.min(s, cookie.limit())))}
2208
2209
2210def cookie.max_age(+v: String) -> Maybe<&2, U32>:
2211 cookie.max_age.secs(v, String.starts_with(v, "-"))
2212
2213def cookie.same_site(+v: String) -> String:
2214 +l = String.to_lower(v)
2215 Bool.pick(String, Bool.or(String.eq(l, "strict"), Bool.or(String.eq(l, "lax"), String.eq(l, "none"))), l, "default")
2216
2217type Av is Data:
2218 AvExpires{}
2219 AvMaxAge{}
2220 AvDomain{}
2221 AvPath{}
2222 AvSecure{}
2223 AvHttpOnly{}
2224 AvSameSite{}
2225 AvOther{}
2226
2227def cookie.av.kind(+k: String) -> Av:
2228 Bool.pick(Av, String.eq(k, "expires"), AvExpires{}, Bool.pick(Av, String.eq(k, "max-age"), AvMaxAge{}, Bool.pick(Av, String.eq(k, "domain"), AvDomain{}, Bool.pick(Av, String.eq(k, "path"), AvPath{}, Bool.pick(Av, String.eq(k, "secure"), AvSecure{}, Bool.pick(Av, String.eq(k, "httponly"), AvHttpOnly{}, Bool.pick(Av, String.eq(k, "samesite"), AvSameSite{}, AvOther{})))))))
2229
2230
2231def cookie.av.put(k: Av, a: Cav, +v: String) -> Cav:
2232 match k a:
2233 case AvExpires{} Cav{e, m, d, p, s, h, ss}:
2234 Cav{Maybe.or(&2, Time.Instant, cookie.date(v), e), m, d, p, s, h, ss}
2235 case AvMaxAge{} Cav{e, m, d, p, s, h, ss}:
2236 Cav{e, Maybe.or(&2, U32, cookie.max_age(v), m), d, p, s, h, ss}
2237 case AvDomain{} Cav{e, m, d, p, s, h, ss}:
2238 Cav{e, m, String.to_lower(Bool.pick(String, String.starts_with(v, "."), String.drop(v, 1n), v)), p, s, h, ss}
2239 case AvPath{} Cav{e, m, d, p, s, h, ss}:
2240 Cav{e, m, d, Some{v}, s, h, ss}
2241 case AvSecure{} Cav{e, m, d, p, s, h, ss}:
2242 Cav{e, m, d, p, True{}, h, ss}
2243 case AvHttpOnly{} Cav{e, m, d, p, s, h, ss}:
2244 Cav{e, m, d, p, s, True{}, ss}
2245 case AvSameSite{} Cav{e, m, d, p, s, h, ss}:
2246 Cav{e, m, d, p, s, h, cookie.same_site(v)}
2247 case AvOther{} Cav{e, m, d, p, s, h, ss}:
2248 Cav{e, m, d, p, s, h, ss}
2249
2250
2251def cookie.av(+a: Cav, kv: String & String & Bool) -> Cav:
2252 (k, v, found) = kv
2253 +tv = trim(v)
2254 Bool.pick(Cav, cookie.over(tv, 1024), a, cookie.av.put(cookie.av.kind(String.to_lower(trim(k))), a, tv))
2255
2256def cookie.avs(xs: List<&2, String>, a: Cav) -> Cav:
2257 match xs:
2258 case Nil{}:
2259 a
2260 case Con{x, t}:
2261 cookie.avs(t, cookie.av(a, cookie.cut(x, 61)))
2262
2263
2264def cookie.nv(r: String & String & Bool) -> String & String:
2265 (a, b, found) = r
2266 match found:
2267 case True{}:
2268 (trim(a), trim(b))
2269 case False{}:
2270 ("", trim(a))
2271
2272def cookie.prefixed(+s: String, +p: String) -> Bool:
2273 String.starts_with(String.to_lower(s), p)
2274
2275
2276
2277
2278def cookie.domain.of(+host: String, +d: String) -> Maybe<&1, String & Bool>:
2279 +suffix = Bool.and(Bool.not(String.is_empty(d)), Bool.not(String.contains(d, ".")))
2280 +d2 = Bool.pick(String, Bool.and(suffix, String.eq(d, host)), "", d)
2281 Bool.pick(Maybe<&1, String & Bool>, Bool.or(Bool.and(suffix, Bool.not(String.eq(d, host))), Bool.not(cookie.ascii(d))), None{},
2282 Bool.pick(Maybe<&1, String & Bool>, String.is_empty(d2), Some{(host, True{})},
2283 Bool.pick(Maybe<&1, String & Bool>, cookie.domain_match(host, d2), Some{(d2, False{})}, None{})))
2284
2285def cookie.path.of(p: Maybe<&2, String>, +req: String) -> String:
2286 match p:
2287 case None{}:
2288 cookie.default_path(req)
2289 case Some{+v}:
2290 Bool.pick(String, String.starts_with(v, "/"), v, cookie.default_path(req))
2291
2292
2293def cookie.make.dm(dm: Maybe<&1, String & Bool>, +name: String, +value: String, +path: String, has_path: Bool, +secure: Bool, http_only: Bool, +same_site: String, expiry: Maybe<&2, Time.Instant>, https: Bool) -> Maybe<&2, Cookie>:
2294 match dm:
2295 case None{}:
2296 None{}
2297 case Some{(domain, +host_only)}:
2298 +host = Bool.and(Bool.and(secure, host_only), Bool.and(has_path, String.eq(path, "/")))
2299 +bad = Bool.or(Bool.or(Bool.and(secure, Bool.not(https)), Bool.and(String.eq(same_site, "none"), Bool.not(secure))),
2300 Bool.or(Bool.or(Bool.and(cookie.prefixed(name, "__secure-"), Bool.not(secure)), Bool.and(cookie.prefixed(name, "__host-"), Bool.not(host))),
2301 Bool.and(String.is_empty(name), Bool.or(cookie.prefixed(value, "__secure-"), cookie.prefixed(value, "__host-")))))
2302 Bool.pick(Maybe<&2, Cookie>, bad, None{}, Some{Cookie{name, value, domain, host_only, path, secure, http_only, same_site, expiry}})
2303
2304
2305def cookie.expiry(+now: Time.Instant, m: Maybe<&2, U32>, e: Maybe<&2, Time.Instant>) -> Maybe<&2, Time.Instant>:
2306 Maybe.or(&2, Time.Instant, cookie.max_age.at(now, m), cookie.expires.cap(now, e))
2307
2308def cookie.make(+now: Time.Instant, a: Url.Abs, +name: String, +value: String, c: Cav) -> Maybe<&2, Cookie>:
2309 Url.Abs{scheme, host, port, target} = a
2310 Cav{e, m, d, +p, secure, http_only, same_site} = c
2311 cookie.make.dm(cookie.domain.of(host, d), name, value, cookie.path.of(p, cookie.target_path(target)), Maybe.is_some(&2, String, p), secure, http_only, same_site, cookie.expiry(now, m, e), String.eq(scheme, "https"))
2312
2313
2314def cookie.parse.nv(+now: Time.Instant, a: Url.Abs, nv: String & String, avs: List<&2, String>) -> Maybe<&2, Cookie>:
2315 (+name, +value) = nv
2316 Bool.pick(Maybe<&2, Cookie>, Bool.or(Bool.and(String.is_empty(name), String.is_empty(value)), cookie.over(name ++ value, 4096)), None{}, cookie.make(now, a, name, value, cookie.avs(avs, cookie.cav())))
2317
2318def cookie.parse.go(+now: Time.Instant, a: Url.Abs, xs: List<&2, String>) -> Maybe<&2, Cookie>:
2319 match xs:
2320 case Nil{}:
2321 None{}
2322 case Con{first, avs}:
2323 cookie.parse.nv(now, a, cookie.nv(cookie.cut(first, 61)), avs)
2324
2325
2326def cookie.parse(+now: Time.Instant, a: Url.Abs, +line: String) -> Maybe<&2, Cookie>:
2327 Bool.pick(Maybe<&2, Cookie>, cookie.ctl(line), None{}, cookie.parse.go(now, a, String.split(line, ';')))
2328
2329def cookie.https(a: Url.Abs) -> Bool:
2330 Url.Abs{scheme, host, port, target} = a
2331 String.eq(scheme, "https")
2332
2333def cookie.secure(c: Cookie) -> Bool:
2334 Cookie{name, value, domain, host_only, path, secure, http_only, same_site, expiry} = c
2335 secure
2336
2337
2338def cookie.same(+a: Cookie, +b: Cookie) -> Bool:
2339 Cookie{n1, v1, d1, h1, p1, s1, o1, ss1, e1} = a
2340 Cookie{n2, v2, d2, h2, p2, s2, o2, ss2, e2} = b
2341 Bool.and(Bool.and(String.eq(n1, n2), String.eq(d1, d2)), Bool.and(Bool.not(Bool.xor(h1, h2)), String.eq(p1, p2)))
2342
2343
2344def cookie.shadows(+old: Cookie, +c: Cookie) -> Bool:
2345 Cookie{n1, v1, +d1, h1, p1, s1, o1, ss1, e1} = old
2346 Cookie{n2, v2, +d2, h2, p2, s2, o2, ss2, e2} = c
2347 Bool.and(Bool.and(String.eq(n1, n2), s1), Bool.and(Bool.or(cookie.domain_match(d1, d2), cookie.domain_match(d2, d1)), cookie.path_match(p2, p1)))
2348
2349def cookie.shadowed(cs: List<&2, Cookie>, +c: Cookie) -> Bool:
2350 match cs:
2351 case Nil{}:
2352 False{}
2353 case Con{+o, t}:
2354 Bool.or(cookie.shadows(o, c), cookie.shadowed(t, c))
2355
2356
2357
2358def cookie.put(cs: List<&2, Cookie>, +c: Cookie, +live: Bool) -> List<&2, Cookie>:
2359 match cs:
2360 case Nil{}:
2361 Bool.pick(List<&2, Cookie>, live, [c], Nil{})
2362 case Con{+o, +t}:
2363 Bool.pick(List<&2, Cookie>, cookie.same(o, c), Bool.pick(List<&2, Cookie>, live, Con{c, t}, t), Con{o, cookie.put(t, c, live)})
2364
2365def jar.put.c(m: Maybe<&2, Cookie>, j: Jar, +https: Bool) -> Jar:
2366 match m:
2367 case None{}:
2368 Jar{now, cs} = j
2369 Jar{now, cs}
2370 case Some{+c}:
2371 Jar{+now, +cs} = j
2372 Jar{now, Bool.pick(List<&2, Cookie>, Bool.and(Bool.not(Bool.or(https, cookie.secure(c))), cookie.shadowed(cs, c)), cs, cookie.put(cs, c, cookie.live(now, c)))}
2373
2374def jar.put.one(j: Jar, +a: Url.Abs, x: String) -> Jar:
2375 Jar{+now, cs} = j
2376 jar.put.c(cookie.parse(now, a, x), Jar{now, cs}, cookie.https(a))
2377
2378def jar.put.all(xs: List<&2, String>, j: Jar, +a: Url.Abs) -> Jar:
2379 match xs:
2380 case Nil{}:
2381 j
2382 case Con{x, t}:
2383 jar.put.all(t, jar.put.one(j, a, x), a)
2384
2385
2386def jar.store.abs(j: Jar, +a: Url.Abs, +h: Map<&2, List<&2, String>>) -> Jar:
2387 jar.put.all(fields(h, "set-cookie"), j, a)
2388
2389def jar.store.url(j: Jar, u: Maybe<&2, Url.Abs>, +h: Map<&2, List<&2, String>>) -> Jar:
2390 match u:
2391 case None{}:
2392 j
2393 case Some{+a}:
2394 jar.store.abs(j, a, h)
2395
2396
2397
2398def jar.store(j: Jar, url: String, res: Res) -> Jar & Res:
2399 Res{status, +headers, body} = res
2400 (jar.store.url(j, Url.absolute(url), headers), Res{status, headers, body})
2401
2402
2403def cookie.sends(+host: String, +path: String, +https: Bool, +c: Cookie) -> Bool:
2404 Cookie{n, v, +d, +ho, +p, s, o, ss, e} = c
2405 Bool.and(Bool.pick(Bool, ho, String.eq(host, d), cookie.domain_match(host, d)), Bool.and(cookie.path_match(path, p), Bool.or(Bool.not(s), https)))
2406
2407def cookie.pick(cs: List<&2, Cookie>, +host: String, +path: String, +https: Bool) -> List<&2, Cookie>:
2408 match cs:
2409 case Nil{}:
2410 Nil{}
2411 case Con{+c, t}:
2412 List.filter.put(Cookie, c, cookie.pick(t, host, path, https), cookie.sends(host, path, https, c))
2413
2414
2415def cookie.longer(a: Cookie, b: Cookie) -> Bool:
2416 Cookie{n1, v1, d1, h1, p1, s1, o1, ss1, e1} = a
2417 Cookie{n2, v2, d2, h2, p2, s2, o2, ss2, e2} = b
2418 Bool.not(Nat.is_lt(String.length(p1), String.length(p2)))
2419
2420def cookie.pair(c: Cookie) -> String:
2421 Cookie{+n, +v, d, h, p, s, o, ss, e} = c
2422 Bool.pick(String, String.is_empty(n), v, n ++ "=" ++ v)
2423
2424def cookie.pairs(cs: List<&2, Cookie>) -> List<&2, String>:
2425 match cs:
2426 case Nil{}:
2427 Nil{}
2428 case Con{c, t}:
2429 Con{cookie.pair(c), cookie.pairs(t)}
2430
2431
2432def jar.string(j: Jar, a: Url.Abs) -> String:
2433 Jar{now, cs} = j
2434 Url.Abs{scheme, +host, port, target} = a
2435 String.join(cookie.pairs(List.sort(~Cookie, ~cookie.longer, cookie.pick(cs, host, cookie.target_path(target), String.eq(scheme, "https")))), "; ")
2436
2437
2438def jar.header(+h: Map<&2, List<&2, String>>, +s: String) -> Map<&2, List<&2, String>>:
2439 +old = String.join(fields(h, "cookie"), "; ")
2440 Bool.pick(Map<&2, List<&2, String>>, String.is_empty(s), h, set(h, "cookie", Bool.pick(String, String.is_empty(old), s, old ++ "; " ++ s)))
2441
2442def jar.apply.url(j: Jar, u: Maybe<&2, Url.Abs>, +h: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
2443 match u:
2444 case None{}:
2445 h
2446 case Some{a}:
2447 jar.header(h, jar.string(j, a))
2448
2449
2450
2451def jar.apply(j: Jar, url: String, +h: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
2452 jar.apply.url(j, Url.absolute(url), h)
2453
2454
2455def jar.opt.apply(m: Maybe<&2, Jar>, a: Url.Abs, +h: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
2456 match m:
2457 case None{}:
2458 h
2459 case Some{j}:
2460 jar.header(h, jar.string(j, a))
2461
2462def jar.opt.res(j: Jar, +a: Url.Abs, r: Result<&1, &1, Err, Res>) -> Maybe<&2, Jar> & Result<&1, &1, Err, Res>:
2463 match r:
2464 case Fail{e}:
2465 (Some{j}, Fail{e})
2466 case Done{res}:
2467 Res{status, +headers, body} = res
2468 (Some{jar.store.abs(j, a, headers)}, Done{Res{status, headers, body}})
2469
2470def jar.opt.store(m: Maybe<&2, Jar>, +a: Url.Abs, r: Result<&1, &1, Err, Res>) -> Maybe<&2, Jar> & Result<&1, &1, Err, Res>:
2471 match m:
2472 case None{}:
2473 (None{}, r)
2474 case Some{j}:
2475 jar.opt.res(j, a, r)
2476
2477def io.recv.words(tls: Bool, s: Socket, max: U32, ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>):
2478 match tls:
2479 case True{}:
2480 Wire.tls.recv.words(s, max, ms)
2481 case False{}:
2482 Wire.recv.words(s, max, ms)
2483
2484def io.send.words(tls: Bool, s: Socket, +len: U32, buf: Array<U32>) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
2485 match tls:
2486 case True{}:
2487 Wire.tls.send.words(s, len, buf)
2488 case False{}:
2489 Wire.send.words(s, len, buf)
2490
2491def io.send.bytes(tls: Bool, s: Socket, b: Bytes.Bytes) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
2492 Bytes.Bytes{+len, buf} = b
2493 io.send.words(tls, s, len, buf)
2494
2495def io.close(tls: Bool, s: Socket) -> IO(Unit):
2496 match tls:
2497 case True{}:
2498 Wire.tls.close(s)
2499 case False{}:
2500 Socket.close(s)
2501
2502def fetch.close(tls: Bool, s: Socket, r: Result<&1, &1, Err, Res>) -> IO(Result<&1, &1, Err, Res>):
2503 do IO<Result<&1, &1, Err, Res>>:
2504 io.close(tls, s)
2505 return r
2506
2507
2508
2509type Out is Type:
2510 OutDone{res: Res, sock: Maybe<&1, Socket>, rest: Bytes.Bytes}
2511 OutFail{e: Err, none: Bool}
2512
2513def fetch.boxed(r: Result<&1, &1, Err, Res>, none: Bool) -> Out:
2514 match r:
2515 case Done{res}:
2516 OutDone{res, None{}, Bytes.new(0)}
2517 case Fail{e}:
2518 OutFail{e, none}
2519
2520def fetch.bad(tls: Bool, s: Socket, e: Maybe<&2, Err>) -> IO(Result<&1, &1, Err, Res>):
2521 match e:
2522 case None{}:
2523 fetch.close(tls, s, Fail{ErrBad{}})
2524 case Some{err}:
2525 fetch.close(tls, s, Fail{err})
2526
2527def fetch.shut(tls: Bool, s: Socket, r: Result<&1, &1, Err, Res>, none: Bool) -> IO(Out):
2528 do IO<Out>:
2529 x : Result<&1, &1, Err, Res> <- fetch.close(tls, s, r)
2530 return fetch.boxed(x, none)
2531
2532def fetch.hold.go(tls: Bool, s: Socket, res: Res, left: Maybe<&1, Bytes.Bytes>) -> IO(Out):
2533 match left:
2534 case Some{x}:
2535 IO.pure(Out, OutDone{res, Some{s}, x})
2536 case None{}:
2537 fetch.shut(tls, s, Done{res}, False{})
2538
2539def fetch.hold(tls: Bool, close: Bool, s: Socket, res: Res, left: Maybe<&1, Bytes.Bytes>) -> IO(Out):
2540 Res{status, +headers, body} = res
2541 fetch.hold.go(tls, s, Res{status, headers, body}, again(close, headers, left))
2542
2543def fetch.failout(tls: Bool, s: Socket, e: Maybe<&2, Err>, none: Bool) -> IO(Out):
2544 do IO<Out>:
2545 r : Result<&1, &1, Err, Res> <- fetch.bad(tls, s, e)
2546 return fetch.boxed(r, none)
2547
2548
2549
2550type Fl is Type:
2551 FlMore{rv: Rv, n: U32}
2552 FlBad{err: Maybe<&2, Err>, none: Bool}
2553 FlDone{res: Res, rest: Maybe<&1, Bytes.Bytes>}
2554
2555def fetch.rt(+n: U32, t: Rt) -> Fl:
2556 match t:
2557 case RtMore{rv}:
2558 FlMore{rv, n}
2559 case RtBad{}:
2560 FlBad{None{}, U32.is_zero(n)}
2561 case RtDone{res, rest}:
2562 FlDone{res, rest}
2563
2564
2565def fetch.over(+n: U32) -> Bool:
2566 Nat.is_lt(Nat.add(fetch.max(), 65536n), U32.to_nat(n))
2567
2568def fetch.piece.cap(over: Bool, +head: Bool, rv: Rv, +n2: U32, +len: U32, buf: Array<U32>) -> Fl:
2569 match over:
2570 case True{}:
2571 FlBad{None{}, False{}}
2572 case False{}:
2573 fetch.rt(n2, rv.step(U32.is_zero(len), head, rv, Bytes.Bytes{len, buf}))
2574
2575def fetch.piece(+head: Bool, rv: Rv, +n: U32, p: U32 & Array<U32>) -> Fl:
2576 (+len, buf) = p
2577 +n2 = (n + len : U32)
2578 fetch.piece.cap(fetch.over(n2), head, rv, n2, len, buf)
2579
2580def fetch.next(+head: Bool, rv: Rv, +n: U32, m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> Socket & Fl:
2581 (s, r) = m
2582 match r:
2583 case Fail{(+code, why)}:
2584 (s, FlBad{Some{err.or_late(code, ErrRead{code, why})}, U32.is_zero(n)})
2585 case Done{p}:
2586 (s, fetch.piece(head, rv, n, p))
2587
2588
2589@unsafe
2590def fetch.loop(+close: Bool, +head: Bool, +tls: Bool, +ms: U32, st: Socket & Fl) -> IO(Out):
2591 (s, fl) = st
2592 match fl:
2593 case FlMore{rv, +n}:
2594 do IO<Out>:
2595 m : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <- io.recv.words(tls, s, 65536, ms)
2596 fetch.loop(close, head, tls, ms, fetch.next(head, rv, n, m))
2597 case FlBad{err, none}:
2598 fetch.failout(tls, s, err, none)
2599 case FlDone{res, rest}:
2600 fetch.hold(tls, close, s, res, rest)
2601
2602def exchange.sent(+tls: Bool, +ms: U32, +close: Bool, +head: Bool, m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Out):
2603 (s, r) = m
2604 match r:
2605 case Fail{(+code, why)}:
2606 fetch.shut(tls, s, Fail{err.or_late(code, ErrWrite{code, why})}, True{})
2607 case Done{u}:
2608 fetch.loop(close, head, tls, ms, (s, FlMore{rv.start(), 0}))
2609
2610def exchange.go(+tls: Bool, +ms: U32, +close: Bool, +head: Bool, s: Socket, wire: Bytes.Bytes) -> IO(Out):
2611 do IO<Out>:
2612 sent : Socket & Result<&1, &1, U32 & String, Unit> <- io.send.bytes(tls, s, wire)
2613 exchange.sent(tls, ms, close, head, sent)
2614
2615def exchange.tup(o: Out) -> Maybe<&1, Socket> & Result<&1, &1, Err, Res> & Bytes.Bytes:
2616 match o:
2617 case OutFail{e, none}:
2618 (None{}, Fail{e}, Bytes.new(0))
2619 case OutDone{res, sock, rest}:
2620 (sock, Done{res}, rest)
2621
2622
2623def exchange(+tls: Bool, +ms: U32, +close: Bool, +head: Bool, s: Socket, wire: Bytes.Bytes) -> IO(Maybe<&1, Socket> & Result<&1, &1, Err, Res> & Bytes.Bytes):
2624 do IO<Maybe<&1, Socket> & Result<&1, &1, Err, Res> & Bytes.Bytes>:
2625 o : Out <- exchange.go(tls, ms, close, head, s, wire)
2626 return exchange.tup(o)
2627
2628
2629type Cert is Data:
2630 Cert{chain: String, key: String}
2631
2632type Proxy is Data:
2633 ProxyDirect{}
2634 ProxyVia{host: String, port: U32, authorization: String}
2635
2636type Route is Data:
2637 RouteDirect{tls: Bool, host: String, port: U32, cert: Maybe<&2, Cert>, ca: String}
2638 RouteProxy{tls: Bool, host: String, port: U32, sni: String,
2639 authority: String, authorization: String, cert: Maybe<&2, Cert>, ca: String}
2640
2641
2642
2643
2644type Conn is Type:
2645 Conn{tls: Bool, s: Socket}
2646 ConnH2{s: Socket, client: H2.Client}
2647
2648type Send is Type:
2649 Send{method: String, target: String, authority: String,
2650 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes}
2651
2652type H2Step is Type:
2653 H2Reply{s: Socket, reply: H2.Reply}
2654 H2Sent{client: H2.Client, events: List<&1, H2.ClientEvent>,
2655 sent: Result<&1, &1, Err, Socket>}
2656 H2Events{s: Socket, client: H2.Client, result: Result<&1, &1, Err, Maybe<&1, Res>>}
2657 H2Received{client: H2.Client,
2658 answer: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>}
2659
2660type TunnelStep is Type:
2661 TunnelRead{s: Socket, raw: String}
2662 TunnelHead{s: Socket, raw: String, frame: Frame}
2663 TunnelGot{raw: String,
2664 answer: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>}
2665
2666type Pool is Type:
2667 Pool{cap: U32, idle: Map<&1, List<&1, Conn>>}
2668
2669def pool.new.with(+cap: U32) -> Pool:
2670 Pool{cap, Map.new(&1, List<&1, Conn>)}
2671
2672
2673def pool.new() -> Pool:
2674 pool.new.with(8)
2675
2676def pool.key(+scheme: String, +host: String, +port: U32) -> String:
2677 scheme ++ "://" ++ host ++ ":" ++ U32.show(port)
2678
2679def pool.key.cert(+base: String, cert: Maybe<&2, Cert>) -> String:
2680 match cert:
2681 case None{}:
2682 base
2683 case Some{Cert{+chain, +key}}:
2684 base ++ "|cert:" ++ Nat.show(String.length(chain)) ++ ":" ++ chain
2685 ++ Nat.show(String.length(key)) ++ ":" ++ key
2686
2687def pool.key.ca(+base: String, +ca: String, empty: Bool) -> String:
2688 match empty:
2689 case True{}:
2690 base
2691 case False{}:
2692 base ++ "|ca:" ++ Nat.show(String.length(ca)) ++ ":" ++ ca
2693
2694def pool.key.trust(+base: String, cert: Maybe<&2, Cert>, +ca: String) -> String:
2695 pool.key.ca(pool.key.cert(base, cert), ca, String.is_empty(ca))
2696
2697def pool.take.of(+cap: U32, +key: String, r: Map<&1, List<&1, Conn>> & Maybe<&1, List<&1, Conn>>) -> Pool & Maybe<&1, Conn>:
2698 (m, got) = r
2699 match got:
2700 case None{}:
2701 (Pool{cap, m}, None{})
2702 case Some{xs}:
2703 match xs:
2704 case Nil{}:
2705 (Pool{cap, m}, None{})
2706 case Con{c, t}:
2707 (Pool{cap, Map.set(&1, List<&1, Conn>, m, key, t)}, Some{c})
2708
2709
2710def pool.take(p: Pool, +key: String) -> Pool & Maybe<&1, Conn>:
2711 Pool{+cap, idle} = p
2712 pool.take.of(cap, key, Map.pop(&1, List<&1, Conn>, idle, key))
2713
2714def conn.close(c: Conn) -> IO(Unit):
2715 match c:
2716 case Conn{tls, s}:
2717 io.close(tls, s)
2718 case ConnH2{s, client}:
2719 Wire.tls.close(s)
2720
2721
2722def conns.cut(xs: List<&1, Conn>, n: Nat) -> IO(List<&1, Conn>):
2723 match xs:
2724 case Nil{}:
2725 IO.pure(List<&1, Conn>, Nil{})
2726 case Con{c, t}:
2727 match n:
2728 case 0n:
2729 do IO<List<&1, Conn>>:
2730 conn.close(c)
2731 conns.cut(t, 0n)
2732 case 1n+k:
2733 do IO<List<&1, Conn>>:
2734 r : List<&1, Conn> <- conns.cut(t, k)
2735 return c <> r
2736
2737def pool.give.of(+cap: U32, +key: String, c: Conn, limit: Nat,
2738 r: Map<&1, List<&1, Conn>> & Maybe<&1, List<&1, Conn>>) -> IO(Pool):
2739 (m, old) = r
2740 do IO<Pool>:
2741 xs : List<&1, Conn> <- conns.cut(c <> Maybe.default(&1, List<&1, Conn>, old, Nil{}), limit)
2742 return Pool{cap, Map.set(&1, List<&1, Conn>, m, key, xs)}
2743
2744
2745def pool.give(p: Pool, +key: String, c: Conn) -> IO(Pool):
2746 Pool{+cap, idle} = p
2747 match c:
2748 case Conn{tls, s}:
2749 pool.give.of(cap, key, Conn{tls, s}, U32.to_nat(cap), Map.pop(&1, List<&1, Conn>, idle, key))
2750 case ConnH2{s, client}:
2751 pool.give.of(cap, key, ConnH2{s, client}, Bool.pick(Nat, U32.is_zero(cap), 0n, 1n),
2752 Map.pop(&1, List<&1, Conn>, idle, key))
2753
2754def pool.close.go(xs: List<&1, List<&1, Conn>>) -> IO(Unit):
2755 match xs:
2756 case Nil{}:
2757 IO.pure(Unit, Unit{})
2758 case Con{cs, t}:
2759 do IO<Unit>:
2760 none : List<&1, Conn> <- conns.cut(cs, 0n)
2761 pool.close.go(t)
2762
2763def pool.close(p: Pool) -> IO(Unit):
2764 Pool{cap, idle} = p
2765 pool.close.go(Map.values(&1, List<&1, Conn>, idle))
2766
2767
2768def pool.idempotent(+method: String) -> Bool:
2769 Bool.or(String.eq(method, "GET"), Bool.or(String.eq(method, "HEAD"), Bool.or(String.eq(method, "OPTIONS"), Bool.or(String.eq(method, "TRACE"), Bool.or(String.eq(method, "PUT"), String.eq(method, "DELETE"))))))
2770
2771def conn.tls(m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Result<&1, &1, Err, Conn>):
2772 (s, r) = m
2773 match r:
2774 case Fail{(+code, why)}:
2775 do IO<Result<&1, &1, Err, Conn>>:
2776 Wire.tls.close(s)
2777 return Fail{err.or_late(code, ErrTls{code, why})}
2778 case Done{u}:
2779 IO.pure(Result<&1, &1, Err, Conn>, Done{Conn{True{}, s}})
2780
2781def conn.h2.sent(client: H2.Client, r: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Result<&1, &1, Err, Conn>):
2782 (s, sent) = r
2783 match sent:
2784 case Fail{(+code, why)}:
2785 do IO<Result<&1, &1, Err, Conn>>:
2786 Wire.tls.close(s)
2787 return Fail{err.or_late(code, ErrWrite{code, why})}
2788 case Done{u}:
2789 IO.pure(Result<&1, &1, Err, Conn>, Done{ConnH2{s, client}})
2790
2791def conn.h2.start(s: Socket, r: H2.Client & HBytes.Bytes) -> IO(Result<&1, &1, Err, Conn>):
2792 (client, HBytes.Bytes{len, buf}) = r
2793 do IO<Result<&1, &1, Err, Conn>>:
2794 sent : Socket & Result<&1, &1, U32 & String, Unit> <- Wire.tls.send.words(s, len, buf)
2795 conn.h2.sent(client, sent)
2796
2797def conn.tls.protocol(proto: String, s: Socket) -> IO(Result<&1, &1, Err, Conn>):
2798 match proto:
2799 case "h2":
2800 conn.h2.start(s, H2.client.start())
2801 case "http/1.1":
2802 IO.pure(Result<&1, &1, Err, Conn>, Done{Conn{True{}, s}})
2803 case "":
2804 IO.pure(Result<&1, &1, Err, Conn>, Done{Conn{True{}, s}})
2805 case other:
2806 do IO<Result<&1, &1, Err, Conn>>:
2807 Wire.tls.close(s)
2808 return Fail{ErrTls{71, "unsupported ALPN: " ++ other}}
2809
2810def conn.tls.alpn(m: Socket & Result<&1, &1, U32 & String, String>) -> IO(Result<&1, &1, Err, Conn>):
2811 (s, r) = m
2812 match r:
2813 case Fail{(+code, why)}:
2814 do IO<Result<&1, &1, Err, Conn>>:
2815 Wire.tls.close(s)
2816 return Fail{err.or_late(code, ErrTls{code, why})}
2817 case Done{proto}:
2818 conn.tls.protocol(proto, s)
2819
2820def conn.secure.store(offer: Bool, s: Socket, host: String, +ms: U32) ->
2821 IO(Result<&1, &1, Err, Conn>):
2822 match offer:
2823 case True{}:
2824 do IO<Result<&1, &1, Err, Conn>>:
2825 hs : Socket & Result<&1, &1, U32 & String, String> <-
2826 Wire.tls.connect.alpn(s, host, ms, "h2,http/1.1")
2827 conn.tls.alpn(hs)
2828 case False{}:
2829 do IO<Result<&1, &1, Err, Conn>>:
2830 hs : Socket & Result<&1, &1, U32 & String, Unit> <- Wire.tls.connect(s, host, ms)
2831 conn.tls(hs)
2832
2833def conn.secure.file(offer: Bool, s: Socket, host: String, +ms: U32, +ca: String) ->
2834 IO(Result<&1, &1, Err, Conn>):
2835 match offer:
2836 case True{}:
2837 do IO<Result<&1, &1, Err, Conn>>:
2838 hs : Socket & Result<&1, &1, U32 & String, String> <-
2839 Wire.tls.connect.alpn.ca(s, host, ms, "h2,http/1.1", ca)
2840 conn.tls.alpn(hs)
2841 case False{}:
2842 do IO<Result<&1, &1, Err, Conn>>:
2843 hs : Socket & Result<&1, &1, U32 & String, Unit> <-
2844 Wire.tls.connect.ca(s, host, ms, ca)
2845 conn.tls(hs)
2846
2847def conn.secure.no.cert(empty: Bool, offer: Bool, s: Socket, host: String, +ms: U32,
2848 +ca: String) -> IO(Result<&1, &1, Err, Conn>):
2849 match empty:
2850 case True{}:
2851 conn.secure.store(offer, s, host, ms)
2852 case False{}:
2853 conn.secure.file(offer, s, host, ms, ca)
2854
2855def conn.secure.tls(cert: Maybe<&2, Cert>, s: Socket, host: String, +ms: U32,
2856 offer: Bool, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
2857 match cert:
2858 case None{}:
2859 conn.secure.no.cert(String.is_empty(ca), offer, s, host, ms, ca)
2860 case Some{Cert{chain, key}}:
2861 do IO<Result<&1, &1, Err, Conn>>:
2862 hs : Socket & Result<&1, &1, U32 & String, Unit> <-
2863 Wire.tls.connect.cert(s, host, ms, chain, key)
2864 conn.tls(hs)
2865
2866
2867def conn.secure(+ms: U32, sni: String, s: Socket, tls: Bool,
2868 cert: Maybe<&2, Cert>, offer: Bool, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
2869 match tls:
2870 case False{}:
2871 IO.pure(Result<&1, &1, Err, Conn>, Done{Conn{False{}, s}})
2872 case True{}:
2873 conn.secure.tls(cert, s, sni, ms, offer, ca)
2874
2875def conn.made(+ms: U32, sni: String, tls: Bool, cert: Maybe<&2, Cert>,
2876 offer: Bool, +ca: String, r: Result<&1, &1, U32 & String, Socket>) ->
2877 IO(Result<&1, &1, Err, Conn>):
2878 match r:
2879 case Fail{(+code, why)}:
2880 IO.pure(Result<&1, &1, Err, Conn>, Fail{err.or_late(code, ErrConnect{code, why})})
2881 case Done{s}:
2882 conn.secure(ms, sni, s, tls, cert, offer, ca)
2883
2884def conn.open(+tls: Bool, +ms: U32, sni: String, ip: String, port: U32) ->
2885 IO(Result<&1, &1, Err, Conn>):
2886 do IO<Result<&1, &1, Err, Conn>>:
2887 c : Result<&1, &1, U32 & String, Socket> <- Wire.connect(ip, port, ms)
2888 conn.made(ms, sni, tls, None{}, False{}, "", c)
2889
2890
2891type Dial is Type:
2892 Dial{port: U32, ms: U32, deadline: Nat, action: IO(Result<&1, &1, U32 & String, Socket>)}
2893
2894def conn.dial.time(+ip: String, +port: U32, +deadline: Nat, now: Nat, expired: Bool) ->
2895 IO(Result<&1, &1, U32 & String, Socket>):
2896 match expired:
2897 case True{}:
2898 IO.pure(Result<&1, &1, U32 & String, Socket>, Fail{(60, "connect timeout")})
2899 case False{}:
2900 Wire.connect(ip, port, U32.from_nat(Nat.sub(deadline, now)))
2901
2902def conn.dial.with(+ip: String, +port: U32, +ms: U32, +deadline: Nat, +now: Nat, unlimited: Bool) ->
2903 IO(Result<&1, &1, U32 & String, Socket>):
2904 match unlimited:
2905 case True{}:
2906 Wire.connect(ip, port, 0)
2907 case False{}:
2908 conn.dial.time(ip, port, deadline, now, Nat.is_le(deadline, now))
2909
2910def conn.dial.try(+ip: String, +port: U32, +ms: U32, +deadline: Nat) ->
2911 IO(Result<&1, &1, U32 & String, Socket>):
2912 do IO<Result<&1, &1, U32 & String, Socket>>:
2913 now : Nat <- IO.now()
2914 conn.dial.with(ip, port, ms, deadline, now, U32.is_zero(ms))
2915
2916def conn.dial.after(r: Result<&1, &1, U32 & String, Socket>, ip: String, +port: U32, +ms: U32, +deadline: Nat) ->
2917 IO(Result<&1, &1, U32 & String, Socket>):
2918 match r:
2919 case Done{s}:
2920 IO.pure(Result<&1, &1, U32 & String, Socket>, Done{s})
2921 case Fail{e}:
2922 conn.dial.try(ip, port, ms, deadline)
2923
2924def conn.dial.more(prev: IO(Result<&1, &1, U32 & String, Socket>), ip: String, +port: U32, +ms: U32, +deadline: Nat) ->
2925 IO(Result<&1, &1, U32 & String, Socket>):
2926 do IO<Result<&1, &1, U32 & String, Socket>>:
2927 r : Result<&1, &1, U32 & String, Socket> <- prev
2928 conn.dial.after(r, ip, port, ms, deadline)
2929
2930def conn.dial.fold(d: Dial, ip: String) -> Dial:
2931 Dial{+port, +ms, +deadline, action} = d
2932 Dial{port, ms, deadline, conn.dial.more(action, ip, port, ms, deadline)}
2933
2934def conn.dial.run(d: Dial) -> IO(Result<&1, &1, U32 & String, Socket>):
2935 Dial{port, ms, deadline, action} = d
2936 action
2937
2938def conn.dial.all(+port: U32, +ms: U32, +start: Nat, first: String, rest: List<&2, String>) ->
2939 IO(Result<&1, &1, U32 & String, Socket>):
2940 match rest:
2941 case Nil{}:
2942 Wire.connect(first, port, ms)
2943 case Con{ip, tail}:
2944 +deadline = Nat.add(start, U32.to_nat(ms))
2945 conn.dial.run(List.foldl(~&2, ~String, ~Dial, ~conn.dial.fold, Con{ip, tail},
2946 Dial{port, ms, deadline, Wire.connect(first, port, ms)}))
2947
2948def conn.open.all(+tls: Bool, +ms: U32, +host: String, +port: U32,
2949 ips: List<&2, String>, cert: Maybe<&2, Cert>, offer: Bool, +ca: String) ->
2950 IO(Result<&1, &1, Err, Conn>):
2951 match ips:
2952 case Nil{}:
2953 IO.pure(Result<&1, &1, Err, Conn>, Fail{ErrDns{}})
2954 case Con{addr, rest}:
2955 do IO<Result<&1, &1, Err, Conn>>:
2956 start : Nat <- IO.now()
2957 r : Result<&1, &1, U32 & String, Socket> <- conn.dial.all(port, ms, start, addr, rest)
2958 conn.made(ms, host, tls, cert, offer, ca, r)
2959
2960def proxy.auth.safe(s: String) -> Bool:
2961 match s:
2962 case SNil{}:
2963 True{}
2964 case SCon{Chr{+c}, rest}:
2965 Bool.and(U32.is_ge(c, 32), Bool.and(U32.is_ne(c, 127), proxy.auth.safe(rest)))
2966
2967def proxy.auth.checked(+decoded: String, valid: Bool) -> Maybe<&2, String>:
2968 match valid:
2969 case False{}:
2970 None{}
2971 case True{}:
2972 Some{"Basic " ++ Bytes.to_base64(Bytes.from_string(decoded))}
2973
2974def proxy.auth.value(m: Maybe<&2, String>) -> Maybe<&2, String>:
2975 match m:
2976 case None{}:
2977 None{}
2978 case Some{+decoded}:
2979 proxy.auth.checked(decoded, Bool.and(Url.has(decoded, 58), proxy.auth.safe(decoded)))
2980
2981def proxy.from.abs(authorization: String, m: Maybe<&2, Url.Abs>) -> Maybe<&2, Proxy>:
2982 match m:
2983 case None{}:
2984 None{}
2985 case Some{Url.Abs{scheme, host, port, target}}:
2986 Some{ProxyVia{host, port, authorization}}
2987
2988def proxy.from.user(user: String, host: String) -> Maybe<&2, Proxy>:
2989 do Maybe<&2, Proxy>:
2990 authorization : String <- proxy.auth.value(Url.pct.decode(user))
2991 proxy.from.abs(authorization, Url.absolute("http://" ++ host))
2992
2993def proxy.from.authority(has_user: Bool, authority: String, split: String & String) ->
2994 Maybe<&2, Proxy>:
2995 match has_user:
2996 case False{}:
2997 proxy.from.abs("", Url.absolute("http://" ++ authority))
2998 case True{}:
2999 (user, host) = split
3000 proxy.from.user(user, host)
3001
3002def proxy.from.parts(parts: String & String) -> Maybe<&2, Proxy>:
3003 (+authority, path) = parts
3004 match path:
3005 case SNil{}:
3006 proxy.from.authority(Url.has(authority, 64), authority, Url.take_to(authority, 64))
3007 case other:
3008 None{}
3009
3010def proxy.from.url.if(valid: Bool, raw: String) -> Maybe<&2, Proxy>:
3011 match valid:
3012 case False{}:
3013 None{}
3014 case True{}:
3015 proxy.from.parts(Url.take_to(String.drop(raw, 7n), 47))
3016
3017def proxy.from.url(+raw: String) -> Maybe<&2, Proxy>:
3018 proxy.from.url.if(String.starts_with(String.to_lower(raw), "http://"), raw)
3019
3020def proxy.no.domain(+host: String, +suffix: String) -> Bool:
3021 Bool.or(String.eq(host, suffix), String.ends_with(host, "." ++ suffix))
3022
3023def proxy.no.suffix(+host: String, +raw: String) -> Bool:
3024 match raw:
3025 case SCon{Chr{46}, +suffix}:
3026 proxy.no.domain(host, suffix)
3027 case other:
3028 proxy.no.domain(host, raw)
3029
3030def proxy.no.port(valid: Bool, +host: String, suffix: String) -> Bool:
3031 match valid:
3032 case False{}:
3033 False{}
3034 case True{}:
3035 proxy.no.suffix(host, suffix)
3036
3037def proxy.no.named(+host: String, +port: U32, parts: String & String) -> Bool:
3038 (suffix, port_text) = parts
3039 match port_text:
3040 case SNil{}:
3041 proxy.no.suffix(host, suffix)
3042 case other:
3043 proxy.no.port(String.eq(port_text, U32.show(port)), host, suffix)
3044
3045def proxy.no.literal(+host: String, +port: U32, +name: String) -> Bool:
3046 Bool.or(String.eq(name, host), Bool.or(String.eq(name, "[" ++ host ++ "]"),
3047 Bool.or(String.eq(name, host ++ ":" ++ U32.show(port)),
3048 String.eq(name, "[" ++ host ++ "]:" ++ U32.show(port)))))
3049
3050def proxy.no.token(+host: String, +port: U32, token: String) -> Bool:
3051 +name = String.to_lower(trim(token))
3052 Bool.or(String.eq(name, "*"),
3053 Bool.or(proxy.no.literal(host, port, name),
3054 Bool.and(Bool.not(Url.has(host, 58)),
3055 proxy.no.named(host, port, Url.take_to(name, 58)))))
3056
3057def proxy.no.list(xs: List<&2, String>, +host: String, +port: U32) -> Bool:
3058 match xs:
3059 case Nil{}:
3060 False{}
3061 case Con{token, rest}:
3062 Bool.or(proxy.no.token(host, port, token), proxy.no.list(rest, host, port))
3063
3064def proxy.no_match(+host: String, +port: U32, no_proxy: String) -> Bool:
3065 proxy.no.list(String.split(no_proxy, ','), String.to_lower(host), port)
3066
3067def proxy.env.value(r: Result<&1, &1, U32 & String, String>) -> String:
3068 match r:
3069 case Fail{e}:
3070 ""
3071 case Done{value}:
3072 value
3073
3074def proxy.env.lower(r: Result<&1, &1, U32 & String, String>, upper: String) ->
3075 IO(String):
3076 match r:
3077 case Done{value}:
3078 IO.pure(String, value)
3079 case Fail{e}:
3080 do IO<String>:
3081 got : Result<&1, &1, U32 & String, String> <- IO.get_env(upper)
3082 return proxy.env.value(got)
3083
3084def proxy.env(lower: String, upper: String) -> IO(String):
3085 do IO<String>:
3086 got : Result<&1, &1, U32 & String, String> <- IO.get_env(lower)
3087 proxy.env.lower(got, upper)
3088
3089def proxy.from.env(value: String) -> Maybe<&2, Proxy>:
3090 match value:
3091 case SNil{}:
3092 Some{ProxyDirect{}}
3093 case other:
3094 proxy.from.url(value)
3095
3096def proxy.choose(bypass: Bool, value: String) -> Maybe<&2, Proxy>:
3097 match bypass:
3098 case True{}:
3099 Some{ProxyDirect{}}
3100 case False{}:
3101 proxy.from.env(value)
3102
3103def proxy.select(+scheme: String, +host: String, +port: U32) ->
3104 IO(Maybe<&2, Proxy>):
3105 +secure = String.eq(scheme, "https")
3106 do IO<Maybe<&2, Proxy>>:
3107 bypass : String <- proxy.env("no_proxy", "NO_PROXY")
3108 value : String <- proxy.env(Bool.pick(String, secure, "https_proxy", "http_proxy"),
3109 Bool.pick(String, secure, "HTTPS_PROXY", "HTTP_PROXY"))
3110 return proxy.choose(proxy.no_match(host, port, bypass), value)
3111
3112def h2.header.skip(+name: String) -> Bool:
3113 match name:
3114 case SCon{Chr{58}, rest}:
3115 True{}
3116 case other:
3117 Bool.or(req.reserved(name),
3118 Bool.or(String.eq(name, "keep-alive"),
3119 Bool.or(String.eq(name, "proxy-connection"),
3120 Bool.or(String.eq(name, "upgrade"), String.eq(name, "te")))))
3121
3122def h2.header.mode(+name: String) -> U32:
3123 Bool.pick(U32,
3124 Bool.or(String.eq(name, "authorization"),
3125 Bool.or(String.eq(name, "cookie"), String.eq(name, "proxy-authorization"))),
3126 2, 0)
3127
3128def h2.header.values(+name: String, +mode: U32, vs: List<&2, String>) -> List<&2, Hpack.Field>:
3129 match vs:
3130 case Nil{}:
3131 Nil{}
3132 case Con{v, tail}:
3133 Hpack.Field{name, sanitize(v), mode} <> h2.header.values(name, mode, tail)
3134
3135def h2.header.tokens(vs: List<&2, String>) -> List<&2, String>:
3136 match vs:
3137 case Nil{}:
3138 Nil{}
3139 case Con{value, rest}:
3140 List.append(&2, String, String.split(value, ','), h2.header.tokens(rest))
3141
3142def h2.header.nominated.one(enabled: Bool, vs: List<&2, String>,
3143 rest: List<&2, String>) -> List<&2, String>:
3144 match enabled:
3145 case True{}:
3146 List.append(&2, String, h2.header.tokens(vs), rest)
3147 case False{}:
3148 rest
3149
3150def h2.header.nominated(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>) ->
3151 List<&2, String>:
3152 match xs:
3153 case Nil{}:
3154 Nil{}
3155 case (name, values) <> tail:
3156 h2.header.nominated.one(String.eq(String.to_lower(name), "connection"),
3157 values, h2.header.nominated(tail))
3158
3159def h2.header.has(xs: List<&2, String>, +key: String) -> Bool:
3160 match xs:
3161 case Nil{}:
3162 False{}
3163 case Con{value, rest}:
3164 Bool.or(String.eq(String.to_lower(trim(value)), key), h2.header.has(rest, key))
3165
3166def h2.header.item(skip: Bool, +name: String, vs: List<&2, String>,
3167 tail: List<&2, Hpack.Field>) -> List<&2, Hpack.Field>:
3168 match skip:
3169 case True{}:
3170 tail
3171 case False{}:
3172 List.append(&2, Hpack.Field, h2.header.values(name, h2.header.mode(name), vs), tail)
3173
3174def h2.header.items(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>,
3175 +nominated: List<&2, String>) -> List<&2, Hpack.Field>:
3176 match xs:
3177 case Nil{}:
3178 Nil{}
3179 case (name, values) <> tail:
3180 +key = String.to_lower(name)
3181 h2.header.item(Bool.or(h2.header.skip(key), h2.header.has(nominated, key)),
3182 key, values, h2.header.items(tail, nominated))
3183
3184def h2.request.fields(+method: String, target: String, authority: String,
3185 headers: Map<&2, List<&2, String>>) -> List<&2, Hpack.Field>:
3186 +xs = Map.to_list(&2, List<&2, String>, headers)
3187 List.append(&2, Hpack.Field,
3188 [Hpack.Field{":method", method, 0}, Hpack.Field{":scheme", "https", 0},
3189 Hpack.Field{":authority", authority, 0}, Hpack.Field{":path", target, 0}],
3190 h2.header.items(xs, h2.header.nominated(xs)))
3191
3192def h2.response.status.value(+value: String, valid: Bool) -> Maybe<&2, U32>:
3193 match valid:
3194 case False{}:
3195 None{}
3196 case True{}:
3197 parse_u32(value)
3198
3199def h2.response.status(xs: List<&2, Hpack.Field>) -> Maybe<&2, U32>:
3200 match xs:
3201 case Nil{}:
3202 None{}
3203 case Con{Hpack.Field{name, +value, mode}, tail}:
3204 match name:
3205 case ":status":
3206 h2.response.status.value(value, Nat.is_eq(String.length(value), 3n))
3207 case other:
3208 h2.response.status(tail)
3209
3210def h2.response.headers(xs: List<&2, Hpack.Field>, acc: Map<&2, List<&2, String>>) ->
3211 Map<&2, List<&2, String>>:
3212 match xs:
3213 case Nil{}:
3214 acc
3215 case Con{Hpack.Field{name, value, mode}, tail}:
3216 match name:
3217 case SCon{Chr{58}, rest}:
3218 h2.response.headers(tail, acc)
3219 case other:
3220 h2.response.headers(tail, add(acc, String.to_lower(name), value))
3221
3222def h2.response.final(interim: Bool, +status: U32, headers: List<&2, Hpack.Field>,
3223 body: HBytes.Bytes) -> Result<&1, &1, Err, Maybe<&1, Res>>:
3224 match interim:
3225 case True{}:
3226 Done{None{}}
3227 case False{}:
3228 HBytes.Bytes{len, buf} = body
3229 Done{Some{Res{status, h2.response.headers(headers, empty()), Bytes.Bytes{len, buf}}}}
3230
3231def h2.response.valid(valid: Bool, +status: U32, headers: List<&2, Hpack.Field>,
3232 body: HBytes.Bytes) -> Result<&1, &1, Err, Maybe<&1, Res>>:
3233 match valid:
3234 case False{}:
3235 Fail{ErrBad{}}
3236 case True{}:
3237 h2.response.final(U32.is_lt(status, 200), status, headers, body)
3238
3239def h2.response.parsed(m: Maybe<&2, U32>, headers: List<&2, Hpack.Field>,
3240 body: HBytes.Bytes) -> Result<&1, &1, Err, Maybe<&1, Res>>:
3241 match m:
3242 case None{}:
3243 Fail{ErrBad{}}
3244 case Some{+status}:
3245 h2.response.valid(Bool.and(U32.is_ge(status, 100), U32.is_lt(status, 600)),
3246 status, headers, body)
3247
3248def h2.response(+fields: List<&2, Hpack.Field>, body: HBytes.Bytes) ->
3249 Result<&1, &1, Err, Maybe<&1, Res>>:
3250 h2.response.parsed(h2.response.status(fields), fields, body)
3251
3252def h2.event.fold(acc: Result<&1, &1, Err, Maybe<&1, Res>>,
3253 event: H2.ClientEvent) -> Result<&1, &1, Err, Maybe<&1, Res>>:
3254 match acc:
3255 case Fail{e}:
3256 Fail{e}
3257 case Done{Some{res}}:
3258 Done{Some{res}}
3259 case Done{None{}}:
3260 match event:
3261 case H2.Response{id, headers, body}:
3262 h2.response(headers, body)
3263 case H2.Reset{id, code}:
3264 Fail{ErrBad{}}
3265 case H2.Shutdown{last, code}:
3266 Done{None{}}
3267 case H2.Pong{data}:
3268 Done{None{}}
3269
3270def h2.send.report(r: Socket & Result<&1, &1, U32 & String, Unit>) ->
3271 IO(Result<&1, &1, Err, Socket>):
3272 (s, sent) = r
3273 match sent:
3274 case Fail{(+code, why)}:
3275 do IO<Result<&1, &1, Err, Socket>>:
3276 Wire.tls.close(s)
3277 return Fail{err.or_late(code, ErrWrite{code, why})}
3278 case Done{unit}:
3279 IO.pure(Result<&1, &1, Err, Socket>, Done{s})
3280
3281def h2.send.one(prev: Result<&1, &1, Err, Socket>, b: HBytes.Bytes) ->
3282 IO(Result<&1, &1, Err, Socket>):
3283 match prev:
3284 case Fail{e}:
3285 IO.pure(Result<&1, &1, Err, Socket>, Fail{e})
3286 case Done{s}:
3287 HBytes.Bytes{len, buf} = b
3288 do IO<Result<&1, &1, Err, Socket>>:
3289 sent : Socket & Result<&1, &1, U32 & String, Unit> <- Wire.tls.send.words(s, len, buf)
3290 h2.send.report(sent)
3291
3292def h2.send.fold(prev: IO(Result<&1, &1, Err, Socket>), b: HBytes.Bytes) ->
3293 IO(Result<&1, &1, Err, Socket>):
3294 do IO<Result<&1, &1, Err, Socket>>:
3295 r : Result<&1, &1, Err, Socket> <- prev
3296 h2.send.one(r, b)
3297
3298def h2.send(xs: List<&1, HBytes.Bytes>, s: Socket) -> IO(Result<&1, &1, Err, Socket>):
3299 List.foldl(~&1, ~HBytes.Bytes, ~IO(Result<&1, &1, Err, Socket>), ~h2.send.fold,
3300 xs, IO.pure(Result<&1, &1, Err, Socket>, Done{s}))
3301
3302def h2.received.step(over: Bool, s: Socket, client: H2.Client,
3303 +len: U32, buf: Array<U32>) -> H2Step:
3304 match over:
3305 case True{}:
3306 H2Reply{s, H2.Failed{H2.Error{1, True{}, 0}}}
3307 case False{}:
3308 H2Reply{s, H2.client.receive(client, HBytes.Bytes{len, buf})}
3309
3310def h2.fail(s: Socket, e: Err) -> IO(Result<&1, &1, Err, Conn & Res>):
3311 do IO<Result<&1, &1, Err, Conn & Res>>:
3312 Wire.tls.close(s)
3313 return Fail{e}
3314
3315def h2.loop(n: Nat, step: H2Step, +ms: U32, +used: U32) ->
3316 IO(Result<&1, &1, Err, Conn & Res>):
3317 match n:
3318 case 0n:
3319 match step:
3320 case H2Reply{s, reply}:
3321 h2.fail(s, ErrBad{})
3322 case H2Sent{client, events, sent}:
3323 match sent:
3324 case Fail{e}:
3325 IO.pure(Result<&1, &1, Err, Conn & Res>, Fail{e})
3326 case Done{s}:
3327 h2.fail(s, ErrBad{})
3328 case H2Events{s, client, result}:
3329 h2.fail(s, ErrBad{})
3330 case H2Received{client, answer}:
3331 (s, r) = answer
3332 h2.fail(s, ErrBad{})
3333 case 1n+p:
3334 match step:
3335 case H2Reply{s, reply}:
3336 match reply:
3337 case H2.Failed{error}:
3338 h2.fail(s, ErrBad{})
3339 case H2.Advanced{client, writes, events}:
3340 do IO<Result<&1, &1, Err, Conn & Res>>:
3341 sent : Result<&1, &1, Err, Socket> <- h2.send(writes, s)
3342 h2.loop(p, H2Sent{client, events, sent}, ms, used)
3343 case H2Sent{client, events, sent}:
3344 match sent:
3345 case Fail{e}:
3346 IO.pure(Result<&1, &1, Err, Conn & Res>, Fail{e})
3347 case Done{s}:
3348 h2.loop(p, H2Events{s, client,
3349 List.foldl(~&1, ~H2.ClientEvent,
3350 ~Result<&1, &1, Err, Maybe<&1, Res>>, ~h2.event.fold,
3351 events, Done{None{}})}, ms, used)
3352 case H2Events{s, client, result}:
3353 match result:
3354 case Fail{e}:
3355 h2.fail(s, e)
3356 case Done{Some{res}}:
3357 IO.pure(Result<&1, &1, Err, Conn & Res>,
3358 Done{(ConnH2{s, client}, res)})
3359 case Done{None{}}:
3360 do IO<Result<&1, &1, Err, Conn & Res>>:
3361 r : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <-
3362 Wire.tls.recv.words(s, 16384, ms)
3363 h2.loop(p, H2Received{client, r}, ms, used)
3364 case H2Received{client, answer}:
3365 match answer:
3366 case (s, Fail{(+code, why)}):
3367 h2.fail(s, err.or_late(code, ErrRead{code, why}))
3368 case (s, Done{(0, buf)}):
3369 h2.fail(s, ErrBad{})
3370 case (s, Done{(+len, buf)}):
3371 +total = (used + len : U32)
3372 h2.loop(p, h2.received.step(fetch.over(total), s, client, len, buf), ms, total)
3373
3374def h2.exchange(s: Socket, client: H2.Client, +ms: U32, request: Send) ->
3375 IO(Result<&1, &1, Err, Conn & Res>):
3376 Send{method, target, authority, headers, Bytes.Bytes{len, buf}} = request
3377 h2.loop(Nat.mul(4n, Nat.add(fetch.max(), 65536n)),
3378 H2Reply{s, H2.client.request(client,
3379 h2.request.fields(method, target, authority, headers), HBytes.Bytes{len, buf})},
3380 ms, 0)
3381
3382def proxy.connect.fail(s: Socket, e: Err) -> IO(Result<&1, &1, Err, Conn>):
3383 do IO<Result<&1, &1, Err, Conn>>:
3384 io.close(False{}, s)
3385 return Fail{e}
3386
3387def proxy.connect.extra(m: Maybe<&2, String>, s: Socket, +ms: U32,
3388 sni: String, cert: Maybe<&2, Cert>, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
3389 match m:
3390 case None{}:
3391 proxy.connect.fail(s, ErrBad{})
3392 case Some{rest}:
3393 match rest:
3394 case SNil{}:
3395 conn.secure(ms, sni, s, True{}, cert, True{}, ca)
3396 case other:
3397 proxy.connect.fail(s, ErrBad{})
3398
3399def proxy.connect.status(ok: Bool, +status: U32, raw: String, s: Socket,
3400 +ms: U32, sni: String, cert: Maybe<&2, Cert>, +ca: String) ->
3401 IO(Result<&1, &1, Err, Conn>):
3402 match ok:
3403 case False{}:
3404 proxy.connect.fail(s, ErrConnect{status, "proxy CONNECT refused"})
3405 case True{}:
3406 proxy.connect.extra(after(raw, True{}), s, ms, sni, cert, ca)
3407
3408def proxy.connect.finish(res: Res, raw: String, s: Socket, +ms: U32,
3409 sni: String, cert: Maybe<&2, Cert>, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
3410 Res{+status, headers, body} = res
3411 proxy.connect.status(Bool.and(U32.is_ge(status, 200), U32.is_lt(status, 300)),
3412 status, raw, s, ms, sni, cert, ca)
3413
3414def proxy.connect.frame(raw: String, over: Bool) -> Frame:
3415 match over:
3416 case True{}:
3417 FrameBad{}
3418 case False{}:
3419 frame(raw, False{}, True{})
3420
3421def proxy.connect.chunk(empty: Bool, s: Socket, raw: String,
3422 +len: U32, buf: Array<U32>) -> TunnelStep:
3423 match empty:
3424 case True{}:
3425 TunnelHead{s, raw, FrameBad{}}
3426 case False{}:
3427 TunnelRead{s, raw ++ Bytes.to_string(Bytes.Bytes{len, buf})}
3428
3429def proxy.connect.loop(n: Nat, step: TunnelStep, +ms: U32, +sni: String,
3430 +cert: Maybe<&2, Cert>, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
3431 match n:
3432 case 0n:
3433 match step:
3434 case TunnelRead{s, raw}:
3435 proxy.connect.fail(s, ErrBad{})
3436 case TunnelHead{s, raw, result}:
3437 proxy.connect.fail(s, ErrBad{})
3438 case TunnelGot{raw, answer}:
3439 (s, result) = answer
3440 proxy.connect.fail(s, ErrBad{})
3441 case 1n+p:
3442 match step:
3443 case TunnelRead{s, +raw}:
3444 proxy.connect.loop(p, TunnelHead{s, raw,
3445 proxy.connect.frame(raw, Nat.is_gt(String.length(raw), 65536n))},
3446 ms, sni, cert, ca)
3447 case TunnelHead{s, raw, result}:
3448 match result:
3449 case FrameBad{}:
3450 proxy.connect.fail(s, ErrBad{})
3451 case FrameMore{}:
3452 do IO<Result<&1, &1, Err, Conn>>:
3453 answer : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <-
3454 io.recv.words(False{}, s, 4096, ms)
3455 proxy.connect.loop(p, TunnelGot{raw, answer}, ms, sni, cert, ca)
3456 case FrameDone{res}:
3457 proxy.connect.finish(res, raw, s, ms, sni, cert, ca)
3458 case TunnelGot{raw, answer}:
3459 match answer:
3460 case (s, Fail{(+code, why)}):
3461 proxy.connect.fail(s, err.or_late(code, ErrRead{code, why}))
3462 case (s, Done{(+len, buf)}):
3463 proxy.connect.loop(p,
3464 proxy.connect.chunk(U32.is_zero(len), s, raw, len, buf),
3465 ms, sni, cert, ca)
3466
3467def proxy.connect.header(+authority: String, authorization: String) -> String:
3468 match authorization:
3469 case SNil{}:
3470 "CONNECT " ++ authority ++ " HTTP/1.1\r\nHost: " ++ authority ++ "\r\n\r\n"
3471 case other:
3472 "CONNECT " ++ authority ++ " HTTP/1.1\r\nHost: " ++ authority
3473 ++ "\r\nProxy-Authorization: " ++ authorization ++ "\r\n\r\n"
3474
3475def proxy.connect.sent(r: Socket & Result<&1, &1, U32 & String, Unit>,
3476 +ms: U32, sni: String, cert: Maybe<&2, Cert>, +ca: String) ->
3477 IO(Result<&1, &1, Err, Conn>):
3478 (s, sent) = r
3479 match sent:
3480 case Fail{(+code, why)}:
3481 proxy.connect.fail(s, err.or_late(code, ErrWrite{code, why}))
3482 case Done{unit}:
3483 proxy.connect.loop(Nat.mul(3n, 65536n), TunnelRead{s, ""}, ms, sni, cert, ca)
3484
3485def proxy.connect(s: Socket, +ms: U32, sni: String, authority: String,
3486 authorization: String, cert: Maybe<&2, Cert>, +ca: String) ->
3487 IO(Result<&1, &1, Err, Conn>):
3488 do IO<Result<&1, &1, Err, Conn>>:
3489 sent : Socket & Result<&1, &1, U32 & String, Unit> <-
3490 io.send.bytes(False{}, s, Bytes.from_string(proxy.connect.header(authority, authorization)))
3491 proxy.connect.sent(sent, ms, sni, cert, ca)
3492
3493def proxy.route.tls(tls: Bool, s: Socket, +ms: U32, sni: String,
3494 authority: String, authorization: String, cert: Maybe<&2, Cert>, +ca: String) ->
3495 IO(Result<&1, &1, Err, Conn>):
3496 match tls:
3497 case False{}:
3498 IO.pure(Result<&1, &1, Err, Conn>, Done{Conn{False{}, s}})
3499 case True{}:
3500 proxy.connect(s, ms, sni, authority, authorization, cert, ca)
3501
3502def proxy.route.connected(tls: Bool, +ms: U32, sni: String, authority: String,
3503 authorization: String, cert: Maybe<&2, Cert>, +ca: String,
3504 r: Result<&1, &1, Err, Conn>) -> IO(Result<&1, &1, Err, Conn>):
3505 match r:
3506 case Fail{e}:
3507 IO.pure(Result<&1, &1, Err, Conn>, Fail{e})
3508 case Done{c}:
3509 match c:
3510 case Conn{plain, s}:
3511 proxy.route.tls(tls, s, ms, sni, authority, authorization, cert, ca)
3512 case ConnH2{s, client}:
3513 do IO<Result<&1, &1, Err, Conn>>:
3514 Wire.tls.close(s)
3515 return Fail{ErrBad{}}
3516
3517def proxy.route.open(+ms: U32, +tls: Bool, +host: String, +port: U32,
3518 +sni: String, +authority: String, +authorization: String,
3519 cert: Maybe<&2, Cert>, +ca: String) -> IO(Result<&1, &1, Err, Conn>):
3520 do IO<Result<&1, &1, Err, Conn>>:
3521 ips : List<&2, String> <- Dns.resolve.all(host)
3522 conn : Result<&1, &1, Err, Conn> <-
3523 conn.open.all(False{}, ms, host, port, ips, None{}, False{}, "")
3524 proxy.route.connected(tls, ms, sni, authority, authorization, cert, ca, conn)
3525
3526
3527def route.open(+ms: U32, offer: Bool, route: Route) -> IO(Result<&1, &1, Err, Conn>):
3528 match route:
3529 case RouteDirect{tls, +host, port, cert, ca}:
3530 do IO<Result<&1, &1, Err, Conn>>:
3531 ips : List<&2, String> <- Dns.resolve.all(host)
3532 conn.open.all(tls, ms, host, port, ips, cert, offer, ca)
3533 case RouteProxy{tls, host, port, sni, authority, authorization, cert, ca}:
3534 proxy.route.open(ms, tls, host, port, sni, authority, authorization, cert, ca)
3535
3536def conn.ex.h1(+tls: Bool, s: Socket, +ms: U32, request: Send) -> IO(Out):
3537 Send{+method, target, authority, headers, body} = request
3538 exchange.go(tls, ms, False{}, String.eq(method, "HEAD"), s,
3539 encode_req.on(method, target, authority, headers, body, False{}))
3540
3541
3542def pool.keep.s(p: Pool, +key: String, +tls: Bool, res: Res, s: Socket, clean: Bool) -> IO(Pool & Result<&1, &1, Err, Res>):
3543 match clean:
3544 case True{}:
3545 do IO<Pool & Result<&1, &1, Err, Res>>:
3546 p2 : Pool <- pool.give(p, key, Conn{tls, s})
3547 return (p2, Done{res})
3548 case False{}:
3549 do IO<Pool & Result<&1, &1, Err, Res>>:
3550 io.close(tls, s)
3551 return (p, Done{res})
3552
3553def pool.keep.m(p: Pool, +key: String, +tls: Bool, res: Res, +clean: Bool, sock: Maybe<&1, Socket>) -> IO(Pool & Result<&1, &1, Err, Res>):
3554 match sock:
3555 case None{}:
3556 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Done{res}))
3557 case Some{s}:
3558 pool.keep.s(p, key, tls, res, s, clean)
3559
3560def pool.keep(p: Pool, +key: String, +tls: Bool, res: Res, sock: Maybe<&1, Socket>, rest: Bytes.Bytes) -> IO(Pool & Result<&1, &1, Err, Res>):
3561 Bytes.Bytes{+len, buf} = rest
3562 pool.keep.m(p, key, tls, res, U32.is_zero(len), sock)
3563
3564def pool.after(p: Pool, +key: String, +tls: Bool, o: Out) -> IO(Pool & Result<&1, &1, Err, Res>):
3565 match o:
3566 case OutFail{e, none}:
3567 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{e}))
3568 case OutDone{res, sock, rest}:
3569 pool.keep(p, key, tls, res, sock, rest)
3570
3571def pool.h2.reusable(c: Conn) -> Bool & Conn:
3572 match c:
3573 case Conn{tls, s}:
3574 (True{}, Conn{tls, s})
3575 case ConnH2{s, H2.Client{input, streams, encoder, decoder, +control}}:
3576 H2.Control{next, active, frame_max, initial, max_streams, send_window,
3577 recv_window, first, ack, continuation, +goaway} = control
3578 (Maybe.is_none(&2, U32, goaway),
3579 ConnH2{s, H2.Client{input, streams, encoder, decoder, control}})
3580
3581type Took is Type:
3582 TookNone{}
3583 TookRaw{c: Conn}
3584 TookSeen{quiet: Bool, c: Conn}
3585
3586def took.of(pm: Pool & Maybe<&1, Conn>) -> Pool & Took:
3587 (p, m) = pm
3588 match m:
3589 case None{}:
3590 (p, TookNone{})
3591 case Some{c}:
3592 (p, TookRaw{c})
3593
3594def took.seen(+tls: Bool, r: Socket & Bool) -> Took:
3595 (s, quiet) = r
3596 TookSeen{quiet, Conn{tls, s}}
3597
3598
3599def h2.idle.kept(state: Bool & Conn) -> Took:
3600 (live, c) = state
3601 match live:
3602 case True{}:
3603 TookRaw{c}
3604 case False{}:
3605 TookSeen{False{}, c}
3606
3607def h2.idle.sent(client: H2.Client, sent: Result<&1, &1, Err, Socket>) -> Took:
3608 match sent:
3609 case Fail{e}:
3610 TookNone{}
3611 case Done{s}:
3612 h2.idle.kept(pool.h2.reusable(ConnH2{s, client}))
3613
3614def h2.idle.reply(s: Socket, reply: H2.Reply) -> IO(Took):
3615 match reply:
3616 case H2.Failed{error}:
3617 IO.pure(Took, TookSeen{False{}, Conn{True{}, s}})
3618 case H2.Advanced{client, writes, events}:
3619 do IO<Took>:
3620 sent : Result<&1, &1, Err, Socket> <- h2.send(writes, s)
3621 return h2.idle.sent(client, sent)
3622
3623def h2.idle.read(client: H2.Client,
3624 got: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> IO(Took):
3625 match got:
3626 case (s, Fail{e}):
3627 IO.pure(Took, TookSeen{False{}, ConnH2{s, client}})
3628 case (s, Done{(0, buf)}):
3629 IO.pure(Took, TookSeen{False{}, ConnH2{s, client}})
3630 case (s, Done{(+len, buf)}):
3631 h2.idle.reply(s, H2.client.receive(client, HBytes.Bytes{len, buf}))
3632
3633
3634def h2.idle.peek(client: H2.Client, r: Socket & Bool) -> IO(Took):
3635 (s, quiet) = r
3636 match quiet:
3637 case True{}:
3638 IO.pure(Took, TookSeen{True{}, ConnH2{s, client}})
3639 case False{}:
3640 do IO<Took>:
3641 got : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <-
3642 Wire.tls.recv.words(s, 16384, 100)
3643 h2.idle.read(client, got)
3644
3645def conn.idle(c: Conn) -> IO(Took):
3646 match c:
3647 case Conn{+tls, s}:
3648 do IO<Took>:
3649 r : Socket & Bool <- Wire.idle(s)
3650 return took.seen(tls, r)
3651 case ConnH2{s, client}:
3652 do IO<Took>:
3653 r : Socket & Bool <- Wire.idle(s)
3654 h2.idle.peek(client, r)
3655
3656
3657def pool.take.go(n: Nat, +key: String, pt: Pool & Took) -> IO(Pool & Maybe<&1, Conn>):
3658 match n pt:
3659 case _ Tuple{p, TookNone{}}:
3660 IO.pure(Pool & Maybe<&1, Conn>, (p, None{}))
3661 case _ Tuple{p, TookSeen{True{}, c}}:
3662 IO.pure(Pool & Maybe<&1, Conn>, (p, Some{c}))
3663 case 0n Tuple{p, TookRaw{c}}:
3664 do IO<Pool & Maybe<&1, Conn>>:
3665 conn.close(c)
3666 return (p, None{})
3667 case 0n Tuple{p, TookSeen{False{}, c}}:
3668 do IO<Pool & Maybe<&1, Conn>>:
3669 conn.close(c)
3670 return (p, None{})
3671 case 1n+k Tuple{p, TookRaw{c}}:
3672 do IO<Pool & Maybe<&1, Conn>>:
3673 t : Took <- conn.idle(c)
3674 pool.take.go(k, key, (p, t))
3675 case 1n+k Tuple{p, TookSeen{False{}, c}}:
3676 do IO<Pool & Maybe<&1, Conn>>:
3677 conn.close(c)
3678 pool.take.go(k, key, took.of(pool.take(p, key)))
3679
3680
3681def pool.take.live(p: Pool, +key: String) -> IO(Pool & Maybe<&1, Conn>):
3682 Pool{+cap, idle} = p
3683 pool.take.go((2n * U32.to_nat(cap) + 2n : Nat), key, took.of(pool.take(Pool{cap, idle}, key)))
3684
3685def pool.h2.keep(p: Pool, +key: String, res: Res, state: Bool & Conn) ->
3686 IO(Pool & Result<&1, &1, Err, Res>):
3687 (reusable, c) = state
3688 match reusable:
3689 case True{}:
3690 do IO<Pool & Result<&1, &1, Err, Res>>:
3691 next : Pool <- pool.give(p, key, c)
3692 return (next, Done{res})
3693 case False{}:
3694 do IO<Pool & Result<&1, &1, Err, Res>>:
3695 conn.close(c)
3696 return (p, Done{res})
3697
3698def pool.h2.result(p: Pool, +key: String, r: Result<&1, &1, Err, Conn & Res>) ->
3699 IO(Pool & Result<&1, &1, Err, Res>):
3700 match r:
3701 case Fail{e}:
3702 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{e}))
3703 case Done{(c, res)}:
3704 pool.h2.keep(p, key, res, pool.h2.reusable(c))
3705
3706def pool.h2.ex(p: Pool, +key: String, +ms: U32, request: Send,
3707 s: Socket, client: H2.Client) -> IO(Pool & Result<&1, &1, Err, Res>):
3708 do IO<Pool & Result<&1, &1, Err, Res>>:
3709 r : Result<&1, &1, Err, Conn & Res> <- h2.exchange(s, client, ms, request)
3710 pool.h2.result(p, key, r)
3711
3712def pool.fresh.conn(p: Pool, +key: String, +ms: U32, request: Send,
3713 r: Result<&1, &1, Err, Conn>) -> IO(Pool & Result<&1, &1, Err, Res>):
3714 match r:
3715 case Fail{e}:
3716 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{e}))
3717 case Done{c}:
3718 match c:
3719 case Conn{+tls, s}:
3720 do IO<Pool & Result<&1, &1, Err, Res>>:
3721 o : Out <- conn.ex.h1(tls, s, ms, request)
3722 pool.after(p, key, tls, o)
3723 case ConnH2{s, client}:
3724 pool.h2.ex(p, key, ms, request, s, client)
3725
3726def pool.fresh(p: Pool, +key: String, +ms: U32, request: Send, route: Route) ->
3727 IO(Pool & Result<&1, &1, Err, Res>):
3728 do IO<Pool & Result<&1, &1, Err, Res>>:
3729 c : Result<&1, &1, Err, Conn> <- route.open(ms, True{}, route)
3730 pool.fresh.conn(p, key, ms, request, c)
3731
3732
3733def pool.retry(+none: Bool, +idem: Bool) -> Bool:
3734 Bool.and(none, idem)
3735
3736def pool.stale(p: Pool, +key: String, +ms: U32, request: Send,
3737 e: Err, again: Bool, route: Route) ->
3738 IO(Pool & Result<&1, &1, Err, Res>):
3739 match again:
3740 case True{}:
3741 pool.fresh(p, key, ms, request, route)
3742 case False{}:
3743 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{e}))
3744
3745def pool.reused(p: Pool, +key: String, +tls: Bool, +ms: U32, +idem: Bool,
3746 spare: Send, o: Out, route: Route) ->
3747 IO(Pool & Result<&1, &1, Err, Res>):
3748 match o:
3749 case OutFail{e, +none}:
3750 pool.stale(p, key, ms, spare, e, pool.retry(none, idem), route)
3751 case OutDone{res, sock, rest}:
3752 pool.keep(p, key, tls, res, sock, rest)
3753
3754
3755def pool.idle.h1.bodies(p: Pool, +key: String, +tls: Bool, +ms: U32, +idem: Bool,
3756 s: Socket, +method: String, +target: String, +authority: String,
3757 +headers: Map<&2, List<&2, String>>, r: Bytes.Bytes & Bytes.Bytes,
3758 route: Route) -> IO(Pool & Result<&1, &1, Err, Res>):
3759 (wire, spare) = r
3760 do IO<Pool & Result<&1, &1, Err, Res>>:
3761 o : Out <- conn.ex.h1(tls, s, ms, Send{method, target, authority, headers, wire})
3762 pool.reused(p, key, tls, ms, idem,
3763 Send{method, target, authority, headers, spare}, o, route)
3764
3765def pool.idle.h1(p: Pool, +key: String, +tls: Bool, +ms: U32, +idem: Bool,
3766 s: Socket, request: Send, route: Route) ->
3767 IO(Pool & Result<&1, &1, Err, Res>):
3768 Send{+method, +target, +authority, +headers, body} = request
3769 pool.idle.h1.bodies(p, key, tls, ms, idem, s, method, target,
3770 authority, headers, Bytes.slice(body, 0, 4294967295), route)
3771
3772def pool.idle.reuse(p: Pool, +key: String, +ms: U32, +idem: Bool,
3773 c: Conn, request: Send, route: Route) ->
3774 IO(Pool & Result<&1, &1, Err, Res>):
3775 match c:
3776 case Conn{tls, s}:
3777 pool.idle.h1(p, key, tls, ms, idem, s, request, route)
3778 case ConnH2{s, client}:
3779 pool.h2.ex(p, key, ms, request, s, client)
3780
3781def pool.idle(taken: Pool & Maybe<&1, Conn>, +key: String, +ms: U32,
3782 +idem: Bool, request: Send, route: Route) ->
3783 IO(Pool & Result<&1, &1, Err, Res>):
3784 (p, m) = taken
3785 match m:
3786 case None{}:
3787 pool.fresh(p, key, ms, request, route)
3788 case Some{c}:
3789 pool.idle.reuse(p, key, ms, idem, c, request, route)
3790
3791def proxy.http.headers(headers: Map<&2, List<&2, String>>,
3792 authorization: String) -> Map<&2, List<&2, String>>:
3793 match authorization:
3794 case SNil{}:
3795 headers
3796 case other:
3797 set(headers, "proxy-authorization", authorization)
3798
3799def proxy.absolute(scheme: String, host_field: String, target: String) -> String:
3800 scheme ++ "://" ++ host_field ++ target
3801
3802def proxy.request(secure: Bool, +scheme: String, method: String,
3803 +target: String, +hf: String, headers: Map<&2, List<&2, String>>,
3804 body: Bytes.Bytes, authorization: String) -> Send:
3805 match secure:
3806 case True{}:
3807 Send{method, target, hf, Map.del(&2, List<&2, String>, headers,
3808 "proxy-authorization"), body}
3809 case False{}:
3810 Send{method, proxy.absolute(scheme, hf, target), hf,
3811 proxy.http.headers(headers, authorization), body}
3812
3813
3814def origin.route(+method: String, headers: Map<&2, List<&2, String>>,
3815 body: Bytes.Bytes, +hf: String, +scheme: String, +host: String, +port: U32,
3816 +target: String, +cert: Maybe<&2, Cert>, +ca: String, proxy: Proxy) ->
3817 String & Send & Route:
3818 match proxy:
3819 case ProxyDirect{}:
3820 (pool.key.trust(pool.key(scheme, host, port), cert, ca),
3821 Send{method, target, hf, headers, body},
3822 RouteDirect{String.eq(scheme, "https"), host, port, cert, ca})
3823 case ProxyVia{+proxy_host, +proxy_port, +authorization}:
3824 +secure = String.eq(scheme, "https")
3825 (pool.key.trust(pool.key(scheme, host, port)
3826 ++ "|proxy:" ++ proxy_host ++ ":" ++ U32.show(proxy_port)
3827 ++ "|auth:" ++ authorization, cert, ca),
3828 proxy.request(secure, scheme, method, target, hf, headers, body, authorization),
3829 RouteProxy{secure, proxy_host, proxy_port, host,
3830 Url.host_field.wrap(host, Url.has(host, 58)) ++ ":" ++ U32.show(port),
3831 authorization, cert, ca})
3832
3833def pool.origin.route(p: Pool, +method: String, +ms: U32, r: String & Send & Route) ->
3834 IO(Pool & Result<&1, &1, Err, Res>):
3835 (+key, request, route) = r
3836 do IO<Pool & Result<&1, &1, Err, Res>>:
3837 taken : Pool & Maybe<&1, Conn> <- pool.take.live(p, key)
3838 pool.idle(taken, key, ms, pool.idempotent(method), request, route)
3839
3840def pool.origin.choice(p: Pool, +method: String, ms: U32,
3841 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, hf: String,
3842 scheme: String, host: String, port: U32, target: String,
3843 cert: Maybe<&2, Cert>, +ca: String, choice: Maybe<&2, Proxy>) ->
3844 IO(Pool & Result<&1, &1, Err, Res>):
3845 match choice:
3846 case None{}:
3847 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{ErrUrl{}}))
3848 case Some{proxy}:
3849 pool.origin.route(p, method, ms, origin.route(method, headers, body, hf,
3850 scheme, host, port, target, cert, ca, proxy))
3851
3852def pool.origin(p: Pool, method: String, ms: U32,
3853 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, hf: String,
3854 a: Url.Abs, known: Bool, cert: Maybe<&2, Cert>, +ca: String,
3855 choice: Maybe<&2, Proxy>) ->
3856 IO(Pool & Result<&1, &1, Err, Res>):
3857 Url.Abs{scheme, host, port, target} = a
3858 match known:
3859 case False{}:
3860 IO.pure(Pool & Result<&1, &1, Err, Res>, (p, Fail{ErrUrl{}}))
3861 case True{}:
3862 pool.origin.choice(p, method, ms, headers, body, hf, scheme, host,
3863 port, target, cert, ca, choice)
3864
3865def pool.scheme.ok(a: Url.Abs) -> Bool:
3866 Url.Abs{+scheme, host, port, target} = a
3867 Bool.or(String.eq(scheme, "http"), String.eq(scheme, "https"))
3868
3869def pool.one.back(h: Hop, pr: Pool & Result<&1, &1, Err, Res>) -> Pool & Result<&1, &1, Err, Res> & Hop:
3870 (p, r) = pr
3871 (p, r, h)
3872
3873
3874def pool.one.of(p: Pool, +method: String, +url: Url.Abs,
3875 headers: Map<&2, List<&2, String>>, sent: Map<&2, List<&2, String>>,
3876 +ms: U32, r: Bytes.Bytes & Bytes.Bytes, cert: Maybe<&2, Cert>, +ca: String) ->
3877 IO(Pool & Result<&1, &1, Err, Res> & Hop):
3878 Url.Abs{+scheme, +host, +port, target} = url
3879 (body, wire) = r
3880 do IO<Pool & Result<&1, &1, Err, Res> & Hop>:
3881 choice : Maybe<&2, Proxy> <- proxy.select(scheme, host, port)
3882 pr : Pool & Result<&1, &1, Err, Res> <-
3883 pool.origin(p, method, ms, sent, wire, Url.host_field(url),
3884 url, pool.scheme.ok(url), cert, ca, choice)
3885 return pool.one.back(Hop{method, url, headers, body}, pr)
3886
3887
3888def pool.one(p: Pool, h: Hop, +ms: U32, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>) -> IO(Pool & Result<&1, &1, Err, Res> & Hop):
3889 Hop{+method, +url, +headers, body} = h
3890 pool.one.of(p, method, url, headers, jar.opt.apply(jar, url, headers), ms, Bytes.slice(body, 0, 4294967295), cert, ca)
3891
3892type Next is Type:
3893 NDone{res: Result<&1, &1, Err, Res>}
3894 NStop{}
3895 NGo{left: Nat, hop: Hop}
3896
3897type Mode is Data:
3898 ModeFollow{}
3899 ModeManual{}
3900 ModeError{}
3901
3902
3903def fetch.hop(left: Nat, h: Hop) -> Next:
3904 match left:
3905 case 0n:
3906 NStop{}
3907 case 1n+p:
3908 NGo{p, h}
3909
3910def fetch.decide.r(left: Nat, r: Res, next: Maybe<&1, Hop>) -> Next:
3911 match next:
3912 case None{}:
3913 NDone{Done{r}}
3914 case Some{h}:
3915 fetch.hop(left, h)
3916
3917def fetch.decide.res(left: Nat, h: Hop, r: Res) -> Next:
3918 Res{+status, +headers, body} = r
3919 fetch.decide.r(left, Res{status, headers, body}, redirect(status, headers, h))
3920
3921def fetch.decide(left: Nat, h: Hop, m: Result<&1, &1, Err, Res>) -> Next:
3922 match m:
3923 case Fail{e}:
3924 NDone{Fail{e}}
3925 case Done{r}:
3926 fetch.decide.res(left, h, r)
3927
3928def fetch.error.res(r: Res, bad: Bool) -> Next:
3929 match bad:
3930 case True{}:
3931 NDone{Fail{ErrRedirect{}}}
3932 case False{}:
3933 NDone{Done{r}}
3934
3935def fetch.error(h: Hop, m: Result<&1, &1, Err, Res>) -> Next:
3936 match m:
3937 case Fail{e}:
3938 NDone{Fail{e}}
3939 case Done{r}:
3940 Res{+status, +headers, body} = r
3941 fetch.error.res(Res{status, headers, body}, Bool.and(redirect.code(status), has_header(headers, "location")))
3942
3943def fetch.policy(mode: Mode, left: Nat, h: Hop, m: Result<&1, &1, Err, Res>) -> Next:
3944 match mode:
3945 case ModeFollow{}:
3946 fetch.decide(left, h, m)
3947 case ModeManual{}:
3948 NDone{m}
3949 case ModeError{}:
3950 fetch.error(h, m)
3951
3952
3953def accept.enc(+h: Map<&2, List<&2, String>>, +enc: String) -> Map<&2, List<&2, String>>:
3954 Bool.pick(Map<&2, List<&2, String>>, has_header(h, "accept-encoding"), h, set(h, "accept-encoding", enc))
3955
3956def fetch.start(method: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, u: Maybe<&2, Url.Abs>, +enc: String) -> Next:
3957 match u:
3958 case None{}:
3959 NDone{Fail{ErrUrl{}}}
3960 case Some{a}:
3961 NGo{20n, Hop{method, a, accept.enc(req.put(Map.to_list(&2, List<&2, String>, headers), empty()), enc), body}}
3962
3963
3964type Hops is Type:
3965 Hops{p: Pool, next: Next, cert: Maybe<&2, Cert>, jar: Maybe<&2, Jar>, ca: String}
3966
3967def pool.next.ca(same: Bool, +ca: String) -> String:
3968 match same:
3969 case True{}:
3970 ca
3971 case False{}:
3972 ""
3973
3974def pool.next.move(p: Pool, +old: String, cert: Maybe<&2, Cert>, jar: Maybe<&2, Jar>, +ca: String, n: Next) -> Hops:
3975 match n:
3976 case NGo{left, h}:
3977 Hop{method, +url, headers, body} = h
3978 +same = String.eq(old, redirect.origin(url))
3979 Hops{p, NGo{left, Hop{method, url, headers, body}},
3980 Bool.pick(Maybe<&2, Cert>, same, cert, None{}), jar, pool.next.ca(same, ca)}
3981 case NDone{r}:
3982 Hops{p, NDone{r}, None{}, jar, ""}
3983 case NStop{}:
3984 Hops{p, NStop{}, None{}, jar, ""}
3985
3986def pool.next.of(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, p: Pool, +url: Url.Abs, h: Hop, r: Maybe<&2, Jar> & Result<&1, &1, Err, Res>) -> Hops:
3987 (jar, m) = r
3988 pool.next.move(p, redirect.origin(url), cert, jar, ca, fetch.policy(mode, left, h, m))
3989
3990
3991def pool.next(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>, x: Pool & Result<&1, &1, Err, Res> & Hop) -> Hops:
3992 (p, m, h) = x
3993 Hop{method, +url, headers, body} = h
3994 pool.next.of(mode, left, cert, ca, p, url, Hop{method, url, headers, body}, jar.opt.store(jar, url, m))
3995
3996def pool.hops.one(+mode: Mode, +ms: U32, +cert: Maybe<&2, Cert>, +ca: String, +jar: Maybe<&2, Jar>, p: Pool, h: Hop, left: Nat) -> IO(Hops):
3997 do IO<Hops>:
3998 x : Pool & Result<&1, &1, Err, Res> & Hop <- pool.one(p, h, ms, cert, ca, jar)
3999 return pool.next(mode, left, cert, ca, jar, x)
4000
4001
4002
4003@unsafe
4004def pool.hops(+mode: Mode, +ms: U32, st: Hops) -> IO(Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>):
4005 Hops{p, n, cert, cj, ca} = st
4006 match n:
4007 case NDone{m}:
4008 IO.pure(Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>, (p, m, cj))
4009 case NStop{}:
4010 IO.pure(Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>, (p, Fail{ErrRedirect{}}, cj))
4011 case NGo{+left, h}:
4012 do IO<Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>>:
4013 next : Hops <- pool.hops.one(mode, ms, cert, ca, cj, p, h, left)
4014 pool.hops(mode, ms, next)
4015
4016
4017def ce.keep(+w: String, +rest: List<&2, String>) -> List<&2, String>:
4018 Bool.pick(List<&2, String>, String.is_empty(w), rest, Con{w, rest})
4019
4020def ce.norm(xs: List<&2, String>) -> List<&2, String>:
4021 match xs:
4022 case Nil{}:
4023 Nil{}
4024 case Con{v, t}:
4025 ce.keep(String.to_lower(trim(v)), ce.norm(t))
4026
4027def ce.split(xs: List<&2, String>) -> List<&2, String>:
4028 match xs:
4029 case Nil{}:
4030 Nil{}
4031 case Con{v, t}:
4032 List.append(&2, String, ce.norm(String.split(v, ',')), ce.split(t))
4033
4034
4035type Dec is Type:
4036 DecOk{b: Bytes.Bytes}
4037 DecBad{}
4038 DecSkip{}
4039
4040def ce.of(m: Maybe<&1, Bytes.Bytes>) -> Dec:
4041 match m:
4042 case None{}:
4043 DecBad{}
4044 case Some{b}:
4045 DecOk{b}
4046
4047
4048def ce.deflate.raw(b: Bytes.Bytes, m: Maybe<&1, Bytes.Bytes>) -> Dec:
4049 match m:
4050 case Some{out}:
4051 DecOk{out}
4052 case None{}:
4053 ce.of(Zlib.inflate(b))
4054
4055def ce.deflate.of(r: Bytes.Bytes & Bytes.Bytes) -> Dec:
4056 (a, b) = r
4057 ce.deflate.raw(b, Zlib.unzlib(a))
4058
4059def ce.deflate(b: Bytes.Bytes) -> Dec:
4060 ce.deflate.of(Bytes.clone(b))
4061
4062def ce.apply.id(on: Bool, b: Bytes.Bytes) -> Dec:
4063 match on:
4064 case True{}:
4065 DecOk{b}
4066 case False{}:
4067 DecSkip{}
4068
4069def ce.apply.de(on: Bool, +c: String, b: Bytes.Bytes) -> Dec:
4070 match on:
4071 case True{}:
4072 ce.deflate(b)
4073 case False{}:
4074 ce.apply.id(String.eq(c, "identity"), b)
4075
4076def ce.apply.gz(on: Bool, +c: String, b: Bytes.Bytes) -> Dec:
4077 match on:
4078 case True{}:
4079 ce.of(Zlib.gunzip(b))
4080 case False{}:
4081 ce.apply.de(String.eq(c, "deflate"), c, b)
4082
4083def ce.apply(+c: String, b: Bytes.Bytes) -> Dec:
4084 ce.apply.gz(Bool.or(String.eq(c, "gzip"), String.eq(c, "x-gzip")), c, b)
4085
4086def ce.go(cs: List<&2, String>, d: Dec) -> Dec:
4087 match cs:
4088 case Nil{}:
4089 d
4090 case Con{+c, t}:
4091 match d:
4092 case DecOk{b}:
4093 ce.go(t, ce.apply(c, b))
4094 case DecBad{}:
4095 DecBad{}
4096 case DecSkip{}:
4097 DecSkip{}
4098
4099def decoded.pick(d: Dec, +status: U32, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> Result<&1, &1, Err, Res>:
4100 match d:
4101 case DecOk{b}:
4102 Done{Res{status, headers, b}}
4103 case DecBad{}:
4104 Fail{ErrBad{}}
4105 case DecSkip{}:
4106 Done{Res{status, headers, body}}
4107
4108
4109def decoded.both(+status: U32, +headers: Map<&2, List<&2, String>>, cs: List<&2, String>, r: Bytes.Bytes & Bytes.Bytes) -> Result<&1, &1, Err, Res>:
4110 (a, body) = r
4111 decoded.pick(ce.go(cs, DecOk{a}), status, headers, body)
4112
4113def decoded.cs(+status: U32, +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, cs: List<&2, String>) -> Result<&1, &1, Err, Res>:
4114 match cs:
4115 case Nil{}:
4116 Done{Res{status, headers, body}}
4117 case Con{c, t}:
4118 decoded.both(status, headers, Con{c, t}, Bytes.clone(body))
4119
4120def decoded.go(empty: Bool, +status: U32, +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> Result<&1, &1, Err, Res>:
4121 match empty:
4122 case True{}:
4123 Done{Res{status, headers, body}}
4124 case False{}:
4125 decoded.cs(status, headers, body, List.reverse(&2, String, ce.split(fields(headers, "content-encoding"))))
4126
4127
4128def decoded(res: Res) -> Result<&1, &1, Err, Res>:
4129 Res{+status, +headers, body} = res
4130 Bytes.Bytes{+len, buf} = body
4131 decoded.go(U32.is_zero(len), status, headers, Bytes.Bytes{len, buf})
4132
4133def decoded.m(r: Result<&1, &1, Err, Res>) -> Maybe<&1, Res>:
4134 match r:
4135 case Done{x}:
4136 Some{x}
4137 case Fail{e}:
4138 None{}
4139
4140def decoded.some(res: Res) -> Maybe<&1, Res>:
4141 decoded.m(decoded(res))
4142
4143def ce.words(r: Result<&1, &1, U32 & String, Bytes.Bytes>) -> Maybe<&1, Bytes.Bytes>:
4144 match r:
4145 case Fail{e}:
4146 None{}
4147 case Done{b}:
4148 Some{b}
4149
4150
4151def ce.eff(~eff: U32 -> Bytes.Bytes -> IO(Result<&1, &1, U32 & String, Bytes.Bytes>), b: Bytes.Bytes) -> IO(Maybe<&1, Bytes.Bytes>):
4152 do IO<Maybe<&1, Bytes.Bytes>>:
4153 r : Result<&1, &1, U32 & String, Bytes.Bytes> <- eff(16777216, b)
4154 return ce.words(r)
4155
4156def ce.dec(d: Dec) -> Maybe<&1, Bytes.Bytes>:
4157 match d:
4158 case DecOk{b}:
4159 Some{b}
4160 case DecBad{}:
4161 None{}
4162 case DecSkip{}:
4163 None{}
4164
4165
4166def ce.io.deflate(on: Bool, b: Bytes.Bytes) -> IO(Maybe<&1, Bytes.Bytes>):
4167 match on:
4168 case True{}:
4169 IO.pure(Maybe<&1, Bytes.Bytes>, ce.dec(ce.deflate(b)))
4170 case False{}:
4171 IO.pure(Maybe<&1, Bytes.Bytes>, Some{b})
4172
4173def ce.io.zstd(on: Bool, +c: String, b: Bytes.Bytes) -> IO(Maybe<&1, Bytes.Bytes>):
4174 match on:
4175 case True{}:
4176 ce.eff(~Zlib.zstd, b)
4177 case False{}:
4178 ce.io.deflate(String.eq(c, "deflate"), b)
4179
4180def ce.io.br(on: Bool, +c: String, b: Bytes.Bytes) -> IO(Maybe<&1, Bytes.Bytes>):
4181 match on:
4182 case True{}:
4183 ce.eff(~Zlib.brotli, b)
4184 case False{}:
4185 ce.io.zstd(String.eq(c, "zstd"), c, b)
4186
4187
4188def ce.io.gz(on: Bool, +c: String, b: Bytes.Bytes) -> IO(Maybe<&1, Bytes.Bytes>):
4189 match on:
4190 case True{}:
4191 ce.eff(~Zlib.libz.inflate, b)
4192 case False{}:
4193 ce.io.br(String.eq(c, "br"), c, b)
4194
4195def ce.io.go(cs: List<&2, String>, m: Maybe<&1, Bytes.Bytes>) -> IO(Maybe<&1, Bytes.Bytes>):
4196 match cs:
4197 case Nil{}:
4198 match m:
4199 case None{}:
4200 IO.pure(Maybe<&1, Bytes.Bytes>, None{})
4201 case Some{b}:
4202 IO.pure(Maybe<&1, Bytes.Bytes>, Some{b})
4203 case Con{+c, t}:
4204 match m:
4205 case None{}:
4206 IO.pure(Maybe<&1, Bytes.Bytes>, None{})
4207 case Some{b}:
4208 do IO<Maybe<&1, Bytes.Bytes>>:
4209 n : Maybe<&1, Bytes.Bytes> <- ce.io.gz(Bool.or(String.eq(c, "gzip"), String.eq(c, "x-gzip")), c, b)
4210 ce.io.go(t, n)
4211
4212def ce.has(xs: List<&2, String>, +c: String) -> Bool:
4213 match xs:
4214 case Nil{}:
4215 False{}
4216 case Con{x, t}:
4217 Bool.or(String.eq(c, x), ce.has(t, c))
4218
4219def ce.known(cs: List<&2, String>, +ok: List<&2, String>) -> Bool:
4220 match cs:
4221 case Nil{}:
4222 True{}
4223 case Con{c, t}:
4224 Bool.and(ce.has(ok, c), ce.known(t, ok))
4225
4226def decoded.io.pick(m: Maybe<&1, Bytes.Bytes>, +status: U32, headers: Map<&2, List<&2, String>>) -> Result<&1, &1, Err, Res>:
4227 match m:
4228 case Some{b}:
4229 Done{Res{status, headers, b}}
4230 case None{}:
4231 Fail{ErrBad{}}
4232
4233def decoded.io.run(known: Bool, +status: U32, +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, cs: List<&2, String>) -> IO(Result<&1, &1, Err, Res>):
4234 match known:
4235 case True{}:
4236 do IO<Result<&1, &1, Err, Res>>:
4237 m : Maybe<&1, Bytes.Bytes> <- ce.io.go(cs, Some{body})
4238 return decoded.io.pick(m, status, headers)
4239 case False{}:
4240 IO.pure(Result<&1, &1, Err, Res>, Done{Res{status, headers, body}})
4241
4242def decoded.io.go(empty: Bool, +enc: String, +status: U32, +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Result<&1, &1, Err, Res>):
4243 match empty:
4244 case True{}:
4245 IO.pure(Result<&1, &1, Err, Res>, Done{Res{status, headers, body}})
4246 case False{}:
4247 +cs = List.reverse(&2, String, ce.split(fields(headers, "content-encoding")))
4248 decoded.io.run(ce.known(cs, Con{"identity", Con{"x-gzip", ce.split(Con{enc, Nil{}})}}), status, headers, body, cs)
4249
4250
4251def decoded.io(+enc: String, res: Res) -> IO(Result<&1, &1, Err, Res>):
4252 Res{+status, +headers, body} = res
4253 Bytes.Bytes{+len, buf} = body
4254 decoded.io.go(U32.is_zero(len), enc, status, headers, Bytes.Bytes{len, buf})
4255
4256
4257def codings.ok(r: Result<&1, &1, U32 & String, Bytes.Bytes>) -> Bool:
4258 match r:
4259 case Fail{(+c, why)}:
4260 Bool.not(U32.is_eq(c, 2))
4261 case Done{p}:
4262 True{}
4263
4264def codings.has(~eff: U32 -> Bytes.Bytes -> IO(Result<&1, &1, U32 & String, Bytes.Bytes>), b: Bytes.Bytes) -> IO(Bool):
4265 do IO<Bool>:
4266 r : Result<&1, &1, U32 & String, Bytes.Bytes> <- eff(0, b)
4267 return codings.ok(r)
4268
4269def codings.add(has: Bool, +enc: String, +c: String) -> String:
4270 Bool.pick(String, has, enc ++ ", " ++ c, enc)
4271
4272
4273def codings() -> IO(String):
4274 do IO<String>:
4275 b : Bool <- codings.has(~Zlib.brotli, Bytes.new(0))
4276 z : Bool <- codings.has(~Zlib.zstd, Bytes.new(0))
4277 return codings.add(z, codings.add(b, "gzip, deflate", "br"), "zstd")
4278
4279
4280def fetch.final(+enc: String, r: Result<&1, &1, Err, Res>) -> IO(Result<&1, &1, Err, Res>):
4281 match r:
4282 case Done{res}:
4283 decoded.io(enc, res)
4284 case Fail{e}:
4285 IO.pure(Result<&1, &1, Err, Res>, Fail{e})
4286
4287def pool.final(+enc: String, pr: Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>) -> IO(Pool & Result<&1, &1, Err, Res>):
4288 (p, r, jar) = pr
4289 do IO<Pool & Result<&1, &1, Err, Res>>:
4290 d : Result<&1, &1, Err, Res> <- fetch.final(enc, r)
4291 return (p, d)
4292
4293def pool.how.enc(+enc: String, p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32, mode: Mode, cert: Maybe<&2, Cert>, +ca: String) -> IO(Pool & Result<&1, &1, Err, Res>):
4294 do IO<Pool & Result<&1, &1, Err, Res>>:
4295 pr : Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar> <- pool.hops(mode, ms, Hops{p, fetch.start(method, headers, body, Url.absolute(url), enc), cert, None{}, ca})
4296 pool.final(enc, pr)
4297
4298def pool.how.cert(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32, mode: Mode, cert: Maybe<&2, Cert>) -> IO(Pool & Result<&1, &1, Err, Res>):
4299 do IO<Pool & Result<&1, &1, Err, Res>>:
4300 enc : String <- codings()
4301 pool.how.enc(enc, p, method, url, headers, body, ms, mode, cert, "")
4302
4303
4304
4305
4306def pool.how(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32, mode: Mode) -> IO(Pool & Result<&1, &1, Err, Res>):
4307 pool.how.cert(p, method, url, headers, body, ms, mode, None{})
4308
4309def pool.fetch.with(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32) -> IO(Pool & Result<&1, &1, Err, Res>):
4310 pool.how(p, method, url, headers, body, ms, ModeFollow{})
4311
4312def pool.fetch(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Pool & Result<&1, &1, Err, Res>):
4313 pool.fetch.with(p, method, url, headers, body, 30000)
4314
4315def pool.fetch.cert.with(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, chain: String, key: String, ms: U32) -> IO(Pool & Result<&1, &1, Err, Res>):
4316 pool.how.cert(p, method, url, headers, body, ms, ModeFollow{}, Some{Cert{chain, key}})
4317
4318def pool.fetch.cert(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, chain: String, key: String) -> IO(Pool & Result<&1, &1, Err, Res>):
4319 pool.fetch.cert.with(p, method, url, headers, body, chain, key, 30000)
4320
4321def pool.fetch.ca.with(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ca: String, ms: U32) -> IO(Pool & Result<&1, &1, Err, Res>):
4322 do IO<Pool & Result<&1, &1, Err, Res>>:
4323 enc : String <- codings()
4324 pool.how.enc(enc, p, method, url, headers, body, ms, ModeFollow{}, None{}, ca)
4325
4326def pool.fetch.ca(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ca: String) -> IO(Pool & Result<&1, &1, Err, Res>):
4327 pool.fetch.ca.with(p, method, url, headers, body, ca, 30000)
4328
4329
4330type Fetch is Type:
4331 Fetch{method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes}
4332
4333def fetch.all.job(p: Pool, f: Fetch) -> IO(Pool & Result<&1, &1, Err, Res>):
4334 Fetch{method, url, headers, body} = f
4335 pool.fetch(p, method, url, headers, body)
4336
4337def fetch.all.pools(n: Nat) -> List<Pool>:
4338 match n:
4339 case 0n:
4340 Nil{}
4341 case 1n+p:
4342 pool.new() <> fetch.all.pools(p)
4343
4344def fetch.all.close(ps: List<Pool>) -> IO(Unit):
4345 match ps:
4346 case Nil{}:
4347 IO.pure(Unit, Unit{})
4348 case p <> rest:
4349 do IO<Unit>:
4350 pool.close(p)
4351 fetch.all.close(rest)
4352
4353def fetch.all.done(r: List<Pool> & List<Result<&1, &1, Err, Res>>) -> IO(List<Result<&1, &1, Err, Res>>):
4354 (ps, rs) = r
4355 do IO<List<Result<&1, &1, Err, Res>>>:
4356 fetch.all.close(ps)
4357 return rs
4358
4359
4360def fetch.all.go(xs: List<Fetch>, +workers: Nat) -> IO(List<Result<&1, &1, Err, Res>>):
4361 match xs:
4362 case Nil{}:
4363 IO.pure(List<Result<&1, &1, Err, Res>>, Nil{})
4364 case x <> rest:
4365 do IO<List<Result<&1, &1, Err, Res>>>:
4366 r : List<Pool> & List<Result<&1, &1, Err, Res>> <-
4367 Conc.pool(~Pool, ~Fetch, ~Result<&1, &1, Err, Res>, ~fetch.all.job, fetch.all.pools(workers), x <> rest)
4368 fetch.all.done(r)
4369
4370def fetch.all.of(+n: U32, nx: Nat & List<Fetch>) -> IO(List<Result<&1, &1, Err, Res>>):
4371 (+len, xs) = nx
4372 fetch.all.go(xs, Nat.min(len, Nat.max(1n, U32.to_nat(n))))
4373
4374
4375
4376
4377
4378def fetch.all(reqs: List<Fetch>, +n: U32) -> IO(List<Result<&1, &1, Err, Res>>):
4379 fetch.all.of(n, Conc.count(Fetch, reqs))
4380
4381
4382def pool.jar.final(+enc: String, j: Jar, pr: Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar>) -> IO(Pool & Jar & Result<&1, &1, Err, Res>):
4383 (p, r, jar) = pr
4384 do IO<Pool & Jar & Result<&1, &1, Err, Res>>:
4385 d : Result<&1, &1, Err, Res> <- fetch.final(enc, r)
4386 return (p, Maybe.default(&2, Jar, jar, j), d)
4387
4388def pool.jar.enc(+enc: String, p: Pool, +j: Jar, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Pool & Jar & Result<&1, &1, Err, Res>):
4389 do IO<Pool & Jar & Result<&1, &1, Err, Res>>:
4390 pr : Pool & Result<&1, &1, Err, Res> & Maybe<&2, Jar> <- pool.hops(ModeFollow{}, 30000, Hops{p, fetch.start(method, headers, body, Url.absolute(url), enc), None{}, Some{j}, ""})
4391 pool.jar.final(enc, j, pr)
4392
4393
4394
4395
4396def pool.fetch.jar(p: Pool, j: Jar, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Pool & Jar & Result<&1, &1, Err, Res>):
4397 do IO<Pool & Jar & Result<&1, &1, Err, Res>>:
4398 enc : String <- codings()
4399 pool.jar.enc(enc, p, j, method, url, headers, body)
4400def fetch.end(pr: Pool & Result<&1, &1, Err, Res>) -> IO(Result<&1, &1, Err, Res>):
4401 (p, r) = pr
4402 do IO<Result<&1, &1, Err, Res>>:
4403 pool.close(p)
4404 return r
4405
4406
4407def fetch.how(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32, mode: Mode) -> IO(Result<&1, &1, Err, Res>):
4408 do IO<Result<&1, &1, Err, Res>>:
4409 pr : Pool & Result<&1, &1, Err, Res> <- pool.how(pool.new(), method, url, headers, body, ms, mode)
4410 fetch.end(pr)
4411
4412def fetch.with(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ms: U32) -> IO(Result<&1, &1, Err, Res>):
4413 fetch.how(method, url, headers, body, ms, ModeFollow{})
4414
4415def fetch.cert.with(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, chain: String, key: String, ms: U32) -> IO(Result<&1, &1, Err, Res>):
4416 do IO<Result<&1, &1, Err, Res>>:
4417 pr : Pool & Result<&1, &1, Err, Res> <- pool.fetch.cert.with(pool.new(), method, url, headers, body, chain, key, ms)
4418 fetch.end(pr)
4419
4420def fetch.cert(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, chain: String, key: String) -> IO(Result<&1, &1, Err, Res>):
4421 fetch.cert.with(method, url, headers, body, chain, key, 30000)
4422
4423def fetch.ca.with(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ca: String, ms: U32) -> IO(Result<&1, &1, Err, Res>):
4424 do IO<Result<&1, &1, Err, Res>>:
4425 pr : Pool & Result<&1, &1, Err, Res> <- pool.fetch.ca.with(pool.new(), method, url, headers, body, ca, ms)
4426 fetch.end(pr)
4427
4428def fetch.ca(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, ca: String) -> IO(Result<&1, &1, Err, Res>):
4429 fetch.ca.with(method, url, headers, body, ca, 30000)
4430
4431
4432def fetch(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Result<&1, &1, Err, Res>):
4433 fetch.with(method, url, headers, body, 30000)
4434
4435def get(url: String) -> IO(Result<&1, &1, Err, Res>):
4436 fetch("GET", url, empty(), Bytes.new(0))
4437
4438
4439def post.json(url: String, v: Json.Val) -> IO(Result<&1, &1, Err, Res>):
4440 fetch("POST", url, set(empty(), "content-type", "application/json"), Json.encode.bytes(v))
4441
4442
4443
4444def resolve.show(a: Url.Abs) -> String:
4445 Url.Abs{+scheme, host, port, +target} = a
4446 scheme ++ "://" ++ Url.host_field(Url.Abs{scheme, host, port, target}) ++ target
4447
4448def resolve.to(m: Maybe<&2, Url.Abs>) -> Maybe<&2, String>:
4449 match m:
4450 case None{}:
4451 None{}
4452 case Some{a}:
4453 Some{resolve.show(a)}
4454
4455def resolve.base(m: Maybe<&2, Url.Abs>, +ref: String) -> Maybe<&2, String>:
4456 match m:
4457 case None{}:
4458 None{}
4459 case Some{b}:
4460 resolve.to(Url.resolve(b, ref))
4461
4462def resolve(base: String, +ref: String) -> Maybe<&2, String>:
4463 resolve.base(Url.absolute(base), ref)
4464
4465
4466
4467def retry.cap() -> U32:
4468 30000
4469
4470def retry.base(n: Nat) -> U32:
4471 match n:
4472 case 0n:
4473 500
4474 case 1n+p:
4475 U32.min((retry.base(p) * 2 : U32), retry.cap())
4476
4477def retry.backoff.half(+half: U32, +r: U32) -> U32:
4478 (half + (r % (half + 1)) : U32)
4479
4480def retry.backoff(n: Nat, +r: U32) -> U32:
4481 retry.backoff.half((retry.base(n) / 2 : U32), r)
4482
4483
4484def retry.after.ms(+ms: Int.I64) -> U32:
4485 Bool.pick(U32, Int.I64.is_neg(ms), 0,
4486 Bool.pick(U32, Int.I64.is_lt(ms, Int.I64.from_u32(retry.cap())), Int.I64.to_u32(ms), retry.cap()))
4487
4488def retry.after.date(m: Maybe<&2, Time.Instant>, now: Time.Instant) -> Maybe<&2, U32>:
4489 match m:
4490 case None{}:
4491 None{}
4492 case Some{date}:
4493 Some{retry.after.ms(Time.Duration.to_ms(Time.Instant.since(date, now)))}
4494
4495
4496def retry.after.secs(+v: String, now: Time.Instant, n: Maybe<&2, U32>) -> Maybe<&2, U32>:
4497 match n:
4498 case None{}:
4499 retry.after.date(Time.http_date.parse(v), now)
4500 case Some{s}:
4501 Some{Bool.pick(U32, Nat.is_lt(5n, String.length(v)), retry.cap(), U32.min((s * 1000 : U32), retry.cap()))}
4502
4503
4504
4505def retry.after(+v: String, now: Time.Instant) -> Maybe<&2, U32>:
4506 retry.after.secs(v, now, parse_u32(v))
4507
4508def retry.wait(m: Maybe<&2, U32>, n: Nat, +r: U32) -> U32:
4509 match m:
4510 case None{}:
4511 retry.backoff(n, r)
4512 case Some{ms}:
4513 ms
4514
4515
4516def retry.delay(+v: String, now: Time.Instant, n: Nat, +r: U32) -> U32:
4517 retry.wait(retry.after(v, now), n, r)
4518
4519
4520def retry.status(+s: U32) -> Bool:
4521 Bool.or(Bool.or(U32.is_eq(s, 408), U32.is_eq(s, 429)),
4522 Bool.or(Bool.or(U32.is_eq(s, 500), U32.is_eq(s, 502)), Bool.or(U32.is_eq(s, 503), U32.is_eq(s, 504))))
4523
4524
4525def retry.fail(e: Err) -> Result<&1, &1, Err, Res> & Maybe<&2, String>:
4526 match e:
4527 case ErrConnect{code, why}:
4528 (Fail{ErrConnect{code, why}}, Some{""})
4529 case ErrTimeout{}:
4530 (Fail{ErrTimeout{}}, Some{""})
4531 case other:
4532 (Fail{other}, None{})
4533
4534def retry.res(res: Res) -> Result<&1, &1, Err, Res> & Maybe<&2, String>:
4535 Res{+status, +headers, body} = res
4536 (Done{Res{status, headers, body}}, Bool.pick(Maybe<&2, String>, retry.status(status), Some{header(headers, "retry-after")}, None{}))
4537
4538def retry.judge.try(r: Result<&1, &1, Err, Res>) -> Result<&1, &1, Err, Res> & Maybe<&2, String>:
4539 match r:
4540 case Fail{e}:
4541 retry.fail(e)
4542 case Done{res}:
4543 retry.res(res)
4544
4545
4546
4547def retry.judge(idem: Bool, r: Result<&1, &1, Err, Res>) -> Result<&1, &1, Err, Res> & Maybe<&2, String>:
4548 match idem:
4549 case False{}:
4550 (r, None{})
4551 case True{}:
4552 retry.judge.try(r)
4553
4554
4555def retry.jitter(r: Result<&1, &1, U32 & String, U32>) -> U32:
4556 match r:
4557 case Fail{e}:
4558 0
4559 case Done{x}:
4560 x
4561
4562def retry.sleep(+v: String, n: Nat) -> IO(Unit):
4563 do IO<Unit>:
4564 now : Time.Instant <- Time.now()
4565 rnd : Result<&1, &1, U32 & String, U32> <- IO.random_u32()
4566 IO.sleep(retry.delay(v, now, n, retry.jitter(rnd)))
4567
4568def retry.kept(body: Bytes.Bytes, j: Result<&1, &1, Err, Res> & Maybe<&2, String>) -> Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String>:
4569 (r, m) = j
4570 (body, r, m)
4571
4572
4573def retry.one(+idem: Bool, +method: String, +url: String, +headers: Map<&2, List<&2, String>>,
4574 r: Bytes.Bytes & Bytes.Bytes) -> IO(Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String>):
4575 (body, spare) = r
4576 do IO<Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String>>:
4577 got : Result<&1, &1, Err, Res> <- fetch(method, url, headers, spare)
4578 return retry.kept(body, retry.judge(idem, got))
4579
4580
4581def retry.go(left: Nat, +n: Nat, +idem: Bool, +method: String, +url: String,
4582 +headers: Map<&2, List<&2, String>>,
4583 t: Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String>) -> IO(Result<&1, &1, Err, Res>):
4584 match left:
4585 case 0n:
4586 (body, r, m) = t
4587 IO.pure(Result<&1, &1, Err, Res>, r)
4588 case 1n+p:
4589 (body, r, m) = t
4590 match m:
4591 case None{}:
4592 IO.pure(Result<&1, &1, Err, Res>, r)
4593 case Some{v}:
4594 do IO<Result<&1, &1, Err, Res>>:
4595 retry.sleep(v, n)
4596 tried : Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String> <-
4597 retry.one(idem, method, url, headers, Bytes.slice(body, 0, 4294967295))
4598 retry.go(p, 1n+n, idem, method, url, headers, tried)
4599
4600
4601
4602
4603
4604
4605def retry.start(enabled: Bool, n: U32, +method: String, +url: String,
4606 +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) ->
4607 IO(Result<&1, &1, Err, Res>):
4608 match enabled:
4609 case False{}:
4610 fetch(method, url, headers, body)
4611 case True{}:
4612 do IO<Result<&1, &1, Err, Res>>:
4613 first : Bytes.Bytes & Result<&1, &1, Err, Res> & Maybe<&2, String> <-
4614 retry.one(True{}, method, url, headers, Bytes.slice(body, 0, 4294967295))
4615 retry.go(U32.to_nat(n), 0n, True{}, method, url, headers, first)
4616
4617def fetch.retry(+n: U32, +method: String, +url: String,
4618 +headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) ->
4619 IO(Result<&1, &1, Err, Res>):
4620 retry.start(Bool.and(U32.is_ne(n, 0), pool.idempotent(method)),
4621 n, method, url, headers, body)
4622
4623
4624
4625type Po is Type:
4626 PoNone{}
4627 PoPiece{b: Bytes.Bytes}
4628 PoReady{b: Bytes.Bytes}
4629 PoEnd{}
4630 PoBad{e: Err}
4631
4632
4633type Stream is Type:
4634 Stream{tls: Bool, s: Socket, ms: U32, res: Res, rb: Rb, out: Po, decs: List<&1, Zlib.Decoder>, back: Maybe<&2, String>}
4635
4636type Pc is Type:
4637 Pc{rb: Rb, out: Po}
4638
4639
4640def pc.po(more: Bool, empty: Bool, b: Bytes.Bytes) -> Po:
4641 match empty:
4642 case True{}:
4643 Bool.pick(Po, more, PoNone{}, PoEnd{})
4644 case False{}:
4645 PoPiece{b}
4646
4647def pc.piece(more: Bool, +rb: Rb, b: Bytes.Bytes) -> Pc:
4648 Bytes.Bytes{+len, buf} = b
4649 Pc{rb, pc.po(more, U32.is_zero(len), Bytes.Bytes{len, buf})}
4650
4651def pc.fed.of(b: Bytes.Bytes, st: Rb) -> Pc:
4652 match st:
4653 case RbDone{}:
4654 pc.piece(False{}, RbDone{}, b)
4655 case RbBad{}:
4656 Pc{RbDone{}, PoBad{ErrBad{}}}
4657 case RbLen{n}:
4658 pc.piece(True{}, RbLen{n}, b)
4659 case RbChunk{st}:
4660 pc.piece(True{}, RbChunk{st}, b)
4661 case RbClose{}:
4662 pc.piece(True{}, RbClose{}, b)
4663
4664def pc.fed(bf: Bf) -> Pc:
4665 Bf{st, parts, rest} = bf
4666 pc.fed.of(rb.body(parts), st)
4667
4668
4669def pc.of(rb: Rb, piece: Bytes.Bytes) -> Pc:
4670 pc.fed(rb.feed(rb, Nil{}, piece))
4671
4672
4673def pc.end(ok: Bool) -> Pc:
4674 match ok:
4675 case True{}:
4676 Pc{RbDone{}, PoEnd{}}
4677 case False{}:
4678 Pc{RbDone{}, PoBad{ErrBad{}}}
4679
4680def pc.eof(st: Rb) -> Pc:
4681 pc.end(rb.eof(st))
4682
4683
4684def stream.pc.of(bf: Bf) -> Pc & Bool:
4685 Bf{st, parts, rest} = bf
4686 Bytes.Bytes{+len, buf} = rest
4687 (pc.fed.of(rb.body(parts), st), Bool.not(U32.is_zero(len)))
4688
4689def stream.pc(rb: Rb, piece: Bytes.Bytes) -> Pc & Bool:
4690 stream.pc.of(rb.feed(rb, Nil{}, piece))
4691
4692
4693def stream.mk(+tls: Bool, s: Socket, +ms: U32, res: Res, decs: List<&1, Zlib.Decoder>, back: Maybe<&2, String>, x: Pc & Bool) -> Stream:
4694 (pc, extra) = x
4695 Pc{rb, out} = pc
4696 Stream{tls, s, ms, res, rb, out, decs, Bool.pick(Maybe<&2, String>, extra, None{}, back)}
4697
4698def stream.got.piece(+tls: Bool, s: Socket, +ms: U32, res: Res, decs: List<&1, Zlib.Decoder>, back: Maybe<&2, String>, +rb: Rb, p: U32 & Array<U32>) -> Stream:
4699 (+len, buf) = p
4700 stream.mk(tls, s, ms, res, decs, back, Bool.pick(Pc & Bool, U32.is_zero(len), (pc.eof(rb), False{}), stream.pc(rb, Bytes.Bytes{len, buf})))
4701
4702def stream.got(+tls: Bool, +ms: U32, res: Res, decs: List<&1, Zlib.Decoder>, back: Maybe<&2, String>, +rb: Rb, m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> Stream:
4703 (s, r) = m
4704 match r:
4705 case Fail{(+code, why)}:
4706 Stream{tls, s, ms, res, RbDone{}, PoBad{err.or_late(code, ErrRead{code, why})}, decs, back}
4707 case Done{p}:
4708 stream.got.piece(tls, s, ms, res, decs, back, rb, p)
4709
4710def stream.after(+rb: Rb) -> Po:
4711 match rb:
4712 case RbDone{}:
4713 PoEnd{}
4714 case RbBad{}:
4715 PoEnd{}
4716 case RbLen{n}:
4717 PoNone{}
4718 case RbChunk{st}:
4719 PoNone{}
4720 case RbClose{}:
4721 PoNone{}
4722
4723type Fed is Type:
4724 Fed{decs: List<&1, Zlib.Decoder>, body: Result<&1, &1, Err, Bytes.Bytes>}
4725
4726def stream.feed.received(done: List<&1, Zlib.Decoder>, m: Zlib.Decoder & Result<&1, &1, U32 & String, Bytes.Bytes>) -> Fed:
4727 (d, r) = m
4728 match r:
4729 case Fail{e}:
4730 Fed{d <> done, Fail{ErrBad{}}}
4731 case Done{b}:
4732 Fed{d <> done, Done{b}}
4733
4734def stream.feed.step(st: Fed, d: Zlib.Decoder) -> IO(Fed):
4735 Fed{done, body} = st
4736 match body:
4737 case Fail{e}:
4738 IO.pure(Fed, Fed{d <> done, Fail{e}})
4739 case Done{b}:
4740 do IO<Fed>:
4741 m : Zlib.Decoder & Result<&1, &1, U32 & String, Bytes.Bytes> <- Zlib.dec.push(d, 16777216, b)
4742 return stream.feed.received(done, m)
4743
4744def stream.feed.one(prev: IO(Fed), d: Zlib.Decoder) -> IO(Fed):
4745 do IO<Fed>:
4746 st : Fed <- prev
4747 stream.feed.step(st, d)
4748
4749def stream.feed.reverse(st: Fed) -> Fed:
4750 Fed{done, body} = st
4751 Fed{List.reverse(&1, Zlib.Decoder, done), body}
4752
4753def stream.feed.all(decs: List<&1, Zlib.Decoder>, b: Bytes.Bytes) -> IO(Fed):
4754 do IO<Fed>:
4755 st : Fed <- List.foldl(~&1, ~Zlib.Decoder, ~IO(Fed), ~stream.feed.one, decs,
4756 IO.pure(Fed, Fed{Nil{}, Done{b}}))
4757 return stream.feed.reverse(st)
4758
4759def stream.ready(empty: Bool, +rb: Rb, b: Bytes.Bytes) -> Po:
4760 match empty:
4761 case True{}:
4762 stream.after(rb)
4763 case False{}:
4764 PoReady{b}
4765
4766def stream.feed.got(+tls: Bool, s: Socket, +ms: U32, res: Res, +rb: Rb, back: Maybe<&2, String>, st: Fed) -> Stream:
4767 Fed{decs, body} = st
4768 match body:
4769 case Fail{e}:
4770 Stream{tls, s, ms, res, rb, PoBad{e}, decs, back}
4771 case Done{b}:
4772 Bytes.Bytes{+len, buf} = b
4773 Stream{tls, s, ms, res, rb, stream.ready(U32.is_zero(len), rb, Bytes.Bytes{len, buf}), decs, back}
4774
4775def stream.feed.st(st: Stream, b: Bytes.Bytes) -> IO(Stream):
4776 Stream{+tls, s, +ms, res, +rb, out, decs, back} = st
4777 match decs:
4778 case Nil{}:
4779 IO.pure(Stream, Stream{tls, s, ms, res, rb, PoReady{b}, Nil{}, back})
4780 case Con{d, rest}:
4781 do IO<Stream>:
4782 fed : Fed <- stream.feed.all(Con{d, rest}, b)
4783 return stream.feed.got(tls, s, ms, res, rb, back, fed)
4784
4785def stream.finish.ok(r: Result<&1, &1, U32 & String, Unit>) -> Bool:
4786 match r:
4787 case Fail{e}:
4788 False{}
4789 case Done{u}:
4790 True{}
4791
4792def stream.finish(decs: List<&1, Zlib.Decoder>) -> IO(Bool):
4793 match decs:
4794 case Nil{}:
4795 IO.pure(Bool, True{})
4796 case Con{d, rest}:
4797 do IO<Bool>:
4798 r : Result<&1, &1, U32 & String, Unit> <- Zlib.dec.finish(d)
4799 tail : Bool <- stream.finish(rest)
4800 return Bool.and(stream.finish.ok(r), tail)
4801
4802def stream.ended(good: Bool, +tls: Bool, s: Socket, +ms: U32, res: Res, +rb: Rb, back: Maybe<&2, String>) ->
4803 Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>:
4804 match good:
4805 case True{}:
4806 (Stream{tls, s, ms, res, rb, PoEnd{}, Nil{}, back}, Done{None{}})
4807 case False{}:
4808 (Stream{tls, s, ms, res, rb, PoBad{ErrBad{}}, Nil{}, back}, Fail{ErrBad{}})
4809
4810
4811@unsafe
4812def stream.read(st: Stream) -> IO(Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>):
4813 Stream{+tls, s, +ms, res, +rb, out, decs, back} = st
4814 match out:
4815 case PoPiece{b}:
4816 do IO<Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>>:
4817 next : Stream <- stream.feed.st(Stream{tls, s, ms, res, rb, PoNone{}, decs, back}, b)
4818 stream.read(next)
4819 case PoReady{b}:
4820 IO.pure(Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>,
4821 (Stream{tls, s, ms, res, rb, stream.after(rb), decs, back}, Done{Some{b}}))
4822 case PoEnd{}:
4823 do IO<Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>>:
4824 good : Bool <- stream.finish(decs)
4825 return stream.ended(good, tls, s, ms, res, rb, back)
4826 case PoBad{+e}:
4827 IO.pure(Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>,
4828 (Stream{tls, s, ms, res, rb, PoBad{e}, decs, back}, Fail{e}))
4829 case PoNone{}:
4830 do IO<Stream & Result<&1, &1, Err, Maybe<&1, Bytes.Bytes>>>:
4831 m : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <- io.recv.words(tls, s, 65536, ms)
4832 stream.read(stream.got(tls, ms, res, decs, back, rb, m))
4833
4834
4835def stream.res(st: Stream) -> Stream & Res:
4836 Stream{tls, s, ms, res, rb, out, decs, back} = st
4837 Res{+status, +headers, body} = res
4838 (Stream{tls, s, ms, Res{status, headers, body}, rb, out, decs, back}, Res{status, headers, Bytes.new(0)})
4839
4840def stream.close(st: Stream) -> IO(Unit):
4841 Stream{tls, s, ms, res, rb, out, decs, back} = st
4842 do IO<Unit>:
4843 good : Bool <- stream.finish(decs)
4844 io.close(tls, s)
4845
4846def stream.po.end(out: Po) -> Bool:
4847 match out:
4848 case PoEnd{}:
4849 True{}
4850 case other:
4851 False{}
4852
4853def stream.rb.done(+rb: Rb) -> Bool:
4854 match rb:
4855 case RbDone{}:
4856 True{}
4857 case other:
4858 False{}
4859
4860def stream.give(whole: Bool, back: Maybe<&2, String>, p: Pool, +tls: Bool, s: Socket) -> IO(Pool):
4861 match whole:
4862 case True{}:
4863 match back:
4864 case Some{+key}:
4865 pool.give(p, key, Conn{tls, s})
4866 case None{}:
4867 do IO<Pool>:
4868 io.close(tls, s)
4869 return p
4870 case False{}:
4871 do IO<Pool>:
4872 io.close(tls, s)
4873 return p
4874
4875
4876def pool.stream.close(p: Pool, st: Stream) -> IO(Pool):
4877 Stream{+tls, s, ms, res, +rb, out, decs, back} = st
4878 do IO<Pool>:
4879 good : Bool <- stream.finish(decs)
4880 stream.give(Bool.and(good, Bool.and(stream.po.end(out), stream.rb.done(rb))), back, p, tls, s)
4881
4882
4883
4884type Sh is Type:
4885 ShMore{buf: Bytes.Bytes, k: U32}
4886 ShBad{err: Maybe<&2, Err>, none: Bool}
4887 ShRes{res: Res, rb: Rb, rest: Bytes.Bytes}
4888
4889def sh.of(hs: Hs) -> Sh:
4890 match hs:
4891 case HsWait{buf, k}:
4892 ShMore{buf, k}
4893 case HsBad{}:
4894 ShBad{None{}, False{}}
4895 case HsSeek{buf, from, k}:
4896 ShBad{None{}, False{}}
4897 case HsRes{res, rb, rest}:
4898 ShRes{res, rb, rest}
4899
4900def sh.none(buf: Bytes.Bytes, +k: U32) -> Bool:
4901 Bytes.Bytes{+len, b} = buf
4902 Bool.and(U32.is_zero(len), U32.is_zero(k))
4903
4904def sh.piece(+head: Bool, buf: Bytes.Bytes, +k: U32, p: U32 & Array<U32>) -> Sh:
4905 Bytes.Bytes{+ol, ob} = buf
4906 (+len, b) = p
4907 Bool.pick(Sh, U32.is_zero(len), ShBad{None{}, Bool.and(U32.is_zero(ol), U32.is_zero(k))}, sh.of(hs.more(head, k, Bytes.Bytes{ol, ob}, Bytes.Bytes{len, b})))
4908
4909def sh.next(+head: Bool, buf: Bytes.Bytes, +k: U32, m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> Socket & Sh:
4910 (s, r) = m
4911 match r:
4912 case Fail{(+code, why)}:
4913 (s, ShBad{Some{err.or_late(code, ErrRead{code, why})}, sh.none(buf, k)})
4914 case Done{p}:
4915 (s, sh.piece(head, buf, k, p))
4916
4917def fetch.fail.of(r: Result<&1, &1, Err, Res>) -> Result<&1, &1, Err, Stream>:
4918 match r:
4919 case Fail{e}:
4920 Fail{e}
4921 case Done{res}:
4922 Fail{ErrBad{}}
4923
4924
4925def stream.back(back: Maybe<&2, String>, +rb: Rb, +headers: Map<&2, List<&2, String>>) -> Maybe<&2, String>:
4926 Bool.pick(Maybe<&2, String>, Bool.and(Bool.not(rb.eof(rb)), Bool.not(res.conn_close(headers))), back, None{})
4927
4928
4929@unsafe
4930def stream.head(+tls: Bool, +ms: U32, +head: Bool, back: Maybe<&2, String>, st: Socket & Sh) -> IO(Bool & Result<&1, &1, Err, Stream>):
4931 (s, sh) = st
4932 match sh:
4933 case ShMore{buf, +k}:
4934 do IO<Bool & Result<&1, &1, Err, Stream>>:
4935 m : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <- io.recv.words(tls, s, 65536, ms)
4936 stream.head(tls, ms, head, back, sh.next(head, buf, k, m))
4937 case ShBad{err, +none}:
4938 do IO<Bool & Result<&1, &1, Err, Stream>>:
4939 r : Result<&1, &1, Err, Res> <- fetch.bad(tls, s, err)
4940 return (none, fetch.fail.of(r))
4941 case ShRes{res, +rb, rest}:
4942 Res{+status, +headers, body} = res
4943 IO.pure(Bool & Result<&1, &1, Err, Stream>, (False{}, Done{stream.mk(tls, s, ms, Res{status, headers, body}, Nil{},
4944 stream.back(back, rb, headers), stream.pc(rb, rest))}))
4945
4946def stream.sent(+tls: Bool, +ms: U32, +head: Bool, back: Maybe<&2, String>, m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Bool & Result<&1, &1, Err, Stream>):
4947 (s, r) = m
4948 match r:
4949 case Fail{(+code, why)}:
4950 do IO<Bool & Result<&1, &1, Err, Stream>>:
4951 io.close(tls, s)
4952 return (True{}, Fail{err.or_late(code, ErrWrite{code, why})})
4953 case Done{u}:
4954 stream.head(tls, ms, head, back, (s, ShMore{Bytes.new(0), 0}))
4955
4956
4957def stream.conn(r: Result<&1, &1, Err, Conn>, +ms: U32, +head: Bool, back: Maybe<&2, String>, wire: Bytes.Bytes) -> IO(Bool & Result<&1, &1, Err, Stream>):
4958 match r:
4959 case Fail{e}:
4960 IO.pure(Bool & Result<&1, &1, Err, Stream>, (False{}, Fail{e}))
4961 case Done{c}:
4962 match c:
4963 case Conn{+tls, s}:
4964 do IO<Bool & Result<&1, &1, Err, Stream>>:
4965 sent : Socket & Result<&1, &1, U32 & String, Unit> <- io.send.bytes(tls, s, wire)
4966 stream.sent(tls, ms, head, back, sent)
4967 case ConnH2{s, client}:
4968 do IO<Bool & Result<&1, &1, Err, Stream>>:
4969 Wire.tls.close(s)
4970 return (False{}, Fail{ErrBad{}})
4971
4972def stream.opened(x: Bool & Result<&1, &1, Err, Stream>) -> Result<&1, &1, Err, Stream>:
4973 (none, r) = x
4974 r
4975
4976
4977def stream.fresh(+ms: U32, +head: Bool, back: Maybe<&2, String>, route: Route, wire: Bytes.Bytes) -> IO(Result<&1, &1, Err, Stream>):
4978 do IO<Result<&1, &1, Err, Stream>>:
4979 c : Result<&1, &1, Err, Conn> <- route.open(ms, False{}, route)
4980 x : Bool & Result<&1, &1, Err, Stream> <- stream.conn(c, ms, head, back, wire)
4981 return stream.opened(x)
4982
4983def stream.stale(again: Bool, p: Pool, +ms: U32, +head: Bool, +key: String, route: Route, spare: Bytes.Bytes, e: Err) ->
4984 IO(Pool & Result<&1, &1, Err, Stream>):
4985 match again:
4986 case True{}:
4987 do IO<Pool & Result<&1, &1, Err, Stream>>:
4988 got : Result<&1, &1, Err, Stream> <- stream.fresh(ms, head, Some{key}, route, spare)
4989 return (p, got)
4990 case False{}:
4991 IO.pure(Pool & Result<&1, &1, Err, Stream>, (p, Fail{e}))
4992
4993def stream.reused(p: Pool, +ms: U32, +head: Bool, +idem: Bool, +key: String, route: Route, spare: Bytes.Bytes,
4994 x: Bool & Result<&1, &1, Err, Stream>) -> IO(Pool & Result<&1, &1, Err, Stream>):
4995 (+none, r) = x
4996 match r:
4997 case Done{st}:
4998 IO.pure(Pool & Result<&1, &1, Err, Stream>, (p, Done{st}))
4999 case Fail{e}:
5000 stream.stale(pool.retry(none, idem), p, ms, head, key, route, spare, e)
5001
5002
5003def stream.idle(p: Pool, +ms: U32, +head: Bool, +idem: Bool, +key: String, route: Route,
5004 r: Bytes.Bytes & Bytes.Bytes, c: Conn) -> IO(Pool & Result<&1, &1, Err, Stream>):
5005 (wire, spare) = r
5006 match c:
5007 case Conn{tls, s}:
5008 do IO<Pool & Result<&1, &1, Err, Stream>>:
5009 x : Bool & Result<&1, &1, Err, Stream> <- stream.conn(Done{Conn{tls, s}}, ms, head, Some{key}, wire)
5010 stream.reused(p, ms, head, idem, key, route, spare, x)
5011 case ConnH2{s, client}:
5012 do IO<Pool & Result<&1, &1, Err, Stream>>:
5013 next : Pool <- pool.give(p, key, ConnH2{s, client})
5014 got : Result<&1, &1, Err, Stream> <- stream.fresh(ms, head, Some{key}, route, spare)
5015 return (next, got)
5016
5017
5018def stream.take(taken: Pool & Maybe<&1, Conn>, +ms: U32, +head: Bool, +idem: Bool, +key: String, route: Route,
5019 wire: Bytes.Bytes) -> IO(Pool & Result<&1, &1, Err, Stream>):
5020 (p, m) = taken
5021 match m:
5022 case None{}:
5023 do IO<Pool & Result<&1, &1, Err, Stream>>:
5024 got : Result<&1, &1, Err, Stream> <- stream.fresh(ms, head, Some{key}, route, wire)
5025 return (p, got)
5026 case Some{c}:
5027 stream.idle(p, ms, head, idem, key, route, Bytes.slice(wire, 0, 4294967295), c)
5028
5029
5030
5031def stream.route(p: Pool, +ms: U32, r: String & Send & Route) -> IO(Pool & Result<&1, &1, Err, Stream>):
5032 (+key, request, route) = r
5033 Send{+method, target, authority, headers, body} = request
5034 do IO<Pool & Result<&1, &1, Err, Stream>>:
5035 taken : Pool & Maybe<&1, Conn> <- pool.take.live(p, key)
5036 stream.take(taken, ms, String.eq(method, "HEAD"), pool.idempotent(method), key, route,
5037 encode_req.on(method, target, authority, headers, body, False{}))
5038
5039def stream.origin.choice(p: Pool, +method: String, +ms: U32,
5040 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, hf: String,
5041 scheme: String, host: String, port: U32, target: String,
5042 cert: Maybe<&2, Cert>, +ca: String, choice: Maybe<&2, Proxy>) ->
5043 IO(Pool & Result<&1, &1, Err, Stream>):
5044 match choice:
5045 case None{}:
5046 IO.pure(Pool & Result<&1, &1, Err, Stream>, (p, Fail{ErrUrl{}}))
5047 case Some{proxy}:
5048 stream.route(p, ms, origin.route(method, headers, body, hf, scheme, host,
5049 port, target, cert, ca, proxy))
5050
5051
5052def stream.origin(p: Pool, +method: String, +ms: U32, headers: Map<&2, List<&2, String>>,
5053 body: Bytes.Bytes, hf: String, a: Url.Abs, known: Bool,
5054 cert: Maybe<&2, Cert>, +ca: String, choice: Maybe<&2, Proxy>) ->
5055 IO(Pool & Result<&1, &1, Err, Stream>):
5056 Url.Abs{scheme, host, port, target} = a
5057 match known:
5058 case False{}:
5059 IO.pure(Pool & Result<&1, &1, Err, Stream>, (p, Fail{ErrUrl{}}))
5060 case True{}:
5061 stream.origin.choice(p, method, ms, headers, body, hf, scheme, host, port,
5062 target, cert, ca, choice)
5063
5064def stream.one.back(h: Hop, pr: Pool & Result<&1, &1, Err, Stream>) -> Pool & Hop & Result<&1, &1, Err, Stream>:
5065 (p, r) = pr
5066 (p, h, r)
5067
5068
5069def stream.one.of(p: Pool, +method: String, +url: Url.Abs, headers: Map<&2, List<&2, String>>, sent: Map<&2, List<&2, String>>,
5070 +ms: U32, r: Bytes.Bytes & Bytes.Bytes, cert: Maybe<&2, Cert>, +ca: String) -> IO(Pool & Hop & Result<&1, &1, Err, Stream>):
5071 Url.Abs{+scheme, +host, +port, target} = url
5072 (body, wire) = r
5073 do IO<Pool & Hop & Result<&1, &1, Err, Stream>>:
5074 choice : Maybe<&2, Proxy> <- proxy.select(scheme, host, port)
5075 got : Pool & Result<&1, &1, Err, Stream> <-
5076 stream.origin(p, method, ms, sent, wire, Url.host_field(url), url,
5077 pool.scheme.ok(url), cert, ca, choice)
5078 return stream.one.back(Hop{method, url, headers, body}, got)
5079
5080
5081def stream.one(p: Pool, h: Hop, +ms: U32, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>) -> IO(Pool & Hop & Result<&1, &1, Err, Stream>):
5082 Hop{+method, +url, +headers, body} = h
5083 stream.one.of(p, method, url, headers, jar.opt.apply(jar, url, headers), ms, Bytes.slice(body, 0, 4294967295), cert, ca)
5084
5085
5086type Sn is Type:
5087 SnGo{left: Nat, hop: Hop}
5088 SnDrop{st: Stream, next: Next}
5089 SnDone{r: Result<&1, &1, Err, Stream>}
5090
5091def sn.of(n: Next) -> Sn:
5092 match n:
5093 case NGo{l, h}:
5094 SnGo{l, h}
5095 case NStop{}:
5096 SnDone{Fail{ErrRedirect{}}}
5097 case NDone{m}:
5098 match m:
5099 case Fail{e}:
5100 SnDone{Fail{e}}
5101 case Done{r}:
5102 SnDone{Fail{ErrBad{}}}
5103
5104def stream.decide.done(st: Stream, m: Result<&1, &1, Err, Res>) -> Sn:
5105 match m:
5106 case Fail{e}:
5107 SnDrop{st, NDone{Fail{e}}}
5108 case Done{r}:
5109 SnDone{Done{st}}
5110
5111def stream.decide.n(st: Stream, n: Next) -> Sn:
5112 match n:
5113 case NDone{m}:
5114 stream.decide.done(st, m)
5115 case NGo{l, h}:
5116 SnDrop{st, NGo{l, h}}
5117 case NStop{}:
5118 SnDrop{st, NStop{}}
5119
5120
5121type StreamHops is Type:
5122 StreamHops{next: Sn, cert: Maybe<&2, Cert>, jar: Maybe<&2, Jar>, ca: String}
5123
5124
5125def stream.trust(+old: String, cert: Maybe<&2, Cert>, +ca: String, n: Next) -> Next & Maybe<&2, Cert> & String:
5126 match n:
5127 case NGo{left, h}:
5128 Hop{method, +url, headers, body} = h
5129 +same = String.eq(old, redirect.origin(url))
5130 (NGo{left, Hop{method, url, headers, body}}, Bool.pick(Maybe<&2, Cert>, same, cert, None{}), pool.next.ca(same, ca))
5131 case NDone{r}:
5132 (NDone{r}, None{}, "")
5133 case NStop{}:
5134 (NStop{}, None{}, "")
5135
5136def stream.decide.trust(st: Stream, jar: Maybe<&2, Jar>, nt: Next & Maybe<&2, Cert> & String) -> StreamHops:
5137 (n, cert, ca) = nt
5138 StreamHops{stream.decide.n(st, n), cert, jar, ca}
5139
5140def stream.decide.jar(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, st: Stream, +old: String, hop: Hop,
5141 r: Maybe<&2, Jar> & Result<&1, &1, Err, Res>) -> StreamHops:
5142 (jar, m) = r
5143 stream.decide.trust(st, jar, stream.trust(old, cert, ca, fetch.policy(mode, left, hop, m)))
5144
5145
5146def stream.decide.of(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>, hop: Hop, sr: Stream & Res) -> StreamHops:
5147 Hop{method, +url, headers, body} = hop
5148 (st, res) = sr
5149 stream.decide.jar(mode, left, cert, ca, st, redirect.origin(url), Hop{method, url, headers, body}, jar.opt.store(jar, url, Done{res}))
5150
5151def stream.decide(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>, x: Hop & Result<&1, &1, Err, Stream>) -> StreamHops:
5152 (hop, r) = x
5153 match r:
5154 case Fail{e}:
5155 StreamHops{SnDone{Fail{e}}, cert, jar, ca}
5156 case Done{st}:
5157 stream.decide.of(mode, left, cert, ca, jar, hop, stream.res(st))
5158
5159def stream.step(+mode: Mode, left: Nat, cert: Maybe<&2, Cert>, +ca: String, jar: Maybe<&2, Jar>,
5160 x: Pool & Hop & Result<&1, &1, Err, Stream>) -> Pool & StreamHops:
5161 (p, hop, r) = x
5162 (p, stream.decide(mode, left, cert, ca, jar, (hop, r)))
5163
5164
5165
5166@unsafe
5167def stream.hops(+mode: Mode, +ms: U32, x: Pool & StreamHops) -> IO(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5168 (p, hs) = x
5169 StreamHops{sn, +cert, +jar, +ca} = hs
5170 match sn:
5171 case SnDone{r}:
5172 IO.pure(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>, (p, jar, r))
5173 case SnGo{left, hop}:
5174 do IO<Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5175 y : Pool & Hop & Result<&1, &1, Err, Stream> <- stream.one(p, hop, ms, cert, ca, jar)
5176 stream.hops(mode, ms, stream.step(mode, left, cert, ca, jar, y))
5177 case SnDrop{st, next}:
5178 do IO<Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5179 q : Pool <- pool.stream.close(p, st)
5180 stream.hops(mode, ms, (q, StreamHops{sn.of(next), cert, jar, ca}))
5181
5182type Opened is Type:
5183 Opened{decs: List<&1, Zlib.Decoder>, bad: Bool}
5184
5185def stream.open.got(st: Opened, r: Result<&1, &1, U32 & String, Zlib.Decoder>) -> Opened:
5186 Opened{decs, bad} = st
5187 match r:
5188 case Fail{e}:
5189 Opened{decs, True{}}
5190 case Done{d}:
5191 Opened{d <> decs, bad}
5192
5193def stream.open.add(st: Opened, m: IO(Result<&1, &1, U32 & String, Zlib.Decoder>)) -> IO(Opened):
5194 do IO<Opened>:
5195 r : Result<&1, &1, U32 & String, Zlib.Decoder> <- m
5196 return stream.open.got(st, r)
5197
5198def stream.open.zstd(zstd: Bool, st: Opened) -> IO(Opened):
5199 match zstd:
5200 case True{}:
5201 stream.open.add(st, Zlib.zstd.new())
5202 case False{}:
5203 IO.pure(Opened, st)
5204
5205def stream.open.br(br: Bool, +c: String, st: Opened) -> IO(Opened):
5206 match br:
5207 case True{}:
5208 stream.open.add(st, Zlib.brotli.new())
5209 case False{}:
5210 stream.open.zstd(String.eq(c, "zstd"), st)
5211
5212def stream.open.gz(gz: Bool, +c: String, st: Opened) -> IO(Opened):
5213 match gz:
5214 case True{}:
5215 stream.open.add(st, Zlib.inflate.new())
5216 case False{}:
5217 stream.open.br(String.eq(c, "br"), c, st)
5218
5219def stream.open.step(+c: String, st: Opened) -> IO(Opened):
5220 Opened{decs, bad} = st
5221 match bad:
5222 case True{}:
5223 IO.pure(Opened, Opened{decs, True{}})
5224 case False{}:
5225 stream.open.gz(Bool.or(String.eq(c, "gzip"), Bool.or(String.eq(c, "x-gzip"), String.eq(c, "deflate"))),
5226 c, Opened{decs, False{}})
5227
5228def stream.open.one(prev: IO(Opened), c: String) -> IO(Opened):
5229 do IO<Opened>:
5230 st : Opened <- prev
5231 stream.open.step(c, st)
5232
5233def stream.open.all(cs: List<&2, String>) -> IO(Opened):
5234 List.foldl(~&2, ~String, ~IO(Opened), ~stream.open.one, cs, IO.pure(Opened, Opened{Nil{}, False{}}))
5235
5236def stream.put.decs(st: Stream, decs: List<&1, Zlib.Decoder>) -> Stream:
5237 Stream{tls, s, ms, res, rb, out, old, back} = st
5238 Stream{tls, s, ms, res, rb, out, decs, back}
5239
5240def stream.init.opened(st: Stream, opened: Opened) -> IO(Result<&1, &1, Err, Stream>):
5241 Opened{decs, bad} = opened
5242 match bad:
5243 case True{}:
5244 do IO<Result<&1, &1, Err, Stream>>:
5245 stream.close(stream.put.decs(st, decs))
5246 return Fail{ErrBad{}}
5247 case False{}:
5248 IO.pure(Result<&1, &1, Err, Stream>, Done{stream.put.decs(st, List.reverse(&1, Zlib.Decoder, decs))})
5249
5250def stream.init.known(known: Bool, st: Stream, cs: List<&2, String>) -> IO(Result<&1, &1, Err, Stream>):
5251 match known:
5252 case False{}:
5253 IO.pure(Result<&1, &1, Err, Stream>, Done{st})
5254 case True{}:
5255 do IO<Result<&1, &1, Err, Stream>>:
5256 opened : Opened <- stream.open.all(cs)
5257 stream.init.opened(st, opened)
5258
5259def stream.init.st(+enc: String, st: Stream) -> IO(Result<&1, &1, Err, Stream>):
5260 Stream{tls, s, ms, res, rb, out, decs, back} = st
5261 Res{+status, +headers, body} = res
5262 +cs = List.reverse(&2, String, ce.split(fields(headers, "content-encoding")))
5263 stream.init.known(ce.known(cs, Con{"identity", Con{"x-gzip", ce.split(Con{enc, Nil{}})}}),
5264 Stream{tls, s, ms, Res{status, headers, body}, rb, out, decs, back}, cs)
5265
5266def stream.init(+enc: String, r: Result<&1, &1, Err, Stream>) -> IO(Result<&1, &1, Err, Stream>):
5267 match r:
5268 case Fail{e}:
5269 IO.pure(Result<&1, &1, Err, Stream>, Fail{e})
5270 case Done{st}:
5271 stream.init.st(enc, st)
5272
5273def stream.init.pool(+enc: String, x: Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>) -> IO(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5274 (p, jar, r) = x
5275 do IO<Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5276 s : Result<&1, &1, Err, Stream> <- stream.init(enc, r)
5277 return (p, jar, s)
5278
5279def stream.start(method: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, u: Maybe<&2, Url.Abs>) -> Sn:
5280 match u:
5281 case None{}:
5282 SnDone{Fail{ErrUrl{}}}
5283 case Some{a}:
5284 SnGo{20n, Hop{method, a, req.put(Map.to_list(&2, List<&2, String>, headers), empty()), body}}
5285
5286def stream.only(x: Maybe<&2, Jar> & Result<&1, &1, Err, Stream>) -> Result<&1, &1, Err, Stream>:
5287 (jar, r) = x
5288 r
5289
5290def pool.stream.only(x: Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>) -> Pool & Result<&1, &1, Err, Stream>:
5291 (p, jar, r) = x
5292 (p, r)
5293
5294
5295def stream.unpooled(x: Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>) -> IO(Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5296 (p, jar, r) = x
5297 do IO<Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5298 pool.close(p)
5299 return (jar, r)
5300
5301
5302def pool.open.raw.how(p: Pool, mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5303 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5304 stream.hops(mode, ms, (p, StreamHops{stream.start(method, headers, body, Url.absolute(url)), cert, jar, ca}))
5305
5306
5307
5308
5309def open.raw.how(mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5310 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5311 do IO<Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5312 x : Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <- pool.open.raw.how(pool.new(), mode, cert, ca, jar, method, url, headers, body, ms)
5313 stream.unpooled(x)
5314
5315
5316def open.raw.with(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Result<&1, &1, Err, Stream>):
5317 do IO<Result<&1, &1, Err, Stream>>:
5318 x : Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <- open.raw.how(ModeFollow{}, None{}, "", None{}, method, url, headers, body, ms)
5319 return stream.only(x)
5320
5321def open.raw(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Result<&1, &1, Err, Stream>):
5322 open.raw.with(method, url, headers, body, 30000)
5323
5324def pool.open.how.enc(+enc: String, p: Pool, mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5325 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5326 do IO<Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5327 x : Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <-
5328 stream.hops(mode, ms, (p, StreamHops{stream.start(method, accept.enc(headers, enc), body, Url.absolute(url)), cert, jar, ca}))
5329 stream.init.pool(enc, x)
5330
5331
5332def pool.open.how(p: Pool, mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5333 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5334 do IO<Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5335 enc : String <- codings()
5336 pool.open.how.enc(enc, p, mode, cert, ca, jar, method, url, headers, body, ms)
5337
5338def pool.open.with(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Pool & Result<&1, &1, Err, Stream>):
5339 do IO<Pool & Result<&1, &1, Err, Stream>>:
5340 x : Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <- pool.open.how(p, ModeFollow{}, None{}, "", None{}, method, url, headers, body, ms)
5341 return pool.stream.only(x)
5342
5343
5344def pool.open(p: Pool, method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Pool & Result<&1, &1, Err, Stream>):
5345 pool.open.with(p, method, url, headers, body, 30000)
5346
5347def open.how.enc(+enc: String, mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5348 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5349 do IO<Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5350 x : Pool & Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <- pool.open.how.enc(enc, pool.new(), mode, cert, ca, jar, method, url, headers, body, ms)
5351 stream.unpooled(x)
5352
5353
5354def open.how(mode: Mode, cert: Maybe<&2, Cert>, ca: String, jar: Maybe<&2, Jar>, method: String, url: String,
5355 headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Maybe<&2, Jar> & Result<&1, &1, Err, Stream>):
5356 do IO<Maybe<&2, Jar> & Result<&1, &1, Err, Stream>>:
5357 enc : String <- codings()
5358 open.how.enc(enc, mode, cert, ca, jar, method, url, headers, body, ms)
5359
5360
5361def open.with(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes, +ms: U32) -> IO(Result<&1, &1, Err, Stream>):
5362 do IO<Result<&1, &1, Err, Stream>>:
5363 x : Maybe<&2, Jar> & Result<&1, &1, Err, Stream> <- open.how(ModeFollow{}, None{}, "", None{}, method, url, headers, body, ms)
5364 return stream.only(x)
5365
5366def open(method: String, url: String, headers: Map<&2, List<&2, String>>, body: Bytes.Bytes) -> IO(Result<&1, &1, Err, Stream>):
5367 open.with(method, url, headers, body, 30000)
5368
5369
5370type Up is Type:
5371 Up{tls: Bool, s: Socket, ms: U32, head: Bool, err: Maybe<&2, Err>}
5372
5373def hex.digit(+n: U32) -> Char:
5374 Chr{Bool.pick(U32, U32.is_lt(n, 10), (48 + n : U32), (87 + n : U32))}
5375
5376def hex.go(k: Nat, +n: U32, acc: String) -> String:
5377 match k:
5378 case 0n:
5379 acc
5380 case 1n+p:
5381 hex.go(p, U32.shrn(n, 4n), SCon{hex.digit(U32.and(n, 15)), acc})
5382
5383def hex.trim(+s: String) -> String:
5384 match s:
5385 case SNil{}:
5386 SNil{}
5387 case SCon{Chr{+c}, +t}:
5388 Bool.pick(String, Bool.and(U32.is_eq(c, 48), Bool.not(String.is_empty(t))), hex.trim(t), SCon{Chr{c}, t})
5389
5390def hex(+n: U32) -> String:
5391 hex.trim(hex.go(8n, n, ""))
5392
5393def up.sent(+tls: Bool, +ms: U32, +head: Bool, m: Socket & Result<&1, &1, U32 & String, Unit>) -> Up:
5394 (s, r) = m
5395 match r:
5396 case Fail{(+code, why)}:
5397 Up{tls, s, ms, head, Some{err.or_late(code, ErrWrite{code, why})}}
5398 case Done{u}:
5399 Up{tls, s, ms, head, None{}}
5400
5401def up.conn(r: Result<&1, &1, Err, Conn>, +ms: U32, +head: Bool, wire: String) -> IO(Result<&1, &1, Err, Up>):
5402 match r:
5403 case Fail{e}:
5404 IO.pure(Result<&1, &1, Err, Up>, Fail{e})
5405 case Done{c}:
5406 match c:
5407 case Conn{+tls, s}:
5408 do IO<Result<&1, &1, Err, Up>>:
5409 sent : Socket & Result<&1, &1, U32 & String, Unit> <- io.send.bytes(tls, s, Bytes.from_string(wire))
5410 return Done{up.sent(tls, ms, head, sent)}
5411 case ConnH2{s, client}:
5412 do IO<Result<&1, &1, Err, Up>>:
5413 Wire.tls.close(s)
5414 return Fail{ErrBad{}}
5415
5416def up.head(+method: String, +target: String, +hf: String, +headers: Map<&2, List<&2, String>>) -> String:
5417 request(method, target, set(set(set(req.put(Map.to_list(&2, List<&2, String>, headers), empty()), "host", hf), "connection", "close"), "transfer-encoding", "chunked"), "")
5418
5419def up.route(+ms: U32, r: String & Send & Route) -> IO(Result<&1, &1, Err, Up>):
5420 (key, request, route) = r
5421 Send{+method, +target, +authority, +headers, body} = request
5422 do IO<Result<&1, &1, Err, Up>>:
5423 c : Result<&1, &1, Err, Conn> <- route.open(ms, False{}, route)
5424 up.conn(c, ms, String.eq(method, "HEAD"), up.head(method, target, authority, headers))
5425
5426def up.origin.choice(+method: String, +ms: U32,
5427 headers: Map<&2, List<&2, String>>, hf: String,
5428 scheme: String, host: String, port: U32, target: String,
5429 cert: Maybe<&2, Cert>, +ca: String, choice: Maybe<&2, Proxy>) ->
5430 IO(Result<&1, &1, Err, Up>):
5431 match choice:
5432 case None{}:
5433 IO.pure(Result<&1, &1, Err, Up>, Fail{ErrUrl{}})
5434 case Some{proxy}:
5435 up.route(ms, origin.route(method, headers, Bytes.new(0), hf, scheme, host,
5436 port, target, cert, ca, proxy))
5437
5438def up.origin(+method: String, +ms: U32, headers: Map<&2, List<&2, String>>,
5439 hf: String, a: Url.Abs, known: Bool, cert: Maybe<&2, Cert>, +ca: String) ->
5440 IO(Result<&1, &1, Err, Up>):
5441 Url.Abs{+scheme, +host, +port, target} = a
5442 match known:
5443 case False{}:
5444 IO.pure(Result<&1, &1, Err, Up>, Fail{ErrUrl{}})
5445 case True{}:
5446 do IO<Result<&1, &1, Err, Up>>:
5447 choice : Maybe<&2, Proxy> <- proxy.select(scheme, host, port)
5448 up.origin.choice(method, ms, headers, hf, scheme, host, port, target,
5449 cert, ca, choice)
5450
5451def up.url(+method: String, +ms: U32, headers: Map<&2, List<&2, String>>,
5452 u: Maybe<&2, Url.Abs>, cert: Maybe<&2, Cert>, +ca: String) ->
5453 IO(Result<&1, &1, Err, Up>):
5454 match u:
5455 case None{}:
5456 IO.pure(Result<&1, &1, Err, Up>, Fail{ErrUrl{}})
5457 case Some{+a}:
5458 up.origin(method, ms, headers, Url.host_field(a), a, pool.scheme.ok(a), cert, ca)
5459
5460
5461def upload.how(cert: Maybe<&2, Cert>, +ca: String, +method: String, url: String,
5462 headers: Map<&2, List<&2, String>>, +ms: U32) -> IO(Result<&1, &1, Err, Up>):
5463 up.url(method, ms, headers, Url.absolute(url), cert, ca)
5464
5465def upload.with(+method: String, url: String, headers: Map<&2, List<&2, String>>, +ms: U32) -> IO(Result<&1, &1, Err, Up>):
5466 upload.how(None{}, "", method, url, headers, ms)
5467
5468def upload(+method: String, url: String, headers: Map<&2, List<&2, String>>) -> IO(Result<&1, &1, Err, Up>):
5469 upload.with(method, url, headers, 30000)
5470
5471def up.wrote(+tls: Bool, +ms: U32, +head: Bool, m: Socket & Result<&1, &1, U32 & String, Unit>) -> Up & Result<&2, &2, Err, Unit>:
5472 (s, r) = m
5473 match r:
5474 case Fail{(+code, why)}:
5475 +e = err.or_late(code, ErrWrite{code, why})
5476 (Up{tls, s, ms, head, Some{e}}, Fail{e})
5477 case Done{u}:
5478 (Up{tls, s, ms, head, None{}}, Done{Unit{}})
5479
5480
5481def up.put(+tls: Bool, s: Socket, +ms: U32, +head: Bool, +len: U32, piece: Bytes.Bytes, skip: Bool) -> IO(Up & Result<&2, &2, Err, Unit>):
5482 match skip:
5483 case True{}:
5484 IO.pure(Up & Result<&2, &2, Err, Unit>, (Up{tls, s, ms, head, None{}}, Done{Unit{}}))
5485 case False{}:
5486 do IO<Up & Result<&2, &2, Err, Unit>>:
5487 sent : Socket & Result<&1, &1, U32 & String, Unit> <- io.send.bytes(tls, s, Bytes.concat(Con{Bytes.from_string(hex(len) ++ "\r\n"), Con{piece, Con{Bytes.from_string("\r\n"), Nil{}}}}))
5488 return up.wrote(tls, ms, head, sent)
5489
5490def up.chunk(+tls: Bool, s: Socket, +ms: U32, +head: Bool, piece: Bytes.Bytes) -> IO(Up & Result<&2, &2, Err, Unit>):
5491 Bytes.Bytes{+len, buf} = piece
5492 up.put(tls, s, ms, head, len, Bytes.Bytes{len, buf}, U32.is_zero(len))
5493
5494
5495def upload.write(up: Up, piece: Bytes.Bytes) -> IO(Up & Result<&2, &2, Err, Unit>):
5496 Up{+tls, s, +ms, +head, err} = up
5497 match err:
5498 case Some{+e}:
5499 IO.pure(Up & Result<&2, &2, Err, Unit>, (Up{tls, s, ms, head, Some{e}}, Fail{e}))
5500 case None{}:
5501 up.chunk(tls, s, ms, head, piece)
5502
5503
5504def upload.finish(up: Up) -> IO(Result<&1, &1, Err, Stream>):
5505 Up{+tls, s, +ms, +head, err} = up
5506 match err:
5507 case Some{e}:
5508 do IO<Result<&1, &1, Err, Stream>>:
5509 io.close(tls, s)
5510 return Fail{e}
5511 case None{}:
5512 do IO<Result<&1, &1, Err, Stream>>:
5513 sent : Socket & Result<&1, &1, U32 & String, Unit> <- io.send.bytes(tls, s, Bytes.from_string("0\r\n\r\n"))
5514 x : Bool & Result<&1, &1, Err, Stream> <- stream.sent(tls, ms, head, None{}, sent)
5515 return stream.opened(x)
5516
5517type ServerConfig is Data:
5518 ServerConfig{host: String, port: U32, body_bytes: U32, header_bytes: U32,
5519 connections: U32, requests: U32, buffered: U32, header_ms: U32, body_ms: U32,
5520 idle_ms: U32, write_ms: U32}
5521
5522
5523type Limits is Data:
5524 Limits{body: Nat, headers: U32, header_ms: U32, body_ms: U32, idle_ms: U32, write_ms: U32}
5525
5526
5527
5528def limits.representable() -> U32:
5529 4294901756
5530
5531def limits.validate.ok(valid: Bool) -> IO(Unit):
5532 match valid:
5533 case True{}:
5534 IO.pure(Unit, Unit{})
5535 case False{}:
5536 IO.die(Unit, 22, "request cap plus 65536-byte recv overshoot and 3-byte packed rounding must fit U32")
5537
5538def limits.validate(+max: Nat) -> IO(Unit):
5539 limits.validate.ok(Nat.is_gt(max, 0n) && Nat.is_le(max, U32.to_nat(limits.representable())))
5540
5541def limits(max: Nat) -> Limits:
5542 Limits{max, 65536, 5000, 30000, 30000, 30000}
5543
5544def limits.config(config: ServerConfig) -> Limits:
5545 ServerConfig{host, port, body, headers, connections, requests, buffered, header_ms, body_ms, idle_ms, write_ms} = config
5546 Limits{U32.to_nat(body), headers, header_ms, body_ms, idle_ms, write_ms}
5547
5548
5549type Phase is Data:
5550 Phase{kind: U32, until: Time.Duration}
5551
5552def phase.new(+kind: U32, ms: U32, now: Time.Duration) -> Phase:
5553 Phase{kind, Time.Duration.add(now, Time.Duration.of_ms(ms))}
5554
5555def phase.remaining(+until: Time.Duration, +now: Time.Duration) -> U32:
5556 Bool.pick(U32, Cmp.is_lt(Time.Duration.cmp(now, until)),
5557 U32.max(1, Int.I64.to_u32(Time.Duration.to_ms(Time.Duration.sub(until, now)))), 0)
5558
5559def bounded.sent(m: Socket & (U32 & Result<&1, &1, U32 & String, Unit>)) -> Socket & Result<&1, &1, U32 & String, Unit>:
5560 (s, (accepted, result)) = m
5561 (s, result)
5562
5563def bounded.send.ready(expired: Bool, s: Socket, b: Bytes.Bytes, ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
5564 match expired:
5565 case True{}:
5566 IO.pure(Socket & Result<&1, &1, U32 & String, Unit>, (s, Fail{(60, "write deadline expired")}))
5567 case False{}:
5568 Bytes.Bytes{len, words} = b
5569 do IO<Socket & Result<&1, &1, U32 & String, Unit>>:
5570 sent : Socket & (U32 & Result<&1, &1, U32 & String, Unit>) <- Wire.send.words.deadline(s, len, words, ms)
5571 return bounded.sent(sent)
5572
5573def bounded.send(s: Socket, b: Bytes.Bytes, until: Time.Duration) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
5574 do IO<Socket & Result<&1, &1, U32 & String, Unit>>:
5575 now : Time.Duration <- Time.mono()
5576 +ms : U32 = phase.remaining(until, now)
5577 bounded.send.ready(U32.is_zero(ms), s, b, ms)
5578
5579def reply.bytes(res: Res, head: Bool, close: Bool) -> Bytes.Bytes:
5580 Res{+status, +headers, body} = res
5581 encode.on(Res{status, Bool.pick(Map<&2, List<&2, String>>, close, set(headers, "connection", "close"), headers), body}, head)
5582
5583def reply.fail.bytes(+status: U32) -> Res:
5584 Res{status, set(empty(), "connection", "close"), Bytes.from_string(reason(status))}
5585
5586def reply.drain.of(m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> Socket & Bool:
5587 (s, r) = m
5588 match r:
5589 case Fail{e}:
5590 (s, False{})
5591 case Done{data}:
5592 (len, words) = data
5593 (s, Bool.not(U32.is_zero(len)))
5594
5595
5596def reply.drain(n: Nat, alive: Bool, +until: Time.Duration, st: Socket & Bool) -> IO(Unit):
5597 match n:
5598 case 0n:
5599 (s, more) = st
5600 Socket.close(s)
5601 case 1n+p:
5602 match alive:
5603 case False{}:
5604 (s, more) = st
5605 Socket.close(s)
5606 case True{}:
5607 (s, more) = st
5608 match more:
5609 case False{}:
5610 Socket.close(s)
5611 case True{}:
5612 do IO<Unit>:
5613 got : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <- Wire.recv.words(s, 65536, 50)
5614 now : Time.Duration <- Time.mono()
5615 reply.drain(p, Cmp.is_lt(Time.Duration.cmp(now, until)), until, reply.drain.of(got))
5616
5617def reply.sent.close(m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Unit):
5618 (s, r) = m
5619 match r:
5620 case Fail{e}:
5621 Socket.close(s)
5622 case Done{u}:
5623 do IO<Unit>:
5624 now : Time.Duration <- Time.mono()
5625 reply.drain(U32.to_nat(512), True{}, Time.Duration.add(now, Time.Duration.of_ms(2500)), (s, True{}))
5626
5627def reply.sent.observe(observer: Completion.Receipt, status: U32,
5628 sent: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Unit):
5629 (socket, result) = sent
5630 match result:
5631 case Fail{(code, message)}:
5632 do IO<Unit>:
5633 Socket.close(socket)
5634 Completion.report(observer, status, Completion.WriteFailed{code, message})
5635 case Done{unit}:
5636 do IO<Unit>:
5637 reply.sent.close((socket, Done{unit}))
5638 Completion.report(observer, status, Completion.HostAccepted{})
5639
5640
5641def reply_fail(observer: Completion.Receipt, s: Socket, +status: U32, ms: U32) -> IO(Unit):
5642 do IO<Unit>:
5643 now : Time.Duration <- Time.mono()
5644 sent : Socket & Result<&1, &1, U32 & String, Unit> <-
5645 bounded.send(s, encode(reply.fail.bytes(status)), Time.Duration.add(now, Time.Duration.of_ms(ms)))
5646 reply.sent.observe(observer, status, sent)
5647
5648
5649
5650type Sv is Type:
5651 SvHead{buf: Bytes.Bytes}
5652 SvLen{req: Req, v11: Bool, left: U32, parts: List<&1, Bytes.Bytes>}
5653 SvChunk{req: Req, v11: Bool, n: U32, st: Dc, parts: List<&1, Bytes.Bytes>}
5654
5655type InputOperation is Data:
5656 ReadInput{}
5657 SendContinue{}
5658
5659
5660type Turn is Type:
5661 TurnClose{}
5662 TurnInputFailed{operation: InputOperation, code: U32, message: String}
5663 TurnFail{status: U32}
5664 TurnReq{req: Req, rest: Bytes.Bytes, v11: Bool}
5665 TurnMore{sv: Sv, cont: Bool}
5666
5667def serve.max() -> Nat:
5668 fetch.max()
5669
5670def sv.none() -> Sv:
5671 SvHead{Bytes.new(0)}
5672
5673def sv.body(req: Req, +v11: Bool, parts: List<&1, Bytes.Bytes>, rest: Bytes.Bytes) -> Turn:
5674 Req{method, path, headers, body} = req
5675 TurnReq{Req{method, path, headers, rb.body(parts)}, rest, v11}
5676
5677def sv.len.cut(req: Req, +v11: Bool, parts: List<&1, Bytes.Bytes>, +len: U32, +left: U32, r: Bytes.Bytes & Bytes.Bytes) -> Turn:
5678 (piece, last) = r
5679 sv.body(req, v11, Con{last, parts}, bytes.snd(Bytes.slice(piece, left, (len - left : U32))))
5680
5681def sv.len.have(short: Bool, req: Req, +v11: Bool, +left: U32, parts: List<&1, Bytes.Bytes>, +len: U32, piece: Bytes.Bytes) -> Turn:
5682 match short:
5683 case True{}:
5684 TurnMore{SvLen{req, v11, (left - len : U32), Con{piece, parts}}, False{}}
5685 case False{}:
5686 sv.len.cut(req, v11, parts, len, left, Bytes.slice(piece, 0, left))
5687
5688def sv.len(req: Req, +v11: Bool, +left: U32, parts: List<&1, Bytes.Bytes>, piece: Bytes.Bytes) -> Turn:
5689 Bytes.Bytes{+len, buf} = piece
5690 sv.len.have(U32.is_lt(len, left), req, v11, left, parts, len, Bytes.Bytes{len, buf})
5691
5692def sv.chunk.of(req: Req, +v11: Bool, +n: U32, c: Ck) -> Turn:
5693 Ck{st, parts, rest} = c
5694 match st:
5695 case DcDone{}:
5696 sv.body(req, v11, parts, rest)
5697 case DcBad{}:
5698 TurnFail{400}
5699 case DcSize{a, b}:
5700 TurnMore{SvChunk{req, v11, n, DcSize{a, b}, parts}, False{}}
5701 case DcExt{x}:
5702 TurnMore{SvChunk{req, v11, n, DcExt{x}, parts}, False{}}
5703 case DcSizeLf{x}:
5704 TurnMore{SvChunk{req, v11, n, DcSizeLf{x}, parts}, False{}}
5705 case DcData{k}:
5706 TurnMore{SvChunk{req, v11, n, DcData{k}, parts}, False{}}
5707 case DcDataCr{}:
5708 TurnMore{SvChunk{req, v11, n, DcDataCr{}, parts}, False{}}
5709 case DcDataLf{}:
5710 TurnMore{SvChunk{req, v11, n, DcDataLf{}, parts}, False{}}
5711 case DcTr{j}:
5712 TurnMore{SvChunk{req, v11, n, DcTr{j}, parts}, False{}}
5713
5714def sv.chunk.parts.cons(+len: U32, buf: Array<U32>, counted: List<&1, Bytes.Bytes> & Nat) -> List<&1, Bytes.Bytes> & Nat:
5715 (rest, n) = counted
5716 (Con{Bytes.Bytes{len, buf}, rest}, Nat.add(U32.to_nat(len), n))
5717
5718def sv.chunk.parts(xs: List<&1, Bytes.Bytes>, old: List<&1, Bytes.Bytes>) -> List<&1, Bytes.Bytes> & Nat:
5719 match xs:
5720 case Nil{}:
5721 (old, 0n)
5722 case Con{b, tail}:
5723 Bytes.Bytes{+len, buf} = b
5724 sv.chunk.parts.cons(len, buf, sv.chunk.parts(tail, old))
5725
5726def sv.chunk.cap(big: Bool, req: Req, +v11: Bool, n: U32, st: Dc, parts: List<&1, Bytes.Bytes>, rest: Bytes.Bytes) -> Turn:
5727 match big:
5728 case True{}:
5729 TurnFail{413}
5730 case False{}:
5731 sv.chunk.of(req, v11, n, Ck{st, parts, rest})
5732
5733def sv.chunk.counted(+max: Nat, req: Req, v11: Bool, n: U32, st: Dc, rest: Bytes.Bytes, counted: List<&1, Bytes.Bytes> & Nat) -> Turn:
5734 (parts, size) = counted
5735 +total = Nat.add(U32.to_nat(n), size)
5736 sv.chunk.cap(Nat.is_lt(max, total) || Nat.is_lt(U32.to_nat(4294967295), total), req, v11,
5737 U32.from_nat(total), st, parts, rest)
5738
5739def sv.chunk.decoded(max: Nat, req: Req, v11: Bool, n: U32, old: List<&1, Bytes.Bytes>, c: Ck) -> Turn:
5740 Ck{st, parts, rest} = c
5741 sv.chunk.counted(max, req, v11, n, st, rest, sv.chunk.parts(parts, old))
5742
5743def sv.chunk(+max: Nat, req: Req, +v11: Bool, +n: U32, st: Dc, parts: List<&1, Bytes.Bytes>, piece: Bytes.Bytes) -> Turn:
5744 sv.chunk.decoded(max, req, v11, n, parts, ck(piece, st, Nil{}))
5745
5746
5747def sv.cont(want: Bool, t: Turn) -> Turn:
5748 match t:
5749 case TurnMore{sv, c}:
5750 TurnMore{sv, want}
5751 case TurnClose{}:
5752 TurnClose{}
5753 case TurnInputFailed{operation, code, message}:
5754 TurnInputFailed{operation, code, message}
5755 case TurnFail{s}:
5756 TurnFail{s}
5757 case TurnReq{req, rest, v11}:
5758 TurnReq{req, rest, v11}
5759
5760def sv.cl.k(big: Bool, req: Req, +v11: Bool, +k: U32, rest: Bytes.Bytes) -> Turn:
5761 match big:
5762 case True{}:
5763 TurnFail{413}
5764 case False{}:
5765 sv.len(req, v11, k, Nil{}, rest)
5766
5767def sv.cl(+max: Nat, req: Req, +v11: Bool, rest: Bytes.Bytes, n: Maybe<&2, U32>) -> Turn:
5768 match n:
5769 case None{}:
5770 TurnFail{400}
5771 case Some{+k}:
5772 sv.cl.k(Nat.is_lt(max, U32.to_nat(k)), req, v11, k, rest)
5773
5774def sv.frame(+max: Nat, req: Req, +v11: Bool, rest: Bytes.Bytes, te: Bool) -> Turn:
5775 Req{method, path, +headers, body} = req
5776 match te:
5777 case True{}:
5778 sv.chunk(max, Req{method, path, headers, body}, v11, 0, dc.start(), Nil{}, rest)
5779 case False{}:
5780 sv.cl(max, Req{method, path, headers, body}, v11, rest, parse_u32(header(headers, "content-length")))
5781
5782def sv.expects(+v11: Bool, +headers: Map<&2, List<&2, String>>) -> Bool:
5783 Bool.and(v11, String.eq(String.to_lower(header(headers, "expect")), "100-continue"))
5784
5785def sv.got.head(+max: Nat, +v11: Bool, rest: Bytes.Bytes, req: Req) -> Turn:
5786 Req{method, path, +headers, body} = req
5787 sv.cont(sv.expects(v11, headers), sv.frame(max, Req{method, path, headers, body}, v11, rest, has_header(headers, "transfer-encoding")))
5788
5789def sv.parsed(+max: Nat, +v11: Bool, rest: Bytes.Bytes, g: Got) -> Turn:
5790 match g:
5791 case GotBad{}:
5792 TurnFail{400}
5793 case GotMore{}:
5794 TurnFail{400}
5795 case GotReq{req}:
5796 TurnReq{req, rest, v11}
5797 case GotHead{req}:
5798 sv.got.head(max, v11, rest, req)
5799
5800def sv.split(+max: Nat, +hlen: U32, +len: U32, r: Bytes.Bytes & Bytes.Bytes) -> Turn:
5801 (buf, hb) = r
5802 +head = Bytes.to_string(hb)
5803 sv.parsed(max, req.v11(head), bytes.snd(Bytes.slice(buf, hlen, (len - hlen : U32))), parse.got(head))
5804
5805def sv.hit.none(big: Bool, buf: Bytes.Bytes) -> Turn:
5806 match big:
5807 case True{}:
5808 TurnFail{431}
5809 case False{}:
5810 TurnMore{SvHead{buf}, False{}}
5811
5812def sv.hit.some(big: Bool, +max: Nat, +hlen: U32, +len: U32, buf: Bytes.Bytes) -> Turn:
5813 match big:
5814 case True{}:
5815 TurnFail{431}
5816 case False{}:
5817 sv.split(max, hlen, len, Bytes.slice(buf, 0, hlen))
5818
5819
5820def sv.hit(+max: Nat, +headers: U32, +len: U32, buf: Bytes.Bytes, at: Maybe<&2, U32>) -> Turn:
5821 match at:
5822 case None{}:
5823 sv.hit.none(U32.is_lt(headers, len), buf)
5824 case Some{+i}:
5825 +hlen = (i + 4 : U32)
5826 sv.hit.some(U32.is_lt(headers, hlen), max, hlen, len, buf)
5827
5828def sv.seek.of(+max: Nat, +headers: U32, +len: U32, +from: U32, buf: Bytes.Bytes, r: Bytes.Bytes & Maybe<&2, U32>) -> Turn:
5829 (tail, m) = r
5830 sv.hit(max, headers, len, buf, found.at(from, m))
5831
5832def sv.seek(+max: Nat, +headers: U32, +len: U32, +from: U32, r: Bytes.Bytes & Bytes.Bytes) -> Turn:
5833 (buf, tail) = r
5834 sv.seek.of(max, headers, len, from, buf, Bytes.find(tail, "\r\n\r\n"))
5835
5836
5837def sv.head.at(+max: Nat, +headers: U32, +from: U32, buf: Bytes.Bytes) -> Turn:
5838 Bytes.Bytes{+len, b} = buf
5839 sv.seek(max, headers, len, from, Bytes.slice(Bytes.Bytes{len, b}, from, (len - from : U32)))
5840
5841def sv.head(+max: Nat, +headers: U32, buf: Bytes.Bytes, piece: Bytes.Bytes) -> Turn:
5842 Bytes.Bytes{+ol, ob} = buf
5843 sv.head.at(max, headers, Bool.pick(U32, U32.is_lt(ol, 3), 0, (ol - 3 : U32)), Bytes.append(Bytes.Bytes{ol, ob}, piece))
5844
5845def sv.closed.pick(empty: Bool) -> Turn:
5846 match empty:
5847 case True{}:
5848 TurnClose{}
5849 case False{}:
5850 TurnFail{400}
5851
5852def sv.closed.head(buf: Bytes.Bytes) -> Turn:
5853 Bytes.Bytes{+len, b} = buf
5854 sv.closed.pick(U32.is_eq(len, 0))
5855
5856
5857def sv.step(closed: Bool, +max: Nat, +headers: U32, sv: Sv, piece: Bytes.Bytes) -> Turn:
5858 match closed:
5859 case True{}:
5860 match sv:
5861 case SvHead{buf}:
5862 sv.closed.head(buf)
5863 case SvLen{req, v11, left, parts}:
5864 TurnFail{400}
5865 case SvChunk{req, v11, n, st, parts}:
5866 TurnFail{400}
5867 case False{}:
5868 match sv:
5869 case SvHead{buf}:
5870 sv.head(max, headers, buf, piece)
5871 case SvLen{req, +v11, +left, parts}:
5872 sv.len(req, v11, left, parts, piece)
5873 case SvChunk{req, +v11, +n, st, parts}:
5874 sv.chunk(max, req, v11, n, st, parts, piece)
5875
5876def sv.frame.req(t: Turn) -> Maybe<&1, String & String>:
5877 match t:
5878 case TurnReq{req, rest, v11}:
5879 Req{method, path, headers, body} = req
5880 Some{(Bytes.to_string(body), Bytes.to_string(rest))}
5881 case TurnMore{sv, c}:
5882 None{}
5883 case TurnFail{s}:
5884 None{}
5885 case TurnClose{}:
5886 None{}
5887 case TurnInputFailed{operation, code, message}:
5888 None{}
5889
5890def sv.frame.go(xs: List<&2, String>, t: Turn) -> Maybe<&1, String & String>:
5891 match xs:
5892 case Nil{}:
5893 sv.frame.req(t)
5894 case Con{+x, rest}:
5895 match t:
5896 case TurnMore{sv, c}:
5897 sv.frame.go(rest, sv.step(False{}, serve.max(), 65536, sv, Bytes.from_string(x)))
5898 case TurnReq{req, r, v11}:
5899 sv.frame.req(TurnReq{req, r, v11})
5900 case TurnFail{s}:
5901 None{}
5902 case TurnClose{}:
5903 None{}
5904 case TurnInputFailed{operation, code, message}:
5905 None{}
5906
5907
5908def serve.frame(xs: List<&2, String>) -> Maybe<&1, String & String>:
5909 sv.frame.go(xs, TurnMore{sv.none(), False{}})
5910
5911def talk.piece(settings: Limits, sv: Sv, p: U32 & Array<U32>) -> Turn:
5912 Limits{max, headers, hm, bm, im, wm} = settings
5913 (+len, buf) = p
5914 sv.step(U32.is_eq(len, 0), max, headers, sv, Bytes.Bytes{len, buf})
5915
5916def talk.next(settings: Limits, sv: Sv, m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> Socket & Turn:
5917 (s, r) = m
5918 match r:
5919 case Fail{(+code, message)}:
5920 (s, Bool.pick(Turn, err.late(code), TurnFail{408}, TurnInputFailed{ReadInput{}, code, message}))
5921 case Done{p}:
5922 (s, talk.piece(settings, sv, p))
5923
5924def talk.after.pick(empty: Bool, settings: Limits, rest: Bytes.Bytes) -> Turn:
5925 match empty:
5926 case True{}:
5927 TurnMore{sv.none(), False{}}
5928 case False{}:
5929 Limits{max, headers, hm, bm, im, wm} = settings
5930 sv.step(False{}, max, headers, sv.none(), rest)
5931
5932def talk.after.rest(settings: Limits, rest: Bytes.Bytes, s: Socket) -> Socket & Turn:
5933 Bytes.Bytes{+len, buf} = rest
5934 (s, talk.after.pick(U32.is_eq(len, 0), settings, Bytes.Bytes{len, buf}))
5935
5936def talk.after.keep(keep: Bool, settings: Limits, rest: Bytes.Bytes, s: Socket) -> Socket & Turn:
5937 match keep:
5938 case False{}:
5939 (s, TurnClose{})
5940 case True{}:
5941 talk.after.rest(settings, rest, s)
5942
5943def talk.after(settings: Limits, keep: Bool, rest: Bytes.Bytes, m: Socket & Result<&1, &1, U32 & String, Unit>) -> (Socket & Turn) & Completion.Outcome:
5944 (s, r) = m
5945 match r:
5946 case Fail{(code, message)}:
5947 ((s, TurnClose{}), Completion.WriteFailed{code, message})
5948 case Done{u}:
5949 (talk.after.keep(keep, settings, rest, s), Completion.HostAccepted{})
5950
5951def talk.send(+settings: Limits, s: Socket, rest: Bytes.Bytes, +headers: Map<&2, List<&2, String>>, v11: Bool, +head: Bool, res: Res) -> IO((Socket & Turn) & Completion.Outcome):
5952 Limits{max, cap, hm, bm, im, wm} = settings
5953 Res{+status, +rh, body} = res
5954 +keep = serve.keep(headers, v11, status, rh)
5955 do IO<(Socket & Turn) & Completion.Outcome>:
5956 now : Time.Duration <- Time.mono()
5957 sent : Socket & Result<&1, &1, U32 & String, Unit> <-
5958 bounded.send(s, reply.bytes(Res{status, rh, body}, head, Bool.not(keep)), Time.Duration.add(now, Time.Duration.of_ms(wm)))
5959 return talk.after(settings, keep, rest, sent)
5960
5961def talk.completed.connection(outcome: Completion.Outcome, next: Socket & Turn) -> IO(Maybe<&1, Socket & Turn>):
5962 match outcome:
5963 case Completion.HostAccepted{}:
5964 (socket, turn) = next
5965 IO.pure(Maybe<&1, Socket & Turn>, Some{(socket, turn)})
5966 case Completion.WriteFailed{code, message}:
5967 (socket, turn) = next
5968 do IO<Maybe<&1, Socket & Turn>>:
5969 Socket.close(socket)
5970 return None{}
5971 case Completion.IncompleteResponse{}:
5972 (socket, turn) = next
5973 do IO<Maybe<&1, Socket & Turn>>:
5974 Socket.close(socket)
5975 return None{}
5976
5977def talk.completed(observer: Completion.Receipt, receipt: Completion.Receipt, +status: U32,
5978 sent: (Socket & Turn) & Completion.Outcome) -> IO(Maybe<&1, Socket & Turn>):
5979 (next, outcome) = sent
5980 +observed = {outcome : Completion.Outcome}
5981 do IO<Maybe<&1, Socket & Turn>>:
5982 remaining : Maybe<&1, Socket & Turn> <- talk.completed.connection(observed, next)
5983 Completion.report(observer, status, observed)
5984 Completion.report(receipt, status, observed)
5985 return remaining
5986
5987def talk.respond(observer: Completion.Receipt, +settings: Limits, s: Socket, rest: Bytes.Bytes,
5988 headers: Map<&2, List<&2, String>>, v11: Bool, head: Bool, reply: Reply) -> IO(Maybe<&1, Socket & Turn>):
5989 Reply{Res{+status, rh, body}, receipt} = reply
5990 do IO<Maybe<&1, Socket & Turn>>:
5991 sent : (Socket & Turn) & Completion.Outcome <- talk.send(settings, s, rest, headers, v11, head, Res{status, rh, body})
5992 talk.completed(observer, receipt, status, sent)
5993
5994def talk.cont(cont: Bool, s: Socket, ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
5995 match cont:
5996 case False{}:
5997 IO.pure(Socket & Result<&1, &1, U32 & String, Unit>, (s, Done{Unit{}}))
5998 case True{}:
5999 do IO<Socket & Result<&1, &1, U32 & String, Unit>>:
6000 now : Time.Duration <- Time.mono()
6001 bounded.send(s, Bytes.from_string("HTTP/1.1 100 Continue\r\n\r\n"), Time.Duration.add(now, Time.Duration.of_ms(ms)))
6002
6003def phase.head(empty: Bool, settings: Limits, now: Time.Duration) -> Phase:
6004 match empty:
6005 case True{}:
6006 Limits{max, cap, hm, bm, im, wm} = settings
6007 phase.new(0, im, now)
6008 case False{}:
6009 Limits{max, cap, hm, bm, im, wm} = settings
6010 phase.new(1, hm, now)
6011
6012def phase.start(settings: Limits, sv: Sv, now: Time.Duration) -> Phase & Sv:
6013 Limits{max, cap, hm, bm, im, wm} = settings
6014 match sv:
6015 case SvHead{buf}:
6016 Bytes.Bytes{+len, words} = buf
6017 (phase.head(U32.is_zero(len), Limits{max, cap, hm, bm, im, wm}, now), SvHead{Bytes.Bytes{len, words}})
6018 case SvLen{req, v11, left, parts}:
6019 (phase.new(2, bm, now), SvLen{req, v11, left, parts})
6020 case SvChunk{req, v11, n, st, parts}:
6021 (phase.new(2, bm, now), SvChunk{req, v11, n, st, parts})
6022
6023def phase.select(settings: Limits, sv: Sv, now: Time.Duration, previous: Maybe<&2, Phase>) -> Phase & Sv:
6024 match previous:
6025 case None{}:
6026 phase.start(settings, sv, now)
6027 case Some{phase}:
6028 (phase, sv)
6029
6030
6031def phase.body(previous: Maybe<&2, Phase>, settings: Limits, now: Time.Duration) -> Phase:
6032 match previous:
6033 case Some{phase}:
6034 phase
6035 case None{}:
6036 Limits{max, cap, hm, bm, im, wm} = settings
6037 phase.new(2, bm, now)
6038
6039def phase.after(+settings: Limits, +now: Time.Duration, +phase: Phase, t: Turn) -> Turn & Phase:
6040 Limits{max, cap, hm, bm, im, wm} = settings
6041 Phase{+kind, until} = phase
6042 match t:
6043 case TurnMore{sv, cont}:
6044 match sv:
6045 case SvHead{buf}:
6046 (TurnMore{SvHead{buf}, cont}, Bool.pick(Phase, U32.is_zero(kind), phase.new(1, hm, now), phase))
6047 case SvLen{req, v11, left, parts}:
6048 (TurnMore{SvLen{req, v11, left, parts}, cont}, Bool.pick(Phase, U32.is_eq(kind, 2), phase, phase.new(2, bm, now)))
6049 case SvChunk{req, v11, n, st, parts}:
6050 (TurnMore{SvChunk{req, v11, n, st, parts}, cont}, Bool.pick(Phase, U32.is_eq(kind, 2), phase, phase.new(2, bm, now)))
6051 case TurnReq{req, rest, v11}:
6052 (TurnReq{req, rest, v11}, phase)
6053 case TurnFail{+status}:
6054 (Bool.pick(Turn, U32.is_zero(kind) && U32.is_eq(status, 408), TurnClose{}, TurnFail{status}), phase)
6055 case TurnClose{}:
6056 (TurnClose{}, phase)
6057 case TurnInputFailed{operation, code, message}:
6058 (TurnInputFailed{operation, code, message}, phase)
6059
6060def phase.expired(phase: Phase) -> Turn:
6061 Phase{kind, until} = phase
6062 Bool.pick(Turn, U32.is_zero(kind), TurnClose{}, TurnFail{408})
6063
6064def talk.received.state(s: Socket, next: Turn & Phase) -> (Socket & Turn) & Phase:
6065 (t, p) = next
6066 ((s, t), p)
6067
6068def talk.received.phase(settings: Limits, phase: Phase, now: Time.Duration, next: Socket & Turn) -> (Socket & Turn) & Phase:
6069 (s, t) = next
6070 talk.received.state(s, phase.after(settings, now, phase, t))
6071
6072def talk.received.late(expired: Bool, +settings: Limits, +phase: Phase, now: Time.Duration, sv: Sv, m: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>) -> (Socket & Turn) & Phase:
6073 match expired:
6074 case True{}:
6075 (s, r) = m
6076 ((s, phase.expired(phase)), phase)
6077 case False{}:
6078 talk.received.phase(settings, phase, now, talk.next(settings, sv, m))
6079
6080
6081
6082
6083def talk.read.running(held: Bool, inbox: Chan(Admission.Command)) -> IO(Bool):
6084 match held:
6085 case True{}: IO.pure(Bool, True{})
6086 case False{}: Admission.running(inbox)
6087
6088type ReadStep is Type:
6089 ReadChecking{socket: Socket, state: Sv, phase: Phase}
6090 ReadAllowed{running: Bool, socket: Socket, state: Sv, phase: Phase}
6091 ReadBudget{ms: U32, socket: Socket, state: Sv, phase: Phase}
6092 ReadReceived{state: Sv, phase: Phase, now: Time.Duration,
6093 result: Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>}
6094 ReadFailed{retry: Bool, socket: Socket, state: Sv, phase: Phase,
6095 now: Time.Duration, error: U32 & String}
6096
6097
6098@unsafe
6099def talk.read.loop(+inbox: Chan(Admission.Command), +held: Bool, +settings: Limits,
6100 step: ReadStep) -> IO((Socket & Turn) & Phase):
6101 match step:
6102 case ReadChecking{s, sv, phase}:
6103 do IO<(Socket & Turn) & Phase>:
6104 running : Bool <- talk.read.running(held, inbox)
6105 talk.read.loop(inbox, held, settings, ReadAllowed{running, s, sv, phase})
6106 case ReadAllowed{running, s, sv, +phase}:
6107 match running:
6108 case False{}:
6109 IO.pure((Socket & Turn) & Phase, ((s, TurnClose{}), phase))
6110 case True{}:
6111 Phase{kind, until} = phase
6112 do IO<(Socket & Turn) & Phase>:
6113 now : Time.Duration <- Time.mono()
6114 +ms : U32 = phase.remaining(until, now)
6115 talk.read.loop(inbox, held, settings,
6116 ReadBudget{Bool.pick(U32, held, ms, U32.min(ms, 50)), s, sv, phase})
6117 case ReadBudget{ms, s, sv, +phase}:
6118 match ms:
6119 case 0:
6120 IO.pure((Socket & Turn) & Phase, ((s, phase.expired(phase)), phase))
6121 case _:
6122 do IO<(Socket & Turn) & Phase>:
6123 got : Socket & Result<&1, &1, U32 & String, U32 & Array<U32>> <- Wire.recv.words(s, 65536, ms)
6124 now : Time.Duration <- Time.mono()
6125 talk.read.loop(inbox, held, settings, ReadReceived{sv, phase, now, got})
6126 case ReadReceived{sv, +phase, +now, got}:
6127 Phase{kind, until} = phase
6128 (s, result) = got
6129 match result:
6130 case Fail{(+code, message)}:
6131 talk.read.loop(inbox, held, settings,
6132 ReadFailed{err.late(code) && Cmp.is_lt(Time.Duration.cmp(now, until)), s, sv, phase, now, (code, message)})
6133 case Done{packed}:
6134 IO.pure((Socket & Turn) & Phase,
6135 talk.received.late(Bool.not(Cmp.is_lt(Time.Duration.cmp(now, until))), settings, phase, now, sv, (s, Done{packed})))
6136 case ReadFailed{retry, s, sv, +phase, +now, error}:
6137 match retry:
6138 case True{}:
6139 talk.read.loop(inbox, held, settings, ReadChecking{s, sv, phase})
6140 case False{}:
6141 Phase{kind, until} = phase
6142 IO.pure((Socket & Turn) & Phase,
6143 talk.received.late(Bool.not(Cmp.is_lt(Time.Duration.cmp(now, until))), settings, phase, now, sv, (s, Fail{error})))
6144
6145def talk.read(+inbox: Chan(Admission.Command), held: Bool, +settings: Limits, sv: Sv, phase: Phase, m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO((Socket & Turn) & Phase):
6146 (s, r) = m
6147 match r:
6148 case Fail{(code, message)}:
6149 IO.pure((Socket & Turn) & Phase, ((s, TurnInputFailed{SendContinue{}, code, message}), phase))
6150 case Done{u}:
6151 talk.read.loop(inbox, held, settings, ReadChecking{s, sv, phase})
6152
6153def talk.recv.selected(+inbox: Chan(Admission.Command), held: Bool, +settings: Limits, s: Socket, cont: Bool, selected: Phase & Sv) -> IO((Socket & Turn) & Phase):
6154 Limits{max, cap, hm, bm, im, wm} = settings
6155 (phase, state) = selected
6156 do IO<(Socket & Turn) & Phase>:
6157 sent : Socket & Result<&1, &1, U32 & String, Unit> <- talk.cont(cont, s, wm)
6158 talk.read(inbox, held, settings, state, phase, sent)
6159
6160def talk.recv(+inbox: Chan(Admission.Command), held: Bool, +settings: Limits, s: Socket, sv: Sv, cont: Bool, previous: Maybe<&2, Phase>) -> IO((Socket & Turn) & Phase):
6161 do IO<(Socket & Turn) & Phase>:
6162 now : Time.Duration <- Time.mono()
6163 talk.recv.selected(inbox, held, settings, s, cont, phase.select(settings, sv, now, previous))
6164
6165type Serving is Type:
6166 Serving{connection: Socket & Turn, phase: Maybe<&2, Phase>}
6167
6168def talk.state(received: (Socket & Turn) & Phase) -> Serving:
6169 (connection, phase) = received
6170 Serving{connection, Some{phase}}
6171
6172def serve.write.state(after: Maybe<&1, Socket & Turn>) -> Maybe<&1, Serving>:
6173 match after:
6174 case None{}:
6175 None{}
6176 case Some{connection}:
6177 Some{Serving{connection, None{}}}
6178
6179
6180
6181
6182def admission.done(+inbox: Chan(Admission.Command), operation: IO(Unit)) -> IO(Unit):
6183 do IO<Unit>:
6184 operation
6185 Admission.give(inbox, 0)
6186
6187def admission.head(req: Req) -> Bool:
6188 Req{method, path, headers, body} = req
6189 String.eq(method, "HEAD")
6190
6191def admission.turn.head(turn: Turn) -> Bool:
6192 match turn:
6193 case TurnReq{req, rest, v11}:
6194 admission.head(req)
6195 case TurnMore{sv, cont}:
6196 match sv:
6197 case SvHead{buf}:
6198 False{}
6199 case SvLen{req, v11, left, parts}:
6200 admission.head(req)
6201 case SvChunk{req, v11, n, state, parts}:
6202 admission.head(req)
6203 case TurnFail{status}:
6204 False{}
6205 case TurnClose{}:
6206 False{}
6207 case TurnInputFailed{operation, code, message}:
6208 False{}
6209
6210
6211def admission.reject(observer: Completion.Receipt, s: Socket, head: Bool, ms: U32) -> IO(Unit):
6212 do IO<Unit>:
6213 now : Time.Duration <- Time.mono()
6214 sent : Socket & Result<&1, &1, U32 & String, Unit> <-
6215 bounded.send(s, reply.bytes(reply.fail.bytes(503), head, True{}), Time.Duration.add(now, Time.Duration.of_ms(ms)))
6216 reply.sent.observe(observer, 503, sent)
6217
6218def admission.request(
6219 ~C: Data, ~h: C -> Req -> IO(Reply), ~observe: C -> U32 -> Completion.Outcome -> IO(Unit),
6220 admitted: Bool, +inbox: Chan(Admission.Command), +context: C, +settings: Limits, s: Socket, req: Req, rest: Bytes.Bytes, v11: Bool
6221) -> IO(Maybe<&1, Socket & Turn>):
6222 match admitted:
6223 case False{}:
6224 Limits{max, cap, hm, bm, im, wm} = settings
6225 do IO<Maybe<&1, Socket & Turn>>:
6226 admission.reject(Completion.Observed{status => outcome => observe(context, status, outcome)}, s, admission.head(req), wm)
6227 return None{}
6228 case True{}:
6229 Req{+method, path, +headers, body} = req
6230 do IO<Maybe<&1, Socket & Turn>>:
6231 res : Reply <- h(context, Req{method, path, headers, body})
6232 next : Maybe<&1, Socket & Turn> <- talk.respond(Completion.Observed{status => outcome => observe(context, status, outcome)}, settings, s, rest, headers, v11, String.eq(method, "HEAD"), res)
6233 Admission.give(inbox, 1)
6234 return next
6235
6236
6237@unsafe
6238def talk(~C: Data, ~h: C -> Req -> IO(Reply), ~observe: C -> U32 -> Completion.Outcome -> IO(Unit), +context: C, +settings: Limits, +inbox: Chan(Admission.Command), st: Maybe<&1, Serving>) -> IO(Unit):
6239 Limits{max, cap, hm, bm, im, wm} = settings
6240 match st:
6241 case None{}:
6242 IO.pure(Unit, Unit{})
6243 case Some{Serving{(s, t), phase}}:
6244 match t:
6245 case TurnMore{sv, cont}:
6246 do IO<Unit>:
6247 received : (Socket & Turn) & Phase <- talk.recv(inbox, False{}, settings, s, sv, cont, phase)
6248 talk(~C, ~h, ~observe, context, settings, inbox, Some{talk.state(received)})
6249 case TurnReq{req, rest, v11}:
6250 do IO<Unit>:
6251 admitted : Bool <- Admission.take(inbox, 1)
6252 next : Maybe<&1, Socket & Turn> <- admission.request(~C, ~h, ~observe, admitted, inbox, context, settings, s, req, rest, v11)
6253 talk(~C, ~h, ~observe, context, settings, inbox, serve.write.state(next))
6254 case TurnFail{status}:
6255 reply_fail(Completion.Observed{status => outcome => observe(context, status, outcome)}, s, status, wm)
6256 case TurnClose{}:
6257 Socket.close(s)
6258 case TurnInputFailed{operation, code, message}:
6259 Socket.close(s)
6260
6261def conn.admitted(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), admitted: Bool, +settings: Limits, +inbox: Chan(Admission.Command), l: Listener, s: Socket) -> IO(Listener):
6262 match admitted:
6263 case False{}:
6264 do IO<Listener>:
6265 Socket.close(s)
6266 return l
6267 case True{}:
6268 do IO<Listener>:
6269 IO.spawn(Unit, admission.done(inbox, talk(~Unit, ~(context => req => h(req)), ~(context => status => outcome => observe(status, outcome)), Unit{}, settings, inbox,
6270 Some{Serving{(s, TurnMore{sv.none(), False{}}), None{}}})))
6271 return l
6272
6273def conn(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), +settings: Limits, +inbox: Chan(Admission.Command), m: Listener & Result<&1, &1, U32 & String, Socket>) -> IO(Listener):
6274 (l, r) = m
6275 do IO<Listener>:
6276 s : Socket <- IO.pass(Socket, r)
6277 admitted : Bool <- Admission.take(inbox, 0)
6278 conn.admitted(~h, ~observe, admitted, settings, inbox, l, s)
6279
6280
6281type StartupError is Data:
6282 InvalidConfig{}
6283 BindError{code: U32, message: String}
6284
6285
6286type Server<-C: Data> is Type:
6287 Server{listener: Listener, context: C, config: ServerConfig, admission: Chan(Admission.Command), drained: Chan(Admission.Counts)}
6288
6289def server.config(host: String, port: U32) -> ServerConfig:
6290 ServerConfig{host, port, 16777216, 65536, 128, 128, 128, 5000, 30000, 30000, 30000}
6291
6292def server.config.with(host: String, port: U32, max: Nat) -> ServerConfig:
6293 ServerConfig{host, port, U32.from_nat(max), 65536, 128, 128, 128, 5000, 30000, 30000, 30000}
6294
6295
6296def server.deadline.valid(+ms: U32) -> Bool:
6297 U32.is_gt(ms, 0) && U32.is_le(ms, 2147483647)
6298
6299def server.config.valid(config: ServerConfig) -> Bool:
6300 ServerConfig{host, port, +body, +headers, connections, requests, buffered, header_ms, body_ms, idle_ms, write_ms} = config
6301 U32.is_le(port, 65535) && U32.is_gt(body, 0) && U32.is_le(body, limits.representable()) &&
6302 U32.is_gt(headers, 0) && U32.is_le(headers, limits.representable()) &&
6303 U32.is_gt(connections, 0) && U32.is_gt(requests, 0) && U32.is_gt(buffered, 0) &&
6304 server.deadline.valid(header_ms) && server.deadline.valid(body_ms) &&
6305 server.deadline.valid(idle_ms) && server.deadline.valid(write_ms)
6306
6307def server.opened(~C: Data, ~O: Type, owner: O, listener: Listener, context: C,
6308 config: ServerConfig, opened: Chan(Admission.Command) & Chan(Admission.Counts)) -> IO(O & Result<&2, &1, StartupError, Server<C>>):
6309 (inbox, drained) = opened
6310 IO.pure(O & Result<&2, &1, StartupError, Server<C>>, (owner, Done{Server{listener, context, config, inbox, drained}}))
6311
6312def server.started(~C: Data, ~O: Type, context: C, owner: O, +config: ServerConfig, result: Result<&1, &1, U32 & String, Listener>) -> IO(O & Result<&2, &1, StartupError, Server<C>>):
6313 ServerConfig{host, port, body, headers, connections, requests, buffered, hm, bm, im, wm} = config
6314 match result:
6315 case Fail{error}:
6316 (code, message) = error
6317 IO.pure(O & Result<&2, &1, StartupError, Server<C>>, (owner, Fail{BindError{code, message}}))
6318 case Done{listener}:
6319 do IO<O & Result<&2, &1, StartupError, Server<C>>>:
6320 opened : Chan(Admission.Command) & Chan(Admission.Counts) <- Admission.open.owner(Admission.Caps{connections, requests, buffered})
6321 server.opened(~C, ~O, owner, listener, context, config, opened)
6322
6323def server.start.valid(~C: Data, ~O: Type, valid: Bool, context: C, owner: O, +config: ServerConfig) -> IO(O & Result<&2, &1, StartupError, Server<C>>):
6324 match valid:
6325 case False{}:
6326 IO.pure(O & Result<&2, &1, StartupError, Server<C>>, (owner, Fail{InvalidConfig{}}))
6327 case True{}:
6328 ServerConfig{host, port, body, headers, connections, requests, buffered, header_ms, body_ms, idle_ms, write_ms} = config
6329 do IO<O & Result<&2, &1, StartupError, Server<C>>>:
6330 result : Result<&1, &1, U32 & String, Listener> <- TCP.listen(host, port)
6331 server.started(~C, ~O, context, owner, config, result)
6332
6333
6334
6335def server.start(~C: Data, ~O: Type, context: C, owner: O, +config: ServerConfig) -> IO(O & Result<&2, &1, StartupError, Server<C>>):
6336 server.start.valid(~C, ~O, server.config.valid(config), context, owner, config)
6337
6338
6339
6340type ServerStats is Data:
6341 ServerStats{inbox: Chan(Admission.Command)}
6342
6343type ServerCounts is Data:
6344 ServerCounts{connections: U32, active: U32, buffered: U32,
6345 connection_high: U32, active_high: U32, buffered_high: U32,
6346 connection_rejected: Nat, active_rejected: Nat, buffered_rejected: Nat}
6347
6348def server.observer(~C: Data, server: Server<C>) -> Server<C> & ServerStats:
6349 Server{listener, context, config, +inbox, drained} = server
6350 (Server{listener, context, config, inbox, drained}, ServerStats{inbox})
6351
6352def server.counts(counts: Maybe<&2, Admission.Counts>) -> Maybe<&2, ServerCounts>:
6353 match counts:
6354 case None{}:
6355 None{}
6356 case Some{Admission.Counts{c, a, b, ch, ah, bh, cr, ar, br}}:
6357 Some{ServerCounts{c, a, b, ch, ah, bh, cr, ar, br}}
6358
6359
6360
6361def server.stats(observer: ServerStats) -> IO(Maybe<&2, ServerCounts>):
6362 ServerStats{inbox} = observer
6363 do IO<Maybe<&2, ServerCounts>>:
6364 counts : Maybe<&2, Admission.Counts> <- Admission.snapshot(inbox)
6365 return server.counts(counts)
6366
6367type ServerControl is Data:
6368 ServerControl{inbox: Chan(Admission.Command)}
6369
6370
6371def server.control(~C: Data, server: Server<C>) -> Server<C> & ServerControl:
6372 Server{listener, context, config, +inbox, drained} = server
6373 (Server{listener, context, config, inbox, drained}, ServerControl{inbox})
6374
6375def server.stop(control: ServerControl) -> IO(Bool):
6376 ServerControl{inbox} = control
6377 Admission.quiesce(inbox)
6378
6379def server.final.counts(counts: Admission.Counts) -> ServerCounts:
6380 Admission.Counts{c, a, b, ch, ah, bh, cr, ar, br} = counts
6381 ServerCounts{c, a, b, ch, ah, bh, cr, ar, br}
6382
6383
6384
6385def server.close(~C: Data, server: Server<C>) -> IO(ServerCounts):
6386 Server{listener, context, config, +inbox, drained} = server
6387 do IO<ServerCounts>:
6388 requested : Bool <- Admission.quiesce(inbox)
6389 Listener.close(listener)
6390 Admission.close(inbox)
6391 counts : Admission.Counts <- IO.join(Admission.Counts, drained)
6392 return server.final.counts(counts)
6393
6394type ServerExit is Data:
6395 ServerStopped{counts: ServerCounts}
6396 ServerAcceptFailed{code: U32, message: String, counts: ServerCounts}
6397
6398def server.finished(stopped: Bool, error: U32 & String, counts: ServerCounts) -> ServerExit:
6399 match stopped:
6400 case True{}:
6401 ServerStopped{counts}
6402 case False{}:
6403 (code, message) = error
6404 ServerAcceptFailed{code, message, counts}
6405
6406def server.finish(~C: Data, stopped: Bool, error: U32 & String, server: Server<C>) -> IO(ServerExit):
6407 do IO<ServerExit>:
6408 counts : ServerCounts <- server.close(~C, server)
6409 return server.finished(stopped, error, counts)
6410
6411def server.admitted(~C: Data, ~operation: C -> Limits -> Chan(Admission.Command) -> Socket -> IO(Unit), admitted: Bool, +context: C, +config: ServerConfig, +inbox: Chan(Admission.Command), drained: Chan(Admission.Counts), listener: Listener, socket: Socket) -> IO(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>):
6412 match admitted:
6413 case False{}:
6414 do IO<Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>>:
6415 Socket.close(socket)
6416 return (Server{listener, context, config, inbox, drained}, Fail{(11, "transport capacity exhausted")})
6417 case True{}:
6418 do IO<Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>>:
6419 finished : Chan(Unit) <- IO.fork(Unit, admission.done(inbox, operation(context, limits.config(config), inbox, socket)))
6420 return (Server{listener, context, config, inbox, drained}, Done{finished})
6421
6422def server.accepted(~C: Data, ~operation: C -> Limits -> Chan(Admission.Command) -> Socket -> IO(Unit), +context: C, +config: ServerConfig, +inbox: Chan(Admission.Command), drained: Chan(Admission.Counts), result: Listener & Result<&1, &1, U32 & String, Socket>) -> IO(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>):
6423 (listener, got) = result
6424 match got:
6425 case Fail{error}:
6426 IO.pure(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>, (Server{listener, context, config, inbox, drained}, Fail{error}))
6427 case Done{socket}:
6428 do IO<Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>>:
6429 admitted : Bool <- Admission.take(inbox, 0)
6430 server.admitted(~C, ~operation, admitted, context, config, inbox, drained, listener, socket)
6431
6432
6433type AcceptStep<-C: Data> is Type:
6434 AcceptChecking{server: Server<C>}
6435 AcceptAllowed{running: Bool, server: Server<C>}
6436 AcceptReceived{result: Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>}
6437 AcceptFailed{retry: Bool, server: Server<C>, error: U32 & String}
6438
6439@unsafe
6440def server.accept.loop(~C: Data, ~operation: C -> Limits -> Chan(Admission.Command) -> Socket -> IO(Unit),
6441 step: AcceptStep<C>) -> IO(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>):
6442 match step:
6443 case AcceptChecking{server}:
6444 Server{listener, context, config, +inbox, drained} = server
6445 do IO<Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>>:
6446 running : Bool <- Admission.running(inbox)
6447 server.accept.loop(~C, ~operation, AcceptAllowed{running, Server{listener, context, config, inbox, drained}})
6448 case AcceptAllowed{running, server}:
6449 match running:
6450 case False{}:
6451 IO.pure(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>, (server, Fail{(0, "server stopped")}))
6452 case True{}:
6453 Server{listener, context, config, inbox, drained} = server
6454 do IO<Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>>:
6455 result : Listener & Result<&1, &1, U32 & String, Socket> <- Wire.accept.deadline(listener, 50)
6456 accepted : Server<C> & Result<&1, &1, U32 & String, Chan(Unit)> <-
6457 server.accepted(~C, ~operation, context, config, inbox, drained, result)
6458 server.accept.loop(~C, ~operation, AcceptReceived{accepted})
6459 case AcceptReceived{accepted}:
6460 (server, result) = accepted
6461 match result:
6462 case Fail{(+code, message)}:
6463 server.accept.loop(~C, ~operation, AcceptFailed{err.late(code), server, (code, message)})
6464 case Done{finished}:
6465 IO.pure(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>, (server, Done{finished}))
6466 case AcceptFailed{retry, server, error}:
6467 match retry:
6468 case True{}: server.accept.loop(~C, ~operation, AcceptChecking{server})
6469 case False{}: IO.pure(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>, (server, Fail{error}))
6470
6471def server.accept.operation(~C: Data, ~operation: C -> Limits -> Chan(Admission.Command) -> Socket -> IO(Unit),
6472 server: Server<C>) -> IO(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>):
6473 server.accept.loop(~C, ~operation, AcceptChecking{server})
6474
6475def server.accept(~C: Data, ~h: C -> Req -> IO(Reply), ~observe: C -> U32 -> Completion.Outcome -> IO(Unit), server: Server<C>) -> IO(Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>):
6476 server.accept.operation(~C,
6477 ~(context => settings => inbox => socket => talk(~C, ~h, ~observe, context, settings, inbox,
6478 Some{Serving{(socket, TurnMore{sv.none(), False{}}), None{}}})), server)
6479
6480def error.stopped(error: U32 & String) -> Bool:
6481 (code, message) = error
6482 U32.is_zero(code) && String.eq(message, "server stopped")
6483
6484type ServerLoop<-C: Data> is Type:
6485 ServerListening{server: Server<C>}
6486 ServerAccepted{result: Server<C> & Result<&1, &1, U32 & String, Chan(Unit)>}
6487 ServerFailed{retry: Bool, server: Server<C>, error: U32 & String}
6488
6489def error.capacity(error: U32 & String) -> Bool:
6490 (code, message) = error
6491 U32.is_eq(code, 11) && String.eq(message, "transport capacity exhausted")
6492
6493
6494
6495@unsafe
6496def server.run.loop(~C: Data, ~operation: C -> Limits -> Chan(Admission.Command) -> Socket -> IO(Unit), step: ServerLoop<C>) -> IO(ServerExit):
6497 match step:
6498 case ServerListening{server}:
6499 do IO<ServerExit>:
6500 result : Server<C> & Result<&1, &1, U32 & String, Chan(Unit)> <- server.accept.operation(~C, ~operation, server)
6501 server.run.loop(~C, ~operation, ServerAccepted{result})
6502 case ServerAccepted{accepted}:
6503 (server, result) = accepted
6504 match result:
6505 case Fail{+error}:
6506 server.run.loop(~C, ~operation, ServerFailed{error.capacity(error), server, error})
6507 case Done{finished}:
6508 do IO<ServerExit>:
6509 Chan.close(Unit, finished)
6510 server.run.loop(~C, ~operation, ServerListening{server})
6511 case ServerFailed{retry, server, +error}:
6512 match retry:
6513 case True{}:
6514 server.run.loop(~C, ~operation, ServerListening{server})
6515 case False{}:
6516 server.finish(~C, error.stopped(error), error, server)
6517
6518def server.run(~C: Data, ~h: C -> Req -> IO(Reply), ~observe: C -> U32 -> Completion.Outcome -> IO(Unit), server: Server<C>) -> IO(ServerExit):
6519 server.run.loop(~C,
6520 ~(context => settings => inbox => socket => talk(~C, ~h, ~observe, context, settings, inbox,
6521 Some{Serving{(socket, TurnMore{sv.none(), False{}}), None{}}})), ServerListening{server})
6522
6523
6524def server.exited(result: ServerExit) -> IO(Unit):
6525 match result:
6526 case ServerStopped{counts}: IO.pure(Unit, Unit{})
6527 case ServerAcceptFailed{code, message, counts}: IO.die(Unit, code, message)
6528
6529def server.start.failed(error: StartupError) -> IO(Unit):
6530 match error:
6531 case InvalidConfig{}: IO.die(Unit, 22, "invalid server configuration")
6532 case BindError{code, message}: IO.die(Unit, code, message)
6533
6534def serve.config.result(result: Result<&2, &2, StartupError, Unit>) -> IO(Unit):
6535 match result:
6536 case Fail{error}: server.start.failed(error)
6537 case Done{unit}: IO.pure(Unit, unit)
6538
6539def serve.stream.nonempty(~S: Type, ~piece: S -> Bytes.Bytes -> IO(S & Bool), empty: Bool, state: S, b: Bytes.Bytes) -> IO(S & Bool):
6540 match empty:
6541 case True{}:
6542 IO.pure(S & Bool, (state, False{}))
6543 case False{}:
6544 piece(state, b)
6545
6546
6547def serve.stream.one(~S: Type, ~piece: S -> Bytes.Bytes -> IO(S & Bool), drop: Bool, state: S, b: Bytes.Bytes) -> IO(S & Bool):
6548 match drop:
6549 case True{}:
6550 IO.pure(S & Bool, (state, True{}))
6551 case False{}:
6552 Bytes.Bytes{+len, buf} = b
6553 serve.stream.nonempty(~S, ~piece, U32.is_eq(len, 0), state, Bytes.Bytes{len, buf})
6554
6555def serve.stream.parts(~S: Type, ~piece: S -> Bytes.Bytes -> IO(S & Bool), xs: List<&1, Bytes.Bytes>, st: S & Bool) -> IO(S & Bool):
6556 match xs:
6557 case Nil{}:
6558 IO.pure(S & Bool, st)
6559 case Con{b, rest}:
6560 (state, drop) = st
6561 do IO<S & Bool>:
6562 next : S & Bool <- serve.stream.one(~S, ~piece, drop, state, b)
6563 serve.stream.parts(~S, ~piece, rest, next)
6564
6565def serve.stream.feed(~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool), req: Req, m: Maybe<&1, S & Bool>, parts: List<&1, Bytes.Bytes>) -> IO(S & Bool):
6566 match m:
6567 case None{}:
6568 do IO<S & Bool>:
6569 state : S <- start(req)
6570 serve.stream.parts(~S, ~piece, parts, (state, False{}))
6571 case Some{st}:
6572 serve.stream.parts(~S, ~piece, parts, st)
6573
6574def serve.stream.respond(~S: Type, ~finish: S -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), +settings: Limits, s: Socket, rest: Bytes.Bytes, +headers: Map<&2, List<&2, String>>, v11: Bool, +method: String, st: S & Bool) -> IO(Maybe<&1, Socket & Turn>):
6575 (state, dropped) = st
6576 do IO<Maybe<&1, Socket & Turn>>:
6577 res : Reply <- finish(state)
6578 talk.respond(Completion.Observed{status => outcome => observe(status, outcome)}, settings, s, rest, headers, v11, String.eq(method, "HEAD"), res)
6579
6580def admission.ready(t: Turn) -> Bool & Turn:
6581 match t:
6582 case TurnReq{req, rest, v11}:
6583 (True{}, TurnReq{req, rest, v11})
6584 case TurnMore{sv, cont}:
6585 match sv:
6586 case SvHead{buf}:
6587 (False{}, TurnMore{SvHead{buf}, cont})
6588 case SvLen{req, v11, left, parts}:
6589 (True{}, TurnMore{SvLen{req, v11, left, parts}, cont})
6590 case SvChunk{req, v11, n, state, parts}:
6591 (True{}, TurnMore{SvChunk{req, v11, n, state, parts}, cont})
6592 case TurnFail{status}:
6593 (False{}, TurnFail{status})
6594 case TurnClose{}:
6595 (False{}, TurnClose{})
6596 case TurnInputFailed{operation, code, message}:
6597 (False{}, TurnInputFailed{operation, code, message})
6598
6599def admission.give.held(held: Bool, inbox: Chan(Admission.Command)) -> IO(Unit):
6600 match held:
6601 case False{}:
6602 IO.pure(Unit, Unit{})
6603 case True{}:
6604 Admission.give(inbox, 1)
6605
6606type StreamServing<-S: Type> is Type:
6607 StreamCheck{held: Bool, connection: Serving, state: Maybe<&1, S & Bool>}
6608 StreamReady{held: Bool, ready: Bool, connection: Serving, state: Maybe<&1, S & Bool>}
6609 StreamChecked{held: Bool, admitted: Bool, connection: Serving, state: Maybe<&1, S & Bool>}
6610 StreamDone{}
6611
6612def serve.stream.after(~S: Type, next: Maybe<&1, Socket & Turn>) -> StreamServing<S>:
6613 match next:
6614 case None{}:
6615 StreamDone{}
6616 case Some{connection}:
6617 StreamCheck{False{}, Serving{connection, None{}}, None{}}
6618
6619def serve.stream.ready(~S: Type, held: Bool, socket: Socket, phase: Maybe<&2, Phase>,
6620 state: Maybe<&1, S & Bool>, result: Bool & Turn) -> StreamServing<S>:
6621 (ready, turn) = result
6622 StreamReady{held, ready, Serving{(socket, turn), phase}, state}
6623
6624def admission.stream.take(needed: Bool, inbox: Chan(Admission.Command)) -> IO(Bool):
6625 match needed:
6626 case False{}:
6627 IO.pure(Bool, True{})
6628 case True{}:
6629 Admission.take(inbox, 1)
6630
6631type StreamAbort is Data:
6632 StreamClosed{}
6633 StreamFailed{status: U32}
6634 StreamInputFailed{operation: InputOperation, code: U32, message: String}
6635
6636
6637
6638def serve.stream.abort(~S: Type, ~abort: S -> StreamAbort -> IO(Unit),
6639 reason: StreamAbort, state: Maybe<&1, S & Bool>) -> IO(Unit):
6640 match state:
6641 case None{}: IO.pure(Unit, Unit{})
6642 case Some{pair}:
6643 (value, dropped) = pair
6644 abort(value, reason)
6645
6646
6647@unsafe
6648def serve.stream.talk(
6649 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool),
6650 ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit), +settings: Limits, +inbox: Chan(Admission.Command), step: StreamServing<S>
6651) -> IO(Unit):
6652 Limits{max, cap, hm, bm, im, wm} = settings
6653 match step:
6654 case StreamDone{}:
6655 IO.pure(Unit, Unit{})
6656 case StreamCheck{held, connection, state}:
6657 Serving{(socket, turn), phase} = connection
6658 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6659 serve.stream.ready(~S, held, socket, phase, state, admission.ready(turn)))
6660 case StreamReady{+held, +ready, connection, state}:
6661 do IO<Unit>:
6662 admitted : Bool <- admission.stream.take(Bool.not(held) && ready, inbox)
6663 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6664 StreamChecked{held || ready, admitted, connection, state})
6665 case StreamChecked{+held, admitted, connection, m}:
6666 match admitted:
6667 case False{}:
6668 Serving{(socket, turn), phase} = connection
6669 do IO<Unit>:
6670 serve.stream.abort(~S, ~abort, StreamFailed{503}, m)
6671 admission.reject(Completion.Observed{status => outcome => observe(status, outcome)}, socket, admission.turn.head(turn), wm)
6672 case True{}:
6673 Serving{(s, t), phase} = connection
6674 match t:
6675 case TurnClose{}:
6676 do IO<Unit>:
6677 Socket.close(s)
6678 serve.stream.abort(~S, ~abort, StreamClosed{}, m)
6679 admission.give.held(held, inbox)
6680 case TurnInputFailed{operation, code, message}:
6681 do IO<Unit>:
6682 Socket.close(s)
6683 serve.stream.abort(~S, ~abort, StreamInputFailed{operation, code, message}, m)
6684 admission.give.held(held, inbox)
6685 case TurnFail{+status}:
6686 do IO<Unit>:
6687 serve.stream.abort(~S, ~abort, StreamFailed{status}, m)
6688 reply_fail(Completion.Observed{status => outcome => observe(status, outcome)}, s, status, wm)
6689 admission.give.held(held, inbox)
6690 case TurnReq{req, rest, v11}:
6691 Req{+method, path, +headers, body} = req
6692 do IO<Unit>:
6693 next : S & Bool <- serve.stream.feed(~S, ~start, ~piece,
6694 Req{method, path, headers, Bytes.new(0)}, m, [body])
6695 after : Maybe<&1, Socket & Turn> <- serve.stream.respond(~S, ~finish, ~observe, settings, s, rest, headers, v11, method, next)
6696 Admission.give(inbox, 1)
6697 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6698 serve.stream.after(~S, after))
6699 case TurnMore{sv, cont}:
6700 match sv:
6701 case SvHead{buf}:
6702 do IO<Unit>:
6703 received : (Socket & Turn) & Phase <- talk.recv(inbox, held, settings, s, SvHead{buf}, cont, phase)
6704 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6705 StreamCheck{held, talk.state(received), m})
6706 case SvLen{req, +v11, +left, parts}:
6707 Req{+method, +path, +headers, body} = req
6708 do IO<Unit>:
6709 now : Time.Duration <- Time.mono()
6710 reading : Phase = phase.body(phase, settings, now)
6711 next : S & Bool <- serve.stream.feed(~S, ~start, ~piece,
6712 Req{method, path, headers, body}, m, List.reverse(&1, Bytes.Bytes, parts))
6713 received : (Socket & Turn) & Phase <- talk.recv(inbox, held, settings, s,
6714 SvLen{Req{method, path, headers, Bytes.new(0)}, v11, left, Nil{}}, cont, Some{reading})
6715 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6716 StreamCheck{held, talk.state(received), Some{next}})
6717 case SvChunk{req, +v11, +n, st, parts}:
6718 Req{+method, +path, +headers, body} = req
6719 do IO<Unit>:
6720 now : Time.Duration <- Time.mono()
6721 reading : Phase = phase.body(phase, settings, now)
6722 next : S & Bool <- serve.stream.feed(~S, ~start, ~piece,
6723 Req{method, path, headers, body}, m, List.reverse(&1, Bytes.Bytes, parts))
6724 received : (Socket & Turn) & Phase <- talk.recv(inbox, held, settings, s,
6725 SvChunk{Req{method, path, headers, Bytes.new(0)}, v11, n, st, Nil{}}, cont, Some{reading})
6726 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6727 StreamCheck{held, talk.state(received), Some{next}})
6728
6729
6730def server.stream.run(
6731 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool),
6732 ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit), server: Server<Unit>
6733) -> IO(ServerExit):
6734 server.run.loop(~Unit,
6735 ~(context => settings => inbox => socket =>
6736 serve.stream.talk(~S, ~start, ~piece, ~finish, ~abort, ~observe, settings, inbox,
6737 StreamCheck{False{}, Serving{(socket, TurnMore{sv.none(), False{}}), None{}}, None{}})),
6738 ServerListening{server})
6739
6740def serve.stream.started(
6741 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool), ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
6742 result: Unit & Result<&2, &1, StartupError, Server<Unit>>
6743) -> IO(Result<&2, &2, StartupError, Unit>):
6744 (owner, got) = result
6745 match got:
6746 case Fail{error}:
6747 IO.pure(Result<&2, &2, StartupError, Unit>, Fail{error})
6748 case Done{server}:
6749 do IO<Result<&2, &2, StartupError, Unit>>:
6750 exited : ServerExit <- server.stream.run(~S, ~start, ~piece, ~finish, ~abort, ~observe, server)
6751 server.exited(exited)
6752 return Done{Unit{}}
6753
6754def serve.stream.config(
6755 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool), ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
6756 config: ServerConfig
6757) -> IO(Result<&2, &2, StartupError, Unit>):
6758 do IO<Result<&2, &2, StartupError, Unit>>:
6759 started : Unit & Result<&2, &1, StartupError, Server<Unit>> <- server.start(~Unit, ~Unit, Unit{}, Unit{}, config)
6760 serve.stream.started(~S, ~start, ~piece, ~finish, ~abort, ~observe, started)
6761
6762type WriterMode is Data:
6763 WriteKnown{left: U32}
6764 WriteChunk{}
6765 WriteNone{}
6766
6767type Writer is Type:
6768 Writer{s: Maybe<&1, Socket>, mode: WriterMode, until: Time.Duration, outcome: Completion.Outcome}
6769
6770type WriteHead<-S: Type> is Type:
6771 WriteHead{status: U32, headers: Map<&2, List<&2, String>>, length: Maybe<&2, U32>, state: S, receipt: Completion.Receipt}
6772
6773def writer.sent(+mode: WriterMode, +until: Time.Duration, m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Writer):
6774 (s, r) = m
6775 match r:
6776 case Fail{(code, message)}:
6777 do IO<Writer>:
6778 Socket.close(s)
6779 return Writer{None{}, mode, until, Completion.WriteFailed{code, message}}
6780 case Done{u}:
6781 IO.pure(Writer, Writer{Some{s}, mode, until, Completion.HostAccepted{}})
6782
6783def writer.raw(w: Writer, b: Bytes.Bytes) -> IO(Writer):
6784 Writer{socket, +mode, +until, outcome} = w
6785 match socket:
6786 case None{}:
6787 IO.pure(Writer, Writer{None{}, mode, until, outcome})
6788 case Some{s}:
6789 do IO<Writer>:
6790 sent : Socket & Result<&1, &1, U32 & String, Unit> <- bounded.send(s, b, until)
6791 writer.sent(mode, until, sent)
6792
6793def writer.chunk(s: Socket, b: Bytes.Bytes, +len: U32, until: Time.Duration) -> IO(Writer):
6794 do IO<Writer>:
6795 head : Writer <- writer.raw(Writer{Some{s}, WriteChunk{}, until, Completion.HostAccepted{}}, Bytes.from_string(hex(len) ++ "\r\n"))
6796 data : Writer <- writer.raw(head, b)
6797 writer.raw(data, Bytes.from_string("\r\n"))
6798
6799def writer.known(ok: Bool, s: Socket, +left: U32, len: U32, b: Bytes.Bytes, +until: Time.Duration) -> IO(Writer):
6800 match ok:
6801 case False{}:
6802 do IO<Writer>:
6803 Socket.close(s)
6804 return Writer{None{}, WriteKnown{left}, until, Completion.IncompleteResponse{}}
6805 case True{}:
6806 writer.raw(Writer{Some{s}, WriteKnown{(left - len : U32)}, until, Completion.HostAccepted{}}, b)
6807
6808def writer.chunk.go(empty: Bool, s: Socket, b: Bytes.Bytes, len: U32, until: Time.Duration) -> IO(Writer):
6809 match empty:
6810 case True{}:
6811 IO.pure(Writer, Writer{Some{s}, WriteChunk{}, until, Completion.HostAccepted{}})
6812 case False{}:
6813 writer.chunk(s, b, len, until)
6814
6815def writer.write.mode(s: Socket, mode: WriterMode, b: Bytes.Bytes, until: Time.Duration) -> IO(Writer):
6816 match mode:
6817 case WriteNone{}:
6818 IO.pure(Writer, Writer{Some{s}, WriteNone{}, until, Completion.HostAccepted{}})
6819 case WriteKnown{+left}:
6820 Bytes.Bytes{+len, buf} = b
6821 writer.known(U32.is_le(len, left), s, left, len, Bytes.Bytes{len, buf}, until)
6822 case WriteChunk{}:
6823 Bytes.Bytes{+len, buf} = b
6824 writer.chunk.go(U32.is_zero(len), s, Bytes.Bytes{len, buf}, len, until)
6825
6826def writer.write(w: Writer, b: Bytes.Bytes) -> IO(Writer):
6827 Writer{socket, mode, until, outcome} = w
6828 match socket:
6829 case None{}:
6830 IO.pure(Writer, Writer{None{}, mode, until, outcome})
6831 case Some{s}:
6832 writer.write.mode(s, mode, b, until)
6833
6834def writer.result(w: Writer) -> Maybe<&1, Socket> & Completion.Outcome:
6835 Writer{socket, mode, until, outcome} = w
6836 (socket, outcome)
6837
6838def writer.finish.known(complete: Bool, s: Socket) -> IO(Maybe<&1, Socket> & Completion.Outcome):
6839 match complete:
6840 case True{}:
6841 IO.pure(Maybe<&1, Socket> & Completion.Outcome, (Some{s}, Completion.HostAccepted{}))
6842 case False{}:
6843 do IO<Maybe<&1, Socket> & Completion.Outcome>:
6844 Socket.close(s)
6845 return (None{}, Completion.IncompleteResponse{})
6846
6847def writer.finish.mode(s: Socket, mode: WriterMode, until: Time.Duration) -> IO(Maybe<&1, Socket> & Completion.Outcome):
6848 match mode:
6849 case WriteNone{}:
6850 IO.pure(Maybe<&1, Socket> & Completion.Outcome, (Some{s}, Completion.HostAccepted{}))
6851 case WriteKnown{left}:
6852 writer.finish.known(U32.is_zero(left), s)
6853 case WriteChunk{}:
6854 do IO<Maybe<&1, Socket> & Completion.Outcome>:
6855 end : Writer <- writer.raw(Writer{Some{s}, WriteChunk{}, until, Completion.HostAccepted{}}, Bytes.from_string("0\r\n\r\n"))
6856 return writer.result(end)
6857
6858def writer.finish(w: Writer) -> IO(Maybe<&1, Socket> & Completion.Outcome):
6859 Writer{socket, mode, until, outcome} = w
6860 match socket:
6861 case None{}:
6862 IO.pure(Maybe<&1, Socket> & Completion.Outcome, (None{}, outcome))
6863 case Some{s}:
6864 writer.finish.mode(s, mode, until)
6865
6866def writer.header.one(m: Map<&2, List<&2, String>>, +key: String, vs: List<&2, String>, framing: Bool) -> Map<&2, List<&2, String>>:
6867 match framing:
6868 case True{}:
6869 m
6870 case False{}:
6871 Map.set(&2, List<&2, String>, m, key, vs)
6872
6873def writer.headers(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>, m: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
6874 match xs:
6875 case Nil{}:
6876 m
6877 case (k, vs) <> rest:
6878 +key = String.to_lower(k)
6879 +framing = Bool.or(String.eq(key, "content-length"), String.eq(key, "transfer-encoding"))
6880 writer.headers(rest, writer.header.one(m, key, vs, framing))
6881
6882def writer.plan.body(headers: Map<&2, List<&2, String>>, length: Maybe<&2, U32>) -> WriterMode & Map<&2, List<&2, String>>:
6883 match length:
6884 case Some{+n}:
6885 (WriteKnown{n}, set(headers, "content-length", U32.show(n)))
6886 case None{}:
6887 (WriteChunk{}, set(headers, "transfer-encoding", "chunked"))
6888
6889def writer.plan(nobody: Bool, headers: Map<&2, List<&2, String>>, length: Maybe<&2, U32>) -> WriterMode & Map<&2, List<&2, String>>:
6890 match nobody:
6891 case True{}:
6892 (WriteNone{}, headers)
6893 case False{}:
6894 writer.plan.body(headers, length)
6895
6896def serve.write.after(settings: Limits, keep: Bool, rest: Bytes.Bytes, socket: Maybe<&1, Socket>) -> Maybe<&1, Socket & Turn>:
6897 match socket:
6898 case None{}:
6899 None{}
6900 case Some{s}:
6901 Some{talk.after.keep(keep, settings, rest, s)}
6902
6903def serve.write.completed(observer: Completion.Receipt, receipt: Completion.Receipt, +status: U32,
6904 settings: Limits, keep: Bool, rest: Bytes.Bytes, result: Maybe<&1, Socket> & Completion.Outcome) -> IO(Maybe<&1, Socket & Turn>):
6905 (socket, outcome) = result
6906 +observed = {outcome : Completion.Outcome}
6907 do IO<Maybe<&1, Socket & Turn>>:
6908 Completion.report(observer, status, observed)
6909 Completion.report(receipt, status, observed)
6910 return serve.write.after(settings, keep, rest, socket)
6911
6912def serve.write.returned(observer: Completion.Receipt, status: U32, settings: Limits, keep: Bool,
6913 rest: Bytes.Bytes, result: Writer & Completion.Receipt) -> IO(Maybe<&1, Socket & Turn>):
6914 (writer, receipt) = result
6915 do IO<Maybe<&1, Socket & Turn>>:
6916 done : Maybe<&1, Socket> & Completion.Outcome <- writer.finish(writer)
6917 serve.write.completed(observer, receipt, status, settings, keep, rest, done)
6918
6919def serve.write.finish(observer: Completion.Receipt, receipt: Completion.Receipt, status: U32,
6920 settings: Limits, keep: Bool, rest: Bytes.Bytes, w: Writer) -> IO(Maybe<&1, Socket & Turn>):
6921 serve.write.returned(observer, status, settings, keep, rest, (w, receipt))
6922
6923def serve.write.body(~S: Type, ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit),
6924 observer: Completion.Receipt, receipt: Completion.Receipt, status: U32, settings: Limits,
6925 keep: Bool, rest: Bytes.Bytes, state: S, s: Socket, mode: WriterMode, suppress: Bool, until: Time.Duration) -> IO(Maybe<&1, Socket & Turn>):
6926 match suppress:
6927 case True{}:
6928 do IO<Maybe<&1, Socket & Turn>>:
6929 dispose(state)
6930 serve.write.finish(observer, receipt, status, settings, keep, rest, Writer{Some{s}, WriteNone{}, until, Completion.HostAccepted{}})
6931 case False{}:
6932 do IO<Maybe<&1, Socket & Turn>>:
6933 result : Writer & Completion.Receipt <- write(state, Writer{Some{s}, mode, until, Completion.HostAccepted{}}, receipt)
6934 serve.write.returned(observer, status, settings, keep, rest, result)
6935
6936def serve.write.open(~S: Type, ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit),
6937 observer: Completion.Receipt, receipt: Completion.Receipt, +status: U32, settings: Limits,
6938 keep: Bool, rest: Bytes.Bytes, state: S, mode: WriterMode, suppress: Bool, until: Time.Duration,
6939 m: Socket & Result<&1, &1, U32 & String, Unit>) -> IO(Maybe<&1, Socket & Turn>):
6940 (s, r) = m
6941 match r:
6942 case Fail{(code, message)}:
6943 +failure = {Completion.WriteFailed{code, message} : Completion.Outcome}
6944 do IO<Maybe<&1, Socket & Turn>>:
6945 Socket.close(s)
6946 dispose(state)
6947 Completion.report(observer, status, failure)
6948 Completion.report(receipt, status, failure)
6949 return None{}
6950 case Done{u}:
6951 serve.write.body(~S, ~write, ~dispose, observer, receipt, status, settings, keep, rest, state, s, mode, suppress, until)
6952
6953def writer.connection(keep: Bool, headers: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
6954 match keep:
6955 case True{}:
6956 headers
6957 case False{}:
6958 set(headers, "connection", "close")
6959
6960def serve.write.send.plan(
6961 ~S: Type, ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit),
6962 observer: Completion.Receipt, receipt: Completion.Receipt, +settings: Limits, s: Socket, rest: Bytes.Bytes,
6963 +keep: Bool, +head: Bool, +status: U32, state: S, plan: WriterMode & Map<&2, List<&2, String>>
6964) -> IO(Maybe<&1, Socket & Turn>):
6965 Limits{max, cap, hm, bm, im, wm} = settings
6966 (mode, headers) = plan
6967 sent_headers = writer.connection(keep, headers)
6968 do IO<Maybe<&1, Socket & Turn>>:
6969 now : Time.Duration <- Time.mono()
6970 +until : Time.Duration = Time.Duration.add(now, Time.Duration.of_ms(wm))
6971 sent : Socket & Result<&1, &1, U32 & String, Unit> <-
6972 bounded.send(s, Bytes.from_string(response(status, sent_headers, "")), until)
6973 serve.write.open(~S, ~write, ~dispose, observer, receipt, status, settings, keep, rest, state, mode,
6974 frame.nobody(status, head), until, sent)
6975
6976def serve.write.send(
6977 ~S: Type, ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), observer: Completion.Receipt, settings: Limits, s: Socket, rest: Bytes.Bytes,
6978 +headers: Map<&2, List<&2, String>>, v11: Bool, +head: Bool, out: WriteHead<S>
6979) -> IO(Maybe<&1, Socket & Turn>):
6980 WriteHead{+status, rh, length, state, receipt} = out
6981 +normalized = writer.headers(Map.to_list(&2, List<&2, String>, rh), empty())
6982 +keep = serve.keep(headers, v11, status, normalized)
6983 serve.write.send.plan(~S, ~write, ~dispose, observer, receipt, settings, s, rest, keep, head, status, state,
6984 writer.plan(frame.nobody(status, False{}), normalized, length))
6985
6986def admission.write(
6987 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
6988 admitted: Bool, +inbox: Chan(Admission.Command), +settings: Limits,
6989 s: Socket, req: Req, rest: Bytes.Bytes, v11: Bool
6990) -> IO(Maybe<&1, Socket & Turn>):
6991 match admitted:
6992 case False{}:
6993 Limits{max, cap, hm, bm, im, wm} = settings
6994 do IO<Maybe<&1, Socket & Turn>>:
6995 admission.reject(Completion.Observed{status => outcome => observe(status, outcome)}, s, admission.head(req), wm)
6996 return None{}
6997 case True{}:
6998 Req{+method, path, +headers, body} = req
6999 do IO<Maybe<&1, Socket & Turn>>:
7000 out : WriteHead<S> <- start(Req{method, path, headers, body})
7001 after : Maybe<&1, Socket & Turn> <- serve.write.send(~S, ~write, ~dispose, Completion.Observed{status => outcome => observe(status, outcome)}, settings, s, rest, headers, v11, String.eq(method, "HEAD"), out)
7002 Admission.give(inbox, 1)
7003 return after
7004
7005@unsafe
7006def serve.write.talk(
7007 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
7008 +settings: Limits, +inbox: Chan(Admission.Command), st: Maybe<&1, Serving>
7009) -> IO(Unit):
7010 Limits{max, cap, hm, bm, im, wm} = settings
7011 match st:
7012 case None{}:
7013 IO.pure(Unit, Unit{})
7014 case Some{Serving{(s, t), phase}}:
7015 match t:
7016 case TurnMore{sv, cont}:
7017 do IO<Unit>:
7018 received : (Socket & Turn) & Phase <- talk.recv(inbox, False{}, settings, s, sv, cont, phase)
7019 serve.write.talk(~S, ~start, ~write, ~dispose, ~observe, settings, inbox, Some{talk.state(received)})
7020 case TurnReq{req, rest, v11}:
7021 do IO<Unit>:
7022 admitted : Bool <- Admission.take(inbox, 1)
7023 after : Maybe<&1, Socket & Turn> <- admission.write(~S, ~start, ~write, ~dispose, ~observe, admitted, inbox, settings, s, req, rest, v11)
7024 serve.write.talk(~S, ~start, ~write, ~dispose, ~observe, settings, inbox, serve.write.state(after))
7025 case TurnFail{status}:
7026 reply_fail(Completion.Observed{status => outcome => observe(status, outcome)}, s, status, wm)
7027 case TurnClose{}:
7028 Socket.close(s)
7029 case TurnInputFailed{operation, code, message}:
7030 Socket.close(s)
7031
7032def server.write.run(
7033 ~S: Type, ~start: Req -> IO(WriteHead<S>),
7034 ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt),
7035 ~dispose: S -> IO(Unit),
7036 ~observe: U32 -> Completion.Outcome -> IO(Unit), server: Server<Unit>
7037) -> IO(ServerExit):
7038 server.run.loop(~Unit,
7039 ~(context => settings => inbox => socket =>
7040 serve.write.talk(~S, ~start, ~write, ~dispose, ~observe, settings, inbox,
7041 Some{Serving{(socket, TurnMore{sv.none(), False{}}), None{}}})),
7042 ServerListening{server})
7043
7044def serve.write.started(
7045 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
7046 result: Unit & Result<&2, &1, StartupError, Server<Unit>>
7047) -> IO(Result<&2, &2, StartupError, Unit>):
7048 (owner, got) = result
7049 match got:
7050 case Fail{error}:
7051 IO.pure(Result<&2, &2, StartupError, Unit>, Fail{error})
7052 case Done{server}:
7053 do IO<Result<&2, &2, StartupError, Unit>>:
7054 exited : ServerExit <- server.write.run(~S, ~start, ~write, ~dispose, ~observe, server)
7055 server.exited(exited)
7056 return Done{Unit{}}
7057
7058def serve.write.config(
7059 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit), config: ServerConfig
7060) -> IO(Result<&2, &2, StartupError, Unit>):
7061 do IO<Result<&2, &2, StartupError, Unit>>:
7062 started : Unit & Result<&2, &1, StartupError, Server<Unit>> <- server.start(~Unit, ~Unit, Unit{}, Unit{}, config)
7063 serve.write.started(~S, ~start, ~write, ~dispose, ~observe, started)
7064
7065def serve.started(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit),
7066 started: Unit & Result<&2, &1, StartupError, Server<Unit>>) -> IO(Unit):
7067 (owner, result) = started
7068 match result:
7069 case Fail{error}: server.start.failed(error)
7070 case Done{server}:
7071 do IO<Unit>:
7072 exited : ServerExit <- server.run(~Unit, ~(context => req => h(req)),
7073 ~(context => status => outcome => observe(status, outcome)), server)
7074 server.exited(exited)
7075
7076def serve.on.with(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), host: String, +port: U32, +max: Nat) -> IO(Unit):
7077 do IO<Unit>:
7078 limits.validate(max)
7079 started : Unit & Result<&2, &1, StartupError, Server<Unit>> <-
7080 server.start(~Unit, ~Unit, Unit{}, Unit{}, server.config.with(host, port, max))
7081 serve.started(~h, ~observe, started)
7082
7083def serve.on(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), host: String, +port: U32) -> IO(Unit):
7084 serve.on.with(~h, ~observe, host, port, serve.max())
7085
7086def serve.with(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), +port: U32, +max: Nat) -> IO(Unit):
7087 serve.on.with(~h, ~observe, "0.0.0.0", port, max)
7088
7089def serve(~h: Req -> IO(Reply), ~observe: U32 -> Completion.Outcome -> IO(Unit), +port: U32) -> IO(Unit):
7090 serve.with(~h, ~observe, port, serve.max())
7091
7092def serve.stream.on.with(
7093 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool),
7094 ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit), host: String, +port: U32, +max: Nat
7095) -> IO(Unit):
7096 do IO<Unit>:
7097 limits.validate(max)
7098 result : Result<&2, &2, StartupError, Unit> <-
7099 serve.stream.config(~S, ~start, ~piece, ~finish, ~abort, ~observe, server.config.with(host, port, max))
7100 serve.config.result(result)
7101
7102def serve.stream.with(
7103 ~S: Type, ~start: Req -> IO(S), ~piece: S -> Bytes.Bytes -> IO(S & Bool),
7104 ~finish: S -> IO(Reply), ~abort: S -> StreamAbort -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit), +port: U32, +max: Nat
7105) -> IO(Unit):
7106 serve.stream.on.with(~S, ~start, ~piece, ~finish, ~abort, ~observe, "0.0.0.0", port, max)
7107
7108def serve.write.on.with(
7109 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
7110 host: String, +port: U32, +max: Nat
7111) -> IO(Unit):
7112 do IO<Unit>:
7113 limits.validate(max)
7114 result : Result<&2, &2, StartupError, Unit> <-
7115 serve.write.config(~S, ~start, ~write, ~dispose, ~observe, server.config.with(host, port, max))
7116 serve.config.result(result)
7117
7118def serve.write.with(
7119 ~S: Type, ~start: Req -> IO(WriteHead<S>), ~write: S -> Writer -> Completion.Receipt -> IO(Writer & Completion.Receipt), ~dispose: S -> IO(Unit), ~observe: U32 -> Completion.Outcome -> IO(Unit),
7120 +port: U32, +max: Nat
7121) -> IO(Unit):
7122 serve.write.on.with(~S, ~start, ~write, ~dispose, ~observe, "0.0.0.0", port, max)