5 title/'HTTP requests and timers'
9# Writing an HTTP request
11There's a variety of arvo services we haven't touched on yet.
12Let's figure out how to make HTTP requests and set timers.
14Let's build an uptime-monitoring system. It'll ping a web
15service periodically and print out an error when the response
16code isn't 2xx. Here's `app/up.hoon`:
19:: Up-ness monitor. Accepts atom url, 'on', or 'off'
21:::: /hoon/up/examples/app
27 $% [%hiss wire unit-iden=$~ mark=%httr cage=[mark=%purl vase=purl]]
31 $% [%on $~] :: enable polling('on')
32 [%off $~] :: disable polling('off)
33 [%target p=cord] :: set poll target('http://...')
36|_ [hid=bowl:gall on=_| in-progress=_| target=@t}
38 |= url-or-command=@t ^- (quip move +>)
40 ?: ?=($on url-or-command) [%on ~]
41 ?: ?=($off url-or-command) [%off ~]
42 [%target url-or-command]
44 $target [~ +>.$(target p.act)]
47 :- ?: |(on in-progress) ~
48 [ost.hid %hiss /request ~ %httr %purl (need (epur target))]~
49 +>.$(on &, in-progress &)
52 |= [wir=wire code=@ud headers=mess body=(unit octs)]
54 ^- [(list move) _+>.$]
55 ?: &((gte code 200) (lth code 300))
56 ~& [%all-is-well code]
58 [ost.hid %wait /timer (add ~s10 now.hid)]~
59 ~& [%we-have-a-problem code]
63 [ost.hid %wait /timer (add ~s10 now.hid)]~
65 |= [wir=wire $~] ^- (quip move +>)
68 [ost.hid %hiss /request ~ %httr %purl (need (epur target))]~
69 [~ +>.$(in-progress |)]
71++ prep ~& target _`. :: computed when the source file changes;
72-- :: here it prints target then resets our state
75There's some fancy stuff going on here. We'll go through it line by
78There's two kinds of cards we're sending to arvo. A `%hiss` move tells
79`%eyre` to make an HTTP request. It expects a `(unit iden)`, which will
80be null here because we aren't doing any authentication; a mark, in this
81case the http request mark `&httr`; and some data in the form of a
82`cage`, which is a `[mark vase]`. This is confusing, so the mold in the
83code quoted above includes a bunch of otherwise unnecessary faces for
84the purpose of illustration. Compare: `[%hiss p=(unit iden) q=mark
87> You can grep zuse.hoon and arvo.hoon for most of these definitions.
88> However, beware the ambiguity surrounding "hiss": there's ++hiss in
89> section 3bI of `zuse` ("Arvo structures"), and there's `%hiss` the
90> move that is sent to %eyre to make our HTTP request happen. Here we're
91> talking about the latter. See `++kiss-eyre` in `zuse`.
93> Recall that `++unit` means "maybe". Formally, "`(unit a)` is
94> either null or a pair of null and a value in `a`". Also recall
95> that to pull a value out of a unit (`u.unit`), you must first
96> verify that the unit is not null (for example with `?~`). See
99A `purl` is a parsed url structure, which can be created with `++epur`,
100which is a function that takes a url as text and parses it into a `(unit
101purl)`. Thus, the result is null if and only if the url is malformed.
102'Purl' is also the mark which will be applied to this result.
104When you send this request, you can expect a `%sigh` with the
105response, which we handle later on in `++sigh-httr`.
107For `%wait`, you just pass a [`@da`]() (absolute date), and arvo will
108produce a `%wake` when the time comes.
110> A timer is guaranteed to not be triggered before the given
111> time, but it's currently impossible to guarantee the timer will be
112> triggered at exactly the requested time.
114Let's take a look at our state:
117|_ [hid=bowl:gall on=_| in-progress=_| target=@t}
120We have three pieces of app-specific state. `target` is the url
121we're monitoring. `on` and `in-progress` are booleans
122representing, respectively, whether or not we're supposed to keep
123monitoring and whether or not we're in the middle of a request.
125The type of booleans is usually written as `?`, which is a union
126of `&` (true) and `|` false. This type defaults to `&` (true). In our
127case, we want both of these to default to `|` (false). Because
128of that, we use `_|` as the type. `_value` means "take the type
129of `value`, but make the default value be `value`". Thus, our
130type is still a boolean, just like `?`, but the default value is
133> This is the same `_` used in `_+>.$`, which means "the same
134> type as the value "+>.$". In other words, the same type as our
135> current context and state.
137Let's take a look at `++poke-atom`. When we're poked with an atom, we
138first check whether the atom is `'off'`. If so, we set our state
139variable `on` to false.
141If not, we check whether the atom is `'on'`. If so, we set `on` and
142`in-progress` to true. If it was already either on or in progress, then
143we don't take any other immediate action. If it was both off and not in
144progress, then we send an HTTP request.
146If the argument is neither 'off' nor 'on', then we assume it's an
147actual url, so we save it in `target`.
149Here's the move that sends the HTTP request:
151[ost.hid %hiss /request ~ %httr %purl (need (epur target))]
154> Remember, we are expected to produce a *list* of moves. Note the `~`
155> after the move in the full example. This is a convenient shortcut for
156> creating a list of a single element. It's part of a small family of
157> such shortcuts. `~[a b c]` is `[a b c ~]`, `[a b c]~` is `[[a b c]
158> ~]` and `\`[a b c]` is `[~ a b c]`. These may be mixed and matched to
159> create various convoluted structures and emojis.
161The correspondence between this move and `[bone card]` can be hard to
162visualize on one line. Here it is more pedantically:
165:* bone=ost.hid :: the move
170 cage=[%purl (need (epur target))]
174When the HTTP response comes back, we handle it with
175`++sigh-httr`, which, along with the wire the request was sent
176on, takes the status code, the response headers, and the response
179We check whether the status code is between 200 and 300. If so,
180all is well, and we print a message saying so. We start the
181timer for ten seconds after the present time.
183If we got a bad status code, then we print out the entire
184response and start the timer again.
186After ten seconds, arvo will give us a `%wake` event, which will
187be handled in `++wake-timer`. If we're still supposed to keep
188monitoring, we send the same HTTP request as before. Otherwise,
189we set `in-progress` to false.
194~fintud-macrep:dojo> |start %up
196~fintud-macrep:dojo> :examples-up &atom 'http://www.google.com'
200~fintud-macrep:dojo> :examples-up &atom 'http://example.com'
203~fintud-macrep:dojo> :examples-up &atom 'http://google.com'
205[%we-have-a-problem 301]
208 [p='X-Frame-Options' q='SAMEORIGIN']
209 [p='X-XSS-Protection' q='1; mode=block']
210 [p='Content-Length' q='219']
212 [p='Cache-Control' q='public, max-age=2592000']
213 [p='Expires' q='Sun, 10 Jan 2016 23:45:05 GMT']
214 [p='Date' q='Fri, 11 Dec 2015 23:45:05 GMT']
215 [p='Content-Type' q='text/html; charset=UTF-8']
216 [p='Location' q='http://www.google.com/']
223 \/'<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">\0a<TITLE>301 Moved</TI\/
224 TLE></HEAD><BODY>\0a<H1>301 Moved</H1>\0aThe document has moved\0a<A HREF="http://www.google.com/">her
225 e</A>.\0d\0a</BODY></HTML>\0d\0a'
230~fintud-macrep:dojo> :up &atom 'off'
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list