diff options
| author | polwex <polwex@sortug.com> | 2025-10-06 07:58:42 +0700 |
|---|---|---|
| committer | polwex <polwex@sortug.com> | 2025-10-06 07:58:42 +0700 |
| commit | 8426c0c275fc824ab55edef4fa99b3a9bacf64d8 (patch) | |
| tree | 8abf5dea2761ef7fc9e089f041bcff9902875931 | |
| parent | 410cc8e8949c6ab0677daebbad10cea5f1b9686c (diff) | |
added iris websockets support
| -rw-r--r-- | arvo/iris.hoon | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/arvo/iris.hoon b/arvo/iris.hoon new file mode 100644 index 0000000..ea2a59f --- /dev/null +++ b/arvo/iris.hoon @@ -0,0 +1,414 @@ +!: +:: http-client +:: +|= our=ship +=, iris +:: +:: +:: internal data structures +:: +=> =~ +:: +:: internal data structures that won't go in zuse +:: +|% ++$ move + :: + $: :: duct: request identifier + :: + =duct + :: + :: + card=(wind note gift) + == +:: +note: private request from light to another vane +:: ++$ note + $% :: %d: to dill + :: + $: %d + :: + :: + $% [%flog =flog:dill] + == == == +-- +:: 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]) + :: connection-by-duct: used for cancellation + :: + connection-by-duct=(map duct @ud) + :: 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 + |= [[eny=@ =duct now=@da rof=roof] =state] + |% + :: +request: makes an external web request + :: + ++ request + |= [=request:http =outbound-config] + ^- [(list move) ^state] + :: if there's already a request on this duct, abort + :: + ?: (~(has by connection-by-duct.state) duct) + ~& %cant-send-second-http-client-request-on-same-duct + [~ 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 ~]] + :: keep track of the duct for cancellation + :: + =. connection-by-duct.state + (~(put by connection-by-duct.state) duct id) + :: 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 + :: +cancel: client cancels an outstanding request + :: + ++ cancel + ^- [(list move) ^state] + :: + ?~ cancel-id=(~(get by connection-by-duct.state) duct) + ~& %iris-invalid-cancel + [~ state] + :: + :- [outbound-duct.state %give %cancel-request u.cancel-id]~ + (cleanup-connection u.cancel-id) + :: +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 + :: we have received a cancel from outside; pass it on to our requester + :: + :_ (cleanup-connection id) + ^- (list move) + :_ ~ + :* duct.u.connection + %give + %http-response + %cancel + ~ + == + == + :: +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 + %http-response + %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 + :: + :_ (cleanup-connection id) + :~ :* duct.connection + %give + %http-response + %finished + response-header + ?:(=(0 p.data) ~ `[mime data]) + == == + :: + ++ cleanup-connection + |= id=@ud + ^- ^state + ?~ con=(~(get by connection-by-id.state) id) + state + %_ state + connection-by-id (~(del by connection-by-id.state) id) + connection-by-duct (~(del by connection-by-duct.state) duct.u.con) + == + -- +-- +:: 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 +:: +|= [now=@da eny=@uvJ rof=roof] +:: allow jets to be registered within this core +:: +~% %http-client ..part ~ +|% +++ call + |= [=duct dud=(unit goof) wrapped-task=(hobo task)] + ^- [(list move) _light-gate] + :: + =/ task=task ((harden task) wrapped-task) + :: + :: XX handle error notifications + :: + ?^ dud + =/ moves=(list move) + [[duct %slip %d %flog %crud [-.task tang.u.dud]] ~] + [moves light-gate] + :: %trim: in response to memory pressure + :: + ?: ?=(%trim -.task) + [~ light-gate] + :: %vega: notifies us of a completed kernel upgrade + :: + ?: ?=(%vega -.task) + [~ light-gate] + :: + =/ event-args [[eny duct now rof] state.ax] + =/ client (per-client-event event-args) + ?- -.task + :: + %born + :: create a cancel for each outstanding connection + :: + :: TODO: We should gracefully retry on restart instead of just sending a + :: cancel. + :: TODO we might not want to do that though! + :: + =/ moves=(list move) + %+ turn ~(tap by connection-by-duct.state.ax) + |= [=^duct @ud] + ^- move + [duct %give %http-response %cancel ~] + :: reset all connection state on born + :: + =: next-id.state.ax 0 + connection-by-id.state.ax ~ + connection-by-duct.state.ax ~ + outbound-duct.state.ax duct + == + :: + [moves light-gate] + :: + %request + =^ moves state.ax (request:client +.task) + [moves light-gate] + :: + %cancel-request + =^ moves state.ax cancel:client + [moves light-gate] + :: + %receive + =^ moves state.ax (receive:client +.task) + [moves light-gate] + :: UIP-125 + :: + %websocket-connect + `light-gate + %websocket-event + `light-gate + == +:: http-client issues no requests to other vanes +:: +++ take + |= [=wire =duct dud=(unit goof) sign=*] + ^- [(list move) _light-gate] + ?< ?=(^ dud) + !! +:: +++ 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 + ^- roon + |= [lyc=gang pov=path car=term bem=beam] + ^- (unit (unit cage)) + =* ren car + =* why=shop &/p.bem + =* syd q.bem + =* lot=coin $/r.bem + =* tyl s.bem + :: + ?. ?=(%& -.why) ~ + =* his p.why + ?: &(?=(%x ren) =(tyl //whey) =([~ ~] lyc)) + =/ maz=(list mass) + :~ nex+&+next-id.state.ax + outbound+&+outbound-duct.state.ax + by-id+&+connection-by-id.state.ax + by-duct+&+connection-by-duct.state.ax + axle+&+ax + == + ``mass+!>(maz) + [~ ~] +-- |
