camber.bend

bend-kit-camber@0.11.0.0 · 1,280 lines · 0 definitions

1# Fixed prepared HTTP applications and bounded affine dependency bundles.
2import Base
3import bend-kit-http@0.35.0.0/http.bend as Http
4import bend-kit-router@0.2.0.0/router.bend as Router
5import bend-kit-router@0.2.0.0/target.bend as Target
6import bend-kit-json@0.6.1.1/json.bend as Json
7import bend-kit-camber@0.11.0.0/response.bend as Response
8import bend-kit-camber@0.11.0.0/input.bend as Input
9import bend-kit-http@0.35.0.0/completion.bend as TransportCompletion
10import bend-kit-camber@0.11.0.0/notices.bend as Notices
11import bend-kit-time@0.2.0.0/time.bend as Time
12import bend-kit-resources@0.1.0.0/resources.bend as Res
13
14type ResponseError is Data:
15  InvalidResponse{}
16  InvalidRedirect{}
17
18def validate.checked(valid: Bool, response: Http.Res) -> Result<&2, &1, ResponseError, Http.Res>:
19  match valid:
20    case True{}:
21      Done{response}
22    case False{}:
23      Fail{InvalidResponse{}}
24
25def validate(response: Http.Res) -> Result<&2, &1, ResponseError, Http.Res>:
26  Http.Res{+status, +headers, body} = response
27  validate.checked(Response.valid(status, headers), Http.Res{status, headers, body})
28
29# The encoder is an explicit public projection, never record reflection.
30def json(~T: Type, ~encode: T -> Json.Val, status: U32, value: T) -> Http.Res:
31  Http.Res{status, Http.set(Http.empty(), "content-type", "application/json"), Json.encode.bytes(encode(value))}
32
33def text(status: U32, value: String) -> Http.Res:
34  Http.Res{status, Http.set(Http.empty(), "content-type", "text/plain; charset=utf-8"), Json.utf8(value)}
35
36def raw(status: U32, body: Http.Body) -> Http.Res:
37  Http.Res{status, Http.empty(), body}
38
39def empty(status: U32) -> Http.Res:
40  raw(status, Http.from_string(""))
41
42def redirect.checked(valid: Bool, status: U32, location: String) -> Result<&2, &1, ResponseError, Http.Res>:
43  match valid:
44    case False{}:
45      Fail{InvalidRedirect{}}
46    case True{}:
47      Done{Http.Res{status, Http.set(Http.empty(), "location", location), Http.from_string("")}}
48
49def redirect(+status: U32, +location: String) -> Result<&2, &1, ResponseError, Http.Res>:
50  redirect.checked((U32.is_eq(status, 301) || U32.is_eq(status, 302) || U32.is_eq(status, 303) || U32.is_eq(status, 307) || U32.is_eq(status, 308)) && Bool.not(String.eq(location, "")) && Response.value(location), status, location)
51
52type Counts is Data:
53  Counts{capacity: Nat, in_use: Nat, completed: Nat, rejected: Nat}
54
55type Outcome<-E: Data, -O: Data> is Data:
56  Success{value: O}
57  Expected{error: E}
58  Exhausted{}
59  Stopped{}
60
61type CloseResult is Data:
62  Closed{}
63  Busy{counts: Counts}
64
65type Message<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
66  Submit{principal: P, input: I, reply: Chan(Outcome<E, O>)}
67  Returned{resource: R}
68  Inspect{reply: Chan(Counts)}
69  Shutdown{reply: Chan(CloseResult)}
70
71# Copy only this context, never the resources. The inbox bounds queued commands;
72# callers must count blocked submissions inside their transport admission limit.
73type Context<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Data:
74  Context{messages: Chan(Message<R, P, I, E, O>)}
75
76# The completion future has one affine owner; request contexts copy only inbox.
77type Owner<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
78  Owner{context: Context<R, P, I, E, O>, finished: Chan(Unit)}
79
80type OwnerClose<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
81  OwnerClosed{}
82  OwnerBusy{owner: Owner<R, P, I, E, O>, counts: Counts}
83
84def owner.context(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
85  owner: Owner<R, P, I, E, O>
86) -> Owner<R, P, I, E, O> & Context<R, P, I, E, O>:
87  Owner{+context, finished} = owner
88  (Owner{context, finished}, context)
89
90def outcome.value(-E: Data, -O: Data, result: Result<&2, &2, E, O>) -> Outcome<E, O>:
91  match result:
92    case Fail{error}:
93      Expected{error}
94    case Done{value}:
95      Success{value}
96
97def dispose(~R: Type, ~destroy: R -> IO(Unit), resources: List<R>) -> IO(Unit):
98  match resources:
99    case Nil{}:
100      IO.pure(Unit, Unit{})
101    case Con{resource, rest}:
102      do IO<Unit>:
103        destroy(resource)
104        dispose(~R, ~destroy, rest)
105
106# Closing Base channels retains buffered messages. Drain them and answer stopped
107# callers, rather than stranding accepted commands behind the close command.
108def drain.message(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
109  ~destroy: R -> IO(Unit), message: Message<R, P, I, E, O>
110) -> IO(Unit):
111  match message:
112    case Submit{principal, input, reply}:
113      do IO<Unit>:
114        sent : Result<&1, &1, Outcome<E, O>, Unit> <- Chan.send(Outcome<E, O>, reply, Stopped{})
115        return Unit{}
116    case Inspect{reply}:
117      Chan.close(Counts, reply)
118    case Shutdown{reply}:
119      do IO<Unit>:
120        sent : Result<&1, &1, CloseResult, Unit> <- Chan.send(CloseResult, reply, Closed{})
121        return Unit{}
122    case Returned{resource}:
123      destroy(resource)
124
125# The resources return through the private owner protocol BEFORE a disposable
126# caller reply. A closed caller reply cannot erase a handle or strand capacity.
127def work.returned(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
128  ~destroy: R -> IO(Unit), accepted: Result<&1, &1, Message<R, P, I, E, O>, Unit>
129) -> IO(Unit):
130  match accepted:
131    case Fail{message}:
132      drain.message(~R, ~P, ~I, ~E, ~O, ~destroy, message)
133    case Done{u}:
134      IO.pure(Unit, Unit{})
135
136def work.return(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
137  ~destroy: R -> IO(Unit),
138  messages: Chan(Message<R, P, I, E, O>), reply: Chan(Outcome<E, O>),
139  result: R & Result<&2, &2, E, O>
140) -> IO(Unit):
141  (returned, outcome) = result
142  do IO<Unit>:
143    accepted : Result<&1, &1, Message<R, P, I, E, O>, Unit> <- Chan.send(Message<R, P, I, E, O>, messages, Returned{returned})
144    work.returned(~R, ~P, ~I, ~E, ~O, ~destroy, accepted)
145    sent : Result<&1, &1, Outcome<E, O>, Unit> <- Chan.send(Outcome<E, O>, reply, outcome.value(E, O, outcome))
146    return Unit{}
147
148def work(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
149  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
150  resource: R, principal: P, input: I,
151  messages: Chan(Message<R, P, I, E, O>), reply: Chan(Outcome<E, O>)
152) -> IO(Unit):
153  do IO<Unit>:
154    result : R & Result<&2, &2, E, O> <- handler(resource, principal, input)
155    work.return(~R, ~P, ~I, ~E, ~O, ~destroy, messages, reply, result)
156
157def drain.step(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
158  ~destroy: R -> IO(Unit), pending: Maybe<&1, Message<R, P, I, E, O>>,
159  next: Unit -> IO(Unit)
160) -> IO(Unit):
161  match pending:
162    case None{}:
163      IO.pure(Unit, Unit{})
164    case Some{message}:
165      do IO<Unit>:
166        drain.message(~R, ~P, ~I, ~E, ~O, ~destroy, message)
167        next(Unit{})
168
169# The closed inbox contains at most room buffered messages.
170def drain(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
171  ~destroy: R -> IO(Unit), fuel: Nat, +messages: Chan(Message<R, P, I, E, O>)
172) -> IO(Unit):
173  match fuel:
174    case 0n:
175      IO.pure(Unit, Unit{})
176    case 1n+p:
177      do IO<Unit>:
178        pending : Maybe<&1, Message<R, P, I, E, O>> <- Chan.recv(Message<R, P, I, E, O>, messages)
179        drain.step(~R, ~P, ~I, ~E, ~O, ~destroy, pending, u => drain(~R, ~P, ~I, ~E, ~O, ~destroy, p, messages))
180
181# Every lent resource holds one pool unit, and all lent units join in one grant.
182type Ledger is Type:
183  Ledger{pool: Res.Pool, lent: Res.Grant, completed: Nat, rejected: Nat}
184
185def ledger.open(capacity: Nat) -> Ledger:
186  Ledger{Res.new(0n, capacity), Res.Grant{0n, 0n}, 0n, 0n}
187
188def ledger.counts(ledger: Ledger) -> Counts & Ledger:
189  Ledger{+pool, lent, +completed, +rejected} = ledger
190  (Counts{Res.capacity(pool), Res.reserved(pool), completed, rejected}, Ledger{pool, lent, completed, rejected})
191
192def ledger.refuse(ledger: Ledger) -> Ledger:
193  Ledger{pool, lent, completed, rejected} = ledger
194  Ledger{pool, lent, completed, 1n+rejected}
195
196def ledger.joined(combined: Res.Combine, pool: Res.Pool, completed: Nat, rejected: Nat) -> Maybe<&1, Ledger>:
197  match combined:
198    case Res.Joined{grant}:
199      Some{Ledger{pool, grant, completed, rejected}}
200    case Res.Apart{first, second}:
201      None{}
202
203def ledger.took(reserved: Res.Reserve, lent: Res.Grant, completed: Nat, rejected: Nat) -> Maybe<&1, Ledger>:
204  match reserved:
205    case Res.Took{pool, grant}:
206      ledger.joined(Res.combine(lent, grant), pool, completed, rejected)
207    case Res.Refused{pool}:
208      None{}
209
210# None means the pool has no free unit for a resource the owner still holds.
211def ledger.lend(ledger: Ledger) -> Maybe<&1, Ledger>:
212  Ledger{pool, lent, completed, rejected} = ledger
213  ledger.took(Res.reserve(pool, 1n), lent, completed, rejected)
214
215def ledger.released(released: Res.Release, rest: Res.Grant, completed: Nat, rejected: Nat) -> Maybe<&1, Ledger>:
216  match released:
217    case Res.Released{pool}:
218      Some{Ledger{pool, rest, 1n+completed, rejected}}
219    case Res.Kept{pool, grant}:
220      None{}
221
222def ledger.split(parts: Res.Split, pool: Res.Pool, completed: Nat, rejected: Nat) -> Maybe<&1, Ledger>:
223  match parts:
224    case Res.Parts{one, rest}:
225      ledger.released(Res.release(pool, one), rest, completed, rejected)
226    case Res.Whole{grant}:
227      None{}
228
229# None means a return with no matching lend.
230def ledger.give(ledger: Ledger) -> Maybe<&1, Ledger>:
231  Ledger{pool, lent, completed, rejected} = ledger
232  ledger.split(Res.split(lent, 1n), pool, completed, rejected)
233
234def ledger.must(booked: Maybe<&1, Ledger>, text: String) -> IO(Ledger):
235  match booked:
236    case None{}:
237      IO.die(Ledger, 1, text)
238    case Some{ledger}:
239      IO.pure(Ledger, ledger)
240
241type OwnerStep<-R: Type, -P: Data, -I: Data, -E: Data, -O: Data> is Type:
242  Waiting{resources: List<R>, ledger: Ledger}
243  Received{message: Message<R, P, I, E, O>, ledger: Ledger, resources: List<R>}
244  Counted{reply: Chan(Counts), counted: Counts & Ledger, resources: List<R>}
245  Closing{reply: Chan(CloseResult), counted: Counts & Ledger, resources: List<R>}
246
247# IO.join closes a one-shot channel; the owner inbox must remain reusable.
248def receive.value(-A: Type, value: Maybe<&1, A>) -> IO(A):
249  match value:
250    case None{}:
251      IO.die(A, 1, "camber internal channel closed unexpectedly")
252    case Some{message}:
253      IO.pure(A, message)
254
255def receive(-A: Type, channel: Chan(A)) -> IO(A):
256  do IO<A>:
257    value : Maybe<&1, A> <- Chan.recv(A, channel)
258    receive.value(A, value)
259
260# Only idle close ends this loop. Busy close never waits on a stalled handler.
261# @unsafe is solely the lifecycle receive loop, not a termination/proof claim.
262@unsafe def owner(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
263  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
264  +messages: Chan(Message<R, P, I, E, O>), +room: U32, step: OwnerStep<R, P, I, E, O>
265) -> IO(Unit):
266  match step:
267    case Waiting{resources, ledger}:
268      do IO<Unit>:
269        message : Message<R, P, I, E, O> <- receive(Message<R, P, I, E, O>, messages)
270        owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Received{message, ledger, resources})
271    case Counted{reply, counted, resources}:
272      (counts, kept) = counted
273      do IO<Unit>:
274        sent : Result<&1, &1, Counts, Unit> <- Chan.send(Counts, reply, counts)
275        owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, kept})
276    case Closing{reply, counted, resources}:
277      (counts, kept) = counted
278      Counts{+capacity, in_use, +completed, +rejected} = counts
279      match in_use:
280        case 0n:
281          do IO<Unit>:
282            dispose(~R, ~destroy, resources)
283            Chan.close(Message<R, P, I, E, O>, messages)
284            sent : Result<&1, &1, CloseResult, Unit> <- Chan.send(CloseResult, reply, Closed{})
285            drain(~R, ~P, ~I, ~E, ~O, ~destroy, U32.to_nat(room), messages)
286        case 1n+p:
287          do IO<Unit>:
288            sent : Result<&1, &1, CloseResult, Unit> <- Chan.send(CloseResult, reply, Busy{Counts{capacity, 1n+p, completed, rejected}})
289            owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, kept})
290    case Received{message, ledger, resources}:
291      match message:
292        case Returned{resource}:
293          do IO<Unit>:
294            given : Ledger <- ledger.must(ledger.give(ledger), "camber: a resource returned without a matching submit")
295            owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resource <> resources, given})
296        case Inspect{reply}:
297          owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Counted{reply, ledger.counts(ledger), resources})
298        case Shutdown{reply}:
299          owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Closing{reply, ledger.counts(ledger), resources})
300        case Submit{principal, input, reply}:
301          match resources:
302            case Nil{}:
303              do IO<Unit>:
304                sent : Result<&1, &1, Outcome<E, O>, Unit> <- Chan.send(Outcome<E, O>, reply, Exhausted{})
305                owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{Nil{}, ledger.refuse(ledger)})
306            case Con{resource, rest}:
307              do IO<Unit>:
308                lent : Ledger <- ledger.must(ledger.lend(ledger), "camber: a held resource found no free pool unit")
309                IO.spawn(Unit, work(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, resource, principal, input, messages, reply))
310                owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{rest, lent})
311
312def build.step(~R: Type, ~E: Data, ~destroy: R -> IO(Unit),
313  result: Result<&2, &1, E, R>, resources: List<R>,
314  next: List<R> -> IO(Result<&2, &1, E, List<R>>)
315) -> IO(Result<&2, &1, E, List<R>>):
316  match result:
317    case Fail{error}:
318      do IO<Result<&2, &1, E, List<R>>>:
319        dispose(~R, ~destroy, resources)
320        return Fail{error}
321    case Done{resource}:
322      next(resource <> resources)
323
324def build(~K: Data, ~R: Type, ~E: Data, ~create: K -> Nat -> IO(Result<&2, &1, E, R>), ~destroy: R -> IO(Unit),
325  remaining: Nat, +config: K, resources: List<R>
326) -> IO(Result<&2, &1, E, List<R>>):
327  match remaining:
328    case 0n:
329      IO.pure(Result<&2, &1, E, List<R>>, Done{resources})
330    case 1n+ +p:
331      do IO<Result<&2, &1, E, List<R>>>:
332        result : Result<&2, &1, E, R> <- create(config, p)
333        build.step(~R, ~E, ~destroy, result, resources, next => build(~K, ~R, ~E, ~create, ~destroy, p, config, next))
334
335def start.ready(~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
336  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
337  capacity: Nat, +room: U32, result: Result<&2, &1, E, List<R>>
338) -> IO(Result<&2, &1, E, Owner<R, P, I, E, O>>):
339  match result:
340    case Fail{error}:
341      IO.pure(Result<&2, &1, E, Owner<R, P, I, E, O>>, Fail{error})
342    case Done{resources}:
343      do IO<Result<&2, &1, E, Owner<R, P, I, E, O>>>:
344        channel : Chan(Message<R, P, I, E, O>) <- Chan.new(Message<R, P, I, E, O>, room)
345        +messages : Chan(Message<R, P, I, E, O>) = channel
346        finished : Chan(Unit) <- IO.fork(Unit, owner(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, messages, room, Waiting{resources, ledger.open(capacity)}))
347        return Done{Owner{Context{messages}, finished}}
348
349# The initializer closes partial handles within a failed bundle; start closes
350# all prior whole bundles. No workers exist until the complete set opens.
351def start(~K: Data, ~R: Type, ~P: Data, ~I: Data, ~E: Data, ~O: Data,
352  ~create: K -> Nat -> IO(Result<&2, &1, E, R>),
353  ~handler: R -> P -> I -> IO(R & Result<&2, &2, E, O>), ~destroy: R -> IO(Unit),
354  config: K, +capacity: Nat, room: U32
355) -> IO(Result<&2, &1, E, Owner<R, P, I, E, O>>):
356  do IO<Result<&2, &1, E, Owner<R, P, I, E, O>>>:
357    result : Result<&2, &1, E, List<R>> <- build(~K, ~R, ~E, ~create, ~destroy, capacity, config, Nil{})
358    start.ready(~R, ~P, ~I, ~E, ~O, ~handler, ~destroy, capacity, room, result)
359
360def submit(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
361  context: Context<R, P, I, E, O>, principal: P, input: I, reply: Chan(Outcome<E, O>)
362) -> IO(Result<&1, &1, Message<R, P, I, E, O>, Unit>):
363  Context{messages} = context
364  do IO<Result<&1, &1, Message<R, P, I, E, O>, Unit>>:
365    sent : Result<&1, &1, Message<R, P, I, E, O>, Unit> <- Chan.send(Message<R, P, I, E, O>, messages, Submit{principal, input, reply})
366    return sent
367
368def call.accepted(-T: Type, -E: Data, -O: Data, accepted: Result<&1, &1, T, Unit>, +reply: Chan(Outcome<E, O>)) -> IO(Outcome<E, O>):
369  match accepted:
370    case Fail{v}:
371      do IO<Outcome<E, O>>:
372        Chan.close(Outcome<E, O>, reply)
373        return Stopped{}
374    case Done{u}:
375      do IO<Outcome<E, O>>:
376        result : Outcome<E, O> <- IO.join(Outcome<E, O>, reply)
377        Chan.close(Outcome<E, O>, reply)
378        return result
379
380def call(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
381  context: Context<R, P, I, E, O>, principal: P, input: I
382) -> IO(Outcome<E, O>):
383  do IO<Outcome<E, O>>:
384    channel : Chan(Outcome<E, O>) <- Chan.new(Outcome<E, O>, 1)
385    +reply : Chan(Outcome<E, O>) = channel
386    accepted : Result<&1, &1, Message<R, P, I, E, O>, Unit> <- submit(R, P, I, E, O, context, principal, input, reply)
387    call.accepted(Message<R, P, I, E, O>, E, O, accepted, reply)
388
389def inspect.value(value: Maybe<&1, Counts>) -> Maybe<&2, Counts>:
390  match value:
391    case None{}:
392      None{}
393    case Some{counts}:
394      Some{counts}
395
396def inspect.sent(-T: Type, sent: Result<&1, &1, T, Unit>, +reply: Chan(Counts)) -> IO(Maybe<&2, Counts>):
397  match sent:
398    case Fail{v}:
399      do IO<Maybe<&2, Counts>>:
400        Chan.close(Counts, reply)
401        return None{}
402    case Done{u}:
403      do IO<Maybe<&2, Counts>>:
404        value : Maybe<&1, Counts> <- Chan.recv(Counts, reply)
405        Chan.close(Counts, reply)
406        return inspect.value(value)
407
408def inspect(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data, context: Context<R, P, I, E, O>) -> IO(Maybe<&2, Counts>):
409  Context{messages} = context
410  do IO<Maybe<&2, Counts>>:
411    channel : Chan(Counts) <- Chan.new(Counts, 1)
412    +reply : Chan(Counts) = channel
413    sent : Result<&1, &1, Message<R, P, I, E, O>, Unit> <- Chan.send(Message<R, P, I, E, O>, messages, Inspect{reply})
414    inspect.sent(Message<R, P, I, E, O>, sent, reply)
415
416def close.sent(-T: Type, sent: Result<&1, &1, T, Unit>, +reply: Chan(CloseResult)) -> IO(CloseResult):
417  match sent:
418    case Fail{v}:
419      do IO<CloseResult>:
420        Chan.close(CloseResult, reply)
421        return Closed{}
422    case Done{u}:
423      do IO<CloseResult>:
424        value : CloseResult <- IO.join(CloseResult, reply)
425        Chan.close(CloseResult, reply)
426        return value
427
428def close.join(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
429  owner: Owner<R, P, I, E, O>
430) -> IO(OwnerClose<R, P, I, E, O>):
431  Owner{context, finished} = owner
432  do IO<OwnerClose<R, P, I, E, O>>:
433    IO.join(Unit, finished)
434    return OwnerClosed{}
435
436def close.completed(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data,
437  result: CloseResult, owner: Owner<R, P, I, E, O>
438) -> IO(OwnerClose<R, P, I, E, O>):
439  match result:
440    case Closed{}:
441      close.join(R, P, I, E, O, owner)
442    case Busy{counts}:
443      IO.pure(OwnerClose<R, P, I, E, O>, OwnerBusy{owner, counts})
444
445# Stop external admissions before close. Busy preserves live context/capacity;
446# retry after admitted operations finish. Stalled operations remain observable.
447def close(-R: Type, -P: Data, -I: Data, -E: Data, -O: Data, owner: Owner<R, P, I, E, O>) -> IO(OwnerClose<R, P, I, E, O>):
448  Owner{Context{+messages}, finished} = owner
449  do IO<OwnerClose<R, P, I, E, O>>:
450    channel : Chan(CloseResult) <- Chan.new(CloseResult, 1)
451    +reply : Chan(CloseResult) = channel
452    sent : Result<&1, &1, Message<R, P, I, E, O>, Unit> <- Chan.send(Message<R, P, I, E, O>, messages, Shutdown{reply})
453    result : CloseResult <- close.sent(Message<R, P, I, E, O>, sent, reply)
454    close.completed(R, P, I, E, O, result, Owner{Context{messages}, finished})
455
456# Policies are prepared Data, not callbacks. Later lifecycle stages can consume
457# these root-to-route declarations without splitting patterns or finding groups.
458type Group<-P: Data> is Data:
459  Group{id: U32, ancestors: List<&2, U32>, policy: P}
460
461type GroupPlan<-P: Data> is Data:
462  GroupPlan{ids: List<&2, U32>, policies: List<&2, P>}
463
464type Route<-A: Data, -P: Data> is Data:
465  Route{method: String, pattern: String, action: A, groups: List<&2, U32>, policy: P}
466
467type Endpoint<-A: Data, -P: Data> is Data:
468  Endpoint{action: A, policies: List<&2, P>}
469
470type Application<-A: Data, -C: Data, -P: Data> is Data:
471  Application{table: Router.Table<Endpoint<A, P>>, context: C, root: P, groups: List<&2, GroupPlan<P>>}
472
473type RegistrationError is Data:
474  RouteError{error: Router.Error}
475  InvalidGroups{groups: List<&2, U32>}
476
477type Match<-P: Data> is Data:
478  Match{target: Target.Target, params: Map<&2, String>, groups: List<&2, U32>, method: String, pattern: String, policies: List<&2, P>}
479
480def groups.equal(xs: List<&2, U32>, ys: List<&2, U32>) -> Bool:
481  match xs ys:
482    case Nil{} Nil{}:
483      True{}
484    case x <> xt y <> yt:
485      U32.is_eq(x, y) && groups.equal(xt, yt)
486    case _ _:
487      False{}
488
489def group.pick(~P: Data, same: Bool, group: Group<P>, later: Unit -> Maybe<&2, Group<P>>) -> Maybe<&2, Group<P>>:
490  match same:
491    case True{}:
492      Some{group}
493    case False{}:
494      later(Unit{})
495
496def group.find(~P: Data, groups: List<&2, Group<P>>, +id: U32) -> Maybe<&2, Group<P>>:
497  match groups:
498    case Nil{}:
499      None{}
500    case Group{+here, ancestors, policy} <> rest:
501      group.pick(~P, U32.is_eq(here, id), Group{here, ancestors, policy}, u => group.find(~P, rest, id))
502
503def group.checked(~P: Data, same: Bool, policy: P) -> Maybe<&2, P>:
504  match same:
505    case False{}:
506      None{}
507    case True{}:
508      Some{policy}
509
510def group.policy(~P: Data, found: Maybe<&2, Group<P>>, ancestors: List<&2, U32>) -> Maybe<&2, P>:
511  match found:
512    case None{}:
513      None{}
514    case Some{Group{id, expected, policy}}:
515      group.checked(~P, groups.equal(expected, ancestors), policy)
516
517def groups.policies(~P: Data, ids: List<&2, U32>, +declared: List<&2, Group<P>>, +ancestors: List<&2, U32>) -> Maybe<&2, List<&2, P>>:
518  match ids:
519    case Nil{}:
520      Some{Nil{}}
521    case +id <> rest:
522      do Maybe<&2, List<&2, P>>:
523        policy : P <- group.policy(~P, group.find(~P, declared, id), ancestors)
524        policies : List<&2, P> <- groups.policies(~P, rest, declared, List.append(&2, U32, ancestors, [id]))
525        return policy <> policies
526
527def groups.unique(~P: Data, found: Maybe<&2, Group<P>>) -> Bool:
528  match found:
529    case None{}:
530      True{}
531    case Some{group}:
532      False{}
533
534def groups.valid(~P: Data, groups: List<&2, Group<P>>, +all: List<&2, Group<P>>) -> Bool:
535  match groups:
536    case Nil{}:
537      True{}
538    case Group{+id, +ancestors, policy} <> +rest:
539      chain = groups.policies(~P, List.append(&2, U32, ancestors, [id]), all, Nil{})
540      groups.unique(~P, group.find(~P, rest, id)) && Maybe.is_some(&2, List<&2, P>, chain) && groups.valid(~P, rest, all)
541
542def routes.policies(~P: Data, result: Maybe<&2, List<&2, P>>, ids: List<&2, U32>) -> Result<&2, &2, RegistrationError, List<&2, P>>:
543  match result:
544    case None{}:
545      Fail{InvalidGroups{ids}}
546    case Some{policies}:
547      Done{policies}
548
549def routes.prepare(~A: Data, ~P: Data, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, +root: P) -> Result<&2, &2, RegistrationError, List<&2, Router.Entry<Endpoint<A, P>>>>:
550  match routes:
551    case Nil{}:
552      Done{Nil{}}
553    case Route{method, pattern, action, +ids, policy} <> rest:
554      do Result<&2, &2, RegistrationError, List<&2, Router.Entry<Endpoint<A, P>>>>:
555        policies : List<&2, P> <- routes.policies(~P, groups.policies(~P, ids, groups, Nil{}), ids)
556        entries : List<&2, Router.Entry<Endpoint<A, P>>> <- routes.prepare(~A, ~P, rest, groups, root)
557        return Router.Entry{method, pattern, Endpoint{action, root <> List.append(&2, P, policies, [policy])}, ids} <> entries
558
559def groups.prepare(~P: Data, groups: List<&2, Group<P>>, +all: List<&2, Group<P>>) -> Result<&2, &2, RegistrationError, List<&2, GroupPlan<P>>>:
560  match groups:
561    case Nil{}:
562      Done{Nil{}}
563    case Group{id, ancestors, policy} <> rest:
564      +ids = List.append(&2, U32, ancestors, [id])
565      do Result<&2, &2, RegistrationError, List<&2, GroupPlan<P>>>:
566        policies : List<&2, P> <- routes.policies(~P, groups.policies(~P, ids, all, Nil{}), ids)
567        plans : List<&2, GroupPlan<P>> <- groups.prepare(~P, rest, all)
568        return GroupPlan{ids, policies} <> plans
569
570def groups.select(~P: Data, same: Bool, policies: List<&2, P>, later: Unit -> List<&2, P>) -> List<&2, P>:
571  match same:
572    case True{}: policies
573    case False{}: later(Unit{})
574
575def groups.prepared(~P: Data, plans: List<&2, GroupPlan<P>>, +ids: List<&2, U32>) -> List<&2, P>:
576  match plans:
577    case Nil{}:
578      Nil{}
579    case GroupPlan{here, policies} <> rest:
580      groups.select(~P, groups.equal(here, ids), policies, u => groups.prepared(~P, rest, ids))
581
582def application.table(~A: Data, ~C: Data, ~P: Data, result: Result<&2, &2, Router.Error, Router.Table<Endpoint<A, P>>>, context: C, root: P, groups: List<&2, GroupPlan<P>>) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
583  match result:
584    case Fail{error}:
585      Fail{RouteError{error}}
586    case Done{table}:
587      Done{Application{table, context, root, groups}}
588
589def application.valid(~A: Data, ~C: Data, ~P: Data, valid: Bool, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, context: C, +root: P) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
590  match valid:
591    case False{}:
592      Fail{InvalidGroups{Nil{}}}
593    case True{}:
594      do Result<&2, &2, RegistrationError, Application<A, C, P>>:
595        entries : List<&2, Router.Entry<Endpoint<A, P>>> <- routes.prepare(~A, ~P, routes, groups, root)
596        plans : List<&2, GroupPlan<P>> <- groups.prepare(~P, groups, groups)
597        application.table(~A, ~C, ~P, Router.prepare(~Endpoint<A, P>, entries), context, root, plans)
598
599# Construction is pure and fallible; no listener or owner is started here.
600def application(~A: Data, ~C: Data, ~P: Data, routes: List<&2, Route<A, P>>, +groups: List<&2, Group<P>>, context: C, root: P) -> Result<&2, &2, RegistrationError, Application<A, C, P>>:
601  application.valid(~A, ~C, ~P, groups.valid(~P, groups, groups), routes, groups, context, root)
602
603def describe(~A: Data, ~C: Data, ~P: Data, app: Application<A, C, P>) -> List<&2, Router.Description>:
604  Application{table, context, root, groups} = app
605  Router.describe(~Endpoint<A, P>, table)
606
607# A closed template can run repeatedly; an affine callback registry cannot.
608def plain(~handler: Http.Req -> IO(Http.Res), req: Http.Req) -> IO(Result<&2, &1, ResponseError, Http.Res>):
609  do IO<Result<&2, &1, ResponseError, Http.Res>>:
610    response : Http.Res <- handler(req)
611    return validate(response)
612
613type Policy<-H: Data> is Data:
614  Policy{id: U32, before: List<&2, H>, transforms: List<&2, H>}
615
616def policy(~H: Data, id: U32, before: List<&2, H>, transforms: List<&2, H>) -> Policy<H>:
617  Policy{id, before, List.reverse(&2, H, transforms)}
618
619type Metadata is Data:
620  Metadata{method: String, target: String, headers: Map<&2, List<&2, String>>}
621
622type Error<-E: Data> is Data:
623  ApplicationError{error: E}
624  Framework{status: U32, allow: String}
625  ResponseInvalid{}
626
627# Adapt reusable input status failures to the existing one-pass dispatch mapper.
628def input(~I: Type, ~E: Data, outcome: Result<&2, &1, U32, I>) -> Result<&2, &1, Error<E>, I>:
629  match outcome:
630    case Fail{status}: Fail{Framework{status, ""}}
631    case Done{value}: Done{value}
632
633def execute.decoded(~I: Type, ~S: Type, ~K: Data, ~E: Data,
634  ~handler: S -> K -> I -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
635  outcome: Result<&2, &1, Error<E>, I>, state: S, config: K
636) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
637  match outcome:
638    case Fail{error}: IO.pure(S & Result<&2, &1, Error<E>, Http.Res>, (state, Fail{error}))
639    case Done{value}: handler(state, config, value)
640
641# An executor, not a second dispatcher: call from dispatch's run template, after
642# all applicable before hooks. Only validated typed input reaches business IO.
643def execute(~I: Type, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
644  ~decoder: K -> Match<Policy<H>> -> Http.Req -> Result<&2, &1, Error<E>, I>,
645  ~handler: S -> K -> I -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
646  state: S, +config: K, selected: Match<Policy<H>>, req: Http.Req
647) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
648  execute.decoded(~I, ~S, ~K, ~E, ~handler, decoder(config, selected, req), state, config)
649
650type Step<-S: Type, -E: Data> is Type:
651  Continue{state: S}
652  Early{state: S, response: Http.Res}
653  Reject{state: S, error: Error<E>}
654
655type Completion<-S: Type, -H: Data> is Type:
656  Completed{state: S, response: Http.Res, entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool}
657
658# Application completion only: no socket write, duration, or peer-receipt claim.
659def minimal() -> Http.Res:
660  empty(500)
661
662def problem.allow(add: Bool, headers: Map<&2, List<&2, String>>, allow: String) -> Map<&2, List<&2, String>>:
663  match add:
664    case True{}: Http.set(headers, "allow", allow)
665    case False{}: headers
666
667def problem.response(allow: String, response: Http.Res) -> Http.Res:
668  Http.Res{+status, headers, body} = response
669  Http.Res{status, problem.allow(U32.is_eq(status, 405), Http.set(headers, "content-type", "application/problem+json"), allow), body}
670
671def problem(+status: U32, title: String, allow: String) -> Http.Res:
672  problem.response(allow, json(~Json.Val, ~(x => x), status, Json.Obj{[(Json.utf8("type"), Json.Str{Json.utf8("about:blank")}), (Json.utf8("title"), Json.Str{Json.utf8(title)}), (Json.utf8("status"), Json.Num{Json.utf8(U32.show(status))})]}))
673
674def default.error(~E: Data, error: Error<E>) -> Http.Res:
675  match error:
676    case Framework{status, allow}:
677      match status:
678        case 400:
679          problem(400, "Bad Request", allow)
680        case 404:
681          problem(404, "Not Found", allow)
682        case 405:
683          problem(405, "Method Not Allowed", allow)
684        case 415:
685          problem(415, "Unsupported Media Type", allow)
686        case _:
687          minimal()
688    case _:
689      minimal()
690
691def default.mapper(~S: Type, ~K: Data, ~E: Data, state: S, config: K, error: Error<E>) -> IO(S & Result<&2, &1, Error<E>, Http.Res>):
692  IO.pure(S & Result<&2, &1, Error<E>, Http.Res>, (state, Done{default.error(~E, error)}))
693
694def mapped.checked(~S: Type, ~H: Data, state: S, entered: List<&2, Policy<H>>, stopped: Bool, result: Result<&2, &1, ResponseError, Http.Res>) -> Completion<S, H>:
695  match result:
696    case Fail{error}:
697      Completed{state, minimal(), entered, True{}, True{}}
698    case Done{response}:
699      Completed{state, response, entered, True{}, stopped}
700
701def mapped.result(~S: Type, ~E: Data, ~H: Data, entered: List<&2, Policy<H>>, stopped: Bool, result: S & Result<&2, &1, Error<E>, Http.Res>) -> Completion<S, H>:
702  (state, outcome) = result
703  match outcome:
704    case Fail{error}:
705      Completed{state, minimal(), entered, True{}, True{}}
706    case Done{response}:
707      mapped.checked(~S, ~H, state, entered, stopped, validate(response))
708
709def failed(~S: Type, ~K: Data, ~E: Data, ~H: Data,
710  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
711  mapped: Bool, state: S, config: K, error: Error<E>, entered: List<&2, Policy<H>>, stopped: Bool
712) -> IO(Completion<S, H>):
713  match mapped:
714    case True{}:
715      IO.pure(Completion<S, H>, Completed{state, minimal(), entered, True{}, True{}})
716    case False{}:
717      do IO<Completion<S, H>>:
718        result : S & Result<&2, &1, Error<E>, Http.Res> <- mapper(state, config, error)
719        return mapped.result(~S, ~E, ~H, entered, stopped, result)
720
721def result(~S: Type, ~K: Data, ~E: Data, ~H: Data,
722  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
723  config: K, entered: List<&2, Policy<H>>, outcome: S & Result<&2, &1, Error<E>, Http.Res>
724) -> IO(Completion<S, H>):
725  (state, response) = outcome
726  match response:
727    case Fail{error}:
728      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, error, entered, False{})
729    case Done{res}:
730      IO.pure(Completion<S, H>, Completed{state, res, entered, False{}, False{}})
731
732def before.next(~S: Type, ~E: Data, step: Step<S, E>, next: S -> IO(Step<S, E>)) -> IO(Step<S, E>):
733  match step:
734    case Continue{state}:
735      next(state)
736    case Early{state, response}:
737      IO.pure(Step<S, E>, Early{state, response})
738    case Reject{state, error}:
739      IO.pure(Step<S, E>, Reject{state, error})
740
741def before(~S: Type, ~K: Data, ~E: Data, ~H: Data,
742  ~hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
743  hooks: List<&2, H>, state: S, +config: K, +meta: Metadata
744) -> IO(Step<S, E>):
745  match hooks:
746    case Nil{}:
747      IO.pure(Step<S, E>, Continue{state})
748    case h <> rest:
749      do IO<Step<S, E>>:
750        step : Step<S, E> <- hook(h, state, config, meta)
751        before.next(~S, ~E, step, s => before(~S, ~K, ~E, ~H, ~hook, rest, s, config, meta))
752
753def transformed(~S: Type, ~K: Data, ~E: Data, ~H: Data,
754  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
755  config: K, entered: List<&2, Policy<H>>, mapped: Bool, outcome: S & Result<&2, &1, Error<E>, Http.Res>
756) -> IO(Completion<S, H>):
757  (state, response) = outcome
758  match response:
759    case Fail{error}:
760      failed(~S, ~K, ~E, ~H, ~mapper, mapped, state, config, error, entered, True{})
761    case Done{res}:
762      IO.pure(Completion<S, H>, Completed{state, res, entered, mapped, False{}})
763
764def transforms(~S: Type, ~K: Data, ~E: Data, ~H: Data,
765  ~hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
766  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
767  hooks: List<&2, H>, +config: K, completion: Completion<S, H>
768) -> IO(Completion<S, H>):
769  match hooks:
770    case Nil{}:
771      IO.pure(Completion<S, H>, completion)
772    case h <> rest:
773      match completion:
774        case Completed{state, response, entered, mapped, True{}}:
775          IO.pure(Completion<S, H>, Completed{state, response, entered, mapped, True{}})
776        case Completed{state, response, +entered, +mapped, False{}}:
777          do IO<Completion<S, H>>:
778            changed : S & Result<&2, &1, Error<E>, Http.Res> <- hook(h, state, config, response)
779            next : Completion<S, H> <- transformed(~S, ~K, ~E, ~H, ~mapper, config, entered, mapped, changed)
780            transforms(~S, ~K, ~E, ~H, ~hook, ~mapper, rest, config, next)
781
782def entered.step(~S: Type, ~K: Data, ~E: Data, ~H: Data,
783  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
784  config: K, entered: List<&2, Policy<H>>, body: Http.Body, step: Step<S, E>, next: S -> List<&2, Policy<H>> -> Http.Body -> IO(Completion<S, H>)
785) -> IO(Completion<S, H>):
786  match step:
787    case Continue{state}:
788      next(state, entered, body)
789    case Early{state, response}:
790      IO.pure(Completion<S, H>, Completed{state, response, entered, False{}, False{}})
791    case Reject{state, error}:
792      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, error, entered, False{})
793
794def scopes(~S: Type, ~K: Data, ~E: Data, ~H: Data,
795  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
796  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
797  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
798  policies: List<&2, Policy<H>>, state: S, +config: K, +meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body,
799  next: S -> List<&2, Policy<H>> -> Http.Body -> IO(Completion<S, H>)
800) -> IO(Completion<S, H>):
801  match policies:
802    case Nil{}:
803      next(state, entered, body)
804    case Policy{+id, +hooks, +unwind} <> rest:
805      scope = {Policy{id, hooks, unwind} : Policy<H>}
806      do IO<Completion<S, H>>:
807        step : Step<S, E> <- before(~S, ~K, ~E, ~H, ~before_hook, hooks, state, config, meta)
808        inner : Completion<S, H> <- entered.step(~S, ~K, ~E, ~H, ~mapper, config, scope <> entered, body, step, s => ids => b => scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, rest, s, config, meta, ids, b, next))
809        transforms(~S, ~K, ~E, ~H, ~after_hook, ~mapper, unwind, config, inner)
810
811def finish.checked(~S: Type, ~K: Data, ~E: Data, ~H: Data,
812  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
813  state: S, config: K, entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool, checked: Result<&2, &1, ResponseError, Http.Res>
814) -> IO(Completion<S, H>):
815  match checked:
816    case Done{response}:
817      IO.pure(Completion<S, H>, Completed{state, response, entered, mapped, stopped})
818    case Fail{error}:
819      failed(~S, ~K, ~E, ~H, ~mapper, mapped, state, config, ResponseInvalid{}, entered, True{})
820
821def finish(~S: Type, ~K: Data, ~E: Data, ~H: Data,
822  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
823  config: K, completion: Completion<S, H>
824) -> IO(Completion<S, H>):
825  Completed{state, response, entered, mapped, stopped} = completion
826  finish.checked(~S, ~K, ~E, ~H, ~mapper, state, config, entered, mapped, stopped, validate(response))
827
828def policies.tail(~H: Data, policies: List<&2, Policy<H>>) -> List<&2, Policy<H>>:
829  match policies:
830    case Nil{}:
831      Nil{}
832    case root <> rest:
833      rest
834
835
836def endpoint(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
837  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
838  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
839  action: A, state: S, +config: K, selected: Match<Policy<H>>, meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
840) -> IO(Completion<S, H>):
841  Metadata{method, target, headers} = meta
842  do IO<Completion<S, H>>:
843    outcome : S & Result<&2, &1, Error<E>, Http.Res> <- run(action, state, config, selected, Http.Req{method, target, headers, body})
844    result(~S, ~K, ~E, ~H, ~mapper, config, entered, outcome)
845
846def framework(~S: Type, ~K: Data, ~E: Data, ~H: Data,
847  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
848  +status: U32, allow: String, state: S, config: K, entered: List<&2, Policy<H>>, body: Http.Body
849) -> IO(Completion<S, H>):
850  match status:
851    case 204:
852      IO.pure(Completion<S, H>, Completed{state, Http.Res{204, Http.set(Http.empty(), "allow", allow), Http.from_string("")}, entered, False{}, False{}})
853    case _:
854      failed(~S, ~K, ~E, ~H, ~mapper, False{}, state, config, Framework{status, allow}, entered, False{})
855
856def choice(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
857  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
858  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
859  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
860  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
861  selected: Router.Choice<Endpoint<A, Policy<H>>>, target: Target.Target,
862  groups: List<&2, GroupPlan<Policy<H>>>, state: S, +config: K, +meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
863) -> IO(Completion<S, H>):
864  match selected:
865    case Router.NotFound{}:
866      framework(~S, ~K, ~E, ~H, ~mapper, 404, "", state, config, entered, body)
867    case Router.MethodMissing{ids, allow}:
868      policies = groups.prepared(~Policy<H>, groups, ids)
869      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies, state, config, meta, entered, body,
870        s => entered => b => framework(~S, ~K, ~E, ~H, ~mapper, 405, allow, s, config, entered, b))
871    case Router.Options{ids, allow}:
872      policies = groups.prepared(~Policy<H>, groups, ids)
873      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies, state, config, meta, entered, body,
874        s => entered => b => framework(~S, ~K, ~E, ~H, ~mapper, 204, allow, s, config, entered, b))
875    case Router.Found{Endpoint{action, +policies}, params, ids, method, pattern}:
876      selected = {Match{target, params, ids, method, pattern, policies} : Match<Policy<H>>}
877      scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, policies.tail(~H, policies), state, config, meta, entered, body,
878        s => entered => b => endpoint(~A, ~S, ~K, ~E, ~H, ~run, ~mapper, action, s, config, selected, meta, entered, b))
879
880def metadata.authority(authority: Maybe<&2, String>, meta: Metadata) -> Metadata:
881  match authority:
882    case None{}:
883      meta
884    case Some{host}:
885      Metadata{method, target, headers} = meta
886      Metadata{method, target, Http.set(headers, "host", host)}
887
888def resolved(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
889  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
890  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
891  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
892  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
893  outcome: Result<&2, &2, Router.Error, Router.Resolved<Endpoint<A, Policy<H>>>>,
894  groups: List<&2, GroupPlan<Policy<H>>>, state: S, config: K, meta: Metadata, entered: List<&2, Policy<H>>, body: Http.Body
895) -> IO(Completion<S, H>):
896  match outcome:
897    case Fail{error}:
898      framework(~S, ~K, ~E, ~H, ~mapper, 400, "", state, config, entered, body)
899    case Done{Router.Resolved{+target, selected}}:
900      Target.Target{segments, query, authority, star} = target
901      choice(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, selected, target, groups, state, config, metadata.authority(authority, meta), entered, body)
902
903def dispatch.application(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
904  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
905  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
906  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
907  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
908  app: Application<A, K, Policy<H>>, state: S, req: Http.Req
909) -> IO(Completion<S, H>):
910  Application{table, +config, root, groups} = app
911  Http.Req{+method, +path, +headers, body} = req
912  +meta = {Metadata{method, path, headers} : Metadata}
913  do IO<Completion<S, H>>:
914    completion : Completion<S, H> <- scopes(~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~mapper, [root], state, config, meta, Nil{}, body,
915      s => entered => b => resolved(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper,
916        Router.resolve(~Endpoint<A, Policy<H>>, table, method, path), groups, s, config, meta, entered, b))
917    finish(~S, ~K, ~E, ~H, ~mapper, config, completion)
918
919def access.disabled(~K: Data, config: K) -> Notices.Access:
920  Notices.Disabled{}
921
922def notice.ignore(~N: Data, ~H: Data, ~E: Data, observer: N, policy: Policy<H>, notice: Notices.Notice) -> IO(Result<&2, &2, E, Unit>):
923  IO.pure(Result<&2, &2, E, Unit>, Done{Unit{}})
924
925def notice.error.ignore(~N: Data, ~E: Data, observer: N, error: E) -> IO(Unit):
926  IO.pure(Unit, Unit{})
927
928def notice.result(~N: Data, ~E: Data, ~report: N -> E -> IO(Unit), observer: N, result: Result<&2, &2, E, Unit>) -> IO(Unit):
929  match result:
930    case Done{unit}: IO.pure(Unit, Unit{})
931    case Fail{error}: report(observer, error)
932
933def notices(~N: Data, ~H: Data, ~E: Data,
934  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
935  ~report: N -> E -> IO(Unit),
936  entered: List<&2, Policy<H>>, +observer: N, +notice: Notices.Notice
937) -> IO(Unit):
938  match entered:
939    case Nil{}: IO.pure(Unit, Unit{})
940    case policy <> rest:
941      do IO<Unit>:
942        result : Result<&2, &2, E, Unit> <- notify(observer, policy, notice)
943        notice.result(~N, ~E, ~report, observer, result)
944        notices(~N, ~H, ~E, ~notify, ~report, rest, observer, notice)
945
946def dispatch.completed(~S: Type, ~N: Data, ~H: Data, ~E: Data,
947  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
948  ~report: N -> E -> IO(Unit),
949  observer: N, access: Notices.Access, started: Time.Duration, completion: Completion<S, H>
950) -> IO(Completion<S, H>):
951  Completed{state, Http.Res{+status, headers, body}, +entered, +mapped, +stopped} = completion
952  do IO<Completion<S, H>>:
953    ended : Time.Duration <- Time.mono()
954    +notice : Notices.Notice = Notices.Notice{status, Time.Duration.sub(ended, started), mapped, stopped, Notices.ApplicationOnly{}}
955    notices(~N, ~H, ~E, ~notify, ~report, entered, observer, notice)
956    Notices.access(access, notice)
957    return Completed{state, Http.Res{status, headers, body}, entered, mapped, stopped}
958
959# The same application pipeline is used live; only this boundary emits direct notices.
960def dispatch(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data, ~N: Data,
961  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
962  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
963  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
964  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
965  ~notify: N -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
966  ~report: N -> E -> IO(Unit),
967  observer: N, access: Notices.Access, app: Application<A, K, Policy<H>>, state: S, req: Http.Req
968) -> IO(Completion<S, H>):
969  do IO<Completion<S, H>>:
970    started : Time.Duration <- Time.mono()
971    completion : Completion<S, H> <- dispatch.application(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, app, state, req)
972    dispatch.completed(~S, ~N, ~H, ~E, ~notify, ~report, observer, access, started, completion)
973
974# The wrapper owns both pipes until its admitted operation really returns.
975# Only disposable HTTP packed buffers cross them, never external handles.
976type BufferedRequest is Data:
977  BufferedRequest{metadata: Metadata, body: Chan(Http.Body), response: Chan(Http.Res)}
978
979type ApplicationReport<-H: Data> is Data:
980  Dispatched{entered: List<&2, Policy<H>>, mapped: Bool, stopped: Bool}
981  CapacityRejected{}
982
983type ServingContext<-A: Data, -S: Type, -K: Data, -H: Data> is Data:
984  ServingContext{application: Application<A, K, Policy<H>>,
985    owner: Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
986
987type Server<-A: Data, -S: Type, -K: Data, -H: Data> is Type:
988  Server{transport: Http.Server<ServingContext<A, S, K, H>>,
989    owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
990
991type StartupError is Data:
992  RegistrationFailed{error: RegistrationError}
993  TransportFailed{error: Http.StartupError}
994  EmptyBundles{}
995
996type ServerExit<-A: Data, -S: Type, -K: Data, -H: Data> is Type:
997  ServerExit{transport: Http.ServerExit, application: OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>}
998
999def server.config(port: U32) -> Http.ServerConfig:
1000  Http.ServerConfig{"127.0.0.1", port, 1048576, 65536, 128, 128, 128, 5000, 30000, 30000, 30000}
1001
1002def server.bundles.cons(~S: Type, resource: S, counted: List<S> & Nat) -> List<S> & Nat:
1003  (returned, count) = counted
1004  (resource <> returned, 1n+count)
1005
1006def server.bundles(~S: Type, resources: List<S>) -> List<S> & Nat:
1007  match resources:
1008    case Nil{}: (Nil{}, 0n)
1009    case resource <> rest:
1010      server.bundles.cons(~S, resource, server.bundles(~S, rest))
1011
1012def server.work.completed(~S: Type, ~H: Data, response_channel: Chan(Http.Res),
1013  completion: Completion<S, H>
1014) -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>):
1015  Completed{state, response, entered, mapped, stopped} = completion
1016  do IO<S & Result<&2, &2, Unit, ApplicationReport<H>>>:
1017    sent : Result<&1, &1, Http.Res, Unit> <- Chan.send(Http.Res, response_channel, response)
1018    return (state, Done{Dispatched{entered, mapped, stopped}})
1019
1020def server.work(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
1021  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
1022  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1023  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1024  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1025  state: S, app: Application<A, K, Policy<H>>, input: BufferedRequest
1026) -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>):
1027  BufferedRequest{Metadata{method, target, headers}, body_channel, response_channel} = input
1028  do IO<S & Result<&2, &2, Unit, ApplicationReport<H>>>:
1029    body : Http.Body <- receive(Http.Body, body_channel)
1030    completion : Completion<S, H> <- dispatch.application(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper,
1031      app, state, Http.Req{method, target, headers, body})
1032    server.work.completed(~S, ~H, response_channel, completion)
1033
1034type NoticePayload<-K: Data, -H: Data> is Data:
1035  NoticePayload{config: K, started: Time.Duration, application: ApplicationReport<H>}
1036
1037def server.notified(~K: Data, ~H: Data, ~E: Data,
1038  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1039  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1040  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1041  +config: K, +notice: Notices.Notice, application: ApplicationReport<H>,
1042  status: U32, outcome: TransportCompletion.Outcome
1043) -> IO(Unit):
1044  match application:
1045    case Dispatched{+entered, +mapped, +stopped}:
1046      do IO<Unit>:
1047        notices(~K, ~H, ~E, ~notify, ~report, entered, config, notice)
1048        Notices.access(access(config), notice)
1049        done(TransportCompletion.Completion{Dispatched{entered, mapped, stopped}, status, outcome})
1050    case CapacityRejected{}:
1051      do IO<Unit>:
1052        Notices.access(access(config), notice)
1053        done(TransportCompletion.Completion{CapacityRejected{}, status, outcome})
1054
1055def server.completed.report(~K: Data, ~H: Data, ~E: Data,
1056  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1057  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1058  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1059  config: K, duration: Time.Duration, application: ApplicationReport<H>, +status: U32, +outcome: TransportCompletion.Outcome
1060) -> IO(Unit):
1061  match application:
1062    case Dispatched{+entered, +mapped, +stopped}:
1063      server.notified(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config,
1064        Notices.Notice{status, duration, mapped, stopped, Notices.Transport{outcome}}, Dispatched{entered, mapped, stopped}, status, outcome)
1065    case CapacityRejected{}:
1066      server.notified(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config,
1067        Notices.Notice{status, duration, False{}, False{}, Notices.Transport{outcome}}, CapacityRejected{}, status, outcome)
1068
1069def server.completed(~K: Data, ~H: Data, ~E: Data,
1070  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1071  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1072  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1073  completion: TransportCompletion.Completion<NoticePayload<K, H>>
1074) -> IO(Unit):
1075  TransportCompletion.Completion{NoticePayload{config, started, +application}, +status, +outcome} = completion
1076  do IO<Unit>:
1077    ended : Time.Duration <- Time.mono()
1078    server.completed.report(~K, ~H, ~E, ~notify, ~report, ~access, ~done, config, Time.Duration.sub(ended, started), application, status, outcome)
1079
1080def server.response(~K: Data, ~H: Data,
1081  ~done: TransportCompletion.Completion<NoticePayload<K, H>> -> IO(Unit),
1082  config: K, started: Time.Duration, outcome: Outcome<Unit, ApplicationReport<H>>, +body: Chan(Http.Body), +response: Chan(Http.Res)
1083) -> IO(Http.Reply):
1084  match outcome:
1085    case Success{report}:
1086      do IO<Http.Reply>:
1087        result : Http.Res <- receive(Http.Res, response)
1088        Chan.close(Http.Body, body)
1089        Chan.close(Http.Res, response)
1090        return Http.reply.complete(~NoticePayload<K, H>, ~done, NoticePayload{config, started, report}, result)
1091    case _:
1092      do IO<Http.Reply>:
1093        # Closing alone retains buffered values in Base. Consume the rejected
1094        # body before closing; the single room1 send has already completed.
1095        discarded : Http.Body <- receive(Http.Body, body)
1096        Chan.close(Http.Body, body)
1097        Chan.close(Http.Res, response)
1098        return Http.reply.complete(~NoticePayload<K, H>, ~done, NoticePayload{config, started, CapacityRejected{}},
1099          Http.Res{503, Http.set(Http.empty(), "connection", "close"), Http.from_string("")})
1100
1101def server.handle(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1102  ~done: TransportCompletion.Completion<NoticePayload<K, H>> -> IO(Unit),
1103  context: ServingContext<A, S, K, H>, req: Http.Req
1104) -> IO(Http.Reply):
1105  ServingContext{+app, owner} = context
1106  Application{table, config, root, groups} = app
1107  Http.Req{method, target, headers, body} = req
1108  do IO<Http.Reply>:
1109    started : Time.Duration <- Time.mono()
1110    +body_channel : Chan(Http.Body) <- Chan.new(Http.Body, 1)
1111    +response_channel : Chan(Http.Res) <- Chan.new(Http.Res, 1)
1112    sent : Result<&1, &1, Http.Body, Unit> <- Chan.send(Http.Body, body_channel, body)
1113    outcome : Outcome<Unit, ApplicationReport<H>> <- call(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>,
1114      owner, app, BufferedRequest{Metadata{method, target, headers}, body_channel, response_channel})
1115    server.response(~K, ~H, ~done, config, started, outcome, body_channel, response_channel)
1116
1117
1118def server.started(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1119  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1120  ~destroy: S -> IO(Unit),
1121  +context: Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>,
1122  room: U32, capacity: Nat,
1123  started: List<S> & Result<&2, &1, Http.StartupError, Http.Server<ServingContext<A, S, K, H>>>
1124) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1125  Context{+messages} = context
1126  (resources, result) = started
1127  match result:
1128    case Fail{error}:
1129      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1130        dispose(~S, ~destroy, resources)
1131        Chan.close(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>, messages)
1132        return Fail{TransportFailed{error}}
1133    case Done{transport}:
1134      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1135        finished : Chan(Unit) <- IO.fork(Unit, owner(~S, ~Application<A, K, Policy<H>>, ~BufferedRequest, ~Unit, ~ApplicationReport<H>,
1136          ~work, ~destroy, messages, room, Waiting{resources, ledger.open(capacity)}))
1137        return Done{Server{transport, Owner{Context{messages}, finished}}}
1138
1139def server.prepared(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1140  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1141  ~destroy: S -> IO(Unit), app: Application<A, K, Policy<H>>, +config: Http.ServerConfig, resources: List<S> & Nat
1142) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1143  Http.ServerConfig{host, port, body, headers, connections, requests, +buffered, hm, bm, im, wm} = config
1144  (bundles, capacity) = resources
1145  match capacity:
1146    case 0n:
1147      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1148        dispose(~S, ~destroy, bundles)
1149        return Fail{EmptyBundles{}}
1150    case 1n+ +rest:
1151      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1152        messages : Chan(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>) <-
1153          Chan.new(Message<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>, buffered)
1154        +context : Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> = Context{messages}
1155        started : List<S> & Result<&2, &1, Http.StartupError, Http.Server<ServingContext<A, S, K, H>>> <-
1156          Http.server.start(~ServingContext<A, S, K, H>, ~List<S>, ServingContext{app, context}, bundles, config)
1157        server.started(~A, ~S, ~K, ~H, ~work, ~destroy, context, buffered, 1n+rest, started)
1158
1159def server.valid(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1160  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1161  ~destroy: S -> IO(Unit), valid: Bool, app: Application<A, K, Policy<H>>, config: Http.ServerConfig, resources: List<S>
1162) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1163  match valid:
1164    case False{}:
1165      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1166        dispose(~S, ~destroy, resources)
1167        return Fail{TransportFailed{Http.InvalidConfig{}}}
1168    case True{}:
1169      server.prepared(~A, ~S, ~K, ~H, ~work, ~destroy, app, config, server.bundles(~S, resources))
1170
1171def server.registered(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1172  ~work: S -> Application<A, K, Policy<H>> -> BufferedRequest -> IO(S & Result<&2, &2, Unit, ApplicationReport<H>>),
1173  ~destroy: S -> IO(Unit), registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1174  +config: Http.ServerConfig, resources: List<S>
1175) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1176  match registration:
1177    case Fail{error}:
1178      do IO<Result<&2, &1, StartupError, Server<A, S, K, H>>>:
1179        dispose(~S, ~destroy, resources)
1180        return Fail{RegistrationFailed{error}}
1181    case Done{app}:
1182      server.valid(~A, ~S, ~K, ~H, ~work, ~destroy, Http.server.config.valid(config), app, config, resources)
1183
1184def server.start(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
1185  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
1186  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1187  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1188  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1189  ~destroy: S -> IO(Unit),
1190  registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1191  config: Http.ServerConfig, resources: List<S>
1192) -> IO(Result<&2, &1, StartupError, Server<A, S, K, H>>):
1193  server.registered(~A, ~S, ~K, ~H,
1194    ~(state => app => input => server.work(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, state, app, input)),
1195    ~destroy, registration, config, resources)
1196
1197def server.control.result(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1198  controlled: Http.Server<ServingContext<A, S, K, H>> & Http.ServerControl,
1199  owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>
1200) -> Server<A, S, K, H> & Http.ServerControl:
1201  (returned, control) = controlled
1202  (Server{returned, owner}, control)
1203
1204def server.control(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> Server<A, S, K, H> & Http.ServerControl:
1205  Server{transport, owner} = server
1206  server.control.result(~A, ~S, ~K, ~H, Http.server.control(~ServingContext<A, S, K, H>, transport), owner)
1207
1208def server.observer.result(~A: Data, ~S: Type, ~K: Data, ~H: Data,
1209  observed: Http.Server<ServingContext<A, S, K, H>> & Http.ServerStats,
1210  owner: Owner<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>
1211) -> Server<A, S, K, H> & Http.ServerStats:
1212  (returned, stats) = observed
1213  (Server{returned, owner}, stats)
1214
1215def server.observer(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> Server<A, S, K, H> & Http.ServerStats:
1216  Server{transport, owner} = server
1217  server.observer.result(~A, ~S, ~K, ~H, Http.server.observer(~ServingContext<A, S, K, H>, transport), owner)
1218
1219def server.dependencies(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>
1220) -> Server<A, S, K, H> & Context<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>>:
1221  Server{transport, Owner{+context, finished}} = server
1222  (Server{transport, Owner{context, finished}}, context)
1223
1224def server.run(~A: Data, ~S: Type, ~K: Data, ~H: Data, ~E: Data,
1225  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1226  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1227  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1228  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1229  server: Server<A, S, K, H>
1230) -> IO(ServerExit<A, S, K, H>):
1231  Server{transport, owner} = server
1232  do IO<ServerExit<A, S, K, H>>:
1233    exited : Http.ServerExit <- Http.server.run(~ServingContext<A, S, K, H>,
1234      ~(context => req => server.handle(~A, ~S, ~K, ~H,
1235        ~(completion => server.completed(~K, ~H, ~E, ~notify, ~report, ~access, ~done, completion)), context, req)),
1236      ~(context => status => outcome => observe(status, outcome)), transport)
1237    closed : OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> <- close(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>, owner)
1238    return ServerExit{exited, closed}
1239
1240# Without starting the run loop, the caller still explicitly drains transport
1241# and joins application cleanup. A stop request alone is never completion.
1242def server.close(~A: Data, ~S: Type, ~K: Data, ~H: Data, server: Server<A, S, K, H>) -> IO(ServerExit<A, S, K, H>):
1243  Server{transport, owner} = server
1244  do IO<ServerExit<A, S, K, H>>:
1245    counts : Http.ServerCounts <- Http.server.close(~ServingContext<A, S, K, H>, transport)
1246    closed : OwnerClose<S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>> <- close(S, Application<A, K, Policy<H>>, BufferedRequest, Unit, ApplicationReport<H>, owner)
1247    return ServerExit{Http.ServerStopped{counts}, closed}
1248
1249def serve.started(~A: Data, ~S: Type, ~K: Data, ~H: Data, ~E: Data,
1250  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1251  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1252  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1253  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1254  started: Result<&2, &1, StartupError, Server<A, S, K, H>>
1255) -> IO(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>):
1256  match started:
1257    case Fail{error}: IO.pure(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>, Fail{error})
1258    case Done{server}:
1259      do IO<Result<&2, &1, StartupError, ServerExit<A, S, K, H>>>:
1260        exited : ServerExit<A, S, K, H> <- server.run(~A, ~S, ~K, ~H, ~E, ~done, ~observe, ~notify, ~report, ~access, server)
1261        return Done{exited}
1262
1263# Owners that need readiness/control use server.start then server.run instead.
1264def serve(~A: Data, ~S: Type, ~K: Data, ~E: Data, ~H: Data,
1265  ~before_hook: H -> S -> K -> Metadata -> IO(Step<S, E>),
1266  ~after_hook: H -> S -> K -> Http.Res -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1267  ~run: A -> S -> K -> Match<Policy<H>> -> Http.Req -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1268  ~mapper: S -> K -> Error<E> -> IO(S & Result<&2, &1, Error<E>, Http.Res>),
1269  ~destroy: S -> IO(Unit),
1270  ~done: TransportCompletion.Completion<ApplicationReport<H>> -> IO(Unit),
1271  ~observe: U32 -> TransportCompletion.Outcome -> IO(Unit),
1272  ~notify: K -> Policy<H> -> Notices.Notice -> IO(Result<&2, &2, E, Unit>),
1273  ~report: K -> E -> IO(Unit), ~access: K -> Notices.Access,
1274  registration: Result<&2, &2, RegistrationError, Application<A, K, Policy<H>>>,
1275  config: Http.ServerConfig, resources: List<S>
1276) -> IO(Result<&2, &1, StartupError, ServerExit<A, S, K, H>>):
1277  do IO<Result<&2, &1, StartupError, ServerExit<A, S, K, H>>>:
1278    started : Result<&2, &1, StartupError, Server<A, S, K, H>> <-
1279      server.start(~A, ~S, ~K, ~E, ~H, ~before_hook, ~after_hook, ~run, ~mapper, ~destroy, registration, config, resources)
1280    serve.started(~A, ~S, ~K, ~H, ~E, ~done, ~observe, ~notify, ~report, ~access, started)