main.bend
emerging-ezhttp@0.9.0.0 · 181 lines · 0 definitions
1# ezhttp: HTTP/1.1 client and server for Bend 2, with auth, cookie and CORS helpers.
2#
3# Shared message types, BYO JSON via encode/decode. TLS for the client stays
4# at the wire/runtime layer.
5import Base
6import emerging-ezhttp@0.9.0.0/src/url.bend as Url
7import emerging-ezhttp@0.9.0.0/src/http.bend as Http
8import emerging-ezhttp@0.9.0.0/src/body.bend as Body
9import emerging-ezhttp@0.9.0.0/src/client.bend as Client
10import emerging-ezhttp@0.9.0.0/src/server.bend as Server
11import emerging-ezhttp@0.9.0.0/src/cookie.bend as Cookie
12import emerging-ezhttp@0.9.0.0/src/cache.bend as Cache
13import emerging-ezhttp@0.9.0.0/src/cors.bend as Cors
14
15# URL parse re-export
16def parse_url(url: String) -> Url.Loc:
17 Url.parse(url)
18
19# response parse re-export
20def parse_response(raw: String) -> Http.Reply:
21 Http.parse(raw)
22
23# request parse re-export (server inbound)
24def parse_request(raw: String) -> Http.Request:
25 Http.ask(raw)
26
27# format a response message (server outbound)
28def format_response(status: U32, headers: List<&2, Http.Header>, body: String)
29 -> String:
30 Http.respond(status, headers, body)
31
32# empty request body
33def empty_body() -> Body.Body:
34 Body.body.empty()
35
36# UTF-8 text request body
37def text_body(s: String) -> Body.Body:
38 Body.body.text(s)
39
40# octet-list request body
41def octets_body(bs: List<&2, U32>) -> Body.Body:
42 Body.body.octets(bs)
43
44# BYO JSON (or any codec): encode a value with the caller's to_text
45def encode(~A: Data, ~to_text: A -> String, value: A) -> Body.Body:
46 Body.body.encode(A, to_text, value)
47
48# BYO JSON (or any codec): decode body text with the caller's from_text
49def decode(
50 ~A: Data,
51 ~from_text: String -> Maybe<&2, A>,
52 s: String
53) -> Maybe<&2, A>:
54 Body.body.decode(A, from_text, s)
55
56# Bearer token header helper
57def bearer(token: String) -> Http.Header:
58 Client.client.bearer(token)
59
60# Basic credentials header (RFC 7617)
61def basic(user: String, password: String) -> Http.Header:
62 Client.client.basic(user, password)
63
64# Set-Cookie header from a parsed cookie (RFC 6265)
65def set_cookie(c: Cookie.Cookie) -> Http.Header:
66 Cookie.cookie.set(c)
67
68# parse one Set-Cookie field value
69def parse_cookie(line: String) -> Maybe<&2, Cookie.Cookie>:
70 Cookie.cookie.parse(line)
71
72# Cookie request header for a list of cookies
73def cookie_header(cs: List<&2, Cookie.Cookie>) -> Http.Header:
74 Cookie.cookie.request(cs)
75
76# parse a Cache-Control field value (RFC 9111)
77def cache_control(value: String) -> Cache.Cc:
78 Cache.cache.parse(value)
79
80# freshness of a Cache-Control value at an age in seconds
81def fresh(value: String, age: Nat) -> Bool:
82 Cache.cache.fresh(Cache.cache.parse(value), age)
83
84# CORS preflight or simple headers on a handler reply
85def cors_reply(cfg: Cors.Cfg, req: Http.Request, reply: Http.Reply) -> Http.Reply:
86 Cors.cors.on(cfg, req, reply)
87
88# GET with no extra headers
89def http.get(url: String) -> IO(Client.Response):
90 Client.client.get(url, [])
91
92# GET with headers
93def http.get_with(url: String, headers: List<&2, Http.Header>)
94 -> IO(Client.Response):
95 Client.client.get(url, headers)
96
97# HEAD with no extra headers
98def http.head(url: String) -> IO(Client.Response):
99 Client.client.head(url, [])
100
101# HEAD with headers
102def http.head_with(url: String, headers: List<&2, Http.Header>)
103 -> IO(Client.Response):
104 Client.client.head(url, headers)
105
106# POST with a body and no extra headers
107def http.post(url: String, body: Body.Body) -> IO(Client.Response):
108 Client.client.post(url, [], body)
109
110# POST with headers and a body
111def http.post_with(
112 url: String,
113 headers: List<&2, Http.Header>,
114 body: Body.Body
115) -> IO(Client.Response):
116 Client.client.post(url, headers, body)
117
118# PUT with a body and no extra headers
119def http.put(url: String, body: Body.Body) -> IO(Client.Response):
120 Client.client.put(url, [], body)
121
122# PUT with headers and a body
123def http.put_with(url: String, headers: List<&2, Http.Header>, body: Body.Body)
124 -> IO(Client.Response):
125 Client.client.put(url, headers, body)
126
127# DELETE with a body and no extra headers
128def http.delete(url: String, body: Body.Body) -> IO(Client.Response):
129 Client.client.delete(url, [], body)
130
131# DELETE with headers and a body
132def http.delete_with(
133 url: String,
134 headers: List<&2, Http.Header>,
135 body: Body.Body
136) -> IO(Client.Response):
137 Client.client.delete(url, headers, body)
138
139# OPTIONS with no extra headers
140def http.options(url: String) -> IO(Client.Response):
141 Client.client.options(url, [])
142
143# OPTIONS with headers
144def http.options_with(url: String, headers: List<&2, Http.Header>)
145 -> IO(Client.Response):
146 Client.client.options(url, headers)
147
148# listen on host:port and serve up to `limit` connections
149def http.serve(
150 ~handle: Http.Request -> Http.Reply,
151 host: String,
152 port: U32,
153 limit: U32
154) -> IO(Unit):
155 Server.server.serve(handle, host, port, limit)
156
157# listen on host:port and serve up to `limit` connections with a handler that
158# does IO; a failed connection is logged and the next one is served
159def http.serve_io(
160 ~handle: Http.Request -> IO(Http.Reply),
161 host: String,
162 port: U32,
163 limit: U32
164) -> IO(Unit):
165 Server.server.serve_io(handle, host, port, limit)
166
167# listen on host:port and serve with an IO handler until the process stops
168@unsafe def http.serve_forever(
169 ~handle: Http.Request -> IO(Http.Reply),
170 host: String,
171 port: U32
172) -> IO(Unit):
173 Server.server.serve_forever(handle, host, port)
174
175# listen on host:port, serve one connection, then stop
176def http.serve_once(
177 ~handle: Http.Request -> Http.Reply,
178 host: String,
179 port: U32
180) -> IO(Unit):
181 Server.server.once(handle, host, port)