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