src/client.bend
emerging-ezhttp@0.21.0.0 · 201 lines · 26 definitions
1
2
3
4import Base
5import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes
6import emerging-ezhttp@0.21.0.0/src/url.bend as Url
7import emerging-ezhttp@0.21.0.0/src/http.bend as Http
8import emerging-ezhttp@0.21.0.0/src/body.bend as Body
9import emerging-ezhttp@0.21.0.0/src/wire.bend as Wire
10import emerging-ezhttp@0.21.0.0/src/wirespec.bend as Spec
11import emerging-ezhttp@0.21.0.0/src/redirect.bend as Redirect
12import emerging-ezhttp@0.21.0.0/src/auth.bend as Auth
13import emerging-ezhttp@0.21.0.0/src/qs.bend as Qs
14import emerging-ezhttp@0.21.0.0/src/packed.bend as Packed
15
16
17
18
19
20type Response is Type:
21 Err{why: String}
22 Response{status: U32, headers: List<&2, Http.Header>, reason: String,
23 bytes: Bytes.Bytes}
24
25
26def client.text(b: Bytes.Bytes) -> String:
27 Qs.octets.text(Bytes.to_string(b))
28
29
30
31def from.got(g: Packed.Got) -> Response:
32 match g:
33 case Packed.Torn{why}:
34 Err{why}
35 case Packed.Got{status, headers, reason, body}:
36 Response{status, Qs.octets.headers(headers), Qs.octets.text(reason), body}
37
38
39
40
41def http.answer(+method: String, a: Wire.Answer) -> Response:
42 match a:
43 case Wire.Failed{answer}:
44 Err{Spec.wire.failed(answer)}
45 case Wire.Received{octets}:
46 from.got(Packed.packed.reply(method, octets))
47
48
49
50def http.send(ex: Spec.Exchange, tail: Bytes.Bytes) -> IO(Response):
51 match ex:
52 case Spec.NoUrl{why}:
53 IO.pure(Response, Err{why})
54 case Spec.Exchange{secure, host, port, +method, req}:
55 do IO<Response>:
56 a : Wire.Answer <- Wire.wire.talk(secure, host, port, method, req, tail)
57 IO.pure(Response, http.answer(method, a))
58
59
60def http.loc.shaped(method: String, l: Url.Loc,
61 headers: List<&2, Http.Header>, r: Body.Shape & Bytes.Bytes)
62 -> IO(Response):
63 (shape, tail) = r
64 http.send(Spec.wire.exchange.body(method, l, headers, shape), tail)
65
66
67def http.loc(method: String, l: Url.Loc, headers: List<&2, Http.Header>,
68 body: Body.Body) -> IO(Response):
69 http.loc.shaped(method, l, headers, Body.body.shape(body))
70
71
72
73def http.send.timed(ms: U32, ex: Spec.Exchange, tail: Bytes.Bytes)
74 -> IO(Response):
75 match ex:
76 case Spec.NoUrl{why}:
77 IO.pure(Response, Err{why})
78 case Spec.Exchange{secure, host, port, +method, req}:
79 do IO<Response>:
80 a : Wire.Answer <- Wire.wire.talk.timed(secure, ms, host, port, method,
81 req, tail)
82 IO.pure(Response, http.answer(method, a))
83
84
85def client.timed.shaped(method: String, url: String,
86 headers: List<&2, Http.Header>, ms: U32, r: Body.Shape & Bytes.Bytes)
87 -> IO(Response):
88 (shape, tail) = r
89 http.send.timed(ms, Spec.wire.exchange.body(method, Url.parse(url), headers,
90 shape), tail)
91
92
93
94def client.timed(method: String, url: String, headers: List<&2, Http.Header>,
95 body: Body.Body, ms: U32) -> IO(Response):
96 client.timed.shaped(method, url, headers, ms, Body.body.shape(body))
97
98
99def client.get.timed(url: String, headers: List<&2, Http.Header>, ms: U32)
100 -> IO(Response):
101 client.timed("GET", url, headers, Body.Empty{}, ms)
102
103
104
105def client.hop(left: Nat, url: String, r: Response) -> Redirect.Hop & Response:
106 match r:
107 case Err{why}:
108 (Redirect.Stop{}, Err{why})
109 case Response{+status, +headers, reason, bytes}:
110 (Redirect.redirect.plan(left, url, status, headers),
111 Response{status, headers, reason, bytes})
112
113
114def client.after(p: Redirect.Hop & Response, go: String -> IO(Response))
115 -> IO(Response):
116 (hop, r) = p
117 match hop:
118 case Redirect.Stop{}:
119 IO.pure(Response, r)
120 case Redirect.Go{next}:
121 go(next)
122
123
124
125
126def client.follow(+left: Nat, +url: String, +headers: List<&2, Http.Header>,
127 body: Body.Body, +ms: U32) -> IO(Response):
128 match left:
129 case 0n:
130 client.timed("GET", url, headers, body, ms)
131 case 1n+p:
132 do IO<Response>:
133 r : Response <- client.timed("GET", url, headers, body, ms)
134 client.after(client.hop(left, url, r),
135 next => client.follow(p, next, headers, Body.Empty{}, ms))
136
137
138def client.request.go(go: Bool, method: String, url: String,
139 headers: List<&2, Http.Header>, body: Body.Body, ms: U32) -> IO(Response):
140 match go:
141 case True{}:
142 client.follow(Redirect.redirect.max(), url, headers, body, ms)
143 case False{}:
144 client.timed(method, url, headers, body, ms)
145
146
147
148
149def client.request.opts(+method: String, url: String,
150 headers: List<&2, Http.Header>, body: Body.Body, ms: U32, follow: Bool)
151 -> IO(Response):
152 client.request.go(Redirect.redirect.follows(method, follow), method, url,
153 headers, body, ms)
154
155
156def client.get.opts(url: String, headers: List<&2, Http.Header>, ms: U32,
157 follow: Bool) -> IO(Response):
158 client.request.opts("GET", url, headers, Body.Empty{}, ms, follow)
159
160
161def client.timed_out(r: Response) -> Bool:
162 match r:
163 case Err{why}:
164 Spec.wire.timed_out(why)
165 case Response{status, headers, reason, bytes}:
166 False{}
167
168
169def client.get(url: String, headers: List<&2, Http.Header>) -> IO(Response):
170 http.loc("GET", Url.parse(url), headers, Body.Empty{})
171
172
173def client.head(url: String, headers: List<&2, Http.Header>) -> IO(Response):
174 http.loc("HEAD", Url.parse(url), headers, Body.Empty{})
175
176
177def client.post(url: String, headers: List<&2, Http.Header>, body: Body.Body)
178 -> IO(Response):
179 http.loc("POST", Url.parse(url), headers, body)
180
181
182def client.put(url: String, headers: List<&2, Http.Header>, body: Body.Body)
183 -> IO(Response):
184 http.loc("PUT", Url.parse(url), headers, body)
185
186
187def client.delete(url: String, headers: List<&2, Http.Header>, body: Body.Body)
188 -> IO(Response):
189 http.loc("DELETE", Url.parse(url), headers, body)
190
191
192def client.options(url: String, headers: List<&2, Http.Header>) -> IO(Response):
193 http.loc("OPTIONS", Url.parse(url), headers, Body.Empty{})
194
195
196def client.bearer(token: String) -> Http.Header:
197 Auth.auth.bearer(token)
198
199
200def client.basic(user: String, password: String) -> Http.Header:
201 Auth.auth.basic(user, password)