on 932d391 help sign in
211 lines 6.9 kB history raw
1---
2next: false
3sort: 9
4title: HTTP Requests and Timers
5---
7# Writing an HTTP request
9There's a variety of arvo services we haven't touched on yet.
10Let's figure out how to make HTTP requests and set timers.
12Let's build an uptime-monitoring system. It'll ping a web
13service periodically and print out an error when the response
14code isn't 2xx. Here's `app/up.hoon`:
16```
17/? 314
18|%
19++ move {bone card}
20++ card
21 $% {%them wire (unit hiss)}
22 {%wait wire @da}
23 ==
24--
25|_ {hid/bowl on/_| in-progress=_| target/@t}
26++ poke-cord
27 |= url/@t
28 ^- {(list move) _+>.$}
29 ?: =('off' url)
30 [~ +>.$(on |)]
31 ?: =('on' url)
32 :_ +>.$(on &, in-progress &)
33 ?: |(on in-progress)
34 ~
35 [ost.hid %them /request ~ (need (epur target)) %get ~ ~]~
36 [~ +>.$(target url)]
37++ thou-request
38 |= {wir/wire code/@ud headers/mess body/(unit octs)}
39 ^- {(list move) _+>.$}
40 ?: &((gte code 200) (lth code 300))
41 ~& [%all-is-well code]
42 :_ +>.$
43 ?: on
44 [ost.hid %wait /timer (add ~s10 now.hid)]~
45 ~
46 ~& [%we-have-a-problem code]
47 ~& [%headers headers]
48 ~& [%body body]
49 :_ +>.$
50 ?: on
51 [ost.hid %wait /timer (add ~s10 now.hid)]~
52 ~
53++ wake-timer
54 |= {wir/wire ~}
55 ?: on
56 :_ +>.$
57 [ost.hid %them /request ~ (need (epur target)) %get ~ ~]~
58 [~ +>.$(in-progress |)]
59--
60```
62There's some fancy stuff going on here. We'll go through it line by
63line, though.
65Firstly, there's two kinds of cards we're sending to arvo. A
66`%them` card makes an HTTP requrest out of a unit `hiss`.
68<blockquote class="blockquote">
69Recall that `++unit` means "maybe". Formally, "`(unit a)` is
70either null or a pair of null and a value in `a`". Also recall
71that to pull a value out of a unit `(u.unit)`, you must first
72verify that the unit is not null (for example with `?~`).
73</blockquote>
75A `hiss` (all of the following terms are defined in `zuse`) is a
76pair of a `purl` and a `moth`. A `purl` is a parsed url
77structure, which can be created with `++epur`, which is a
78function that takes a url as text and parses it into a `(unit
79purl)`. Thus, the result is null if and only if the url is malformed.
81A `moth` is a treble of a `meth`, a `math`, and `(unit octs)`.
82`meth` is the HTTP method, in this case `%get`. `math` is a map
83of HTTP headers. The `(unit octs)` is a possible octet stream
84representing the body. If it's null, then no body is sent (as in
85this case).
87<blockquote class="blockquote">
88An octect stream is a pair of the length in bytes of the data
89plus the data itself. You can use `++taco` takes text and
90turns it into an octet strem.
91</blockquote>
93When you send this request, you can expect a `%thou` with the
94response, which we handle later on in `++thou-request`.
96For `%wait`, you just pass a [`@da`]() (absolute date), and arvo will
97produce a `%wake` when the time comes.
99<blockquote class="blockquote">
100A timer is guaranteed to not be triggered before the given
101time, but it's currently impossible to guarantee the timer will be
102triggered at exactly the requested time.
103</blockquote>
105Let's take a look at our state:
107```
108|_ {hid/bowl on/_| in-progress=_| target/@t}
109```
111We have three pieces of app-specific state. `target` is the url
112we're monitoring. `on` and `in-progress` are booleans
113representing, respectively, whether or not we're supposed to keep
114monitoring and whether or not we're in the middle of a request.
116The type of booleans is usually written as `?`, which is a union
117of `&` (true) and `|` false. This type defaults to `&` (true). In our
118case, we want both of these to default to `|` (false). Because
119of that, we use `_|` as the type. `_value` means "take the type
120of `value`, but make the default value be `value`". Thus, our
121type is still a boolean, just like `?`, but the default value is
122`|` (false).
124<blockquote class="blockquote">
125This is the same `_` used in `_+>.$`, which means "the same
126type as the value "+>.$". In other words, the same type as our
127current context and state.
128</blockquote>
130Let's take a look at `++poke-cord`. When we're poked with a
131cord, we first check whether it's `'off'`. If so, we set `on` to
132false.
134If not, we check whether it's `on`. If so, we set `on` and
135`in-progress` to true. If it was already either on or in
136progress, then we don't take any other immediate action. If it
137was both off and not in progress, then we send an HTTP request.
138This request follows the pattern in `++hiss` well. `(need (epur
139target))` is the parsed url, `%get` is the HTTP method, `~`
140means no extra headers, and another `~` means no body.
142<blockquote class="blockquote">
143Note the `~` at the end of the move. This is a convenient
144shortcut for creating a list of a single element. It's part of
145a small family of such shortcuts. `~[a b c]` is `[a b c ~]`,
146`[a b c]~` is `[[a b c] ~]` and `\`[a b c]` is `[~ a b c]`.
147These may be mixed and matched to create various convoluted
148structures and emojis.
149</blockquote>
151If the argument is neither 'off' nor 'on', then we assume it's an
152actual url, so we save it in `target`.
154When the HTTP response comes back, we handle it with
155`++thou-request`, which, along with the wire the request was sent
156on, takes the status code, the response headers, and the response
157body.
159We check whether the status code is between 200 and 300. If so,
160all is well, and we print a message saying so. We start the
161timer for ten seconds after the present time.
163If we got a bad status code, then we print out the entire
164response and start the timer again.
166After ten seconds, arvo will give us a `%wake` event, which will
167be handled in `++wake-timer`. If we're still supposed to keep
168monitoring, we send the same HTTP request as before. Otherwise,
169we set `in-progress` to false.
171Let's try it out:
173```
174~fintud-macrep:dojo> |start %up
175>=
176~fintud-macrep:dojo> :up &cord 'http://www.google.com'
177>=
178[%all-is-well 200]
179[%all-is-well 200]
180~fintud-macrep:dojo> :up &cord 'http://example.com'
181>=
182[%all-is-well 200]
183~fintud-macrep:dojo> :up &cord 'http://google.com'
184>=
185[%we-have-a-problem 301]
186[ %headers
187 ~[
188 [p='X-Frame-Options' q='SAMEORIGIN']
189 [p='X-XSS-Protection' q='1; mode=block']
190 [p='Content-Length' q='219']
191 [p='Server' q='gws']
192 [p='Cache-Control' q='public, max-age=2592000']
193 [p='Expires' q='Sun, 10 Jan 2016 23:45:05 GMT']
194 [p='Date' q='Fri, 11 Dec 2015 23:45:05 GMT']
195 [p='Content-Type' q='text/html; charset=UTF-8']
196 [p='Location' q='http://www.google.com/']
197 ]
199[ %body
200 [ ~
201 [ p=219
202 q
203 \/'<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">\0a<TITLE>301 Moved</TI\/
204 TLE></HEAD><BODY>\0a<H1>301 Moved</H1>\0aThe document has moved\0a<A HREF="http://www.google.com/">her
205 e</A>.\0d\0a</BODY></HTML>\0d\0a'
206 \/ \/
207 ]
208 ]
210~fintud-macrep:dojo> :up &cord 'off'
211```