carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package edge
2
3import (
4 "net/http"
5
6 "github.com/carlosframework/platform/internal/registry"
7 "github.com/carlosframework/platform/internal/seeds"
8)
9
10// ImportVerifyHeader carries an uncommitted import's verify token
11// (seeded-instances spec §5.4). The CLI's verify sends it (instances_import.go).
12const ImportVerifyHeader = "Carlos-Import-Verify"
13
14// importGateAdmit is the import gate (spec §5.4, codex 15). host is the
15// request's host and rt the route it resolved to after the door's scope
16// check. ServeHTTP calls it before the meter, the password gate, the cache
17// and the activator, so a refused request is not served, cached, billed or
18// woken.
19//
20// The verify header is removed from every request that gets this far, so
21// the token never reaches an app, gated or not. It is never logged or
22// written into an answer.
23//
24// While host's own row, or the row the request resolved to, carries a gate,
25// exactly one request passes: a GET (the CLI's verify, spec §5) whose token
26// hashes to the gate, compared in constant time (seeds.TokenMatches), and
27// resolved to host's own row. An alias resolves to its primary's row, so an
28// alias of a gated host is that host. A path route does not: a prefix under
29// a gated host that selects another row is refused, token or not, because
30// the verify is the import's own request and a prefix's target is another
31// route; and an ungated host's prefix that selects a gated row is refused
32// for the same reason. ImportGateClosed (a rollback) matches no token.
33//
34// Everything else gets 503 with Retry-After: 5 and Cache-Control: no-store.
35func (e *Edge) importGateAdmit(w http.ResponseWriter, r *http.Request, host string, rt registry.Route) bool {
36 token := r.Header.Get(ImportVerifyHeader)
37 r.Header.Del(ImportVerifyHeader)
38 front := rt
39 if up, ok := lookupIn(e.current(), host); ok {
40 front = up.route
41 }
42 if rt.ImportGate == "" && front.ImportGate == "" {
43 return true
44 }
45 if front.Host == rt.Host && r.Method == http.MethodGet && seeds.TokenMatches(rt.ImportGate, token) {
46 return true
47 }
48 w.Header().Set("Retry-After", "5")
49 w.Header().Set("Cache-Control", "no-store")
50 http.Error(w, "this instance is being imported. Try again in a few seconds.", http.StatusServiceUnavailable)
51 return false
52}
53