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