2018-09-20 02:29:36 +03:00
|
|
|
!:
|
|
|
|
:: lighter than eyre
|
|
|
|
::
|
|
|
|
|= pit=vase
|
2018-09-28 21:24:41 +03:00
|
|
|
=, light
|
2018-09-20 02:29:36 +03:00
|
|
|
:: 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
|
2018-11-16 00:43:10 +03:00
|
|
|
$% :: %b: to behn
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
$: %b
|
|
|
|
::
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
$% [%rest p=@da]
|
|
|
|
[%wait p=@da]
|
2018-11-15 21:27:10 +03:00
|
|
|
== ==
|
|
|
|
:: %f: to ford
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
$: %f
|
|
|
|
::
|
|
|
|
::
|
|
|
|
$% [%build our=@p live=? schematic=schematic:ford]
|
2018-10-24 01:08:17 +03:00
|
|
|
[%kill our=@p]
|
2018-09-21 02:36:04 +03:00
|
|
|
== ==
|
|
|
|
:: %g: to gall
|
|
|
|
::
|
|
|
|
$: %g
|
|
|
|
::
|
|
|
|
::
|
|
|
|
$% [%deal id=sock data=cush:gall]
|
2018-09-20 02:29:36 +03:00
|
|
|
== == ==
|
2018-09-24 21:48:19 +03:00
|
|
|
:: +sign: private response from another vane to ford
|
|
|
|
::
|
|
|
|
+$ sign
|
2018-11-20 01:59:58 +03:00
|
|
|
$% :: %b: from behn
|
|
|
|
::
|
|
|
|
$: %b
|
|
|
|
::
|
|
|
|
::
|
|
|
|
$% [%wake ~]
|
|
|
|
== ==
|
|
|
|
:: %f: from ford
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
|
|
|
$: %f
|
|
|
|
::
|
|
|
|
::
|
|
|
|
$% [%made date=@da result=made-result:ford]
|
|
|
|
== ==
|
|
|
|
:: %g: from gall
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
|
|
|
$: %g
|
|
|
|
::
|
2018-10-04 00:05:36 +03:00
|
|
|
::
|
|
|
|
$% [%unto p=cuft:gall]
|
2018-10-03 21:36:45 +03:00
|
|
|
== == ==
|
2018-09-20 02:29:36 +03:00
|
|
|
--
|
|
|
|
:: more structures
|
|
|
|
::
|
|
|
|
|%
|
|
|
|
++ axle
|
|
|
|
$: :: date: date at which light's state was updated to this data structure
|
|
|
|
::
|
|
|
|
date=%~2018.9.12
|
|
|
|
:: ship: the ship name.
|
|
|
|
::
|
|
|
|
:: TODO: Remove when we single home.
|
|
|
|
::
|
|
|
|
ship=(unit ship)
|
|
|
|
:: client-state: state of outbound requests
|
|
|
|
::
|
2018-10-26 02:32:54 +03:00
|
|
|
client-state=state:client
|
2018-09-20 02:29:36 +03:00
|
|
|
:: server-state: state of inbound requests
|
|
|
|
::
|
|
|
|
=server-state
|
|
|
|
==
|
2018-10-10 21:51:52 +03:00
|
|
|
:: +client: light as an http client
|
|
|
|
::
|
|
|
|
++ client
|
|
|
|
|%
|
|
|
|
:: +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
|
|
|
|
::
|
2018-10-26 02:32:54 +03:00
|
|
|
connection-by-id=(map @ud [=duct =in-progress-http-request])
|
2018-10-10 21:51:52 +03:00
|
|
|
==
|
|
|
|
:: +in-progress-http-request: state around an outbound http
|
|
|
|
::
|
|
|
|
+$ in-progress-http-request
|
|
|
|
$: :: remaining-redirects: http limit of number of redirects before error
|
|
|
|
::
|
2018-10-26 02:32:54 +03:00
|
|
|
remaining-redirects=@ud
|
2018-10-10 21:51:52 +03:00
|
|
|
:: remaining-retries: number of times to retry the request
|
|
|
|
::
|
2018-10-26 02:32:54 +03:00
|
|
|
remaining-retries=@ud
|
2018-10-10 21:51:52 +03:00
|
|
|
:: chunks: a list of partial results returned from unix
|
|
|
|
::
|
|
|
|
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)
|
|
|
|
==
|
|
|
|
--
|
2018-09-20 02:29:36 +03:00
|
|
|
:: +server-state: state relating to open inbound HTTP connections
|
|
|
|
::
|
|
|
|
+$ server-state
|
|
|
|
$: :: bindings: actions to dispatch to when a binding matches
|
|
|
|
::
|
|
|
|
:: Eyre is responsible for keeping its bindings sorted so that it
|
|
|
|
:: will trigger on the most specific binding first. Eyre should send
|
|
|
|
:: back an error response if an already bound binding exists.
|
|
|
|
::
|
|
|
|
:: TODO: It would be nice if we had a path trie. We could decompose
|
|
|
|
:: the :binding into a (map (unit @t) (trie knot =action)).
|
|
|
|
::
|
|
|
|
bindings=(list [=binding =duct =action])
|
2018-09-24 21:48:19 +03:00
|
|
|
:: connections: open http connections not fully complete
|
2018-09-21 02:36:04 +03:00
|
|
|
::
|
2018-09-24 21:48:19 +03:00
|
|
|
connections=(map duct outstanding-connection)
|
2018-09-27 02:18:40 +03:00
|
|
|
:: authentication-state: state managed by the +authentication core
|
|
|
|
::
|
|
|
|
=authentication-state
|
2018-11-15 21:27:10 +03:00
|
|
|
:: channel-state: state managed by the +channel core
|
|
|
|
::
|
|
|
|
=channel-state
|
2018-09-24 21:48:19 +03:00
|
|
|
==
|
|
|
|
:: +outstanding-connection: open http connections not fully complete:
|
|
|
|
::
|
|
|
|
:: This refers to outstanding connections where the connection to
|
|
|
|
:: outside is opened and we are currently waiting on ford or an app to
|
|
|
|
:: produce the results.
|
|
|
|
::
|
|
|
|
+$ outstanding-connection
|
|
|
|
$: :: action: the action that had matched
|
|
|
|
::
|
|
|
|
=action
|
2018-10-25 00:31:19 +03:00
|
|
|
:: inbound-request: the original request which caused this connection
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
2018-10-25 00:31:19 +03:00
|
|
|
=inbound-request
|
2018-09-24 21:48:19 +03:00
|
|
|
:: code: the status code, if sent
|
|
|
|
::
|
|
|
|
code=(unit @ud)
|
|
|
|
:: headers: the headers, if sent
|
|
|
|
::
|
|
|
|
headers=(unit header-list)
|
|
|
|
:: bytes-sent: the total bytes sent in response
|
2018-09-21 02:36:04 +03:00
|
|
|
::
|
2018-09-24 21:48:19 +03:00
|
|
|
bytes-sent=@ud
|
2018-09-20 02:29:36 +03:00
|
|
|
==
|
|
|
|
:: +action: the action to take when a binding matches an incoming request
|
|
|
|
::
|
|
|
|
+$ action
|
|
|
|
$% :: dispatch to a generator
|
|
|
|
::
|
2018-10-11 01:26:51 +03:00
|
|
|
[%gen =generator]
|
2018-09-20 02:29:36 +03:00
|
|
|
:: dispatch to an application
|
|
|
|
::
|
|
|
|
[%app app=term]
|
2018-09-27 02:18:40 +03:00
|
|
|
:: internal authentication page
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
[%authentication ~]
|
|
|
|
:: gall channel system
|
2018-11-13 22:12:59 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
[%channel ~]
|
2018-09-27 02:18:40 +03:00
|
|
|
==
|
2018-10-26 02:32:54 +03:00
|
|
|
:: +authentication-state: state used in the login system
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
|
|
|
+$ authentication-state
|
2018-11-15 21:27:10 +03:00
|
|
|
$: :: sessions: a mapping of session cookies to session information
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
|
|
|
sessions=(map @uv session)
|
|
|
|
==
|
|
|
|
:: +session: server side data about a session
|
|
|
|
::
|
|
|
|
+$ session
|
2018-11-15 21:27:10 +03:00
|
|
|
$: :: expiry-time: when this session expires
|
2018-10-26 02:32:54 +03:00
|
|
|
::
|
|
|
|
:: We check this server side, too, so we aren't relying on the browser
|
|
|
|
:: to properly handle cookie expiration as a security mechanism.
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
2018-09-27 02:18:40 +03:00
|
|
|
expiry-time=@da
|
2018-10-26 02:32:54 +03:00
|
|
|
::
|
|
|
|
:: TODO: We should add a system for individual capabilities; we should
|
|
|
|
:: mint some sort of long lived cookie for mobile apps which only has
|
|
|
|
:: access to a single application path.
|
2018-09-20 02:29:36 +03:00
|
|
|
==
|
2018-11-15 21:27:10 +03:00
|
|
|
:: channel-state: state used in the channel system
|
|
|
|
::
|
|
|
|
+$ channel-state
|
|
|
|
$: :: session: mapping between an arbitrary key to a channel
|
|
|
|
::
|
|
|
|
session=(map @t channel)
|
|
|
|
==
|
2018-11-21 21:34:22 +03:00
|
|
|
:: +timer: a reference to a timer so we can cancel or update it.
|
|
|
|
::
|
|
|
|
+$ timer
|
|
|
|
$: :: date: time when the timer will fire
|
|
|
|
::
|
|
|
|
date=@da
|
|
|
|
:: duct: duct that set the timer so we can cancel
|
|
|
|
::
|
|
|
|
=duct
|
|
|
|
==
|
2018-11-15 21:27:10 +03:00
|
|
|
:: channel: connection to the browser
|
|
|
|
::
|
|
|
|
:: Channels are the main method where a webpage communicates with Gall
|
|
|
|
:: apps. Subscriptions and pokes are issues with PUT requests on a path,
|
|
|
|
:: while GET requests on that same path open a persistent EventSource
|
|
|
|
:: channel.
|
|
|
|
::
|
|
|
|
:: The EventSource API is a sequence number based API that browser provide
|
|
|
|
:: which allow the server to push individual events to the browser over a
|
|
|
|
:: connection held open. In case of reconnection, the browser will send a
|
|
|
|
:: 'Last-Event-Id: ' header to the server; the server then resends all
|
|
|
|
:: events since then.
|
|
|
|
::
|
2018-11-21 21:34:22 +03:00
|
|
|
:: TODO: Send \n as a heartbeat every 20 seconds.
|
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
+$ channel
|
2018-11-21 04:06:04 +03:00
|
|
|
$: :: channel-state: expiration time or the duct currently listening
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
:: For each channel, there is at most one open EventSource
|
|
|
|
:: connection. A 400 is issues on duplicate attempts to connect to the
|
|
|
|
:: same channel. When an EventSource isn't connected, we set a timer
|
|
|
|
:: to reap the subscriptions. This timer shouldn't be too short
|
|
|
|
:: because the
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-21 21:34:22 +03:00
|
|
|
state=(each timer duct)
|
2018-11-15 21:27:10 +03:00
|
|
|
:: next-id: next sequence number to use
|
|
|
|
::
|
|
|
|
next-id=@ud
|
|
|
|
:: events: unacknowledged events
|
|
|
|
::
|
|
|
|
:: We keep track of all events where we haven't received a
|
|
|
|
:: 'Last-Event-Id: ' response from the client or a per-poke {'ack':
|
|
|
|
:: ...} call. When there's an active EventSource connection on this
|
|
|
|
:: channel, we send the event but we still add it to events because we
|
|
|
|
:: can't assume it got received until we get an acknowledgment.
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
events=(qeu [id=@ud lines=wall])
|
2018-11-15 21:27:10 +03:00
|
|
|
:: subscriptions: gall subscriptions
|
|
|
|
::
|
|
|
|
:: We maintain a list of subscriptions so if a channel times out, we
|
|
|
|
:: can cancel all the subscriptions we've made.
|
|
|
|
::
|
2018-11-20 01:59:58 +03:00
|
|
|
subscriptions=(list [ship=@p app=term =path])
|
2018-11-15 21:27:10 +03:00
|
|
|
==
|
|
|
|
:: channel-request: an action requested on a channel
|
|
|
|
::
|
|
|
|
+$ channel-request
|
|
|
|
$% :: %ack: acknowledges that the client has received events up to :id
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
[%ack event-id=@ud]
|
2018-11-15 21:27:10 +03:00
|
|
|
:: %poke: pokes an application, translating :json to :mark.
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
[%poke request-id=@ud ship=@p app=term mark=@tas =json]
|
2018-11-15 21:27:10 +03:00
|
|
|
:: %subscribe: subscribes to an application path
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
[%subscribe request-id=@ud ship=@p app=term =path]
|
2018-11-15 21:27:10 +03:00
|
|
|
:: %unsubscribe: unsubscribes from an application path
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
[%unsubscribe request-id=@ud ship=@p app=term =path]
|
2018-11-15 21:27:10 +03:00
|
|
|
==
|
2018-11-20 01:59:58 +03:00
|
|
|
:: channel-timeout: the delay before a channel should be reaped
|
|
|
|
::
|
|
|
|
++ channel-timeout ~h12
|
2018-09-20 02:29:36 +03:00
|
|
|
--
|
|
|
|
:: utilities
|
|
|
|
::
|
|
|
|
|%
|
2018-11-15 21:27:10 +03:00
|
|
|
:: +parse-channel-request: parses a list of channel-requests
|
|
|
|
::
|
2018-11-16 00:43:10 +03:00
|
|
|
:: Parses a json array into a list of +channel-request. If any of the items
|
|
|
|
:: in the list fail to parse, the entire thing fails so we can 400 properly
|
|
|
|
:: to the client.
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
++ parse-channel-request
|
2018-11-16 00:43:10 +03:00
|
|
|
|= request-list=json
|
|
|
|
^- (unit (list channel-request))
|
|
|
|
:: parse top
|
|
|
|
::
|
|
|
|
=, dejs-soft:format
|
|
|
|
=- ((ar -) request-list)
|
|
|
|
::
|
|
|
|
|= item=json
|
|
|
|
^- (unit channel-request)
|
|
|
|
::
|
|
|
|
?~ maybe-key=((ot action+so ~) item)
|
|
|
|
~
|
|
|
|
?: =('ack' u.maybe-key)
|
2018-11-21 04:06:04 +03:00
|
|
|
((pe %ack (ot event-id+ni ~)) item)
|
2018-11-16 00:43:10 +03:00
|
|
|
?: =('poke' u.maybe-key)
|
2018-11-21 04:06:04 +03:00
|
|
|
((pe %poke (ot id+ni ship+(su fed:ag) app+so mark+(su sym) json+some ~)) item)
|
2018-11-16 00:43:10 +03:00
|
|
|
?: =('subscribe' u.maybe-key)
|
|
|
|
%. item
|
|
|
|
%+ pe %subscribe
|
2018-11-21 04:06:04 +03:00
|
|
|
(ot id+ni ship+(su fed:ag) app+so path+(su ;~(pfix fas (more fas urs:ab))) ~)
|
2018-11-16 00:43:10 +03:00
|
|
|
?: =('unsubscribe' u.maybe-key)
|
|
|
|
%. item
|
|
|
|
%+ pe %unsubscribe
|
2018-11-21 04:06:04 +03:00
|
|
|
(ot id+ni ship+(su fed:ag) app+so path+(su ;~(pfix fas (more fas urs:ab))) ~)
|
2018-11-16 00:43:10 +03:00
|
|
|
:: if we reached this, we have an invalid action key. fail parsing.
|
|
|
|
::
|
|
|
|
~
|
2018-09-21 02:36:04 +03:00
|
|
|
:: +file-not-found-page: 404 page for when all other options failed
|
|
|
|
::
|
|
|
|
++ file-not-found-page
|
|
|
|
|= url=@t
|
|
|
|
^- octs
|
|
|
|
%- as-octs:mimes:html
|
|
|
|
%- crip
|
|
|
|
%- en-xml:html
|
|
|
|
;html
|
|
|
|
;head
|
|
|
|
;title:"404 Not Found"
|
|
|
|
==
|
|
|
|
;body
|
|
|
|
;h1:"Not Found"
|
|
|
|
;p:"The requested URL {<(trip url)>} was not found on this server."
|
|
|
|
==
|
|
|
|
==
|
2018-09-27 02:18:40 +03:00
|
|
|
:: +login-page: internal page to login to an Urbit
|
|
|
|
::
|
|
|
|
++ login-page
|
|
|
|
|= redirect-url=(unit @t)
|
|
|
|
^- octs
|
|
|
|
=+ redirect-str=?~(redirect-url "" (trip u.redirect-url))
|
|
|
|
%- as-octs:mimes:html
|
|
|
|
%- crip
|
|
|
|
%- en-xml:html
|
|
|
|
;html
|
|
|
|
;head
|
|
|
|
;title:"Sign in"
|
|
|
|
==
|
|
|
|
;body
|
|
|
|
;h1:"Sign in"
|
|
|
|
;form(action "/~/login", method "post", enctype "application/x-www-form-urlencoded")
|
|
|
|
;input(type "password", name "password", placeholder "passcode");
|
|
|
|
;input(type "hidden", name "redirect", value redirect-str);
|
|
|
|
;button(type "submit"):"Login"
|
|
|
|
==
|
|
|
|
==
|
|
|
|
==
|
2018-11-21 04:06:04 +03:00
|
|
|
:: +render-tang-to-marl: renders a tang and adds <br/> tags between each line
|
2018-10-11 01:26:51 +03:00
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
++ render-tang-to-marl
|
2018-10-11 01:26:51 +03:00
|
|
|
|= {wid/@u tan/tang}
|
|
|
|
^- marl
|
|
|
|
=/ raw=(list tape) (zing (turn tan |=(a/tank (wash 0^wid a))))
|
|
|
|
::
|
|
|
|
|- ^- marl
|
|
|
|
?~ raw ~
|
|
|
|
[;/(i.raw) ;br; $(raw t.raw)]
|
2018-11-21 04:06:04 +03:00
|
|
|
:: +render-tang-to-wall: renders tang as text lines
|
|
|
|
::
|
|
|
|
++ render-tang-to-wall
|
|
|
|
|= {wid/@u tan/tang}
|
|
|
|
^- wall
|
|
|
|
(zing (turn tan |=(a=tank (wash 0^wid a))))
|
|
|
|
:: +wall-to-octs: text to binary output
|
|
|
|
::
|
|
|
|
++ wall-to-octs
|
|
|
|
|= =wall
|
|
|
|
^- (unit octs)
|
|
|
|
::
|
|
|
|
?: =(~ wall)
|
|
|
|
~
|
|
|
|
::
|
|
|
|
:- ~
|
|
|
|
%- as-octs:mimes:html
|
|
|
|
%- crip
|
|
|
|
%- zing
|
|
|
|
%+ turn wall
|
|
|
|
|= t=tape
|
|
|
|
"{t}\0a"
|
2018-10-10 21:51:52 +03:00
|
|
|
:: +internal-server-error: 500 page, with a tang
|
|
|
|
::
|
|
|
|
++ internal-server-error
|
2018-10-11 01:26:51 +03:00
|
|
|
|= [authorized=? url=@t t=tang]
|
2018-10-10 21:51:52 +03:00
|
|
|
^- octs
|
|
|
|
%- as-octs:mimes:html
|
|
|
|
%- crip
|
|
|
|
%- en-xml:html
|
|
|
|
;html
|
|
|
|
;head
|
|
|
|
;title:"500 Internal Server Error"
|
|
|
|
==
|
|
|
|
;body
|
|
|
|
;h1:"Internal Server Error"
|
|
|
|
;p:"There was an error while handling the request for {<(trip url)>}."
|
|
|
|
;* ?: authorized
|
|
|
|
;=
|
2018-11-21 04:06:04 +03:00
|
|
|
;code:"*{(render-tang-to-marl 80 t)}"
|
2018-10-10 21:51:52 +03:00
|
|
|
==
|
|
|
|
~
|
|
|
|
==
|
|
|
|
==
|
|
|
|
:: +format-ud-as-integer: prints a number for consumption outside urbit
|
|
|
|
::
|
|
|
|
++ format-ud-as-integer
|
|
|
|
|= a=@ud
|
|
|
|
^- @t
|
|
|
|
?: =(0 a) '0'
|
|
|
|
%- crip
|
|
|
|
%- flop
|
|
|
|
|- ^- tape
|
|
|
|
?:(=(0 a) ~ [(add '0' (mod a 10)) $(a (div a 10))])
|
2018-10-11 21:28:27 +03:00
|
|
|
:: +path-matches: returns %.y if :prefix is a prefix of :full
|
|
|
|
::
|
|
|
|
++ path-matches
|
|
|
|
|= [prefix=path full=path]
|
|
|
|
^- ?
|
|
|
|
?~ prefix
|
|
|
|
%.y
|
|
|
|
?~ full
|
|
|
|
%.n
|
|
|
|
?. =(i.prefix i.full)
|
|
|
|
%.n
|
|
|
|
$(prefix t.prefix, full t.full)
|
2018-09-20 02:29:36 +03:00
|
|
|
:: +get-header: returns the value for :header, if it exists in :header-list
|
|
|
|
::
|
|
|
|
++ get-header
|
|
|
|
|= [header=@t =header-list]
|
|
|
|
^- (unit @t)
|
|
|
|
::
|
|
|
|
?~ header-list
|
|
|
|
~
|
|
|
|
::
|
|
|
|
?: =(key.i.header-list header)
|
|
|
|
`value.i.header-list
|
|
|
|
::
|
|
|
|
$(header-list t.header-list)
|
2018-10-04 00:05:36 +03:00
|
|
|
:: +simplified-url-parser: returns [(each @if @t) (unit port=@ud)]
|
|
|
|
::
|
|
|
|
++ simplified-url-parser
|
|
|
|
;~ plug
|
|
|
|
;~ pose
|
|
|
|
%+ stag %ip
|
|
|
|
=+ tod=(ape:ag ted:ab)
|
|
|
|
%+ bass 256
|
|
|
|
;~(plug tod (stun [3 3] ;~(pfix dot tod)))
|
|
|
|
::
|
|
|
|
(stag %site (cook crip (star ;~(pose dot alp))))
|
|
|
|
==
|
|
|
|
;~ pose
|
|
|
|
(stag ~ ;~(pfix col dim:ag))
|
|
|
|
(easy ~)
|
|
|
|
==
|
|
|
|
==
|
2018-10-26 02:32:54 +03:00
|
|
|
:: +per-client-event: per-event client core
|
|
|
|
::
|
|
|
|
++ per-client-event
|
|
|
|
|= [[our=@p eny=@ =duct now=@da scry=sley] state=state:client]
|
|
|
|
|%
|
|
|
|
++ fetch
|
|
|
|
|= [=http-request =outbound-config]
|
|
|
|
^- [(list move) state:client]
|
|
|
|
:: 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?
|
|
|
|
::
|
|
|
|
:: :- [duct %pass /fetch
|
|
|
|
[~ state]
|
|
|
|
--
|
|
|
|
:: +per-server-event: per-event server core
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
++ per-server-event
|
2018-11-15 21:27:10 +03:00
|
|
|
:: gate that produces the +per-server-event core from event information
|
|
|
|
::
|
2018-09-27 02:18:40 +03:00
|
|
|
|= [[our=@p eny=@ =duct now=@da scry=sley] state=server-state]
|
2018-09-20 02:29:36 +03:00
|
|
|
|%
|
2018-09-21 02:36:04 +03:00
|
|
|
:: +request: starts handling an inbound http request
|
|
|
|
::
|
2018-09-20 02:29:36 +03:00
|
|
|
++ request
|
|
|
|
|= [secure=? =address =http-request]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
2018-10-01 21:37:30 +03:00
|
|
|
=+ host=(get-header 'host' header-list.http-request)
|
2018-09-20 02:29:36 +03:00
|
|
|
=+ action=(get-action-for-binding host url.http-request)
|
2018-09-21 02:36:04 +03:00
|
|
|
:: if no action matches, send the built in 404 page.
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
?~ action
|
2018-09-27 02:18:40 +03:00
|
|
|
%^ return-static-data-on-duct 404 'text/html'
|
|
|
|
(file-not-found-page url.http-request)
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
|
|
|
=/ authenticated (request-is-logged-in:authentication http-request)
|
2018-09-21 02:36:04 +03:00
|
|
|
:: record that we started an asynchronous response
|
|
|
|
::
|
2018-10-25 00:31:19 +03:00
|
|
|
=/ connection=outstanding-connection
|
|
|
|
[u.action [authenticated secure address http-request] ~ ~ 0]
|
|
|
|
=. connections.state
|
|
|
|
(~(put by connections.state) duct connection)
|
2018-09-27 21:16:59 +03:00
|
|
|
::
|
2018-09-20 02:29:36 +03:00
|
|
|
?- -.u.action
|
|
|
|
::
|
|
|
|
%gen
|
2018-09-21 02:36:04 +03:00
|
|
|
::
|
2018-10-11 01:26:51 +03:00
|
|
|
=- [[duct %pass /run-build %f %build our live=%.n schematic=-]~ state]
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
2018-10-11 01:26:51 +03:00
|
|
|
=- [%cast [our desk.generator.u.action] %mime -]
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
2018-10-11 01:26:51 +03:00
|
|
|
:+ %call
|
|
|
|
:+ %call
|
|
|
|
[%core [[our desk.generator.u.action] (flop path.generator.u.action)]]
|
|
|
|
:: TODO: Figure out what goes in generators. We need to slop the
|
|
|
|
:: prelude with the arguments passed in.
|
|
|
|
::
|
|
|
|
[%$ %noun !>([[now=now eny=eny bek=[our desk.generator.u.action [%da now]]] ~ ~])]
|
|
|
|
[%$ %noun !>([authenticated http-request])]
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
%app
|
2018-09-21 02:36:04 +03:00
|
|
|
:_ state
|
|
|
|
:_ ~
|
|
|
|
:^ duct %pass /run-app/[app.u.action]
|
|
|
|
^- note
|
|
|
|
:^ %g %deal [our our]
|
|
|
|
:: todo: i don't entirely understand gall; there's a way to make a gall
|
|
|
|
:: use a %handle arm instead of a sub-%poke with the
|
|
|
|
:: %handle-http-request type.
|
|
|
|
::
|
|
|
|
^- cush:gall
|
2018-10-24 00:25:20 +03:00
|
|
|
:* app.u.action
|
|
|
|
%poke
|
|
|
|
%handle-http-request
|
2018-10-25 00:31:19 +03:00
|
|
|
!>(inbound-request.connection)
|
2018-10-24 00:25:20 +03:00
|
|
|
==
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
%authentication
|
2018-09-27 02:18:40 +03:00
|
|
|
(handle-request:authentication secure address http-request)
|
2018-11-13 22:12:59 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
%channel
|
2018-11-21 04:06:04 +03:00
|
|
|
(handle-request:by-channel secure authenticated address http-request)
|
2018-09-27 02:18:40 +03:00
|
|
|
==
|
2018-10-22 21:36:30 +03:00
|
|
|
:: +cancel-request: handles a request being externally aborted
|
|
|
|
::
|
|
|
|
++ cancel-request
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
~& [%cancel-request duct]
|
2018-10-24 00:25:20 +03:00
|
|
|
::
|
|
|
|
?~ connection=(~(get by connections.state) duct)
|
|
|
|
:: nothing has handled this connection
|
|
|
|
::
|
|
|
|
[~ state]
|
|
|
|
::
|
2018-10-24 01:08:17 +03:00
|
|
|
=. connections.state (~(del by connections.state) duct)
|
|
|
|
::
|
2018-10-24 00:25:20 +03:00
|
|
|
?- -.action.u.connection
|
|
|
|
::
|
|
|
|
%gen
|
2018-10-24 01:08:17 +03:00
|
|
|
:_ state
|
|
|
|
[duct %pass /run-build %f %kill our]~
|
2018-10-24 00:25:20 +03:00
|
|
|
::
|
|
|
|
%app
|
|
|
|
:_ state
|
|
|
|
:_ ~
|
|
|
|
:^ duct %pass /run-app/[app.action.u.connection]
|
|
|
|
^- note
|
|
|
|
:^ %g %deal [our our]
|
|
|
|
:: todo: i don't entirely understand gall; there's a way to make a gall
|
|
|
|
:: use a %handle arm instead of a sub-%poke with the
|
|
|
|
:: %handle-http-request type.
|
|
|
|
::
|
|
|
|
^- cush:gall
|
|
|
|
:* app.action.u.connection
|
|
|
|
%poke
|
|
|
|
%handle-http-cancel
|
2018-10-25 00:31:19 +03:00
|
|
|
!>(inbound-request.u.connection)
|
2018-10-24 00:25:20 +03:00
|
|
|
==
|
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
%authentication
|
2018-10-24 00:25:20 +03:00
|
|
|
[~ state]
|
2018-11-13 22:12:59 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
%channel
|
2018-11-13 22:12:59 +03:00
|
|
|
:: todo: this part actually matters.
|
|
|
|
::
|
|
|
|
[~ state]
|
2018-10-24 00:25:20 +03:00
|
|
|
==
|
2018-09-27 02:18:40 +03:00
|
|
|
:: +return-static-data-on-duct: returns one piece of data all at once
|
|
|
|
::
|
|
|
|
++ return-static-data-on-duct
|
|
|
|
|= [code=@ content-type=@t data=octs]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
:_ state
|
|
|
|
:_ ~
|
|
|
|
:+ duct %give
|
|
|
|
:* %http-response %start
|
|
|
|
status-code=code
|
|
|
|
^= headers
|
2018-10-01 21:37:30 +03:00
|
|
|
:~ ['content-type' content-type]
|
2018-10-10 21:51:52 +03:00
|
|
|
['content-length' (format-ud-as-integer p.data)]
|
2018-09-27 02:18:40 +03:00
|
|
|
==
|
|
|
|
data=[~ data]
|
|
|
|
complete=%.y
|
2018-09-20 02:29:36 +03:00
|
|
|
==
|
2018-09-27 02:18:40 +03:00
|
|
|
:: +authentication: per-event authentication as this Urbit's owner
|
|
|
|
::
|
|
|
|
:: Right now this hard codes the authentication page using the old +code
|
|
|
|
:: system, but in the future should be pluggable so we can use U2F or
|
|
|
|
:: WebAuthn or whatever is more secure than passwords.
|
|
|
|
::
|
|
|
|
++ authentication
|
|
|
|
|%
|
|
|
|
:: +handle-request: handles an http request for the
|
|
|
|
::
|
|
|
|
++ handle-request
|
|
|
|
|= [secure=? =address =http-request]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
:: if we received a simple get, just return the page
|
|
|
|
::
|
|
|
|
?: =('GET' method.http-request)
|
|
|
|
:: parse the arguments out of request uri
|
|
|
|
::
|
|
|
|
=+ request-line=(parse-request-line url.http-request)
|
|
|
|
%^ return-static-data-on-duct 200 'text/html'
|
|
|
|
(login-page (get-header 'redirect' args.request-line))
|
|
|
|
:: if we are not a post, return an error
|
|
|
|
::
|
|
|
|
?. =('POST' method.http-request)
|
|
|
|
~& [%something-other-than-get-post-on-login method.http-request]
|
|
|
|
(return-static-data-on-duct 400 'text/html' (login-page ~))
|
|
|
|
:: we are a post, and must process the body type as form data
|
|
|
|
::
|
|
|
|
?~ body.http-request
|
|
|
|
(return-static-data-on-duct 400 'text/html' (login-page ~))
|
|
|
|
::
|
|
|
|
=/ parsed=(unit (list [key=@t value=@t]))
|
|
|
|
(rush q.u.body.http-request yquy:de-purl:html)
|
|
|
|
?~ parsed
|
|
|
|
(return-static-data-on-duct 400 'text/html' (login-page ~))
|
|
|
|
::
|
|
|
|
?~ password=(get-header 'password' u.parsed)
|
|
|
|
(return-static-data-on-duct 400 'text/html' (login-page ~))
|
|
|
|
:: check that the password is correct
|
|
|
|
::
|
|
|
|
?. =(u.password code)
|
|
|
|
(return-static-data-on-duct 400 'text/html' (login-page ~))
|
|
|
|
:: mint a unique session cookie
|
|
|
|
::
|
|
|
|
=/ session=@uv
|
|
|
|
|-
|
|
|
|
=/ candidate=@uv (~(raw og eny) 128)
|
|
|
|
?. (~(has by sessions.authentication-state.state) candidate)
|
|
|
|
candidate
|
|
|
|
$(eny (shas %try-again candidate))
|
|
|
|
:: record cookie and record expiry time
|
|
|
|
::
|
|
|
|
=. sessions.authentication-state.state
|
|
|
|
(~(put by sessions.authentication-state.state) session (add now ~h24))
|
|
|
|
::
|
|
|
|
=/ cookie-line
|
|
|
|
%- crip
|
2018-10-25 00:31:19 +03:00
|
|
|
"urbauth={<session>}; Path=/; Max-Age=86400"
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
|
|
|
=/ new-location=@t
|
|
|
|
?~ redirect=(get-header 'redirect' u.parsed)
|
|
|
|
'/'
|
|
|
|
u.redirect
|
2018-10-25 00:31:19 +03:00
|
|
|
~& [%minting http-request]
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
|
|
|
:_ state
|
|
|
|
:_ ~
|
|
|
|
:+ duct %give
|
|
|
|
:* %http-response %start
|
|
|
|
status-code=307
|
|
|
|
^= headers
|
2018-10-01 21:37:30 +03:00
|
|
|
:~ ['location' new-location]
|
|
|
|
['set-cookie' cookie-line]
|
2018-09-27 02:18:40 +03:00
|
|
|
==
|
|
|
|
data=~
|
|
|
|
complete=%.y
|
|
|
|
==
|
|
|
|
:: +request-is-logged-in: checks to see if the request is authenticated
|
|
|
|
::
|
2018-09-27 21:16:59 +03:00
|
|
|
:: We are considered logged in if this http-request has an urbauth
|
|
|
|
:: Cookie which is not expired.
|
|
|
|
::
|
2018-09-27 02:18:40 +03:00
|
|
|
++ request-is-logged-in
|
|
|
|
|= =http-request
|
|
|
|
^- ?
|
2018-09-27 21:16:59 +03:00
|
|
|
:: are there cookies passed with this request?
|
|
|
|
::
|
|
|
|
:: TODO: In HTTP2, the client is allowed to put multiple 'Cookie'
|
|
|
|
:: headers.
|
|
|
|
::
|
2018-10-01 21:37:30 +03:00
|
|
|
?~ cookie-header=(get-header 'cookie' header-list.http-request)
|
2018-09-27 21:16:59 +03:00
|
|
|
%.n
|
|
|
|
:: is the cookie line is valid?
|
|
|
|
::
|
|
|
|
?~ cookies=(rush u.cookie-header cock:de-purl:html)
|
|
|
|
%.n
|
|
|
|
:: is there an urbauth cookie?
|
|
|
|
::
|
|
|
|
?~ urbauth=(get-header 'urbauth' u.cookies)
|
|
|
|
%.n
|
|
|
|
:: is this formatted like a valid session cookie?
|
|
|
|
::
|
|
|
|
?~ session-id=(rush u.urbauth ;~(pfix (jest '0v') viz:ag))
|
|
|
|
%.n
|
|
|
|
:: is this a session that we know about?
|
|
|
|
::
|
|
|
|
?~ session=(~(get by sessions.authentication-state.state) u.session-id)
|
|
|
|
%.n
|
|
|
|
:: is this session still valid?
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
2018-09-27 21:16:59 +03:00
|
|
|
(lte now expiry-time.u.session)
|
2018-09-27 02:18:40 +03:00
|
|
|
:: +code: returns the same as |code
|
|
|
|
::
|
|
|
|
:: This has the problem where the signature for sky vs sley.
|
|
|
|
::
|
|
|
|
++ code
|
|
|
|
^- @ta
|
|
|
|
'lidlut-tabwed-pillex-ridrup'
|
|
|
|
:: =+ pax=/(scot %p our)/code/(scot %da now)/(scot %p our)
|
|
|
|
:: %^ rsh 3 1
|
|
|
|
:: (scot %p (@ (need ((sloy scry) [151 %noun] %a pax))))
|
|
|
|
--
|
2018-11-15 21:27:10 +03:00
|
|
|
:: +channel: per-event handling of requests to the channel system
|
|
|
|
::
|
|
|
|
:: Eyre offers a remote interface to your Urbit through channels, which
|
|
|
|
:: are persistent connections on the server which
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
++ by-channel
|
2018-11-15 21:27:10 +03:00
|
|
|
:: moves: the moves to be sent out at the end of this event, reversed
|
|
|
|
::
|
|
|
|
=| moves=(list move)
|
|
|
|
|%
|
|
|
|
:: +handle-request: handles an http request for the subscription system
|
|
|
|
::
|
|
|
|
++ handle-request
|
|
|
|
|= [secure=? authenticated=? =address =http-request]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
:: if we're not authenticated error, but don't redirect.
|
|
|
|
::
|
|
|
|
:: We don't redirect because subscription stuff is never the toplevel
|
|
|
|
:: page; issuing a redirect won't help.
|
|
|
|
::
|
|
|
|
?. authenticated
|
|
|
|
:: TODO: Real 400 page.
|
|
|
|
::
|
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error authenticated url.http-request ~)
|
|
|
|
:: parse out the path key the subscription is on
|
|
|
|
::
|
|
|
|
=+ request-line=(parse-request-line url.http-request)
|
|
|
|
?. ?=([@t @t @t ~] site.request-line)
|
2018-11-21 04:06:04 +03:00
|
|
|
:: url is not of the form '/~/subscription/'
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error authenticated url.http-request ~)
|
2018-11-21 04:06:04 +03:00
|
|
|
:: channel-id: unique channel id parsed out of url
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
=+ channel-id=i.t.t.site.request-line
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
?: =('PUT' method.http-request)
|
|
|
|
:: PUT methods starts/modifies a channel, and returns a result immediately
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
(on-put-request channel-id http-request)
|
|
|
|
::
|
|
|
|
?: =('GET' method.http-request)
|
|
|
|
(on-get-request channel-id http-request)
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
~& %session-not-a-put
|
|
|
|
[~ state]
|
|
|
|
:: +handle-cancel: cancels an ongoing subscription
|
|
|
|
::
|
|
|
|
::++ handle-cancel
|
2018-11-21 04:06:04 +03:00
|
|
|
:: +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.
|
|
|
|
::
|
|
|
|
++ on-get-request
|
|
|
|
|= [channel-id=@t =http-request]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
:: if there's no channel-id, we must 404
|
|
|
|
::
|
|
|
|
?~ maybe-channel=(~(get by session.channel-state.state) channel-id)
|
|
|
|
%^ return-static-data-on-duct 404 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
|
|
|
:: if there's already a duct listening to this channel, we must 400
|
|
|
|
::
|
|
|
|
?: ?=([%| *] state.u.maybe-channel)
|
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
|
|
|
:: when opening an event-stream, we must cancel our timeout timer
|
|
|
|
::
|
|
|
|
=. moves
|
|
|
|
:_ moves
|
|
|
|
^- move
|
2018-11-21 21:34:22 +03:00
|
|
|
:^ duct.p.state.u.maybe-channel %pass /channel/timeout/[channel-id]
|
|
|
|
[%b %rest date.p.state.u.maybe-channel]
|
2018-11-21 04:06:04 +03:00
|
|
|
:: the http-request may include a 'Last-Event-Id' header
|
|
|
|
::
|
|
|
|
=/ maybe-last-event-id=(unit @ud)
|
|
|
|
?~ maybe-raw-header=(get-header 'Last-Event-ID' header-list.http-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
|
|
|
|
%- zing
|
|
|
|
%- flop
|
|
|
|
=/ queue events.u.maybe-channel
|
|
|
|
=| events=(list wall)
|
|
|
|
|-
|
|
|
|
^+ events
|
|
|
|
?: =(~ queue)
|
|
|
|
events
|
|
|
|
=^ head queue ~(get to queue)
|
|
|
|
$(events [lines.p.head events])
|
|
|
|
:: send the start event to the client
|
|
|
|
::
|
|
|
|
=. moves
|
|
|
|
:_ moves
|
|
|
|
:+ duct %give
|
|
|
|
:* %http-response %start 200
|
|
|
|
:~ ['content-type' 'text/event-stream']
|
|
|
|
['cache-control' 'no-cache']
|
|
|
|
['connection' 'keep-alive']
|
|
|
|
==
|
|
|
|
(wall-to-octs event-replay)
|
|
|
|
complete=%.n
|
|
|
|
==
|
|
|
|
:: clear the event queue and record the duct for future output
|
|
|
|
::
|
|
|
|
=. session.channel-state.state
|
|
|
|
%+ ~(jab by session.channel-state.state) channel-id
|
|
|
|
|= =channel
|
|
|
|
channel(events ~, state [%| duct])
|
|
|
|
::
|
|
|
|
[moves state]
|
|
|
|
:: +acknowledge-events: removes events before :last-event-id on :channel-id
|
|
|
|
::
|
|
|
|
++ acknowledge-events
|
|
|
|
|= [channel-id=@t last-event-id=@u]
|
|
|
|
^- server-state
|
|
|
|
%_ state
|
|
|
|
session.channel-state
|
|
|
|
%+ ~(jab by session.channel-state.state) channel-id
|
|
|
|
|= =channel
|
|
|
|
^+ channel
|
|
|
|
:: if the queue is empty, don't do anything else
|
|
|
|
::
|
|
|
|
?~ maybe-top=~(top to events.channel)
|
|
|
|
channel
|
|
|
|
:: if the oldest event is older than the event queue, pop it
|
|
|
|
::
|
|
|
|
?: (gte last-event-id id.u.maybe-top)
|
|
|
|
$(events.channel ~(nap to events.channel))
|
|
|
|
::
|
|
|
|
channel
|
|
|
|
==
|
2018-11-15 21:27:10 +03:00
|
|
|
:: +on-put-request: handles a PUT request
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
:: 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.
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
++ on-put-request
|
2018-11-21 04:06:04 +03:00
|
|
|
|= [channel-id=@t =http-request]
|
2018-11-15 21:27:10 +03:00
|
|
|
^- [(list move) server-state]
|
|
|
|
:: error when there's no body
|
|
|
|
::
|
|
|
|
?~ body.http-request
|
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
2018-11-16 02:27:49 +03:00
|
|
|
:: if the incoming body isn't json, this is a bad request, 400.
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
?~ maybe-json=(de-json:html q.u.body.http-request)
|
2018-11-15 21:27:10 +03:00
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
2018-11-16 02:27:49 +03:00
|
|
|
:: parse the json into an array of +channel-request items
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
?~ maybe-requests=(parse-channel-request u.maybe-json)
|
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
|
|
|
:: while weird, the request list could be empty
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
?: =(~ u.maybe-requests)
|
2018-11-15 21:27:10 +03:00
|
|
|
%^ return-static-data-on-duct 400 'text/html'
|
|
|
|
(internal-server-error %.y url.http-request ~)
|
2018-11-21 04:06:04 +03:00
|
|
|
:: check for the existence of the channel-id
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
:: if we have no session, create a new one set to expire in
|
|
|
|
:: :channel-timeout from now.
|
|
|
|
::
|
2018-11-17 01:30:23 +03:00
|
|
|
:: TODO: This is wrong. We always want to potentially update the
|
|
|
|
:: expiration time if there's no eventsource attached.
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
=? ..on-put-request !(~(has by session.channel-state.state) channel-id)
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
=/ expiration-time=@da (add now channel-timeout)
|
|
|
|
%_ ..on-put-request
|
2018-11-16 02:27:49 +03:00
|
|
|
session.channel-state.state
|
2018-11-21 04:06:04 +03:00
|
|
|
%+ ~(put by session.channel-state.state) channel-id
|
2018-11-21 21:34:22 +03:00
|
|
|
[[%& expiration-time duct] 0 ~ ~]
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
moves
|
|
|
|
:_ moves
|
|
|
|
^- move
|
2018-11-21 04:06:04 +03:00
|
|
|
[duct %pass /channel/timeout/[channel-id] %b %wait expiration-time]
|
2018-11-15 21:27:10 +03:00
|
|
|
==
|
|
|
|
:: for each request, execute the action passed in
|
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
=+ requests=u.maybe-requests
|
2018-11-20 01:59:58 +03:00
|
|
|
:: gall-moves: put moves here first so we can flop for ordering
|
|
|
|
::
|
|
|
|
:: TODO: Have an error state where any invalid duplicate subscriptions
|
|
|
|
:: or other errors cause the entire thing to fail with a 400 and a tang.
|
|
|
|
::
|
|
|
|
=| gall-moves=(list move)
|
2018-11-15 21:27:10 +03:00
|
|
|
|-
|
2018-11-17 01:30:23 +03:00
|
|
|
::
|
2018-11-15 21:27:10 +03:00
|
|
|
?~ requests
|
2018-11-17 01:30:23 +03:00
|
|
|
:: this is a PUT request; we must mark it as complete
|
|
|
|
::
|
|
|
|
=. moves
|
|
|
|
:_ moves
|
|
|
|
^- move
|
|
|
|
:+ duct %give
|
|
|
|
:* %http-response %start
|
|
|
|
status-code=200
|
|
|
|
headers=~
|
|
|
|
data=~
|
|
|
|
complete=%.y
|
|
|
|
==
|
|
|
|
::
|
2018-11-20 01:59:58 +03:00
|
|
|
[(weld (flop gall-moves) moves) state]
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
?- -.i.requests
|
|
|
|
%ack
|
|
|
|
!!
|
|
|
|
::
|
|
|
|
%poke
|
|
|
|
::
|
2018-11-20 01:59:58 +03:00
|
|
|
=. gall-moves
|
|
|
|
:_ gall-moves
|
2018-11-17 01:30:23 +03:00
|
|
|
^- move
|
2018-11-21 04:06:04 +03:00
|
|
|
:^ duct %pass /channel/poke/[channel-id]/(scot %ud request-id.i.requests)
|
2018-11-17 01:30:23 +03:00
|
|
|
=, i.requests
|
|
|
|
[%g %deal `sock`[our ship] `cush:gall`[app %punk mark %json !>(json)]]
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
$(requests t.requests)
|
|
|
|
::
|
|
|
|
%subscribe
|
2018-11-20 01:59:58 +03:00
|
|
|
::
|
|
|
|
=. gall-moves
|
|
|
|
:_ gall-moves
|
|
|
|
^- move
|
2018-11-21 04:06:04 +03:00
|
|
|
:^ duct %pass
|
|
|
|
/channel/subscription/[channel-id]/(scot %ud request-id.i.requests)
|
2018-11-20 01:59:58 +03:00
|
|
|
=, i.requests
|
|
|
|
[%g %deal [our ship] `cush:gall`[app %peel %json path]]
|
|
|
|
:: TODO: Check existence to prevent duplicates?
|
|
|
|
::
|
|
|
|
=. session.channel-state.state
|
2018-11-21 04:06:04 +03:00
|
|
|
%+ ~(jab by session.channel-state.state) channel-id
|
|
|
|
|= =channel
|
2018-11-20 01:59:58 +03:00
|
|
|
^+ channel
|
|
|
|
=, i.requests
|
|
|
|
channel(subscriptions [[ship app path] subscriptions.channel])
|
|
|
|
::
|
|
|
|
$(requests t.requests)
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
|
|
|
%unsubscribe
|
|
|
|
!!
|
|
|
|
==
|
2018-11-21 04:06:04 +03:00
|
|
|
:: +on-gall-response: turns a gall response into an event
|
|
|
|
::
|
|
|
|
++ on-gall-response
|
|
|
|
|= [channel-id=@t request-id=@ud =cuft:gall]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
?+ -.cuft ~|([%invalid-gall-response -.cuft] !!)
|
|
|
|
%coup
|
|
|
|
=/ =json
|
|
|
|
=, enjs:format
|
|
|
|
%- pairs :~
|
|
|
|
['response' [%s 'poke']]
|
|
|
|
['id' (numb request-id)]
|
|
|
|
?~ p.cuft
|
|
|
|
['ok' [%s 'ok']]
|
|
|
|
['err' (wall (render-tang-to-wall 100 u.p.cuft))]
|
|
|
|
==
|
|
|
|
::
|
|
|
|
(emit-event channel-id [(en-json:html json)]~)
|
|
|
|
::
|
|
|
|
%diff
|
|
|
|
=/ =json
|
|
|
|
=, enjs:format
|
|
|
|
%- pairs :~
|
|
|
|
['response' [%s 'diff']]
|
|
|
|
['id' (numb request-id)]
|
|
|
|
:- 'json'
|
|
|
|
?> =(%json p.p.cuft)
|
|
|
|
((hard json) q.q.p.cuft)
|
|
|
|
==
|
|
|
|
::
|
|
|
|
(emit-event channel-id [(en-json:html json)]~)
|
|
|
|
::
|
|
|
|
%reap
|
|
|
|
=/ =json
|
|
|
|
=, enjs:format
|
|
|
|
%- pairs :~
|
|
|
|
['response' [%s 'subscribe']]
|
|
|
|
['id' (numb request-id)]
|
|
|
|
?~ p.cuft
|
|
|
|
['ok' [%s 'ok']]
|
|
|
|
['err' (wall (render-tang-to-wall 100 u.p.cuft))]
|
|
|
|
==
|
|
|
|
::
|
|
|
|
(emit-event channel-id [(en-json:html json)]~)
|
|
|
|
==
|
|
|
|
:: +emit-event: records an event occurred, possibly sending to client
|
|
|
|
::
|
|
|
|
:: When an event occurs, we need to record it, even if we immediately
|
|
|
|
:: send it to a connected browser so in case of disconnection, we can
|
|
|
|
:: resend it.
|
|
|
|
::
|
|
|
|
:: This function is responsible for taking the raw json lines and
|
|
|
|
:: converting them into a text/event-stream. The :event-stream-lines
|
|
|
|
:: then may get sent, and are stored for later resending until
|
|
|
|
:: acknowledged by the client.
|
|
|
|
::
|
|
|
|
++ emit-event
|
|
|
|
|= [channel-id=@t json-text=wall]
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
=/ channel=channel
|
|
|
|
(~(got by session.channel-state.state) channel-id)
|
|
|
|
::
|
|
|
|
=/ event-id next-id.channel
|
|
|
|
::
|
|
|
|
=/ event-stream-lines=wall
|
|
|
|
%- weld :_ [""]~
|
|
|
|
:- "id: {<event-id>}"
|
|
|
|
%+ turn json-text
|
|
|
|
|= =tape
|
|
|
|
(weld "data: " tape)
|
|
|
|
:: if a client is connected, send this event to them.
|
|
|
|
::
|
|
|
|
=? moves ?=([%| *] state.channel)
|
|
|
|
:_ moves
|
|
|
|
:+ p.state.channel %give
|
|
|
|
:* %http-response %continue
|
|
|
|
::
|
|
|
|
^= data
|
|
|
|
:- ~
|
|
|
|
%- as-octs:mimes:html
|
|
|
|
(crip (of-wall:format event-stream-lines))
|
|
|
|
::
|
|
|
|
complete=%.n
|
|
|
|
==
|
|
|
|
::
|
|
|
|
:- moves
|
|
|
|
%_ state
|
|
|
|
channel-state
|
|
|
|
%+ ~(jab by session.channel-state.state) channel-id
|
|
|
|
|= =^channel
|
|
|
|
^+ channel
|
|
|
|
::
|
|
|
|
%_ channel
|
|
|
|
next-id +(next-id.channel)
|
|
|
|
events (~(put to events.channel) [event-id event-stream-lines])
|
|
|
|
==
|
|
|
|
==
|
2018-11-15 21:27:10 +03:00
|
|
|
:: +on-channel-timeout: we received a wake to clear an old session
|
|
|
|
::
|
|
|
|
++ on-channel-timeout
|
2018-11-21 04:06:04 +03:00
|
|
|
|= channel-id=@t
|
2018-11-15 21:27:10 +03:00
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
=/ session
|
2018-11-21 04:06:04 +03:00
|
|
|
(~(got by session.channel-state.state) channel-id)
|
2018-11-15 21:27:10 +03:00
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
:_ %_ state
|
|
|
|
session.channel-state
|
2018-11-21 04:06:04 +03:00
|
|
|
(~(del by session.channel-state.state) channel-id)
|
2018-11-16 02:27:49 +03:00
|
|
|
==
|
2018-11-15 21:27:10 +03:00
|
|
|
:: produce a list of moves which cancels every gall subscription
|
|
|
|
::
|
2018-11-16 02:27:49 +03:00
|
|
|
%+ turn subscriptions.session
|
2018-11-20 01:59:58 +03:00
|
|
|
|= [ship=@p app=term =path]
|
2018-11-16 02:27:49 +03:00
|
|
|
^- move
|
|
|
|
:: todo: double check this; which duct should we be canceling on? does
|
|
|
|
:: gall strongly bind to a duct as a cause like ford does?
|
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
:^ duct %pass /channel/subscription/[channel-id]
|
2018-11-16 02:27:49 +03:00
|
|
|
[%g %deal [our ship] app %pull ~]
|
2018-11-15 21:27:10 +03:00
|
|
|
--
|
2018-10-10 21:51:52 +03:00
|
|
|
:: +handle-ford-response: translates a ford response for the outside world
|
|
|
|
::
|
|
|
|
:: TODO: Get the authentication state and source url here.
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
2018-10-10 21:51:52 +03:00
|
|
|
++ handle-ford-response
|
|
|
|
|= made-result=made-result:ford
|
|
|
|
^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
?: ?=(%incomplete -.made-result)
|
|
|
|
%^ return-static-data-on-duct 500 'text/html'
|
|
|
|
:: TODO: Thread original URL and authentication state here.
|
2018-10-11 01:26:51 +03:00
|
|
|
(internal-server-error %.y 'http://' tang.made-result)
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
|
|
|
?: ?=(%error -.build-result.made-result)
|
|
|
|
%^ return-static-data-on-duct 500 'text/html'
|
2018-10-11 01:26:51 +03:00
|
|
|
(internal-server-error %.y 'http://' message.build-result.made-result)
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
|
|
|
=/ =cage (result-to-cage:ford build-result.made-result)
|
|
|
|
::
|
|
|
|
%- handle-response
|
|
|
|
=/ result=mime ((hard mime) q.q.cage)
|
|
|
|
::
|
|
|
|
^- raw-http-response
|
|
|
|
:* %start
|
|
|
|
200
|
|
|
|
^- header-list
|
|
|
|
:~ ['content-type' (en-mite:mimes:html p.result)]
|
|
|
|
['content-length' (format-ud-as-integer p.q.result)]
|
|
|
|
==
|
|
|
|
`(unit octs)`[~ q.result]
|
|
|
|
complete=%.y
|
|
|
|
==
|
|
|
|
:: +handle-response: check a response for correctness and send to earth
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
|
|
|
++ handle-response
|
|
|
|
|= =raw-http-response
|
|
|
|
^- [(list move) server-state]
|
|
|
|
:: verify that this is a valid response on the duct
|
|
|
|
::
|
|
|
|
?~ connection-state=(~(get by connections.state) duct)
|
|
|
|
~& [%invalid-outstanding-connection duct]
|
|
|
|
[~ state]
|
|
|
|
::
|
|
|
|
|^ ^- [(list move) server-state]
|
|
|
|
::
|
|
|
|
?- -.raw-http-response
|
|
|
|
::
|
|
|
|
%start
|
|
|
|
?^ code.u.connection-state
|
|
|
|
~& [%http-multiple-start duct]
|
|
|
|
error-connection
|
|
|
|
::
|
|
|
|
=. connections.state
|
|
|
|
%+ ~(jab by connections.state) duct
|
|
|
|
|= connection=outstanding-connection
|
|
|
|
%_ connection
|
|
|
|
code `status-code.raw-http-response
|
|
|
|
headers `headers.raw-http-response
|
|
|
|
bytes-sent ?~(data.raw-http-response 0 p.u.data.raw-http-response)
|
|
|
|
==
|
|
|
|
::
|
|
|
|
=? state complete.raw-http-response
|
|
|
|
log-complete-request
|
|
|
|
::
|
|
|
|
pass-response
|
|
|
|
::
|
|
|
|
%continue
|
|
|
|
?~ code.u.connection-state
|
|
|
|
~& [%http-continue-without-start duct]
|
|
|
|
error-connection
|
|
|
|
::
|
|
|
|
=. connections.state
|
|
|
|
%+ ~(jab by connections.state) duct
|
|
|
|
|= connection=outstanding-connection
|
|
|
|
=+ size=?~(data.raw-http-response 0 p.u.data.raw-http-response)
|
|
|
|
connection(bytes-sent (add bytes-sent.connection size))
|
|
|
|
::
|
|
|
|
=? state complete.raw-http-response
|
|
|
|
log-complete-request
|
|
|
|
::
|
|
|
|
pass-response
|
|
|
|
::
|
|
|
|
%cancel
|
|
|
|
:: todo: log this differently from an ise.
|
|
|
|
::
|
|
|
|
error-connection
|
|
|
|
==
|
|
|
|
::
|
|
|
|
++ pass-response
|
|
|
|
^- [(list move) server-state]
|
|
|
|
[[duct %give %http-response raw-http-response]~ state]
|
|
|
|
::
|
|
|
|
++ log-complete-request
|
|
|
|
:: todo: log the complete request
|
|
|
|
::
|
|
|
|
:: remove all outstanding state for this connection
|
|
|
|
::
|
|
|
|
=. connections.state
|
|
|
|
(~(del by connections.state) duct)
|
|
|
|
state
|
|
|
|
::
|
|
|
|
++ error-connection
|
|
|
|
:: todo: log application error
|
|
|
|
::
|
|
|
|
:: remove all outstanding state for this connection
|
|
|
|
::
|
|
|
|
=. connections.state
|
|
|
|
(~(del by connections.state) duct)
|
|
|
|
:: respond to outside with %error
|
|
|
|
::
|
|
|
|
^- [(list move) server-state]
|
|
|
|
[[duct %give %http-response %cancel ~]~ state]
|
|
|
|
--
|
2018-09-20 02:29:36 +03:00
|
|
|
:: +add-binding: conditionally add a pairing between binding and action
|
|
|
|
::
|
|
|
|
:: Adds =binding =action if there is no conflicting bindings.
|
|
|
|
::
|
|
|
|
++ add-binding
|
|
|
|
|= [=binding =action]
|
|
|
|
::
|
|
|
|
=/ to-search bindings.state
|
|
|
|
|-
|
|
|
|
^- [(list move) server-state]
|
|
|
|
?~ to-search
|
|
|
|
:- [duct %give %bound %.y binding]~
|
|
|
|
=. bindings.state
|
2018-10-11 21:28:27 +03:00
|
|
|
:: store in reverse alphabetical order so that longer paths are first
|
|
|
|
::
|
|
|
|
%- flop
|
2018-09-20 02:29:36 +03:00
|
|
|
%+ sort [[binding duct action] bindings.state]
|
|
|
|
|= [[a=^binding *] [b=^binding *]]
|
|
|
|
::
|
|
|
|
?: =(site.a site.b)
|
|
|
|
(aor path.a path.b)
|
|
|
|
:: alphabetize based on site
|
|
|
|
::
|
|
|
|
(aor ?~(site.a '' u.site.a) ?~(site.b '' u.site.b))
|
|
|
|
state
|
|
|
|
::
|
|
|
|
?: =(binding binding.i.to-search)
|
|
|
|
:- [duct %give %bound %.n binding]~
|
|
|
|
state
|
|
|
|
::
|
|
|
|
$(to-search t.to-search)
|
|
|
|
:: +remove-binding: removes a binding if it exists and is owned by this duct
|
|
|
|
::
|
|
|
|
++ remove-binding
|
|
|
|
|= =binding
|
|
|
|
::
|
|
|
|
^- server-state
|
|
|
|
%_ state
|
|
|
|
bindings
|
|
|
|
%+ skip bindings.state
|
|
|
|
|= [item-binding=^binding item-duct=^duct =action]
|
|
|
|
^- ?
|
|
|
|
&(=(item-binding binding) =(item-duct duct))
|
|
|
|
==
|
|
|
|
:: +get-action-for-binding: finds an action for an incoming web request
|
|
|
|
::
|
|
|
|
++ get-action-for-binding
|
|
|
|
|= [raw-host=(unit @t) url=@t]
|
|
|
|
^- (unit action)
|
|
|
|
:: process :raw-host
|
|
|
|
::
|
|
|
|
:: If we are missing a 'Host:' header, if that header is a raw IP
|
|
|
|
:: address, or if the 'Host:' header refers to [our].urbit.org, we want
|
|
|
|
:: to return ~ which is the binding for our Urbit identity.
|
|
|
|
::
|
|
|
|
:: Otherwise, return the site given.
|
|
|
|
::
|
|
|
|
=/ host=(unit @t)
|
|
|
|
?~ raw-host
|
|
|
|
~
|
2018-10-03 21:36:45 +03:00
|
|
|
:: Parse the raw-host so that we can ignore ports, usernames, etc.
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
2018-10-04 00:05:36 +03:00
|
|
|
=+ parsed=(rush u.raw-host simplified-url-parser)
|
|
|
|
?~ parsed
|
2018-09-20 02:29:36 +03:00
|
|
|
~
|
2018-10-03 21:36:45 +03:00
|
|
|
:: if the url is a raw IP, assume default site.
|
|
|
|
::
|
2018-10-04 00:05:36 +03:00
|
|
|
?: ?=([%ip *] -.u.parsed)
|
|
|
|
~
|
|
|
|
:: if the url is "localhost", assume default site.
|
|
|
|
::
|
|
|
|
?: =([%site 'localhost'] -.u.parsed)
|
|
|
|
~
|
|
|
|
:: render our as a tape, and cut off the sig in front.
|
|
|
|
::
|
|
|
|
=/ with-sig=tape (scow %p our)
|
|
|
|
?> ?=(^ with-sig)
|
|
|
|
?: =(u.raw-host (crip t.with-sig))
|
|
|
|
:: [our].urbit.org is the default site
|
|
|
|
::
|
|
|
|
~
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
raw-host
|
|
|
|
:: url is the raw thing passed over the 'Request-Line'.
|
|
|
|
::
|
2018-09-27 02:18:40 +03:00
|
|
|
:: todo: this is really input validation, and we should return a 500 to
|
|
|
|
:: the client.
|
|
|
|
::
|
|
|
|
=/ request-line (parse-request-line url)
|
|
|
|
=/ parsed-url=(list @t) site.request-line
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
=/ bindings bindings.state
|
|
|
|
|-
|
|
|
|
::
|
|
|
|
?~ bindings
|
|
|
|
~
|
|
|
|
::
|
2018-10-11 21:28:27 +03:00
|
|
|
?: (path-matches path.binding.i.bindings parsed-url)
|
2018-09-20 02:29:36 +03:00
|
|
|
`action.i.bindings
|
|
|
|
::
|
|
|
|
$(bindings t.bindings)
|
|
|
|
--
|
2018-09-27 02:18:40 +03:00
|
|
|
::
|
|
|
|
::
|
|
|
|
++ parse-request-line
|
|
|
|
|= url=@t
|
|
|
|
^- [[(unit @ta) site=(list @t)] args=(list [key=@t value=@t])]
|
|
|
|
(fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~])
|
2018-09-20 02:29:36 +03:00
|
|
|
--
|
|
|
|
:: 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=@ scry-gate=sley]
|
|
|
|
:: allow jets to be registered within this core
|
|
|
|
::
|
|
|
|
~% %light ..is ~
|
|
|
|
|%
|
|
|
|
++ call
|
|
|
|
|= [=duct type=* wrapped-task=(hobo task:able)]
|
|
|
|
^- [p=(list move) q=_light-gate]
|
|
|
|
::
|
|
|
|
=/ task=task:able
|
|
|
|
?. ?=(%soft -.wrapped-task)
|
|
|
|
wrapped-task
|
2018-10-01 21:37:30 +03:00
|
|
|
~| [%p-wrapped-task p.wrapped-task]
|
2018-09-20 02:29:36 +03:00
|
|
|
((hard task:able) p.wrapped-task)
|
|
|
|
::
|
|
|
|
?- -.task
|
|
|
|
:: %init: tells us what our ship name is
|
|
|
|
::
|
|
|
|
%init
|
|
|
|
::
|
|
|
|
=. ship.ax [~ our.task]
|
2018-09-27 02:18:40 +03:00
|
|
|
:: initial value for the login handler
|
|
|
|
::
|
|
|
|
=. bindings.server-state.ax
|
2018-11-15 21:27:10 +03:00
|
|
|
:~ [[~ /~/login] duct [%authentication ~]]
|
|
|
|
[[~ /~/channel] duct [%channel ~]]
|
2018-09-27 02:18:40 +03:00
|
|
|
==
|
2018-09-20 02:29:36 +03:00
|
|
|
[~ light-gate]
|
2018-09-28 21:24:41 +03:00
|
|
|
:: %born: new unix process
|
|
|
|
::
|
|
|
|
%born
|
|
|
|
::
|
|
|
|
~& [%todo-handle-born p.task]
|
2018-10-26 02:32:54 +03:00
|
|
|
:: TODO: reset the next-id for client state here.
|
|
|
|
::
|
|
|
|
|
2018-10-24 21:25:55 +03:00
|
|
|
:: close previously open connections
|
2018-09-28 21:24:41 +03:00
|
|
|
::
|
2018-10-24 21:25:55 +03:00
|
|
|
:: When we have a new unix process, every outstanding open connection is
|
|
|
|
:: dead. For every duct, send an implicit close connection.
|
|
|
|
::
|
|
|
|
=^ closed-connections=(list move) server-state.ax
|
|
|
|
=/ connections=(list [=^duct *])
|
|
|
|
~(tap by connections.server-state.ax)
|
|
|
|
::
|
|
|
|
=| closed-connections=(list move)
|
|
|
|
|-
|
|
|
|
?~ connections
|
|
|
|
[closed-connections server-state.ax]
|
|
|
|
::
|
|
|
|
=/ event-args
|
|
|
|
[[(need ship.ax) eny duct.i.connections now scry-gate] server-state.ax]
|
|
|
|
=/ cancel-request cancel-request:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax cancel-request
|
|
|
|
::
|
|
|
|
$(closed-connections (weld moves closed-connections), connections t.connections)
|
|
|
|
::
|
|
|
|
:_ light-gate
|
|
|
|
;: weld
|
|
|
|
:: hand back default configuration for now
|
|
|
|
::
|
|
|
|
[duct %give %form *http-config]~
|
|
|
|
::
|
|
|
|
closed-connections
|
|
|
|
==
|
2018-09-28 21:24:41 +03:00
|
|
|
::
|
|
|
|
:: %live: no idea what this is for
|
|
|
|
::
|
|
|
|
%live
|
|
|
|
::
|
|
|
|
~& [%todo-live p.task q.task]
|
|
|
|
::
|
|
|
|
[~ light-gate]
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
:: %inbound-request: handles an inbound http request
|
|
|
|
::
|
|
|
|
%inbound-request
|
2018-10-01 21:24:16 +03:00
|
|
|
::
|
|
|
|
:: TODO: This is uncommit
|
|
|
|
::
|
|
|
|
~| [%ship ship.ax]
|
2018-09-27 02:18:40 +03:00
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
2018-09-20 02:29:36 +03:00
|
|
|
=/ request request:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax
|
|
|
|
(request +.task)
|
|
|
|
[moves light-gate]
|
2018-10-22 21:36:30 +03:00
|
|
|
::
|
|
|
|
::
|
|
|
|
::
|
|
|
|
%cancel-inbound-request
|
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
|
|
|
=/ cancel-request cancel-request:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax cancel-request
|
|
|
|
[moves light-gate]
|
2018-10-26 02:32:54 +03:00
|
|
|
::
|
|
|
|
:: %fetch
|
|
|
|
::
|
|
|
|
%fetch
|
|
|
|
~& %todo-fetch
|
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] client-state.ax]
|
|
|
|
=/ fetch fetch:(per-client-event event-args)
|
|
|
|
=^ moves client-state.ax (fetch +.task)
|
|
|
|
[moves light-gate]
|
|
|
|
::
|
|
|
|
:: %cancel-fetch
|
|
|
|
::
|
|
|
|
%cancel-fetch
|
|
|
|
~& %todo-cancel-fetch
|
|
|
|
[~ light-gate]
|
|
|
|
::
|
|
|
|
:: %receive: receives http data from unix
|
|
|
|
::
|
|
|
|
%receive
|
|
|
|
~& %todo-receive
|
|
|
|
[~ light-gate]
|
2018-09-20 02:29:36 +03:00
|
|
|
::
|
|
|
|
:: %connect / %serve
|
|
|
|
::
|
|
|
|
?(%connect %serve)
|
2018-09-27 02:18:40 +03:00
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
2018-09-20 02:29:36 +03:00
|
|
|
=/ add-binding add-binding:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax
|
|
|
|
%+ add-binding binding.task
|
|
|
|
?- -.task
|
|
|
|
%connect [%app app.task]
|
2018-10-11 01:26:51 +03:00
|
|
|
%serve [%gen generator.task]
|
2018-09-20 02:29:36 +03:00
|
|
|
==
|
|
|
|
[moves light-gate]
|
|
|
|
::
|
|
|
|
:: %disconnect
|
|
|
|
::
|
|
|
|
%disconnect
|
2018-09-27 02:18:40 +03:00
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
2018-09-20 02:29:36 +03:00
|
|
|
=/ remove-binding remove-binding:(per-server-event event-args)
|
|
|
|
=. server-state.ax (remove-binding binding.task)
|
|
|
|
[~ light-gate]
|
|
|
|
==
|
|
|
|
::
|
2018-09-24 21:48:19 +03:00
|
|
|
++ take
|
|
|
|
|= [=wire =duct wrapped-sign=(hypo sign)]
|
|
|
|
^- [p=(list move) q=_light-gate]
|
|
|
|
:: unwrap :sign, ignoring unneeded +type in :p.wrapped-sign
|
|
|
|
::
|
|
|
|
=/ =sign q.wrapped-sign
|
|
|
|
:: :wire must at least contain two parts, the type and the build
|
|
|
|
::
|
2018-10-11 01:26:51 +03:00
|
|
|
?> ?=([@ *] wire)
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
|
|
|
|^ ^- [p=(list move) q=_light-gate]
|
|
|
|
::
|
2018-10-10 21:51:52 +03:00
|
|
|
?+ i.wire
|
|
|
|
~|([%bad-take-wire wire] !!)
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
2018-10-10 21:51:52 +03:00
|
|
|
%run-app run-app
|
|
|
|
%run-build run-build
|
2018-11-20 01:59:58 +03:00
|
|
|
%channel channel
|
2018-10-10 21:51:52 +03:00
|
|
|
==
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
|
|
|
++ run-app
|
2018-10-03 21:36:45 +03:00
|
|
|
::
|
|
|
|
?. ?=([%g %unto %http-response *] sign)
|
|
|
|
:: entirely normal to get things other than http-response calls, but we
|
|
|
|
:: don't care.
|
|
|
|
::
|
|
|
|
[~ light-gate]
|
2018-09-24 21:48:19 +03:00
|
|
|
::
|
2018-09-27 02:18:40 +03:00
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
2018-09-24 21:48:19 +03:00
|
|
|
=/ handle-response handle-response:(per-server-event event-args)
|
2018-10-03 21:36:45 +03:00
|
|
|
=^ moves server-state.ax (handle-response raw-http-response.p.sign)
|
2018-09-24 21:48:19 +03:00
|
|
|
[moves light-gate]
|
2018-10-10 21:51:52 +03:00
|
|
|
::
|
|
|
|
++ run-build
|
|
|
|
::
|
|
|
|
?> ?=([%f %made *] sign)
|
|
|
|
::
|
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
|
|
|
=/ handle-ford-response handle-ford-response:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax (handle-ford-response result.sign)
|
|
|
|
[moves light-gate]
|
2018-11-20 01:59:58 +03:00
|
|
|
::
|
|
|
|
++ channel
|
|
|
|
::
|
|
|
|
=/ event-args [[(need ship.ax) eny duct now scry-gate] server-state.ax]
|
|
|
|
:: channel callback wires are triples.
|
|
|
|
::
|
|
|
|
?> ?=([@ @ @t *] wire)
|
|
|
|
::
|
|
|
|
?+ i.t.wire
|
|
|
|
~|([%bad-channel-wire wire] !!)
|
|
|
|
::
|
|
|
|
%timeout
|
|
|
|
=/ on-channel-timeout
|
2018-11-21 04:06:04 +03:00
|
|
|
on-channel-timeout:by-channel:(per-server-event event-args)
|
2018-11-20 01:59:58 +03:00
|
|
|
=^ moves server-state.ax
|
|
|
|
(on-channel-timeout i.t.t.wire)
|
|
|
|
[moves light-gate]
|
2018-11-21 21:34:22 +03:00
|
|
|
::
|
|
|
|
:: %wake
|
|
|
|
:: [~ move
|
2018-11-20 01:59:58 +03:00
|
|
|
::
|
2018-11-21 04:06:04 +03:00
|
|
|
?(%poke %subscription)
|
|
|
|
?> ?=([%g %unto *] sign)
|
|
|
|
?> ?=([@ @ @t @ *] wire)
|
|
|
|
=/ on-gall-response
|
|
|
|
on-gall-response:by-channel:(per-server-event event-args)
|
|
|
|
=^ moves server-state.ax
|
|
|
|
(on-gall-response i.t.t.wire `@ud`(slav %ud i.t.t.t.wire) p.sign)
|
|
|
|
[moves light-gate]
|
2018-11-20 01:59:58 +03:00
|
|
|
==
|
2018-09-24 21:48:19 +03:00
|
|
|
--
|
|
|
|
::
|
2018-09-20 02:29:36 +03:00
|
|
|
++ light-gate ..$
|
2018-09-28 21:24:41 +03:00
|
|
|
:: +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
|
|
|
|
|= *
|
|
|
|
[~ ~]
|
2018-09-20 02:29:36 +03:00
|
|
|
--
|