From 5422715c9b17df9fdac30f412745c87085dc4199 Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Wed, 16 Aug 2023 10:47:20 +0200 Subject: [PATCH 01/29] gall: send %cork only if hearing %ack for %leave --- pkg/arvo/sys/vane/gall.hoon | 111 +++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 26 deletions(-) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index b4020feabf..399269d9bf 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -44,7 +44,7 @@ +$ move [=duct move=(wind note-arvo gift-arvo)] :: $state-13: overall gall state, versioned :: -+$ state-13 [%13 state] ++$ state-14 [%14 state] :: $state: overall gall state :: :: system-duct: TODO document @@ -53,6 +53,7 @@ :: yokes: running agents :: blocked: moves to agents that haven't been started yet :: bug: debug printing configuration +:: leaves: retry nacked %leaves timer, if set :: +$ state $: system-duct=duct @@ -61,6 +62,7 @@ yokes=(map term yoke) blocked=(map term (qeu blocked-move)) =bug + leaves=(unit [=duct =wire date=@da]) == :: $routes: new cuff; TODO: document :: @@ -162,13 +164,14 @@ :: $spore: structures for update, produced by +stay :: +$ spore - $: %13 + $: %14 system-duct=duct outstanding=(map [wire duct] (qeu remote-request)) contacts=(set ship) eggs=(map term egg) blocked=(map term (qeu blocked-move)) =bug + leaves=(unit [=duct =wire date=@da]) == :: $egg: migratory agent state; $yoke with .old-state instead of .agent :: @@ -192,7 +195,7 @@ -- :: adult gall vane interface, for type compatibility with pupa :: -=| state=state-13 +=| state=state-14 |= [now=@da eny=@uvJ rof=roof] =* gall-payload . ~% %gall-top ..part ~ @@ -220,7 +223,8 @@ ++ mo-core . ++ mo-abed |=(hun=duct mo-core(hen hun)) ++ mo-abet [(flop moves) gall-payload] - ++ mo-give |=(g=gift mo-core(moves [[hen give+g] moves])) + ++ mo-emit |=(=move mo-core(moves [move moves])) + ++ mo-give |=(=gift (mo-emit hen give+gift)) ++ mo-talk |= rup=(each suss tang) ^- [wire note-arvo] @@ -230,8 +234,8 @@ %& [%text "gall: {(t q)}ed %{(t p)}":[t=trip p.rup]] %| [%talk leaf+"gall: failed" (flop p.rup)] == - ++ mo-pass |=(p=[wire note-arvo] mo-core(moves [[hen pass+p] moves])) - ++ mo-slip |=(p=note-arvo mo-core(moves [[hen slip+p] moves])) + ++ mo-pass |=(p=[wire note-arvo] (mo-emit hen pass+p)) + ++ mo-slip |=(p=note-arvo (mo-emit hen slip+p)) ++ mo-past |= =(list [wire note-arvo]) ?~ list @@ -370,12 +374,8 @@ =. outstanding.state =/ stand (~(gut by outstanding.state) [wire hen] *(qeu remote-request)) - %+ ~(put by outstanding.state) [wire hen] - (~(gas to stand) ?.(?=(%leave -.deal) ~[-.deal] ~[%leave])) - =. mo-core (mo-pass wire note-arvo) - ?. ?=(%leave -.deal) - mo-core - (mo-pass wire [%a [%cork ship]]) + (~(put by outstanding.state) [wire hen] (~(put to stand) -.deal)) + (mo-pass wire note-arvo) :: +mo-track-ship: subscribe to ames and jael for notices about .ship :: ++ mo-track-ship @@ -529,6 +529,10 @@ :: ?+ sign-arvo !! [%ames %done *] + =/ err=(unit tang) + ?~ error=error.sign-arvo + ~ + `[[%leaf (trip tag.u.error)] tang.u.error] =^ remote-request outstanding.state ?~ t.t.t.wire =/ full-wire sys+wire @@ -545,29 +549,42 @@ =^ rr stand ~(get to stand) :- rr ?: =(~ stand) + :: outstanding leaves are only deleted when acked + :: + ?: &(?=(^ err) ?=(%leave rr)) + outstanding.state (~(del by outstanding.state) [full-wire hen]) (~(put by outstanding.state) [full-wire hen] stand) :: non-null case of wire is old, remove on next breach after :: 2019/12 :: [;;(remote-request i.t.t.t.wire) outstanding.state] - :: - =/ err=(unit tang) - ?~ error=error.sign-arvo - ~ - `[[%leaf (trip tag.u.error)] tang.u.error] - :: send a %cork if we get a nack upon initial subscription + :: send a %cork if we get a %nack upon initial subscription :: =? mo-core &(?=(^ err) |(?=(%watch-as remote-request) ?=(%watch remote-request))) - (mo-pass [%sys wire] %a %cork ship) + (mo-pass sys+wire %a %cork ship) :: ?- remote-request %watch-as (mo-give %unto %watch-ack err) %watch (mo-give %unto %watch-ack err) %poke (mo-give %unto %poke-ack err) - %leave mo-core %missing ~>(%slog.[3 'gall: missing'] mo-core) + :: + %leave + :: if we get an %ack for a %leave, send %cork. otherwise, + :: the /nacked-leaves timer will re-send the %leave eventually. + :: + ?~ err + (mo-pass sys+wire %a %cork ship) + :: if first time hearing a %nack for a %leave, after upgrade, + :: set up timer + :: + =? mo-core ?=(~ leaves.state) + (mo-emit [/gall]~ %pass /nacked-leaves %b %wait `@da`(add now ~m2)) + =? leaves.state ?=(~ leaves.state) + `[[/gall]~ /nacked-leaves `@da`(add now ~m2)] + mo-core == :: [%ames %boon *] @@ -895,6 +912,13 @@ ^+ mo-core =. dudes.bug.state (sy dudes) mo-core + :: + ++ mo-handle-nacked-leaves + |= =wire + ^+ mo-core + ?> ?=([%sys %way @ @ ~] wire) + (mo-pass wire %a %plea (slav %p &3.wire) %g /ge/[&4.wire] %0 %u ~) + :: :: +ap: agent engine :: :: An inner, agent-level core. The sample refers to the agent we're @@ -1915,16 +1939,27 @@ :: ++ load |^ |= old=spore-any - =? old ?=(%7 -.old) (spore-7-to-8 old) - =? old ?=(%8 -.old) (spore-8-to-9 old) - =? old ?=(%9 -.old) (spore-9-to-10 old) + =? old ?=(%7 -.old) (spore-7-to-8 old) + =? old ?=(%8 -.old) (spore-8-to-9 old) + =? old ?=(%9 -.old) (spore-9-to-10 old) =? old ?=(%10 -.old) (spore-10-to-11 old) =? old ?=(%11 -.old) (spore-11-to-12 old) =? old ?=(%12 -.old) (spore-12-to-13 old) - ?> ?=(%13 -.old) + =? old ?=(%13 -.old) (spore-13-to-14 old) + ?> ?=(%14 -.old) gall-payload(state old) :: - +$ spore-any $%(spore spore-7 spore-8 spore-9 spore-10 spore-11 spore-12) + +$ spore-any + $%(spore spore-7 spore-8 spore-9 spore-10 spore-11 spore-12 spore-13) + +$ spore-13 + $: %13 + system-duct=duct + outstanding=(map [wire duct] (qeu remote-request)) + contacts=(set ship) + eggs=(map term egg) + blocked=(map term (qeu blocked-move)) + =bug + == +$ spore-12 $: %12 system-duct=duct @@ -2115,7 +2150,7 @@ :: ++ spore-12-to-13 |= old=spore-12 - ^- spore + ^- spore-13 %= old - %13 eggs @@ -2125,6 +2160,13 @@ ?: ?=(%nuke -.e) e e(sky [sky.e ken:*$>(%live egg)]) == + :: + :: added nacked leaves timer + :: + ++ spore-13-to-14 + |= old=spore-13 + ^- spore + old(- %14, bug [bug.old ~]) -- :: +scry: standard scry :: @@ -2337,6 +2379,23 @@ =/ =gift ?>(?=([%behn %heck %gall *] syn) +>+.syn) [[duct %give gift]~ gall-payload] :: + ?: ?=([%nacked-leaves ~] wire) + =; core=_(mo-abed:mo ~) + =< mo-abet + (mo-emit:core [/gall]~ %pass /nacked-leaves %b %wait `@da`(add now ~m2)) + %- ~(rep by outstanding.state) + |= [[[=^wire =^duct] stand=(qeu remote-request)] core=_(mo-abed:mo ~)] + ?: =(~ stand) core + =^ rr stand ~(get to stand) + :: sanity check in the outstanding queue: + :: if there's a %leave, that should be the only request + :: + ~? >>> &(?=(%leave rr) =(^ stand)) + "outstanding queue not empty [{} {} {}]" + =? core &(?=(%leave rr) =(~ stand)) + (mo-handle-nacked-leaves:(mo-abed:core duct) wire) + core + :: ~| [%gall-take-failed wire] ?> ?=([?(%sys %use) *] wire) =< mo-abet From cf937374c82fac6d74002a42d1a48096b678926a Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Wed, 16 Aug 2023 14:20:34 +0200 Subject: [PATCH 02/29] gall: put +on-bad-nonce log under %odd flag The change introduced in 5422715c9b17df9fdac30f412745c87085dc4199 makes it very frequent for subscribers to get stale facts on the subscriptions they are trying to leave so to not clutter their dojo, we put the log under the %odd flag --- pkg/arvo/sys/vane/gall.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index 399269d9bf..62a33f876b 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -1543,7 +1543,7 @@ ++ on-bad-nonce |= stored-nonce=@ %. ap-core - %- slog :~ + %+ trace odd.veb.bug.state :~ =/ nonces [expected=stored-nonce got=nonce] =/ ok |(?=(?(%fact %kick) -.sign) =(~ p.sign)) leaf+"{}: stale {<-.sign>} {} ok={}" From 9b1ea79991e40f8aabc1f7a38286430f6c539a99 Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Thu, 17 Aug 2023 15:05:53 +0200 Subject: [PATCH 03/29] ames: reinitialize congestion control values --- pkg/arvo/sys/vane/ames.hoon | 47 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index 6e71d1caad..95cda5b422 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -579,7 +579,7 @@ crypto-core=acru:ames =bug snub=[form=?(%allow %deny) ships=(set ship)] - cong=[msg=@ud mem=@ud] + cong=[msg=_5 mem=_100.000] == :: +$ azimuth-state [=symmetric-key =life =rift =public-key sponsor=ship] @@ -1084,6 +1084,7 @@ snub=[form=?(%allow %deny) ships=(set ship)] cong=[msg=@ud mem=@ud] == ++$ ames-state-15 ames-state-14 :: $bug: debug printing configuration :: :: veb: verbosity toggles @@ -1224,7 +1225,8 @@ [%12 ames-state-12] [%13 ames-state-13] [%14 ames-state-14] - [%15 ^ames-state] + [%15 ames-state-15] + [%16 ^ames-state] == :: |= [now=@da eny=@ rof=roof] @@ -1347,7 +1349,7 @@ :: lifecycle arms; mostly pass-throughs to the contained adult ames :: ++ scry scry:adult-core - ++ stay [%15 %larva queued-events ames-state.adult-gate] + ++ stay [%16 %larva queued-events ames-state.adult-gate] ++ load |= $= old $% $: %4 @@ -1428,6 +1430,13 @@ [%adult state=ames-state-14] == == $: %15 + $% $: %larva + events=(qeu queued-event) + state=ames-state-15 + == + [%adult state=ames-state-15] + == == + $: %16 $% $: %larva events=(qeu queued-event) state=_ames-state.adult-gate @@ -1552,12 +1561,23 @@ =. queued-events events.old larval-gate :: - [%15 %adult *] (load:adult-core %15 state.old) + [%15 %adult *] + =. cached-state `[%15 state.old] + ~> %slog.0^leaf/"ames: larva reload" + larval-gate :: [%15 %larva *] ~> %slog.1^leaf/"ames: larva: load" + =. cached-state `[%15 state.old] =. queued-events events.old - =. adult-gate (load:adult-core %15 state.old) + larval-gate + :: + [%16 %adult *] (load:adult-core %16 state.old) + :: + [%16 %larva *] + ~> %slog.1^leaf/"ames: larva: load" + =. queued-events events.old + =. adult-gate (load:adult-core %16 state.old) larval-gate == :: @@ -1604,7 +1624,9 @@ 14+(state-13-to-14:load:adult-core +.u.cached-state) =? u.cached-state ?=(%14 -.u.cached-state) 15+(state-14-to-15:load:adult-core +.u.cached-state) - ?> ?=(%15 -.u.cached-state) + =? u.cached-state ?=(%15 -.u.cached-state) + 16+(state-15-to-16:load:adult-core +.u.cached-state) + ?> ?=(%16 -.u.cached-state) =. ames-state.adult-gate +.u.cached-state [moz larval-core(cached-state ~)] -- @@ -4708,15 +4730,15 @@ [moves ames-gate] :: +stay: extract state before reload :: -++ stay [%15 %adult ames-state] +++ stay [%16 %adult ames-state] :: +load: load in old state after reload :: ++ load =< |= $= old-state - $% [%15 ^ames-state] + $% [%16 ^ames-state] == ^+ ames-gate - ?> ?=(%15 -.old-state) + ?> ?=(%16 -.old-state) ames-gate(ames-state +.old-state) :: all state transitions are called from larval ames :: @@ -4877,11 +4899,16 @@ :: ++ state-14-to-15 |= old=ames-state-14 - ^- ^ames-state + ^- ames-state-15 =. rift.old !< =rift q:(need (need (rof ~ %j `beam`[[our %rift %da now] /(scot %p our)]))) old + :: + ++ state-15-to-16 + |= old=ames-state-15 + ^- ^ames-state + old(cong [5 100.000]) -- :: +scry: dereference namespace :: From ce2582cc3afce60b9d1be07c85d38b0ea2633704 Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Thu, 17 Aug 2023 15:13:10 +0200 Subject: [PATCH 04/29] ames: only reinitialize congestion if bunted --- pkg/arvo/sys/vane/ames.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index 95cda5b422..8250494340 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -4908,7 +4908,7 @@ ++ state-15-to-16 |= old=ames-state-15 ^- ^ames-state - old(cong [5 100.000]) + old(cong ?.(=(cong.old [0 0]) cong.old [5 100.000])) -- :: +scry: dereference namespace :: From 7242e56be5b47a7519978d196733e0ece26848a7 Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Fri, 18 Aug 2023 13:57:16 +0200 Subject: [PATCH 05/29] gall: clear leaves.state after handling all leaves Instead of setting a new timer every time, we wait to hear a new %nack for a %leave to do so, accumulating any unacked %leaves in the state for up to ~m2. When the timers comes back, we re-send all outsanting %leaves and skipp setting up a new timer. --- pkg/arvo/sys/vane/gall.hoon | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index 62a33f876b..e6f8d01448 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -577,8 +577,8 @@ :: ?~ err (mo-pass sys+wire %a %cork ship) - :: if first time hearing a %nack for a %leave, after upgrade, - :: set up timer + :: if first time hearing a %nack for a %leave, after upgrade + :: or if all outstanding %leaves have been handled, set up timer :: =? mo-core ?=(~ leaves.state) (mo-emit [/gall]~ %pass /nacked-leaves %b %wait `@da`(add now ~m2)) @@ -2380,9 +2380,10 @@ [[duct %give gift]~ gall-payload] :: ?: ?=([%nacked-leaves ~] wire) - =; core=_(mo-abed:mo ~) - =< mo-abet - (mo-emit:core [/gall]~ %pass /nacked-leaves %b %wait `@da`(add now ~m2)) + :: next time a %leave gets nacked, the state and timer will be reset again. + :: + =. leaves.state ~ + =< mo-abet %- ~(rep by outstanding.state) |= [[[=^wire =^duct] stand=(qeu remote-request)] core=_(mo-abed:mo ~)] ?: =(~ stand) core From e6bc865425666591660a70f4fbe73b67a0e91d5b Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Fri, 18 Aug 2023 16:52:54 +0200 Subject: [PATCH 06/29] gall: fix error --- pkg/arvo/sys/vane/gall.hoon | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index e6f8d01448..301b1d789b 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -2380,14 +2380,14 @@ [[duct %give gift]~ gall-payload] :: ?: ?=([%nacked-leaves ~] wire) - :: next time a %leave gets nacked, the state and timer will be reset again. - :: - =. leaves.state ~ - =< mo-abet + =; core=_mo-core:mo + :: next time a %leave gets nacked, the state and timer will be set again. + :: + mo-abet:core(leaves.state ~) %- ~(rep by outstanding.state) - |= [[[=^wire =^duct] stand=(qeu remote-request)] core=_(mo-abed:mo ~)] + |= [[[=^wire =^duct] stand=(qeu remote-request)] core=_mo-core:mo] ?: =(~ stand) core - =^ rr stand ~(get to stand) + =^ rr stand ~(get to stand) :: sanity check in the outstanding queue: :: if there's a %leave, that should be the only request :: From 6a6e07d49f5d32fa9e55d4a16c8cb221396740f4 Mon Sep 17 00:00:00 2001 From: pkova Date: Mon, 4 Sep 2023 22:09:52 +0300 Subject: [PATCH 07/29] lull, ames: add %dear task to receive lane from unix --- pkg/arvo/sys/lull.hoon | 2 ++ pkg/arvo/sys/vane/ames.hoon | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pkg/arvo/sys/lull.hoon b/pkg/arvo/sys/lull.hoon index 158d432f97..4fbefc172e 100644 --- a/pkg/arvo/sys/lull.hoon +++ b/pkg/arvo/sys/lull.hoon @@ -764,6 +764,7 @@ :: Messaging Tasks :: :: %hear: packet from unix + :: %dear: lane from unix :: %heed: track peer's responsiveness; gives %clog if slow :: %jilt: stop tracking peer's responsiveness :: %cork: request to delete message flow @@ -794,6 +795,7 @@ +$ task $+ ames-task $% [%hear =lane =blob] + [%dear =ship =lane] [%heed =ship] [%jilt =ship] [%cork =ship] diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index aa80b20211..e8ca3fa9bd 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -2092,6 +2092,27 @@ %^ enqueue-alien-todo ship ship-state |= todos=alien-agenda todos(heeds (~(del in heeds.todos) duct)) + :: +on-dear: handle lane from unix + :: + ++ on-dear + |= [=ship =lane] + ^+ event-core + ?: ?=(%.y -.lane) + event-core + =/ ip=@if (end [0 32] p.lane) + =/ pt=@ud (cut 0 [32 16] p.lane) + ?: =(%czar (clan:title ship)) + %- %^ ev-trace odd.veb ship + |.("ignoring %dear lane {(scow %if ip)}:{(scow %ud pt)} for galaxy") + event-core + =/ peer-state=(unit peer-state) (get-peer-state ship) + ?~ peer-state + %- %^ ev-trace odd.veb ship + |.("no peer-state for ship, ignoring %dear") + event-core + %- %^ ev-trace rcv.veb ship + |.("incoming %dear lane {(scow %if ip)}:{(scow %ud pt)}") + abet:(on-dear:(abed-peer:pe ship u.peer-state) lane) :: +on-hear: handle raw packet receipt :: ++ on-hear @@ -3273,6 +3294,11 @@ =. keens (~(put by keens) path *keen-state) fi-abet:(fi-start:(abed:fi path) duct) :: + ++ on-dear + |= =lane + ^+ peer-core + peer-core(route.peer-state `[%.y lane]) + :: ++ on-tame ^+ peer-core peer-core(route.peer-state ~) @@ -4934,6 +4960,7 @@ ?- -.task %born on-born:event-core %hear (on-hear:event-core [lane blob ~]:task) + %dear (on-dear:event-core +.task) %heed (on-heed:event-core ship.task) %init on-init:event-core %jilt (on-jilt:event-core ship.task) From 328e085e416a04a279de9818b96192c0ba28c701 Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Tue, 5 Sep 2023 16:17:06 +0300 Subject: [PATCH 08/29] lick: initialize unix-duct before %born --- pkg/arvo/sys/vane/lick.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/sys/vane/lick.hoon b/pkg/arvo/sys/vane/lick.hoon index ac57d77afd..2b43da200a 100644 --- a/pkg/arvo/sys/vane/lick.hoon +++ b/pkg/arvo/sys/vane/lick.hoon @@ -11,7 +11,7 @@ :: +$ lick-state $: %0 - unix-duct=duct + unix-duct=_`duct`[//lick ~] owners=(map name duct) == :: From dabff19cd930bfbcaa873a4aea5ca9b93dbc557d Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Wed, 6 Sep 2023 17:34:34 -0400 Subject: [PATCH 09/29] gall: add scry endpoint for backups --- pkg/arvo/sys/vane/gall.hoon | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index 5ec8d6cfdc..b433f2b8a1 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -2290,6 +2290,24 @@ [~ ~] [~ ~ atom+!>(u.nonce)] :: + ?: ?& =(%v care) + =([%$ %da now] coin) + =(our ship) + == + =/ yok (~(get by yokes.state) dap) + ?. ?=([~ %live *] yok) + [~ ~] + =/ =egg + %= u.yok + code ~ + agent + :- %| + ?: ?=(%| -.agent.u.yok) + p.agent.u.yok + on-save:p.agent.u.yok + == + ``noun+!>([-:*spore egg]) + :: ?: ?& =(%w care) =([%$ %da now] coin) =(our ship) From babfd75daf866841eff709fc733138519e6f8bda Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Wed, 13 Sep 2023 14:57:28 +0300 Subject: [PATCH 10/29] roller-rpc: do not assert team:title for http-requests --- pkg/arvo/app/roller-rpc.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/roller-rpc.hoon b/pkg/arvo/app/roller-rpc.hoon index 3b49d0eb3c..8308494a13 100644 --- a/pkg/arvo/app/roller-rpc.hoon +++ b/pkg/arvo/app/roller-rpc.hoon @@ -45,7 +45,6 @@ |= [=mark =vase] ^- (quip card _this) |^ - ?> (team:title our.bowl src.bowl) ?+ mark (on-poke:def mark vase) %handle-http-request =+ !<([id=@ta req=inbound-request:eyre] vase) @@ -53,6 +52,7 @@ (handle-http-request id req) :: %azimuth-action + ?> (team:title our.bowl src.bowl) =+ !<([%disconnect bind=binding:eyre] vase) ~& >>> "disconnecting at {}" :_ this From bf4d7c92e12291644dbdc4f75d12aa405d8780a6 Mon Sep 17 00:00:00 2001 From: pkova Date: Thu, 14 Sep 2023 18:17:29 +0300 Subject: [PATCH 11/29] ames: make dead flow consolidation toggleable, default off --- pkg/arvo/sys/lull.hoon | 2 +- pkg/arvo/sys/vane/ames.hoon | 78 +++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/pkg/arvo/sys/lull.hoon b/pkg/arvo/sys/lull.hoon index 158d432f97..a6b96277fc 100644 --- a/pkg/arvo/sys/lull.hoon +++ b/pkg/arvo/sys/lull.hoon @@ -787,7 +787,7 @@ :: %snub: set packet blocklist to .ships :: %spew: set verbosity toggles :: %cong: adjust congestion control parameters - :: %stir: recover from timer desync + :: %stir: recover from timer desync and assorted debug commands :: %trim: release memory :: %vega: kernel reload notification :: diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index aa80b20211..de5857f3f8 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -556,7 +556,7 @@ :: bug: debug printing configuration :: snub: blocklist for incoming packets :: cong: parameters for marking a flow as clogged -:: dead: dead flow consolidation timer, if set +:: dead: dead flow consolidation timer and recork timer, if set :: +$ ames-state $+ ames-state @@ -1786,8 +1786,6 @@ =^ moz u.cached-state ?. ?=(%16 -.u.cached-state) [~ u.cached-state] :_ 17+(state-16-to-17:load:adult-core +.u.cached-state) - ~> %slog.0^leaf/"ames: init dead flow consolidation timer" - :- [[/ames]~ %pass /dead-flow %b %wait `@da`(add now ~m2)] ?^ moz moz :: if we have just added the timer in state-7-to-8, skip =; recork-timer=(list [@da duct]) ?^ recork-timer ~ @@ -2009,15 +2007,26 @@ event-core :: +on-stir: recover from timer desync, setting new timers as needed :: - :: .arg is unused, meant to ease future debug commands + :: .arg can be %rift or %dead :: ++ on-stir |= arg=@t ^+ event-core |^ ?+ arg do-stir %rift do-rift + %dead do-dead == :: + ++ do-dead + =/ ded=(unit dead-timer) +.flow.dead.ames-state + ?~ ded + %- (slog leaf+"ames: switching to dead flow consolidation" ~) + (wake-dead-flows %.y ~) + %- (slog leaf+"ames: turning off dead flow consolidation" ~) + =. event-core + (emit:event-core duct.u.ded %pass wire.u.ded %b %rest date.u.ded) + (wake-dead-flows %.n ~) + :: ++ do-rift =/ =rift =- ~|(%no-rift (,@ q.q:(need (need -)))) @@ -2433,6 +2442,32 @@ ++ cork-bone |=(=bone abet:(on-cork-flow:peer-core bone)) ++ kill-bone |=(=bone abet:(on-kill-flow:peer-core bone)) -- + :: +wake-dead-flow: call on-wake on all dead flows, optionally setting new + :: dead flow timer + :: + ++ wake-dead-flows + |= [set-new-timer=? error=(unit tang)] + ^+ event-core + =. flow.dead.ames-state.event-core + ?. set-new-timer + flow/~ + flow/`[~[/ames] /dead-flow `@da`(add now ~m2)] + =. event-core + ?. set-new-timer + event-core + (emit:event-core ~[/ames] %pass /dead-flow %b %wait `@da`(add now ~m2)) + %- ~(rep by peers.ames-state) + |= [[=ship =ship-state] core=_event-core] + ^+ event-core + =/ peer-state=(unit peer-state) (get-peer-state:core ship) + ?~ peer-state core + %- ~(rep by snd.u.peer-state) + |= [[=bone =message-pump-state] cor=_core] + ?. ?& =(~m2 rto.metrics.packet-pump-state.message-pump-state) + ?=(^ next-wake.packet-pump-state.message-pump-state) + == + cor + abet:(on-wake:(abed-peer:pe:cor ship u.peer-state) bone error) :: +on-take-wake: receive wakeup or error notification from behn :: ++ on-take-wake @@ -2453,22 +2488,7 @@ (request-attestation u.ship) :: ?: ?=([%dead-flow ~] wire) - =; cor=event-core - =. flow.dead.ames-state.cor - flow/`[~[/ames] /dead-flow `@da`(add now ~m2)] - (emit:cor duct %pass /dead-flow %b %wait `@da`(add now ~m2)) - %- ~(rep by peers.ames-state) - |= [[=ship =ship-state] core=_event-core] - ^+ event-core - =/ peer-state=(unit peer-state) (get-peer-state:core ship) - ?~ peer-state core - %- ~(rep by snd.u.peer-state) - |= [[=bone =message-pump-state] cor=_core] - ?. ?& =(~m2 rto.metrics.packet-pump-state.message-pump-state) - ?=(^ next-wake.packet-pump-state.message-pump-state) - == - cor - abet:(on-wake:(abed-peer:pe:cor ship u.peer-state) bone error) + (wake-dead-flows %.y error) :: ?. ?=([%recork ~] wire) =/ res=(unit ?([%fine her=ship =^wire] [%pump her=ship =bone])) @@ -2837,17 +2857,15 @@ :: =* duct unix-duct.ames-state :: - =^ dead-moves dead.ames-state - ?. ?=([~ ~] [+.flow +.cork]:dead.ames-state) - `dead.ames-state - :- :- [~[/ames] %pass /dead-flow %b %wait `@da`(add now ~m2)] - [~[/ames] %pass /recork %b %wait `@da`(add now ~d1)]~ - :- flow/`[~[/ames] /dead-flow `@da`(add now ~m2)] - cork/`[~[/ames] /recork `@da`(add now ~d1)] + =^ cork-moves cork.dead.ames-state + ?. ?=(~ +.cork.dead.ames-state) + `cork.dead.ames-state + :- [~[/ames] %pass /recork %b %wait `@da`(add now ~d1)]~ + cork/`[~[/ames] /recork `@da`(add now ~d1)] :: %- emil %+ weld - dead-moves + cork-moves ^- (list move) :~ [duct %give %turf turfs] [duct %pass /ping %g %deal [our our /ames] %ping %poke %noun !>(%kick)] @@ -4071,7 +4089,7 @@ :: and we don't want to consolidate that :: =? peer-core ?=(^ new-wake) - ?: =(~m2 rto.metrics.state) + ?: ?&(?=(^ +.flow.dead.ames-state) =(~m2 rto.metrics.state)) peer-core (pu-emit %b %wait u.new-wake) :: @@ -5173,7 +5191,7 @@ %= old cong :+ cong.old - flow/`[~[/ames] /dead-flow `@da`(add now ~m2)] + flow/~ cork/`[~[/ames] /recork `@da`(add now ~d1)] :: peers From 58698a428c7d5ef5a9e068d0fedd2f2cc833bdf6 Mon Sep 17 00:00:00 2001 From: pkova Date: Thu, 14 Sep 2023 22:48:58 +0300 Subject: [PATCH 12/29] ames: cancel pump timers when toggling to dead flow consolidation --- pkg/arvo/sys/vane/ames.hoon | 41 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index de5857f3f8..d25f5fddc7 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -2019,13 +2019,31 @@ :: ++ do-dead =/ ded=(unit dead-timer) +.flow.dead.ames-state - ?~ ded - %- (slog leaf+"ames: switching to dead flow consolidation" ~) - (wake-dead-flows %.y ~) - %- (slog leaf+"ames: turning off dead flow consolidation" ~) - =. event-core - (emit:event-core duct.u.ded %pass wire.u.ded %b %rest date.u.ded) - (wake-dead-flows %.n ~) + ?^ ded + %- (slog leaf+"ames: turning off dead flow consolidation" ~) + =. event-core + (emit:event-core duct.u.ded %pass wire.u.ded %b %rest date.u.ded) + (wake-dead-flows:event-core %.n ~) + :: + %- (slog leaf+"ames: switching to dead flow consolidation" ~) + =; cor=event-core + (wake-dead-flows:cor %.y ~) + %- ~(rep by peers.ames-state:event-core) + |= [[=ship =ship-state] core=_event-core] + ^+ event-core + =/ peer-state=(unit peer-state) (get-peer-state:core ship) + ?~ peer-state core + %- ~(rep by snd.u.peer-state) + |= [[=bone =message-pump-state] cor=_core] + ^+ event-core + =/ next-wake next-wake.packet-pump-state.message-pump-state + ?. ?& =(~m2 rto.metrics.packet-pump-state.message-pump-state) + ?=(^ next-wake) + == + cor + =/ peer-core (abed-peer:pe:cor ship u.peer-state) + =/ message-pump (abed:mu:peer-core bone) + abet:(pu-emit:packet-pump:message-pump %b %rest u.next-wake) :: ++ do-rift =/ =rift @@ -2456,18 +2474,21 @@ ?. set-new-timer event-core (emit:event-core ~[/ames] %pass /dead-flow %b %wait `@da`(add now ~m2)) - %- ~(rep by peers.ames-state) + %- ~(rep by peers.ames-state:event-core) |= [[=ship =ship-state] core=_event-core] ^+ event-core =/ peer-state=(unit peer-state) (get-peer-state:core ship) ?~ peer-state core + =/ peer-core (abed-peer:pe:core ship u.peer-state) + =< abet + ^+ peer-core %- ~(rep by snd.u.peer-state) - |= [[=bone =message-pump-state] cor=_core] + |= [[=bone =message-pump-state] cor=_peer-core] ?. ?& =(~m2 rto.metrics.packet-pump-state.message-pump-state) ?=(^ next-wake.packet-pump-state.message-pump-state) == cor - abet:(on-wake:(abed-peer:pe:cor ship u.peer-state) bone error) + (on-wake:cor bone error) :: +on-take-wake: receive wakeup or error notification from behn :: ++ on-take-wake From 9929fcc6fb2e6ed7e636691839a99dcc784728a3 Mon Sep 17 00:00:00 2001 From: pkova Date: Thu, 14 Sep 2023 23:13:24 +0300 Subject: [PATCH 13/29] tests: revert test now that dead flow consolidation is default off --- tests/sys/grq.hoon | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/sys/grq.hoon b/tests/sys/grq.hoon index 605cf66eac..fc03ddc1eb 100644 --- a/tests/sys/grq.hoon +++ b/tests/sys/grq.hoon @@ -306,6 +306,7 @@ 0xb.130c.ab37.ca24.49cd.aecb.23ba.70f1.6f1c.4d00.124e.c9a5. 3413.3843.d81c.47c4.7040.6e62.3700.0200.0132.e1ab.9000 == + :- ~[/ames] [%pass /pump/~bud/0 %b %wait ~1111.1.5..00.02.00] == == :: publisher ames hears %kick ack From b427c9e800869c324614a6c311b9b60532a1de8d Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 1 Aug 2023 00:20:13 +0200 Subject: [PATCH 14/29] eyre: serve 503 if bound agent is not running Previously, for endpoints bound to agents, we would pass the request onto the agent even if the agents wasn't currently running. Here, we make eyre check to see if the agent is actually running, before passing the request on. If the bound agent is not running, eyre serves a 503 synchronously instead. This way, we avoid cluttering up the gall queue for the bound agent. --- pkg/arvo/sys/vane/eyre.hoon | 25 ++++++++++++++++++------- tests/sys/vane/eyre.hoon | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index a9af7d9f07..454d956cba 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -789,8 +789,7 @@ %. (~(put by connections.state) duct connection) (trace 2 |.("{} creating local")) :: - :_ state - (subscribe-to-app [%ours ~] app.act inbound-request.connection) + (request-to-app [%ours ~] app.act inbound-request.connection) :: +request: starts handling an inbound http request :: ++ request @@ -964,8 +963,7 @@ == :: %app - :_ state - (subscribe-to-app identity app.action inbound-request.connection) + (request-to-app identity app.action inbound-request.connection) :: %authentication (handle-request:authentication secure host address [suv identity] request) @@ -1100,11 +1098,24 @@ %^ return-static-data-on-duct status 'text/html' (error-page status authenticated url.request tape) -- - :: +subscribe-to-app: subscribe to app and poke it with request data + :: +request-to-app: subscribe to app and poke it with request data :: - ++ subscribe-to-app + ++ request-to-app |= [=identity app=term =inbound-request:eyre] - ^- (list move) + ^- (quip move server-state) + :: if the agent isn't running, we synchronously serve a 503 + :: + ?. !<(? q:(need (need (rof ~ /eyre %gu [our app da+now] /$)))) + %^ return-static-data-on-duct 503 'text/html' + %: error-page + 503 + ?=(%ours -.identity) + url.request.inbound-request + "%{(trip app)} not running" + == + :: otherwise, subscribe to the agent and poke it with the request + :: + :_ state :~ %+ deal-as /watch-response/[eyre-id] [identity our app %watch /http-response/[eyre-id]] diff --git a/tests/sys/vane/eyre.hoon b/tests/sys/vane/eyre.hoon index 3599e27ef6..ab029cac77 100644 --- a/tests/sys/vane/eyre.hoon +++ b/tests/sys/vane/eyre.hoon @@ -375,6 +375,7 @@ |= [gang pov=path =view =beam] ^- (unit (unit cage)) ?: =(%gd view) ``noun+!>(%base) + ?: =(%gu view) ``noun+!>(=(%app1 q.beam)) ?: &(=(%ca view) =(/gen/handler/hoon s.beam)) :+ ~ ~ vase+!>(!>(|=(* |=(* [[%404 ~] ~])))) @@ -553,6 +554,21 @@ (take /watch-response/[eyre-id] ~[/http-blah] sign) =/ headers ['content-type' 'text/html']~ (expect-moves mos (ex-continue-response `[3 'ya!'] %.n) ~) +:: +++ test-dead-app-request + %- eval-mare + =/ m (mare ,~) + ;< ~ bind:m perform-init-wo-timer + ;< ~ bind:m (wait ~d1) + :: dead-app binds successfully + :: + ;< ~ bind:m (connect %dead-app /) + ;< ~ bind:m (wait ~d1) + :: outside requests a path that dead-app has bound to + :: + ;< mos=(list move) bind:m (get '/' ~) + =/ body `(error-page:eyre-gate 503 %.n '/' "%dead-app not running") + (expect-moves mos (ex-response 503 ['content-type' 'text/html']~ body) ~) :: tests an app redirecting to the login handler, which then receives a post :: and redirects back to app :: From 4affae8181092cb718d3e9e63805e87e710a6638 Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 13 Sep 2023 13:37:39 +0200 Subject: [PATCH 15/29] eyre: GETting non-existent channels creates them Previously, a channel could only be created by sending a PUT request, and a GET request to receive the channel's stream would only succeed after channel creation had happened that way. This forces client libraries, that generally have an explicit "set up" step before allowing normal operation, to do strange things, like sending faux pokes (commonly hi-ing oneself) before connecting to the channel's stream as normal. Here, we update the GET request handling for channels to allow requests for non-existent channels. When this happens, the channel will be created, and eyre tracks the request as normal. We do some... gentle restructuring... of +on-get-request:by-channel to let the new creation case share code with the "already exists" codepath. In the process, we find that duct-to-key was never getting updated in the case where we replace the original channel request/connection with the new incoming one. We fix this, it's trivial. We also identify two other areas with vaguely-incorrect behavior, but consider them less important and out of scope. We also add a test case for "create channel through GET". --- pkg/arvo/sys/vane/eyre.hoon | 120 +++++++++++++++++++++--------------- tests/sys/vane/eyre.hoon | 26 ++++++-- 2 files changed, 92 insertions(+), 54 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index 454d956cba..09d04a30f8 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -2122,7 +2122,7 @@ duct-to-key.channel-state (~(del by duct-to-key.channel-state.state) duct) == - :: +set-timeout-timer-for: sets a timeout timer on a channel + :: +update-timeout-timer-for: sets a timeout timer on a channel :: :: This creates a channel if it doesn't exist, cancels existing timers :: if they're already set (we cannot have duplicate timers), and (if @@ -2196,53 +2196,76 @@ ++ on-get-request |= [channel-id=@t [session-id=@uv =identity] =request:http] ^- [(list move) server-state] - :: if there's no channel-id, we must 404 - ::TODO but arm description says otherwise? - :: - ?~ maybe-channel=(~(get by session.channel-state.state) channel-id) - %^ return-static-data-on-duct 404 'text/html' - (error-page 404 | url.request ~) - :: find the channel creator's identity, make sure it matches - :: - ?. =(identity identity.u.maybe-channel) - %^ return-static-data-on-duct 403 'text/html' - (error-page 403 | url.request ~) - :: find the requested "mode" and make sure it doesn't conflict - :: =/ mode=?(%json %jam) (find-channel-mode %'GET' header-list.request) - ?. =(mode mode.u.maybe-channel) - %^ return-static-data-on-duct 406 'text/html' - =; msg=tape (error-page 406 %.y url.request msg) - "channel already established in {(trip mode.u.maybe-channel)} mode" - :: when opening an event-stream, we must cancel our timeout timer - :: if there's no duct already bound. Else, kill the old request - :: and replace it - :: - =^ cancel-moves state - ?. ?=([%| *] state.u.maybe-channel) - :_ state - (cancel-timeout-move channel-id p.state.u.maybe-channel)^~ - =/ cancel-heartbeat - ?~ heartbeat.u.maybe-channel ~ - :_ ~ - %+ cancel-heartbeat-move channel-id - [date duct]:u.heartbeat.u.maybe-channel - =- [(weld cancel-heartbeat -<) ->] - (handle-response(duct p.state.u.maybe-channel) [%cancel ~]) - :: the request may include a 'Last-Event-Id' header - :: - =/ maybe-last-event-id=(unit @ud) - ?~ maybe-raw-header=(get-header:http 'last-event-id' header-list.request) - ~ - (rush u.maybe-raw-header dum:ag) - :: flush events older than the passed in 'Last-Event-ID' - :: - =? state ?=(^ maybe-last-event-id) - (acknowledge-events channel-id u.maybe-last-event-id) - :: combine the remaining queued events to send to the client - :: - =/ event-replay=wall + =^ [exit=? =wall moves=(list move)] state + :: the request may include a 'Last-Event-Id' header + :: + =/ maybe-last-event-id=(unit @ud) + ?~ maybe-raw-header=(get-header:http 'last-event-id' header-list.request) + ~ + (rush u.maybe-raw-header dum:ag) + :: if the channel doesn't exist yet, simply instantiate it here + :: + ?~ maybe-channel=(~(get by session.channel-state.state) channel-id) + =- [[| ~ ~] state(session.channel-state -)] + %+ ~(put by session.channel-state.state) channel-id + ::NOTE some other fields initialized at the end of this arm + %* . *channel + identity identity + next-id (fall maybe-last-event-id 0) + last-ack now + == + :: if the channel does exist, we put some demands on the get request, + :: and may need to do some cleanup for prior requests. + :: + :: find the channel creator's identity, make sure it matches + :: + ?. =(identity identity.u.maybe-channel) + =^ mos state + %^ return-static-data-on-duct 403 'text/html' + (error-page 403 | url.request ~) + [[& ~ mos] state] + :: make sure the request "mode" doesn't conflict with a prior request + :: + ::TODO or could we change that on the spot, given that only a single + :: request will ever be listening to this channel? + ?. =(mode mode.u.maybe-channel) + =^ mos state + %^ return-static-data-on-duct 406 'text/html' + =; msg=tape (error-page 406 %.y url.request msg) + "channel already established in {(trip mode.u.maybe-channel)} mode" + [[& ~ mos] state] + :: when opening an event-stream, we must cancel our timeout timer + :: if there's no duct already bound. else, kill the old request, + :: we will replace its duct at the end of this arm + :: + =^ cancel-moves state + ?: ?=([%& *] state.u.maybe-channel) + :_ state + (cancel-timeout-move channel-id p.state.u.maybe-channel)^~ + =. duct-to-key.channel-state.state + (~(del by duct-to-key.channel-state.state) p.state.u.maybe-channel) + =/ cancel-heartbeat + ?~ heartbeat.u.maybe-channel ~ + :_ ~ + %+ cancel-heartbeat-move channel-id + [date duct]:u.heartbeat.u.maybe-channel + =- [(weld cancel-heartbeat -<) ->] + (handle-response(duct p.state.u.maybe-channel) [%cancel ~]) + :: flush events older than the passed in 'Last-Event-ID' + :: + =? state ?=(^ maybe-last-event-id) + (acknowledge-events channel-id u.maybe-last-event-id) + ::TODO that did not remove them from the u.maybe-channel queue though! + :: we may want to account for maybe-last-event-id, for efficiency. + :: (the client _should_ ignore events it heard previously if we do + :: end up re-sending them, but _requiring_ that feels kinda risky) + :: + :: combine the remaining queued events to send to the client + :: + =; event-replay=wall + [[| - cancel-moves] state] %- zing %- flop =/ queue events.u.maybe-channel @@ -2260,6 +2283,7 @@ (channel-event-to-tape u.maybe-channel request-id channel-event) ?~ said $ $(events [(event-tape-to-wall id +.u.said) events]) + ?: exit [moves state] :: send the start event to the client :: =^ http-moves state @@ -2270,7 +2294,7 @@ ['cache-control' 'no-cache'] ['connection' 'keep-alive'] == - (wall-to-octs event-replay) + (wall-to-octs wall) complete=%.n == :: associate this duct with this session key @@ -2300,7 +2324,7 @@ heartbeat (some [heartbeat-time duct]) == :: - [[heartbeat :(weld http-moves cancel-moves moves)] state] + [[heartbeat :(weld http-moves moves)] state] :: +acknowledge-events: removes events before :last-event-id on :channel-id :: ++ acknowledge-events diff --git a/tests/sys/vane/eyre.hoon b/tests/sys/vane/eyre.hoon index ab029cac77..4e59d5dd72 100644 --- a/tests/sys/vane/eyre.hoon +++ b/tests/sys/vane/eyre.hoon @@ -262,7 +262,7 @@ == :: ++ ex-channel-response - |= body=@t + |= body=(unit @t) |= mov=move ^- tang ?. ?=([[[%http-blah ~] ~] %give %response %start * * %.n] mov) @@ -273,7 +273,7 @@ ['connection' 'keep-alive'] ['set-cookie' cookie-string] == - =/ body `(as-octs:mimes:html body) + =/ body (bind body as-octs:mimes:html) ;: weld (expect-eq !>(200) !>(status-code.response-header.http-event.p.card.mov)) (expect-eq !>(body) !>(data.http-event.p.card.mov)) @@ -743,6 +743,20 @@ =/ wire /channel/subscription/'0123456789abcdef'/1/~nul/two/~nul (expect-moves mos (ex-gall-deal wire ~nul %two %leave ~) ~) :: +++ test-channel-open-with-get + %- eval-mare + =/ m (mare ,~) + ;< ~ bind:m perform-init-wo-timer + ;< ~ bind:m perform-born + ;< ~ bind:m (wait ~d1) + ;< ~ bind:m perform-authentication-2 + ;< mos=(list move) bind:m + (get '/~/channel/0123456789abcdef' cookie) + ;< now=@da bind:m get-now + =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20)) + =/ mov-2 (ex-channel-response ~) + (expect-moves mos mov-1 mov-2 ~) +:: ++ test-channel-results-before-open %- eval-mare =/ m (mare ,~) @@ -776,7 +790,7 @@ ;< now=@da bind:m get-now =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20)) =/ mov-2 - %- ex-channel-response + %+ ex-channel-response ~ ''' id: 0 data: {"ok":"ok","id":0,"response":"poke"} @@ -920,7 +934,7 @@ ;< now=@da bind:m get-now =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20)) =/ mov-2 - %- ex-channel-response + %+ ex-channel-response ~ ''' id: 0 data: {"ok":"ok","id":0,"response":"poke"} @@ -1022,7 +1036,7 @@ =/ heartbeat (add now ~s20) =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' heartbeat) =/ mov-2 - %- ex-channel-response + %+ ex-channel-response ~ ''' id: 0 data: {"ok":"ok","id":0,"response":"poke"} @@ -1092,7 +1106,7 @@ =/ heartbeat (add now ~s20) =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' heartbeat) =/ mov-2 - %- ex-channel-response + %+ ex-channel-response ~ ''' id: 2 data: {"json":[1],"id":1,"response":"diff"} From 34148f9f443988b2d44eae37859a9c3c805d219f Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 13 Sep 2023 13:56:48 +0200 Subject: [PATCH 16/29] eyre: allow PUTting empty channel-request list Previously, we would reject this with a 400 error. Considering the request body is expected to contain "array of requests" and that arrays may be empty, we really should not be rejecting the requests. Prior to 156ca21472, sending the empty array would have been convenient for channel creation. Empty arrays getting rejected forced clients to inject a faux poke (commonly hi-ing oneself). With that recent change, the most common case for wanting to PUT the empty list of requests is largely obsolete, but one can still imagine it being useful for clients that want to keep their channel alive without necessarily being connected to it. This also implements sloppier clients from running into 400 responses when they submit an empty "command queue" for whatever. Regardless, there seems to be no clear reason why the empty request list _shouldn't_ be accepted and processed as normal. We add a small test to ensure eyre accepts this. --- pkg/arvo/sys/vane/eyre.hoon | 5 ----- tests/sys/vane/eyre.hoon | 12 ++++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index 09d04a30f8..e52da9ecfb 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -2372,11 +2372,6 @@ ?: ?=(%| -.maybe-requests) %^ return-static-data-on-duct 400 'text/html' (error-page 400 & url.request (trip p.maybe-requests)) - :: while weird, the request list could be empty - :: - ?: =(~ p.maybe-requests) - %^ return-static-data-on-duct 400 'text/html' - (error-page 400 %.y url.request "empty list of actions") :: check for the existence of the channel-id :: :: if we have no session, create a new one set to expire in diff --git a/tests/sys/vane/eyre.hoon b/tests/sys/vane/eyre.hoon index 4e59d5dd72..db6e35a989 100644 --- a/tests/sys/vane/eyre.hoon +++ b/tests/sys/vane/eyre.hoon @@ -757,6 +757,18 @@ =/ mov-2 (ex-channel-response ~) (expect-moves mos mov-1 mov-2 ~) :: +++ test-channel-put-zero-requests + %- eval-mare + =/ m (mare ,~) + ;< ~ bind:m perform-init-start-channel-2 + ;< ~ bind:m (wait ~m1) + ;< mos=(list move) bind:m + (put '/~/channel/0123456789abcdef' cookie '[]') + =/ mov-1 ex-204 + =/ mov-2 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00) + =/ mov-3 (ex-wait /channel/timeout/'0123456789abcdef' ~1111.1.2..12.01.00) + (expect-moves mos mov-1 mov-2 mov-3 ~) +:: ++ test-channel-results-before-open %- eval-mare =/ m (mare ,~) From 5fe2d6ea98df15259e9e6fe2675dbb837a2a709a Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Thu, 21 Sep 2023 00:03:32 +0300 Subject: [PATCH 17/29] eyre: allow header-list to contain multiple cookies --- pkg/arvo/sys/vane/eyre.hoon | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index e52da9ecfb..fb8915be97 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -1358,14 +1358,15 @@ ^- (unit @uv) :: are there cookies passed with this request? :: - :: TODO: In HTTP2, the client is allowed to put multiple 'Cookie' - :: headers. + =/ cookie-header=@t + %+ roll header-list.request + |= [[key=@t value=@t] c=@t] + ?. =(key 'cookie') + c + (cat 3 (cat 3 c ?~(c 0 '; ')) value) + :: is the cookie line valid? :: - ?~ cookie-header=(get-header:http 'cookie' header-list.request) - ~ - :: is the cookie line is valid? - :: - ?~ cookies=(rush u.cookie-header cock:de-purl:html) + ?~ cookies=(rush cookie-header cock:de-purl:html) ~ :: is there an urbauth cookie? :: From 042340e8e471df651cc60da0f4bfaafbab8e07d1 Mon Sep 17 00:00:00 2001 From: pkova Date: Tue, 26 Sep 2023 21:02:41 +0300 Subject: [PATCH 18/29] clay: iterate over all aeons in +read-at-tako --- pkg/arvo/sys/vane/clay.hoon | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/arvo/sys/vane/clay.hoon b/pkg/arvo/sys/vane/clay.hoon index 7820f11e1e..e3cfd6178f 100644 --- a/pkg/arvo/sys/vane/clay.hoon +++ b/pkg/arvo/sys/vane/clay.hoon @@ -4533,7 +4533,12 @@ ?. ?| =(0v0 tak) ?& (~(has by hut.ran) tak) ?| (~(any by hit.dom) |=(=tako =(tak tako))) :: fast-path - (~(has in (reachable-takos (aeon-to-tako:ze let.dom))) tak) + |- ^- ? + ?: (lte let.dom 1) + %.n + ?| (~(has in (reachable-takos (aeon-to-tako:ze let.dom))) tak) + $(let.dom (dec let.dom)) + == == |(?=(~ for) (may-read u.for care.mun tak path.mun)) == == From 55787fed62f453863a8f72084f3758e80ff8092c Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Thu, 28 Sep 2023 19:22:53 +0300 Subject: [PATCH 19/29] clay: return all takos in /cs/bloc scry --- pkg/arvo/sys/vane/clay.hoon | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/sys/vane/clay.hoon b/pkg/arvo/sys/vane/clay.hoon index 7820f11e1e..5e1409119d 100644 --- a/pkg/arvo/sys/vane/clay.hoon +++ b/pkg/arvo/sys/vane/clay.hoon @@ -4315,8 +4315,12 @@ :- -:!>(*(map lobe page)) ^- (map lobe page) %- %~ rep in - %- reachable-takos - (~(got by hit.dom) let.dom) + |- ^- (set tako) + =/ ts=(set tako) + %- reachable-takos + (~(got by hit.dom) let.dom) + ?: (lte let.dom 1) ts + (~(uni in ts) $(let.dom (dec let.dom))) |= [t=tako o=(map lobe page)] %- ~(gas by o) %+ turn From fb3579c3a9560e2f01e26ae0acf000e7e7df03e9 Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Fri, 29 Sep 2023 16:00:03 +0300 Subject: [PATCH 20/29] runtime-version: parse current version properly --- pkg/arvo/ted/runtime-version.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/ted/runtime-version.hoon b/pkg/arvo/ted/runtime-version.hoon index d0a679bba2..e2c55590b5 100644 --- a/pkg/arvo/ted/runtime-version.hoon +++ b/pkg/arvo/ted/runtime-version.hoon @@ -12,7 +12,7 @@ ++ parse-current-version |= current=vere ^- @t - (rear rev.current) + (slav %ta (rear rev.current)) :: ++ is-equal-version |= [latest=@t current=vere] From 4dae1911243242ca85089324747ce53139df9151 Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Mon, 2 Oct 2023 16:21:15 +0300 Subject: [PATCH 21/29] ames: fix merge mishap in previous commit --- pkg/arvo/sys/vane/ames.hoon | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index 1af1a5200f..d25f5fddc7 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -1190,7 +1190,6 @@ $% [%kroc dry=?] $<(%kroc task) == - :: $bug: debug printing configuration :: :: veb: verbosity toggles @@ -1556,13 +1555,6 @@ [%adult state=ames-state-16] == == $: %17 - $% $: %larva - events=(qeu queued-event) - state=ames-state-15 - == - [%adult state=ames-state-15] - == == - $: %16 $% $: %larva events=(qeu queued-event) state=_ames-state.adult-gate From 5e48ff75de4f0ee9d006f2204510fcf33454c472 Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Tue, 3 Oct 2023 09:40:45 +0200 Subject: [PATCH 22/29] gall: add %15 state migration --- pkg/arvo/sys/vane/gall.hoon | 43 ++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index e27084d0da..20ac4d8369 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -42,9 +42,9 @@ :: $move: Arvo-level move :: +$ move [=duct move=(wind note-arvo gift-arvo)] -:: $state-13: overall gall state, versioned +:: $state-15: overall gall state, versioned :: -+$ state-14 [%14 state] ++$ state-15 [%15 state] :: $state: overall gall state :: :: system-duct: TODO document @@ -164,7 +164,7 @@ :: $spore: structures for update, produced by +stay :: +$ spore - $: %14 + $: %15 system-duct=duct outstanding=(map [wire duct] (qeu remote-request)) contacts=(set ship) @@ -195,7 +195,7 @@ -- :: adult gall vane interface, for type compatibility with pupa :: -=| state=state-14 +=| state=state-15 |= [now=@da eny=@uvJ rof=roof] =* gall-payload . ~% %gall-top ..part ~ @@ -1986,11 +1986,30 @@ =? old ?=(%11 -.old) (spore-11-to-12 old) =? old ?=(%12 -.old) (spore-12-to-13 old) =? old ?=(%13 -.old) (spore-13-to-14 old) - ?> ?=(%14 -.old) + =? old ?=(%14 -.old) (spore-14-to-15 old) + ?> ?=(%15 -.old) gall-payload(state old) :: +$ spore-any - $%(spore spore-7 spore-8 spore-9 spore-10 spore-11 spore-12 spore-13) + $% spore + spore-7 + spore-8 + spore-9 + spore-10 + spore-11 + spore-12 + spore-13 + spore-14 + == + +$ spore-14 + $: %14 + system-duct=duct + outstanding=(map [wire duct] (qeu remote-request)) + contacts=(set ship) + eggs=(map term egg) + blocked=(map term (qeu blocked-move)) + =bug + == +$ spore-13 $: %13 system-duct=duct @@ -2205,11 +2224,11 @@ ?: ?=(%nuke -.e) e e(sky [sky.e ken:*$>(%live egg)]) == - :: added provenance path to routes and nacked-leaves timer + :: added provenance path to routes :: ++ spore-13-to-14 |= old=spore-13 - ^- spore + ^- spore-14 %= old - %14 :: @@ -2224,9 +2243,13 @@ %= blocked attributing.routes [ship=attributing.routes.blocked path=/] == - :: - bug [bug.old ~] == + :: added nacked-leaves timer + :: + ++ spore-14-to-15 + |= old=spore-14 + ^- spore + old(- %15, bug [bug.old ~]) -- :: +scry: standard scry :: From fec9950d2134540fa6b499e3a6791fdff1958eb0 Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 3 Oct 2023 17:25:46 +0200 Subject: [PATCH 23/29] eyre: prevent login redirect loops Instead of auto-redirecting the login page if we're already logged in, we simply present it as normal. If we're logged in as the local identity, we present just a "continue" button in place of the +code input field. If we're authenticated through eauth, or as a guest, we present a smaller "proceed as" link underneath the login form. This way, when apps redirect to the login page because the visitor isn't _sufficiently_ authenticated, eyre doesn't just throw them right back into the app with still insufficient creds. --- pkg/arvo/sys/vane/eyre.hoon | 50 ++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index fb8915be97..2b5362fe26 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -366,6 +366,8 @@ } button[type=submit] { margin-top: 1rem; + } + button[type=submit], a.button { font-size: 1rem; padding: 0.5rem 1rem; border-radius: 0.5rem; @@ -373,6 +375,7 @@ color: var(--white); border: none; font-weight: 600; + text-decoration: none; } input:invalid ~ button[type=submit] { border-color: currentColor; @@ -380,7 +383,7 @@ color: var(--gray-400); pointer-events: none; } - span.guest { + span.guest, span.guest a { color: var(--gray-400); } span.failed { @@ -475,8 +478,13 @@ ;div#local ;p:"Urbit ID" ;input(value "{(scow %p our)}", disabled "true", class "mono"); - ;p:"Access Key" + ;+ ?: =(%ours -.identity) + ;div + ;p:"Already authenticated" + ;a.button/"{(trip (fall redirect-url '/'))}":"Continue" + == ;form(action "/~/login", method "post", enctype "application/x-www-form-urlencoded") + ;p:"Access Key" ;input =type "password" =name "password" @@ -519,10 +527,13 @@ ;button(name "eauth", type "submit"):"Continue" == == - ;* ?. ?=(%fake -.identity) ~ + ;* ?: ?=(%ours -.identity) ~ =+ id=(trim 29 (scow %p who.identity)) + =+ as="proceed as{?:(?=(%fake -.identity) " guest" "")}" ;+ ;span.guest.mono - ; Current guest identity: + ; Or + ;a/"{(trip (fall redirect-url '/'))}":"{as}" + ; : ;br; ; {p.id} ;br; @@ -1195,33 +1206,14 @@ =/ with-eauth=(unit ?) ?: =(~ eauth-url:eauth) ~ `?=(^ (get-header:http 'eauth' args.request-line)) - :: if we received a simple get: redirect if logged in, otherwise - :: show login page + :: if we received a simple get: show the login page + :: + ::NOTE we never auto-redirect, to avoid redirect loops with apps that + :: send unprivileged users to the login screen :: ?: =('GET' method.request) - ?. (request-is-logged-in request) - %^ return-static-data-on-duct 200 'text/html' - (login-page redirect our identity with-eauth %.n) - =/ session-id (session-id-from-request request) - :: session-id should always be populated here since we are logged in - ?~ session-id - %^ return-static-data-on-duct 200 'text/html' - (login-page redirect our identity with-eauth %.n) - =/ cookie-line=@t - (session-cookie-string u.session-id &) - =/ actual-redirect - ?~ redirect '/' - ?:(=(u.redirect '') '/' u.redirect) - %- handle-response - :* %start - :- status-code=303 - ^= headers - :~ ['location' actual-redirect] - ['set-cookie' cookie-line] - == - data=~ - complete=%.y - == + %^ return-static-data-on-duct 200 'text/html' + (login-page redirect our identity with-eauth %.n) :: if we are not a post, return an error :: ?. =('POST' method.request) From 14266c8d4608dd75be5cab866f9d722a9d44620d Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 4 Oct 2023 22:42:50 +0200 Subject: [PATCH 24/29] eyre: GETting non-existent channels 404s Contrary to the argument made by 4affae8, this is the _actually correct_ behavior. Not creating server-side resources in response to GETs respects the expected method semantics, and more importantly, serving a 404 is an important signal for clients trying to connect to a channel they were using previously. Without that, they have no way of telling whether, when reconnecting, if their channel was reaped in the mean time or not. The "empty PUT" affordance provided by 34148f9 makes requiring a PUT request for channel creation more reasonable. We leave the general refactoring done by #6789 in place, but do emphasize the reasoning given here with a few additional comments. --- pkg/arvo/sys/vane/eyre.hoon | 55 +++++++++++++++++++------------------ tests/sys/vane/eyre.hoon | 7 ++--- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index fb8915be97..59427c22c8 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -2191,12 +2191,20 @@ [%b %rest expiration-time] :: +on-get-request: handles a GET request :: - :: GET requests open a channel for the server to send events to the - :: client in text/event-stream format. + :: GET requests connect to a channel for the server to send events to + :: the client in text/event-stream format. :: ++ on-get-request |= [channel-id=@t [session-id=@uv =identity] =request:http] ^- [(list move) server-state] + :: if the channel doesn't exist, we cannot serve it. + :: this 404 also lets clients know if their channel was reaped since + :: they last connected to it. + :: + ?. (~(has by session.channel-state.state) channel-id) + %^ return-static-data-on-duct 404 'text/html' + (error-page 404 | url.request ~) + :: =/ mode=?(%json %jam) (find-channel-mode %'GET' header-list.request) =^ [exit=? =wall moves=(list move)] state @@ -2206,23 +2214,14 @@ ?~ maybe-raw-header=(get-header:http 'last-event-id' header-list.request) ~ (rush u.maybe-raw-header dum:ag) - :: if the channel doesn't exist yet, simply instantiate it here - :: - ?~ maybe-channel=(~(get by session.channel-state.state) channel-id) - =- [[| ~ ~] state(session.channel-state -)] - %+ ~(put by session.channel-state.state) channel-id - ::NOTE some other fields initialized at the end of this arm - %* . *channel - identity identity - next-id (fall maybe-last-event-id 0) - last-ack now - == - :: if the channel does exist, we put some demands on the get request, - :: and may need to do some cleanup for prior requests. + =/ channel + (~(got by session.channel-state.state) channel-id) + :: we put some demands on the get request, and may need to do some + :: cleanup for prior requests. :: :: find the channel creator's identity, make sure it matches :: - ?. =(identity identity.u.maybe-channel) + ?. =(identity identity.channel) =^ mos state %^ return-static-data-on-duct 403 'text/html' (error-page 403 | url.request ~) @@ -2231,34 +2230,34 @@ :: ::TODO or could we change that on the spot, given that only a single :: request will ever be listening to this channel? - ?. =(mode mode.u.maybe-channel) + ?. =(mode mode.channel) =^ mos state %^ return-static-data-on-duct 406 'text/html' =; msg=tape (error-page 406 %.y url.request msg) - "channel already established in {(trip mode.u.maybe-channel)} mode" + "channel already established in {(trip mode.channel)} mode" [[& ~ mos] state] :: when opening an event-stream, we must cancel our timeout timer :: if there's no duct already bound. else, kill the old request, :: we will replace its duct at the end of this arm :: =^ cancel-moves state - ?: ?=([%& *] state.u.maybe-channel) + ?: ?=([%& *] state.channel) :_ state - (cancel-timeout-move channel-id p.state.u.maybe-channel)^~ + (cancel-timeout-move channel-id p.state.channel)^~ =. duct-to-key.channel-state.state - (~(del by duct-to-key.channel-state.state) p.state.u.maybe-channel) + (~(del by duct-to-key.channel-state.state) p.state.channel) =/ cancel-heartbeat - ?~ heartbeat.u.maybe-channel ~ + ?~ heartbeat.channel ~ :_ ~ %+ cancel-heartbeat-move channel-id - [date duct]:u.heartbeat.u.maybe-channel + [date duct]:u.heartbeat.channel =- [(weld cancel-heartbeat -<) ->] - (handle-response(duct p.state.u.maybe-channel) [%cancel ~]) + (handle-response(duct p.state.channel) [%cancel ~]) :: flush events older than the passed in 'Last-Event-ID' :: =? state ?=(^ maybe-last-event-id) (acknowledge-events channel-id u.maybe-last-event-id) - ::TODO that did not remove them from the u.maybe-channel queue though! + ::TODO that did not remove them from the channel queue though! :: we may want to account for maybe-last-event-id, for efficiency. :: (the client _should_ ignore events it heard previously if we do :: end up re-sending them, but _requiring_ that feels kinda risky) @@ -2269,7 +2268,7 @@ [[| - cancel-moves] state] %- zing %- flop - =/ queue events.u.maybe-channel + =/ queue events.channel =| events=(list wall) |- ^+ events @@ -2281,7 +2280,7 @@ :: since conversion failure also gets caught during first receive. :: we can't do anything about this, so consider it unsupported. =/ said - (channel-event-to-tape u.maybe-channel request-id channel-event) + (channel-event-to-tape channel request-id channel-event) ?~ said $ $(events [(event-tape-to-wall id +.u.said) events]) ?: exit [moves state] @@ -2346,6 +2345,8 @@ :: :: PUT requests send commands from the client to the server. We receive :: a set of commands in JSON format in the body of the message. + :: channels don't exist until a PUT request is sent. it's valid for + :: this request to contain an empty list of commands. :: ++ on-put-request |= [channel-id=@t =identity =request:http] diff --git a/tests/sys/vane/eyre.hoon b/tests/sys/vane/eyre.hoon index db6e35a989..fd4b3b68db 100644 --- a/tests/sys/vane/eyre.hoon +++ b/tests/sys/vane/eyre.hoon @@ -749,13 +749,12 @@ ;< ~ bind:m perform-init-wo-timer ;< ~ bind:m perform-born ;< ~ bind:m (wait ~d1) - ;< ~ bind:m perform-authentication-2 ;< mos=(list move) bind:m (get '/~/channel/0123456789abcdef' cookie) ;< now=@da bind:m get-now - =/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20)) - =/ mov-2 (ex-channel-response ~) - (expect-moves mos mov-1 mov-2 ~) + =/ headers ['content-type' 'text/html']~ + =/ body `(error-page:eyre-gate 404 %.n '/~/channel/0123456789abcdef' ~) + (expect-moves mos (ex-response 404 headers body) ~) :: ++ test-channel-put-zero-requests %- eval-mare From bdc4cb118db85f76c738c903ffe21b1d9c69d26b Mon Sep 17 00:00:00 2001 From: yosoyubik Date: Fri, 6 Oct 2023 16:54:00 +0200 Subject: [PATCH 25/29] dbug: add color for closing/corked flows ...lightyellow for closing and lightred for corked --- pkg/arvo/app/dbug.hoon | 32 +- pkg/arvo/app/debug/js/index.js | 67581 +++++++++++++++++++++- pkg/arvo/app/debug/js/tile.js | 19367 ++++++- pkg/arvo/sys/vane/ames.hoon | 17 + pkg/interface/dbug/src/js/views/ames.js | 19 +- 5 files changed, 87002 insertions(+), 14 deletions(-) diff --git a/pkg/arvo/app/dbug.hoon b/pkg/arvo/app/dbug.hoon index a14d1d3dcc..cc993c9074 100644 --- a/pkg/arvo/app/dbug.hoon +++ b/pkg/arvo/app/dbug.hoon @@ -628,6 +628,8 @@ :: duct: ['/paths', ...], :: message-num: 123 :: }, ...], + :: closing: [bone, ..., bone], + :: corked: [bone, ..., bone], :: heeds: [['/paths', ...] ...] :: scries: :: -> { =path @@ -706,8 +708,8 @@ |^ =/ mix=(list flow) =- (sort - dor) %+ welp - (turn ~(tap by snd) (tack %snd)) - (turn ~(tap by rcv) (tack %rcv)) + (turn ~(tap by snd) (tack %snd closing corked)) + (turn ~(tap by rcv) (tack %rcv closing corked)) =/ [forward=(list flow) backward=(list flow)] %+ skid mix |= [=bone *] @@ -719,6 +721,8 @@ :: +$ flow $: =bone + closing=? + corked=? :: $= state $% [%snd message-pump-state] @@ -727,17 +731,17 @@ == :: ++ tack - |* =term + |* [=term closing=(set bone) corked=(set bone)] |* [=bone =noun] - [bone [term noun]] + [bone (~(has in closing) bone) (~(has in corked) bone) [term noun]] :: ++ build |= flow ^- json %+ frond -.state ?- -.state - %snd (snd-with-bone ossuary bone +.state) - %rcv (rcv-with-bone ossuary bone +.state) + %snd (snd-with-bone ossuary bone closing corked +.state) + %rcv (rcv-with-bone ossuary bone closing corked +.state) == -- :: @@ -749,6 +753,10 @@ :* 'message-num'^(numb message-num) (bone-to-pairs bone ossuary) == + :: + 'closing'^(set-array closing numb) + :: + 'corked'^(set-array corked numb) :: 'heeds'^(set-array heeds from-duct) :: @@ -756,10 +764,12 @@ == :: ++ snd-with-bone - |= [=ossuary =bone message-pump-state] + |= [=ossuary =bone closing=? corked=? message-pump-state] ^- json %- pairs - :* 'current'^(numb current) + :* 'closing'^b+closing + 'corked'^b+corked + 'current'^(numb current) 'next'^(numb next) :: :- 'unsent-messages' :: as byte sizes @@ -811,10 +821,12 @@ == :: ++ rcv-with-bone - |= [=ossuary =bone message-sink-state] + |= [=ossuary =bone closing=? corked=? message-sink-state] ^- json %- pairs - :* 'last-acked'^(numb last-acked) + :* 'closing'^b+closing + 'corked'^b+corked + 'last-acked'^(numb last-acked) 'last-heard'^(numb last-heard) :: :- 'pending-vane-ack' diff --git a/pkg/arvo/app/debug/js/index.js b/pkg/arvo/app/debug/js/index.js index 6139750824..31a52007e7 100644 --- a/pkg/arvo/app/debug/js/index.js +++ b/pkg/arvo/app/debug/js/index.js @@ -1 +1,67580 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("buffer")):"function"==typeof define&&define.amd?define("index",["buffer"],t):t((e=e||self).buffer)}(this,function(e){"use strict";e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e;var t="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},n=t.performance||{},r=(n.now||n.mozNow||n.msNow||n.oNow||n.webkitNow,"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{});function i(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function o(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function a(e,t){return e(t={exports:{}},t.exports),t.exports}var s=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,l=Object.prototype.propertyIsEnumerable;var c=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,r,i=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),o=1;o1?t-1:0),r=1;r1?t-1:0),r=1;r0&&"string"==typeof n[n.length-1]&&0===n[n.length-1].indexOf("\n in"))){var r=A.ReactDebugCurrentFrame.getStackAddendum();""!==r&&(t+="%s",n=n.concat([r]))}var i=n.map(function(e){return""+e});i.unshift("Warning: "+t),Function.prototype.apply.call(console[e],console,i);try{var o=0,a="Warning: "+t.replace(/%s/g,function(){return n[o++]});throw new Error(a)}catch(e){}}e(A,{ReactDebugCurrentFrame:P,ReactComponentTreeHook:{}});var L={};function F(e,t){var n=e.constructor,r=n&&(n.displayName||n.name)||"ReactClass",i=r+"."+t;L[i]||(j("Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",t,r),L[i]=!0)}var z={isMounted:function(e){return!1},enqueueForceUpdate:function(e,t,n){F(e,"forceUpdate")},enqueueReplaceState:function(e,t,n,r){F(e,"replaceState")},enqueueSetState:function(e,t,n,r){F(e,"setState")}},W={};function B(e,t,n){this.props=e,this.context=t,this.refs=W,this.updater=n||z}Object.freeze(W),B.prototype.isReactComponent={},B.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},B.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};var Y={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},H=function(e,t){Object.defineProperty(B.prototype,e,{get:function(){I("%s(...) is deprecated in plain JavaScript React classes. %s",t[0],t[1])}})};for(var V in Y)Y.hasOwnProperty(V)&&H(V,Y[V]);function $(){}function q(e,t,n){this.props=e,this.context=t,this.refs=W,this.updater=n||z}$.prototype=B.prototype;var G=q.prototype=new $;G.constructor=q,e(G,B.prototype),G.isPureReactComponent=!0;var Q,K,Z,X=Object.prototype.hasOwnProperty,J={key:!0,ref:!0,__self:!0,__source:!0};function ee(e){if(X.call(e,"ref")){var t=Object.getOwnPropertyDescriptor(e,"ref").get;if(t&&t.isReactWarning)return!1}return void 0!==e.ref}function te(e){if(X.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return void 0!==e.key}Z={};var ne=function(e,t,n,r,o,a,s){var u={$$typeof:i,type:e,key:t,ref:n,props:s,_owner:a,_store:{}};return Object.defineProperty(u._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(u,"_self",{configurable:!1,enumerable:!1,writable:!1,value:r}),Object.defineProperty(u,"_source",{configurable:!1,enumerable:!1,writable:!1,value:o}),Object.freeze&&(Object.freeze(u.props),Object.freeze(u)),u};function re(e,t,n){var r,i={},o=null,a=null,s=null,u=null;if(null!=t)for(r in ee(t)&&(a=t.ref,function(e){if("string"==typeof e.ref&&N.current&&e.__self&&N.current.stateNode!==e.__self){var t=R(N.current.type);Z[t]||(j('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://fb.me/react-strict-mode-string-ref',R(N.current.type),e.ref),Z[t]=!0)}}(t)),te(t)&&(o=""+t.key),s=void 0===t.__self?null:t.__self,u=void 0===t.__source?null:t.__source,t)X.call(t,r)&&!J.hasOwnProperty(r)&&(i[r]=t[r]);var l=arguments.length-2;if(1===l)i.children=n;else if(l>1){for(var c=Array(l),f=0;f.")}return t}(t);if(!xe[n]){xe[n]=!0;var r="";e&&e._owner&&e._owner!==N.current&&(r=" It was passed a child from "+R(e._owner.type)+"."),D(e),j('Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',n,r),D(null)}}}function Se(e,t){if("object"==typeof e)if(Array.isArray(e))for(var n=0;n",u=" Did you accidentally export a JSX literal instead of a component?"):l=typeof e,j("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",l,u)}var f=re.apply(this,arguments);if(null==f)return f;if(s)for(var d=2;d1){for(var h=Array(d),p=0;p is not supported and will be removed in a future major release. Did you mean to render instead?")),n.Provider},set:function(e){n.Provider=e}},_currentValue:{get:function(){return n._currentValue},set:function(e){n._currentValue=e}},_currentValue2:{get:function(){return n._currentValue2},set:function(e){n._currentValue2=e}},_threadCount:{get:function(){return n._threadCount},set:function(e){n._threadCount=e}},Consumer:{get:function(){return r||(r=!0,j("Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),n.Consumer}}}),n.Consumer=o,n._currentRenderer=null,n._currentRenderer2=null,n},t.createElement=Oe,t.createFactory=Ae,t.createRef=function(){var e={current:null};return Object.seal(e),e},t.forwardRef=function(e){return null!=e&&e.$$typeof===g?j("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):"function"!=typeof e?j("forwardRef requires a render function but was given %s.",null===e?"null":typeof e):0!==e.length&&2!==e.length&&j("forwardRef render functions accept exactly two parameters: props and ref. %s",1===e.length?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),null!=e&&(null==e.defaultProps&&null==e.propTypes||j("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?")),{$$typeof:h,render:e}},t.isValidElement=ie,t.lazy=function(e){var t,n,r={$$typeof:v,_ctor:e,_status:-1,_result:null};return Object.defineProperties(r,{defaultProps:{configurable:!0,get:function(){return t},set:function(e){j("React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),t=e,Object.defineProperty(r,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return n},set:function(e){j("React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),n=e,Object.defineProperty(r,"propTypes",{enumerable:!0})}}}),r},t.memo=function(e,t){return _e(e)||j("memo: The first argument must be a component. Instead received: %s",null===e?"null":typeof e),{$$typeof:g,type:e,compare:void 0===t?null:t}},t.useCallback=function(e,t){return we().useCallback(e,t)},t.useContext=function(e,t){var n=we();if(void 0!==t&&j("useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: %s.%s",t,"number"==typeof t&&Array.isArray(arguments[2])?"\n\nDid you call array.map(useContext)? Calling Hooks inside a loop is not supported. Learn more at https://fb.me/rules-of-hooks":""),void 0!==e._context){var r=e._context;r.Consumer===e?j("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):r.Provider===e&&j("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return n.useContext(e,t)},t.useDebugValue=function(e,t){return we().useDebugValue(e,t)},t.useEffect=function(e,t){return we().useEffect(e,t)},t.useImperativeHandle=function(e,t,n){return we().useImperativeHandle(e,t,n)},t.useLayoutEffect=function(e,t){return we().useLayoutEffect(e,t)},t.useMemo=function(e,t){return we().useMemo(e,t)},t.useReducer=function(e,t,n){return we().useReducer(e,t,n)},t.useRef=function(e){return we().useRef(e)},t.useState=function(e){return we().useState(e)},t.version="16.14.0"}()}),S=(E.Children,E.Component,E.Fragment,E.Profiler,E.PureComponent,E.StrictMode,E.Suspense,E.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,E.cloneElement,E.createContext,E.createElement,E.createFactory,E.createRef,E.forwardRef,E.isValidElement,E.lazy,E.memo,E.useCallback,E.useContext,E.useDebugValue,E.useEffect,E.useImperativeHandle,E.useLayoutEffect,E.useMemo,E.useReducer,E.useRef,E.useState,E.version,a(function(e){e.exports=E})),T=S.Component,N=S.createRef,C=S.createElement,M=S.useState,R=S.useRef,P=S.useEffect,O=S.Fragment,D=a(function(e,t){var n,r,i,o,a;if("undefined"==typeof window||"function"!=typeof MessageChannel){var s=null,u=null,l=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(e){throw setTimeout(l,0),e}},c=Date.now();t.unstable_now=function(){return Date.now()-c},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(l,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},o=function(){return!1},a=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var m=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof m&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var g=d.now();t.unstable_now=function(){return d.now()-g}}var v=!1,y=null,b=-1,_=5,w=0;o=function(){return t.unstable_now()>=w},a=function(){},t.unstable_forceFrameRate=function(e){0>e||125>>1,i=e[r];if(!(void 0!==i&&0N(a,n))void 0!==u&&0>N(u,a)?(e[r]=u,e[s]=n,r=s):(e[r]=a,e[o]=n,r=o);else{if(!(void 0!==u&&0>N(u,n)))break e;e[r]=u,e[s]=n,r=s}}}return t}return null}function N(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var C=[],M=[],R=1,P=null,O=3,D=!1,A=!1,I=!1;function j(e){for(var t=S(M);null!==t;){if(null===t.callback)T(M);else{if(!(t.startTime<=e))break;T(M),t.sortIndex=t.expirationTime,E(C,t)}t=S(M)}}function U(e){if(I=!1,j(e),!A)if(null!==S(C))A=!0,n(L);else{var t=S(M);null!==t&&r(U,t.startTime-e)}}function L(e,n){A=!1,I&&(I=!1,i()),D=!0;var a=O;try{for(j(n),P=S(C);null!==P&&(!(P.expirationTime>n)||e&&!o());){var s=P.callback;if(null!==s){P.callback=null,O=P.priorityLevel;var u=s(P.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?P.callback=u:P===S(C)&&T(C),j(n)}else T(C);P=S(C)}if(null!==P)var l=!0;else{var c=S(M);null!==c&&r(U,c.startTime-n),l=!1}return l}finally{P=null,O=a,D=!1}}function F(e){switch(e){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var z=a;t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){A||D||(A=!0,n(L))},t.unstable_getCurrentPriorityLevel=function(){return O},t.unstable_getFirstCallbackNode=function(){return S(C)},t.unstable_next=function(e){switch(O){case 1:case 2:case 3:var t=3;break;default:t=O}var n=O;O=t;try{return e()}finally{O=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=z,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=O;O=e;try{return t()}finally{O=n}},t.unstable_scheduleCallback=function(e,o,a){var s=t.unstable_now();if("object"==typeof a&&null!==a){var u=a.delay;u="number"==typeof u&&0s?(e.sortIndex=u,E(M,e),null===S(C)&&e===S(M)&&(I?i():I=!0,r(U,u-s))):(e.sortIndex=a,E(C,e),A||D||(A=!0,n(L))),e},t.unstable_shouldYield=function(){var e=t.unstable_now();j(e);var n=S(C);return n!==P&&null!==P&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime=x},o=function(){},t.unstable_forceFrameRate=function(e){e<0||e>125?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):k=e>0?Math.floor(1e3/e):5};var E=new MessageChannel,S=E.port2;E.port1.onmessage=function(){if(null!==_){var e=t.unstable_now();x=e+k;try{_(!0,e)?S.postMessage(null):(b=!1,_=null)}catch(e){throw S.postMessage(null),e}}else b=!1},e=function(e){_=e,b||(b=!0,S.postMessage(null))},n=function(e,n){w=p(function(){e(t.unstable_now())},n)},r=function(){m(w),w=-1}}function T(e,t){var n=e.length;e.push(t),function(e,t,n){var r=n;for(;;){var i=r-1>>>1,o=e[i];if(!(void 0!==o&&M(o,t)>0))return;e[i]=t,e[r]=o,r=i}}(e,t,n)}function N(e){var t=e[0];return void 0===t?null:t}function C(e){var t=e[0];if(void 0!==t){var n=e.pop();return n!==t&&(e[0]=n,function(e,t,n){var r=n,i=e.length;for(;r$){if(($*=2)>V)return console.error("Scheduler Profiling: Event log exceeded maximum size. Don't forget to call `stopLoggingProfilingEvents()`."),void oe();var n=new Int32Array(4*$);n.set(G),q=n.buffer,G=n}G.set(e,t)}}function oe(){var e=q;return $=0,q=null,G=null,Q=0,e}function ae(e,t){F[Y]++,null!==G&&ie([K,1e3*t,e.id,e.priorityLevel])}function se(e,t){F[z]=R,F[W]=0,F[Y]--,null!==G&&ie([Z,1e3*t,e.id])}function ue(e,t){F[z]=R,F[W]=0,F[B]=0,null!==G&&ie([te,1e3*t,e.id,j])}var le=-1,ce=250,fe=5e3,de=1e4,he=1073741823,pe=[],me=[],ge=1,ve=null,ye=D,be=!1,_e=!1,we=!1;function ke(e){for(var t=N(me);null!==t;){if(null===t.callback)C(me);else{if(!(t.startTime<=e))return;C(me),t.sortIndex=t.expirationTime,T(pe,t),ae(t,e),t.isQueued=!0}t=N(me)}}function xe(t){if(we=!1,ke(t),!_e)if(null!==N(pe))_e=!0,e(Ee);else{var r=N(me);null!==r&&n(xe,r.startTime-t)}}function Ee(e,n){var i;i=n,null!==G&&ie([re,1e3*i,U]),_e=!1,we&&(we=!1,r()),be=!0;var o=ye;try{if(!s)return Se(e,n);try{return Se(e,n)}catch(e){if(null!==ve){var a=t.unstable_now();!function(e,t){F[z]=R,F[W]=0,F[Y]--,null!==G&&ie([X,1e3*t,e.id])}(ve,a),ve.isQueued=!1}throw e}}finally{ve=null,ye=o,be=!1,function(e){U++,null!==G&&ie([ne,1e3*e,U])}(t.unstable_now())}}function Se(e,r){var o,s,u=r;for(ke(u),ve=N(pe);null!==ve&&!a&&(!(ve.expirationTime>u)||e&&!i());){var l=ve.callback;if(null!==l){ve.callback=null,ye=ve.priorityLevel;var c=ve.expirationTime<=u;o=ve,s=u,j++,F[z]=o.priorityLevel,F[W]=o.id,F[B]=j,null!==G&&ie([ee,1e3*s,o.id,j]);var f=l(c);u=t.unstable_now(),"function"==typeof f?(ve.callback=f,ue(ve,u)):(se(ve,u),ve.isQueued=!1,ve===N(pe)&&C(pe)),ke(u)}else C(pe);ve=N(pe)}if(null!==ve)return!0;var d=N(me);return null!==d&&n(xe,d.startTime-u),!1}function Te(e){switch(e){case P:return le;case O:return ce;case I:return he;case A:return de;case D:default:return fe}}var Ne=o,Ce={startLoggingProfilingEvents:function(){$=H,q=new ArrayBuffer(4*$),G=new Int32Array(q),Q=0},stopLoggingProfilingEvents:oe,sharedProfilingBuffer:L};t.unstable_IdlePriority=I,t.unstable_ImmediatePriority=P,t.unstable_LowPriority=A,t.unstable_NormalPriority=D,t.unstable_Profiling=Ce,t.unstable_UserBlockingPriority=O,t.unstable_cancelCallback=function(e){e.isQueued&&(function(e,t){F[Y]--,null!==G&&ie([J,1e3*t,e.id])}(e,t.unstable_now()),e.isQueued=!1),e.callback=null},t.unstable_continueExecution=function(){_e||be||(_e=!0,e(Ee))},t.unstable_getCurrentPriorityLevel=function(){return ye},t.unstable_getFirstCallbackNode=function(){return N(pe)},t.unstable_next=function(e){var t;switch(ye){case P:case O:case D:t=D;break;default:t=ye}var n=ye;ye=t;try{return e()}finally{ye=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=Ne,t.unstable_runWithPriority=function(e,t){switch(e){case P:case O:case D:case A:case I:break;default:e=D}var n=ye;ye=e;try{return t()}finally{ye=n}},t.unstable_scheduleCallback=function(i,o,a){var s,u,l=t.unstable_now();if("object"==typeof a&&null!==a){var c=a.delay;s="number"==typeof c&&c>0?l+c:l,u="number"==typeof a.timeout?a.timeout:Te(i)}else u=Te(i),s=l;var f=s+u,d={id:ge++,callback:o,priorityLevel:i,startTime:s,expirationTime:f,sortIndex:-1,isQueued:!1};return s>l?(d.sortIndex=s,T(me,d),null===N(pe)&&d===N(me)&&(we?r():we=!0,n(xe,s-l))):(d.sortIndex=f,T(pe,d),ae(d,l),d.isQueued=!0,_e||be||(_e=!0,e(Ee))),d},t.unstable_shouldYield=function(){var e=t.unstable_now();ke(e);var n=N(pe);return n!==ve&&null!==ve&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime