Class: Tep::App
- Inherits:
-
Object
- Object
- Tep::App
- Defined in:
- lib/tep/app.rb
Instance Attribute Summary collapse
-
#after_filter ⇒ Object
Returns the value of attribute after_filter.
-
#asset_bodies ⇒ Object
Returns the value of attribute asset_bodies.
-
#asset_etags ⇒ Object
Returns the value of attribute asset_etags.
-
#asset_mimes ⇒ Object
Returns the value of attribute asset_mimes.
-
#auth_bearer_secret ⇒ Object
Shared HS256 secret consumed by Tep::AuthBearerToken.
-
#auth_filter ⇒ Object
The auth-filter runs BEFORE before_filter so handler bodies and user filters always see a populated req.identity.
-
#auth_oauth2_clients ⇒ Object
Per-process OAuth2 client registry + ephemeral authorization-code store.
-
#auth_oauth2_codes ⇒ Object
Returns the value of attribute auth_oauth2_codes.
-
#before_filter ⇒ Object
Returns the value of attribute before_filter.
-
#broadcast_pg_channel ⇒ Object
Returns the value of attribute broadcast_pg_channel.
-
#broadcast_pg_conn ⇒ Object
Returns the value of attribute broadcast_pg_conn.
-
#broadcast_pg_enabled ⇒ Object
PG-backed cross-worker pub/sub state.
-
#broadcast_subs ⇒ Object
Per-process Broadcast subscriber registry.
-
#nf_handler ⇒ Object
Returns the value of attribute nf_handler.
-
#openai_backend ⇒ Object
Tep::Llm::OpenAI::Server backend (Battery 7).
-
#openai_events ⇒ Object
Tep::Events emitter for the openai-server (7.1c).
-
#presence_entries ⇒ Object
Per-process Presence entry registry.
-
#presence_pg_conn ⇒ Object
Returns the value of attribute presence_pg_conn.
-
#presence_pg_enabled ⇒ Object
PG-mirror state for cross-worker visibility.
-
#presence_pg_worker_id ⇒ Object
Returns the value of attribute presence_pg_worker_id.
-
#router ⇒ Object
Returns the value of attribute router.
-
#sched_current ⇒ Object
Returns the value of attribute sched_current.
-
#sched_fibers ⇒ Object
Returns the value of attribute sched_fibers.
-
#sched_io_fd ⇒ Object
Returns the value of attribute sched_io_fd.
-
#sched_io_mode ⇒ Object
Returns the value of attribute sched_io_mode.
-
#sched_io_ready ⇒ Object
Returns the value of attribute sched_io_ready.
-
#sched_wake_at ⇒ Object
Returns the value of attribute sched_wake_at.
-
#session_secret ⇒ Object
Returns the value of attribute session_secret.
-
#static_root ⇒ Object
Returns the value of attribute static_root.
-
#tls_cert ⇒ Object
Returns the value of attribute tls_cert.
-
#tls_key ⇒ Object
Returns the value of attribute tls_key.
-
#user_bg_started ⇒ Object
Tep::Job background-worker idempotency flag.
Class Method Summary collapse
Instance Method Summary collapse
- #add_asset(path, body, mime) ⇒ Object
- #add_route(verb, pattern, handler) ⇒ Object
- #dispatch(req, res) ⇒ Object
-
#initialize ⇒ App
constructor
A new instance of App.
- #set_after(f) ⇒ Object
- #set_auth_bearer_secret(s) ⇒ Object
- #set_auth_filter(f) ⇒ Object
- #set_before(f) ⇒ Object
- #set_broadcast_pg_channel(s) ⇒ Object
- #set_broadcast_pg_conn(c) ⇒ Object
- #set_broadcast_pg_enabled(v) ⇒ Object
- #set_not_found(h) ⇒ Object
- #set_openai_backend(b) ⇒ Object
- #set_openai_events(e) ⇒ Object
- #set_presence_pg_conn(c) ⇒ Object
- #set_presence_pg_enabled(v) ⇒ Object
- #set_presence_pg_worker_id(s) ⇒ Object
- #set_session_secret(s) ⇒ Object
- #set_static_root(root) ⇒ Object
-
#set_tls_cert(s) ⇒ Object
Inbound TLS cert/key paths (tep#148 phase 2).
- #set_tls_key(s) ⇒ Object
- #try_static(req, res) ⇒ Object
Constructor Details
#initialize ⇒ App
Returns a new instance of App.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/tep/app.rb', line 74 def initialize @router = Router.new @static_root = "" @session_secret = "" @tls_cert = "" # inbound TLS cert path (tep#148 ph2; "" = plain HTTP) @tls_key = "" # inbound TLS key path @before_filter = Filter.new # no-op default @after_filter = Filter.new @auth_filter = Filter.new # no-op until Tep::Auth.install! @auth_bearer_secret = "" # Type-seed the OAuth2 registries with a single dummy entry + # immediate drop so the PtrArray slot type is pinned. @auth_oauth2_clients = [Tep::AuthOAuth2Client.new("_", "", "", [:_])] @auth_oauth2_clients.delete_at(0) @auth_oauth2_codes = [Tep::AuthOAuth2Code.new("_", "", "", "", 0)] @auth_oauth2_codes.delete_at(0) # Same type-seed pattern for the Broadcast subscriber registry. @broadcast_subs = [Tep::BroadcastSubscription.new("_", -1, 0)] @broadcast_subs.delete_at(0) # And for the Presence entry registry. @presence_entries = [Tep::PresenceEntry.new("_", "", :human, "", -1, 0)] @presence_entries.delete_at(0) @presence_pg_enabled = 0 @presence_pg_worker_id = "" @broadcast_pg_enabled = 0 @broadcast_pg_channel = "" # Seed broadcast_pg_conn later via lib/tep.rb's setter seed # (APP.set_broadcast_pg_conn(PG::Connection.new(""))) -- module # load order means PG::Connection isn't safely callable from # App#initialize when this is loaded before pg.rb's full surface. @nf_handler = Handler.new @asset_bodies = Tep.str_hash # path -> bytes (filled at boot @asset_mimes = Tep.str_hash # by Tep::Assets._add lines # the bin/tep translator emits) @asset_etags = Tep.str_hash # path -> content-hash ETag (#152) # FiberSlot array for the cooperative scheduler. Initialise # with a noop-bodied slot to pin the array element type, then # drop it. Each slot holds one Fiber + a timer entry in the # parallel `sched_wake_at` int array. @sched_fibers = [Tep::FiberSlot.new(Fiber.new { Tep.seed_fiber_noop })] @sched_fibers.delete_at(0) @sched_wake_at = [0] @sched_wake_at.delete_at(0) @sched_current = -1 # currently-running fiber idx # (-1 = scheduler root). # Parallel I/O-wait arrays. `sched_io_fd[i] == -1` means the # fiber isn't parked on I/O (pure timer wait, or ready). When # parked: `sched_io_mode[i]` carries the requested READ/WRITE # bits, and tick() writes back the observed-ready bits into # `sched_io_ready[i]`. io_wait returns those bits to its caller. @sched_io_fd = [0] @sched_io_fd.delete_at(0) @sched_io_mode = [0] @sched_io_mode.delete_at(0) @sched_io_ready = [0] @sched_io_ready.delete_at(0) @user_bg_started = false end |
Instance Attribute Details
#after_filter ⇒ Object
Returns the value of attribute after_filter.
14 15 16 |
# File 'lib/tep/app.rb', line 14 def after_filter @after_filter end |
#asset_bodies ⇒ Object
Returns the value of attribute asset_bodies.
65 66 67 |
# File 'lib/tep/app.rb', line 65 def asset_bodies @asset_bodies end |
#asset_etags ⇒ Object
Returns the value of attribute asset_etags.
65 66 67 |
# File 'lib/tep/app.rb', line 65 def @asset_etags end |
#asset_mimes ⇒ Object
Returns the value of attribute asset_mimes.
65 66 67 |
# File 'lib/tep/app.rb', line 65 def asset_mimes @asset_mimes end |
#auth_bearer_secret ⇒ Object
Shared HS256 secret consumed by Tep::AuthBearerToken. Stored on APP (rather than a class var) so spinel routes the read through the canonical instance-attr path.
26 27 28 |
# File 'lib/tep/app.rb', line 26 def auth_bearer_secret @auth_bearer_secret end |
#auth_filter ⇒ Object
The auth-filter runs BEFORE before_filter so handler bodies and user filters always see a populated req.identity. Separate slot (rather than wedging into before_filter) so user-installed filters and the auth populate don’t fight for the single slot tep otherwise imposes. Default is a no-op Tep::Filter; the Auth battery installs Tep::AuthFilter on top via Tep::Auth.install!.
22 23 24 |
# File 'lib/tep/app.rb', line 22 def auth_filter @auth_filter end |
#auth_oauth2_clients ⇒ Object
Per-process OAuth2 client registry + ephemeral authorization-code store. See Tep::AuthOAuth2 for the issuance flow.
29 30 31 |
# File 'lib/tep/app.rb', line 29 def auth_oauth2_clients @auth_oauth2_clients end |
#auth_oauth2_codes ⇒ Object
Returns the value of attribute auth_oauth2_codes.
30 31 32 |
# File 'lib/tep/app.rb', line 30 def auth_oauth2_codes @auth_oauth2_codes end |
#before_filter ⇒ Object
Returns the value of attribute before_filter.
14 15 16 |
# File 'lib/tep/app.rb', line 14 def before_filter @before_filter end |
#broadcast_pg_channel ⇒ Object
Returns the value of attribute broadcast_pg_channel.
53 54 55 |
# File 'lib/tep/app.rb', line 53 def broadcast_pg_channel @broadcast_pg_channel end |
#broadcast_pg_conn ⇒ Object
Returns the value of attribute broadcast_pg_conn.
54 55 56 |
# File 'lib/tep/app.rb', line 54 def broadcast_pg_conn @broadcast_pg_conn end |
#broadcast_pg_enabled ⇒ Object
PG-backed cross-worker pub/sub state. ‘broadcast_pg_enabled` is 0 when off, 1 when on. The dedicated LISTEN connection lives in `broadcast_pg_conn`; channel name in `broadcast_pg_channel`. Configured by Tep::Broadcast.enable_pg_backend.
52 53 54 |
# File 'lib/tep/app.rb', line 52 def broadcast_pg_enabled @broadcast_pg_enabled end |
#broadcast_subs ⇒ Object
Per-process Broadcast subscriber registry. Each entry pairs a topic with an output fd; publish iterates + writes the payload to every matching fd.
34 35 36 |
# File 'lib/tep/app.rb', line 34 def broadcast_subs @broadcast_subs end |
#nf_handler ⇒ Object
Returns the value of attribute nf_handler.
14 15 16 |
# File 'lib/tep/app.rb', line 14 def nf_handler @nf_handler end |
#openai_backend ⇒ Object
Tep::Llm::OpenAI::Server backend (Battery 7). Set by Server.use(backend) at boot; the route handlers dispatch through it per request. Seeded with a base Backend in lib/tep.rb (after openai_server.rb loads – not in initialize, since the class isn’t defined yet there), same pattern as broadcast_pg_conn.
60 61 62 |
# File 'lib/tep/app.rb', line 60 def openai_backend @openai_backend end |
#openai_events ⇒ Object
Tep::Events emitter for the openai-server (7.1c). Configured by Server.serve!(events_jsonl); empty path => zero-overhead disabled. Late-seeded for the same reason as openai_backend.
64 65 66 |
# File 'lib/tep/app.rb', line 64 def openai_events @openai_events end |
#presence_entries ⇒ Object
Per-process Presence entry registry. Each entry is one (principal, session, topic) tracking, with kind/agent_id + structured-status fields inline. See Tep::Presence.
38 39 40 |
# File 'lib/tep/app.rb', line 38 def presence_entries @presence_entries end |
#presence_pg_conn ⇒ Object
Returns the value of attribute presence_pg_conn.
46 47 48 |
# File 'lib/tep/app.rb', line 46 def presence_pg_conn @presence_pg_conn end |
#presence_pg_enabled ⇒ Object
PG-mirror state for cross-worker visibility. ‘enabled` is 0 when off, 1 when on. `worker_id` uniquely identifies this worker’s rows in the tep_presence table (PID + boot epoch so a restart on the same PID isn’t aliased). See Tep::Presence.enable_pg_mirror.
44 45 46 |
# File 'lib/tep/app.rb', line 44 def presence_pg_enabled @presence_pg_enabled end |
#presence_pg_worker_id ⇒ Object
Returns the value of attribute presence_pg_worker_id.
45 46 47 |
# File 'lib/tep/app.rb', line 45 def presence_pg_worker_id @presence_pg_worker_id end |
#router ⇒ Object
Returns the value of attribute router.
12 13 14 |
# File 'lib/tep/app.rb', line 12 def router @router end |
#sched_current ⇒ Object
Returns the value of attribute sched_current.
66 67 68 |
# File 'lib/tep/app.rb', line 66 def sched_current @sched_current end |
#sched_fibers ⇒ Object
Returns the value of attribute sched_fibers.
66 67 68 |
# File 'lib/tep/app.rb', line 66 def sched_fibers @sched_fibers end |
#sched_io_fd ⇒ Object
Returns the value of attribute sched_io_fd.
67 68 69 |
# File 'lib/tep/app.rb', line 67 def sched_io_fd @sched_io_fd end |
#sched_io_mode ⇒ Object
Returns the value of attribute sched_io_mode.
67 68 69 |
# File 'lib/tep/app.rb', line 67 def sched_io_mode @sched_io_mode end |
#sched_io_ready ⇒ Object
Returns the value of attribute sched_io_ready.
67 68 69 |
# File 'lib/tep/app.rb', line 67 def sched_io_ready @sched_io_ready end |
#sched_wake_at ⇒ Object
Returns the value of attribute sched_wake_at.
66 67 68 |
# File 'lib/tep/app.rb', line 66 def sched_wake_at @sched_wake_at end |
#session_secret ⇒ Object
Returns the value of attribute session_secret.
12 13 14 |
# File 'lib/tep/app.rb', line 12 def session_secret @session_secret end |
#static_root ⇒ Object
Returns the value of attribute static_root.
12 13 14 |
# File 'lib/tep/app.rb', line 12 def static_root @static_root end |
#tls_cert ⇒ Object
Returns the value of attribute tls_cert.
13 14 15 |
# File 'lib/tep/app.rb', line 13 def tls_cert @tls_cert end |
#tls_key ⇒ Object
Returns the value of attribute tls_key.
13 14 15 |
# File 'lib/tep/app.rb', line 13 def tls_key @tls_key end |
#user_bg_started ⇒ Object
Tep::Job background-worker idempotency flag. App-level so a single-shot spawn from a before-filter doesn’t fire repeatedly. Per-worker (each prefork child has its own Tep::APP, so each worker spawns one background fiber).
72 73 74 |
# File 'lib/tep/app.rb', line 72 def user_bg_started @user_bg_started end |
Class Method Details
.guess_mime(path) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/tep/app.rb', line 275 def self.guess_mime(path) lower = path.downcase if lower.end_with?(".html") || lower.end_with?(".htm") return "text/html; charset=utf-8" end if lower.end_with?(".css"); return "text/css"; end if lower.end_with?(".js"); return "application/javascript"; end if lower.end_with?(".json"); return "application/json"; end if lower.end_with?(".png"); return "image/png"; end if lower.end_with?(".jpg") || lower.end_with?(".jpeg"); return "image/jpeg"; end if lower.end_with?(".gif"); return "image/gif"; end if lower.end_with?(".svg"); return "image/svg+xml"; end if lower.end_with?(".txt"); return "text/plain; charset=utf-8"; end "application/octet-stream" end |
Instance Method Details
#add_asset(path, body, mime) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/tep/app.rb', line 133 def add_asset(path, body, mime) @asset_bodies[path] = body @asset_mimes[path] = mime # Content-hash ETag for cache revalidation (#152). SHA-1 is used # purely as a fast content fingerprint here (not a security hash -- # collision resistance is irrelevant for an ETag, same as git's # content addressing). Computed once at boot. (Binary bodies with # embedded NULs hash by their leading bytes via the FFI string # boundary; still stable per content, which is all an ETag needs.) @asset_etags[path] = Crypto.sp_crypto_sha1_hex(body) end |
#add_route(verb, pattern, handler) ⇒ Object
159 160 161 |
# File 'lib/tep/app.rb', line 159 def add_route(verb, pattern, handler) @router.add(verb, pattern, handler) end |
#dispatch(req, res) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/tep/app.rb', line 178 def dispatch(req, res) # Pull a signed session cookie into req.session, when configured. secret = Tep.session_secret if secret.length > 0 cv = req.[Tep::COOKIE_NAME] if cv.length > 0 req.session.load_from(cv, secret) end end asset_served = false # Auth filter populates req.identity (anonymous or matched # provider's Identity) before the user's before-filter runs, # so user code can always rely on req.identity being set. @auth_filter.before(req, res) if res.halted # Auth filter signalled "deny" -- skip the user filter + # route dispatch, fall through to after-filter + session. end @before_filter.before(req, res) if !res.halted # Bundled assets (everything under <app>/assets/, baked into # the binary by bin/tep) take precedence over the route # table. Match by exact path; on hit we set the body + ct # and skip route dispatch + 404 fallback. The after-filter # and session cookie writing still run normally. if Tep::Assets.serve(req.path, res) asset_served = true end end if !res.halted && !asset_served route = @router.match(req) # `pass` loop: a handler can signal skip-to-next-route by # setting req.passed. Iterate until a handler doesn't pass, # or we run out of matching routes. served = false while route != nil && !served route.fold_captures(req) req.passed = false out = route.handler.handle(req, res) if req.passed idx = @router.index_of(route) route = @router.match_after(req, idx) else res.set_body_if_empty(out) served = true end end if !served if !try_static(req, res) out = @nf_handler.handle(req, res) res.set_status(404) if out.length > 0 res.set_body_if_empty(out) else res.set_body_if_empty("<h1>404 Not Found</h1><p>" + req.verb + " " + req.path + "</p>\n") end end end end @after_filter.after(req, res) # If the handler / filters mutated the session, sign + emit a # Set-Cookie line. Path=/ so the cookie applies to the whole # app; HttpOnly to keep it out of JS. secret_w = Tep.session_secret if secret_w.length > 0 && req.session.dirty opts = Tep.str_hash opts["Path"] = "/" opts["HttpOnly"] = "" opts["SameSite"] = "Lax" res.(Tep::COOKIE_NAME, req.session.(secret_w), opts) end end |
#set_after(f) ⇒ Object
165 |
# File 'lib/tep/app.rb', line 165 def set_after(f); @after_filter = f; end |
#set_auth_bearer_secret(s) ⇒ Object
167 |
# File 'lib/tep/app.rb', line 167 def set_auth_bearer_secret(s); @auth_bearer_secret = s; end |
#set_auth_filter(f) ⇒ Object
166 |
# File 'lib/tep/app.rb', line 166 def set_auth_filter(f); @auth_filter = f; end |
#set_before(f) ⇒ Object
164 |
# File 'lib/tep/app.rb', line 164 def set_before(f); @before_filter = f; end |
#set_broadcast_pg_channel(s) ⇒ Object
169 |
# File 'lib/tep/app.rb', line 169 def set_broadcast_pg_channel(s); @broadcast_pg_channel = s; end |
#set_broadcast_pg_conn(c) ⇒ Object
170 |
# File 'lib/tep/app.rb', line 170 def set_broadcast_pg_conn(c); @broadcast_pg_conn = c; end |
#set_broadcast_pg_enabled(v) ⇒ Object
168 |
# File 'lib/tep/app.rb', line 168 def set_broadcast_pg_enabled(v); @broadcast_pg_enabled = v; end |
#set_not_found(h) ⇒ Object
176 |
# File 'lib/tep/app.rb', line 176 def set_not_found(h); @nf_handler = h; end |
#set_openai_backend(b) ⇒ Object
174 |
# File 'lib/tep/app.rb', line 174 def set_openai_backend(b); @openai_backend = b; end |
#set_openai_events(e) ⇒ Object
175 |
# File 'lib/tep/app.rb', line 175 def set_openai_events(e); @openai_events = e; end |
#set_presence_pg_conn(c) ⇒ Object
173 |
# File 'lib/tep/app.rb', line 173 def set_presence_pg_conn(c); @presence_pg_conn = c; end |
#set_presence_pg_enabled(v) ⇒ Object
171 |
# File 'lib/tep/app.rb', line 171 def set_presence_pg_enabled(v); @presence_pg_enabled = v; end |
#set_presence_pg_worker_id(s) ⇒ Object
172 |
# File 'lib/tep/app.rb', line 172 def set_presence_pg_worker_id(s); @presence_pg_worker_id = s; end |
#set_session_secret(s) ⇒ Object
145 146 147 |
# File 'lib/tep/app.rb', line 145 def set_session_secret(s) @session_secret = s end |
#set_static_root(root) ⇒ Object
163 |
# File 'lib/tep/app.rb', line 163 def set_static_root(root); @static_root = root; end |
#set_tls_cert(s) ⇒ Object
Inbound TLS cert/key paths (tep#148 phase 2). Set via Tep.tls_cert= / Tep.tls_key=; read by Tep::Server.run at boot.
151 152 153 |
# File 'lib/tep/app.rb', line 151 def set_tls_cert(s) @tls_cert = s end |
#set_tls_key(s) ⇒ Object
155 156 157 |
# File 'lib/tep/app.rb', line 155 def set_tls_key(s) @tls_key = s end |
#try_static(req, res) ⇒ Object
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/tep/app.rb', line 254 def try_static(req, res) if @static_root.length == 0 return false end if req.verb != "GET" && req.verb != "HEAD" return false end if Tep.str_find(req.path, "..", 0) >= 0 return false end full = @static_root + req.path sz = Sock.sphttp_filesize(full) if sz < 0 return false end res.headers["Content-Type"] = App.guess_mime(full) res.headers["X-Tep-Static"] = "1" res.send_file(full) true end |