urbit/sys/vane/lient.hoon

315 lines
8.3 KiB
Plaintext
Raw Normal View History

!:
:: http-client
::
|= pit=vase
=, http-client
::
::
:: internal data structures
::
=> =~
::
:: internal data structures that won't go in zuse
::
|%
+$ move
::
$: :: duct: request identifier
::
=duct
::
::
card=(wind note gift:able)
==
:: +note: private request from light to another vane
::
+$ note _~
--
:: more structures
::
|%
+$ axle
$: :: date: date at which light's state was updated to this data structure
::
date=%~2019.2.8
::
::
=state
==
:: +state:client: state relating to open outbound HTTP connections
::
+$ state
$: :: next-id: monotonically increasing id number for the next connection
::
next-id=@ud
:: connection-by-id: open connections to the
::
connection-by-id=(map @ud [=duct =in-progress-http-request])
:: outbound-duct: the duct to send outbound requests on
::
outbound-duct=duct
==
:: +in-progress-http-request: state around an outbound http
::
+$ in-progress-http-request
$: :: remaining-redirects: http limit of number of redirects before error
::
remaining-redirects=@ud
:: remaining-retries: number of times to retry the request
::
remaining-retries=@ud
:: response-header: the response headers from the %start packet
::
:: We send the response headers with each %http-progress, so we must
:: save them.
::
response-header=(unit response-header:http)
:: chunks: a list of partial results returned from unix
::
:: This list of octs must be flopped before it is composed as the
:: final response, as we want to be able to quickly insert.
::
chunks=(list octs)
:: bytes-read: the sum of the size of the :chunks
::
bytes-read=@ud
:: expected-size: the expected content-length of the http request
::
expected-size=(unit @ud)
==
--
::
|%
:: +combine-octs: combine multiple octs into one
::
++ combine-octs
|= a=(list octs)
^- octs
:- %+ roll a
|= [=octs sum=@ud]
(add sum p.octs)
(can 3 a)
:: +per-client-event: per-event client core
::
++ per-client-event
|= [[our=@p eny=@ =duct now=@da scry=sley] =state]
|%
:: +request: makes an external web request
::
++ request
|= [=request:http =outbound-config]
^- [(list move) ^state]
:: get the next id for this request
::
=^ id next-id.state [next-id.state +(next-id.state)]
:: add a new open session
::
=. connection-by-id.state
%+ ~(put by connection-by-id.state) id
=, outbound-config
[duct [redirects retries ~ ~ 0 ~]]
:: start the download
::
:: the original eyre keeps track of the duct on %born and then sends a
:: %give on that duct. this seems like a weird inversion of
:: responsibility, where we should instead be doing a pass to unix. the
:: reason we need to manually build ids is because we aren't using the
:: built in duct system.
::
:: email discussions make it sound like fixing that might be hard, so
:: maybe i should just live with the way it is now?
::
:- [outbound-duct.state %give %request id request]~
state
:: +receive: receives a response to an http-request we made
::
:: TODO: Right now, we are not following redirect and not handling retries
:: correctly. We need to do this.
::
++ receive
|= [id=@ud =http-event:http]
^- [(list move) ^state]
:: ensure that this is a valid receive
::
?~ connection=(~(get by connection-by-id.state) id)
~& [%eyre-unknown-receive id]
[~ state]
::
?- -.http-event
%start
:: TODO: Handle redirects and retries here, before we start dispatching
:: back to the application.
::
:: record data from the http response that only comes from %start
::
=. connection-by-id.state
%+ ~(jab by connection-by-id.state) id
|= [duct=^duct =in-progress-http-request]
::
=. expected-size.in-progress-http-request
?~ str=(get-header:http 'content-length' headers.response-header.http-event)
~
::
(rush u.str dum:ag)
::
=. response-header.in-progress-http-request
`response-header:http-event
::
[duct in-progress-http-request]
::
?: complete.http-event
(send-finished id data.http-event)
::
(record-and-send-progress id data.http-event)
::
%continue
?: complete.http-event
(send-finished id data.http-event)
::
(record-and-send-progress id data.http-event)
::
%cancel
~& [%eyre-received-cancel id]
[~ state]
==
:: +record-and-send-progress: save incoming data and send progress report
::
++ record-and-send-progress
|= [id=@ud data=(unit octs)]
^- [(list move) ^state]
::
=. connection-by-id.state
%+ ~(jab by connection-by-id.state) id
|= [duct=^duct =in-progress-http-request]
:: record the data chunk and size, if it exists
::
=? chunks.in-progress-http-request
?=(^ data)
[u.data chunks.in-progress-http-request]
=? bytes-read.in-progress-http-request
?=(^ data)
(add bytes-read.in-progress-http-request p.u.data)
::
[duct in-progress-http-request]
::
=/ connection (~(got by connection-by-id.state) id)
:_ state
^- (list move)
:_ ~
:* duct.connection
%give
%progress
(need response-header.in-progress-http-request.connection)
bytes-read.in-progress-http-request.connection
expected-size.in-progress-http-request.connection
data
==
:: +send-finished: sends the %finished, cleans up the session state
::
++ send-finished
|= [id=@ud data=(unit octs)]
^- [(list move) ^state]
::
=/ connection (~(got by connection-by-id.state) id)
:: reassemble the octs that we've received into their final form
::
=/ data=octs
%- combine-octs
%- flop
::
?~ data
chunks.in-progress-http-request.connection
[u.data chunks.in-progress-http-request.connection]
::
=/ response-header=response-header:http
(need response-header.in-progress-http-request.connection)
::
=/ mime=@t
?~ mime-type=(get-header:http 'content-type' headers.response-header)
'application/octet-stream'
u.mime-type
:- :~ :* duct.connection
%give
%finished
response-header
?:(=(0 p.data) ~ `[mime data])
== ==
state(connection-by-id (~(del by connection-by-id.state) id))
--
--
:: end the =~
::
. ==
:: begin with a default +axle as a blank slate
::
=| ax=axle
:: a vane is activated with current date, entropy, and a namespace function
::
|= [our=ship now=@da eny=@uvJ scry-gate=sley]
:: allow jets to be registered within this core
::
~% %http-client ..is ~
|%
++ call
|= [=duct type=* wrapped-task=(hobo task:able)]
^- [(list move) _light-gate]
::
=/ task=task:able
?. ?=(%soft -.wrapped-task)
wrapped-task
~| [%p-wrapped-task p.wrapped-task]
((hard task:able) p.wrapped-task)
::
?: ?=(%vega -.task)
[~ light-gate]
::
=/ event-args [[our eny duct now scry-gate] state.ax]
=/ client (per-client-event event-args)
?- -.task
::
%born
:: TODO: reset the next-id for client state here.
::
:: send requests on the duct passed in with born.
::
=. outbound-duct.state.ax duct
[~ light-gate]
::
%request
=^ moves state.ax (request:client +.task)
[moves light-gate]
::
%cancel-request
~& %todo-cancel-request
[~ light-gate]
::
%receive
=^ moves state.ax (receive:client +.task)
[moves light-gate]
==
:: http-client issues no requests to other vanes
::
++ take
|= [=wire =duct wrapped-sign=*]
^- [(list move) _light-gate]
!!
::
++ light-gate ..$
:: +load: migrate old state to new state (called on vane reload)
::
++ load
|= old=axle
^+ ..^$
::
~! %loading
..^$(ax old)
:: +stay: produce current state
::
++ stay `axle`ax
:: +scry: request a path in the urbit namespace
::
++ scry
|= *
[~ ~]
--