src/step.bend
emerging-ezhttp@0.19.0.0 · 62 lines · 0 definitions
1# ezhttp/src/step: what the server decides for one connection, kept pure so
2# a law can state it. server.bend runs the effects and asks this module what
3# each outcome means; it decides nothing itself.
4import Base
5import emerging-ezhttp@0.19.0.0/src/http.bend as Http
6import emerging-ezhttp@0.19.0.0/src/qs.bend as Qs
7
8# what a request asks of the server: answer it now, or call the handler
9type Step is Data:
10 Answer{reply: Http.Reply}
11 Call{req: Http.Request}
12
13# an effect's outcome on one connection: the value, or the line to log for
14# a connection that is lost
15type Got<-A: Type> is Type:
16 Kept{value: A}
17 Lost{line: String}
18
19# a request received as octets, as text: its method, target, header names
20# and values and body each decoded as UTF-8 (SPEC.md HTTP-SRV-2); a bad one
21# keeps its reason
22def step.text(req: Http.Request) -> Http.Request:
23 match req:
24 case Http.Bad{why}:
25 Http.Bad{why}
26 case Http.Request{method, target, headers, body}:
27 Http.Request{Qs.octets.text(method), Qs.octets.text(target),
28 Qs.octets.headers(headers), Qs.octets.text(body)}
29
30# a bad request is answered 400 with its reason and never reaches the
31# handler; a good one goes to the handler as it was parsed
32def step.of(req: Http.Request) -> Step:
33 match req:
34 case Http.Bad{why}:
35 Answer{Http.Reply{400, [], why}}
36 case Http.Request{method, target, headers, body}:
37 Call{Http.Request{method, target, headers, body}}
38
39# the line logged for a failed effect: `ezhttp: <phase>: <why>`
40def step.line(phase: String, why: String) -> String:
41 "ezhttp: " ++ phase ++ ": " ++ why
42
43# a failed accept or recv loses the connection instead of ending the server
44def step.got(
45 -A: Type,
46 phase: String,
47 res: Result<&1, &1, U32 & String, A>
48) -> Got<A>:
49 match res:
50 case Fail{(code, why)}:
51 Lost{step.line(phase, why)}
52 case Done{value}:
53 Kept{value}
54
55# a failed send loses the connection too; the bytes it did not send are dropped
56def step.sent(res: Result<&1, &1, (U32 & String) & List<&2, U32>, Unit>)
57 -> Got<Unit>:
58 match res:
59 case Fail{((code, why), left)}:
60 Lost{step.line("write", why)}
61 case Done{value}:
62 Kept{value}