tep 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Makefile +42 -2
- data/README.md +4 -4
- data/SINATRA_COMPAT.md +20 -20
- data/bin/tep +47 -10
- data/examples/api_gateway/app.rb +1 -1
- data/examples/blog/app.rb +17 -17
- data/examples/chat/app.rb +12 -12
- data/examples/chatbot/README.md +2 -2
- data/examples/chatbot/app.rb +24 -24
- data/examples/llm_gateway/app.rb +4 -4
- data/examples/pg_hello.rb +11 -1
- data/lib/spinel_kit/hex.rb +65 -0
- data/lib/spinel_kit/json.rb +151 -0
- data/lib/spinel_kit/json_decoder.rb +396 -0
- data/lib/{tep/logger.rb → spinel_kit/log.rb} +25 -21
- data/lib/spinel_kit/url.rb +166 -0
- data/lib/tep/auth_bearer_token.rb +6 -6
- data/lib/tep/auth_oauth2.rb +4 -4
- data/lib/tep/broadcast.rb +18 -80
- data/lib/tep/events.rb +37 -37
- data/lib/tep/http.rb +3 -3
- data/lib/tep/job.rb +2 -2
- data/lib/tep/jwt.rb +4 -4
- data/lib/tep/live_view.rb +4 -4
- data/lib/tep/llm.rb +13 -45
- data/lib/tep/mcp.rb +12 -12
- data/lib/tep/multipart.rb +1 -1
- data/lib/tep/net.rb +8 -3
- data/lib/tep/openai_server.rb +102 -94
- data/lib/tep/parser.rb +2 -2
- data/lib/tep/pg.rb +468 -14
- data/lib/tep/presence.rb +33 -329
- data/lib/tep/proxy.rb +7 -7
- data/lib/tep/request.rb +1 -1
- data/lib/tep/response.rb +1 -1
- data/lib/tep/router.rb +1 -1
- data/lib/tep/session.rb +2 -2
- data/lib/tep/version.rb +1 -1
- data/lib/tep.rb +57 -137
- data/spinel-ext.json +6 -0
- data/test/helper.rb +95 -8
- data/test/run_parallel.rb +44 -7
- data/test/test_auth.rb +17 -17
- data/test/test_auth_oauth2.rb +5 -5
- data/test/test_broadcast_pg.rb +1 -0
- data/test/test_http_pool.rb +4 -4
- data/test/test_http_pool_send.rb +3 -3
- data/test/test_json.rb +12 -12
- data/test/test_jwt.rb +4 -4
- data/test/test_live_view.rb +3 -3
- data/test/test_llm.rb +12 -9
- data/test/test_llm_gateway.rb +2 -2
- data/test/test_logger.rb +2 -2
- data/test/test_openai_server.rb +10 -1
- data/test/test_password.rb +3 -3
- data/test/test_pg.rb +1 -0
- data/test/test_presence_pg.rb +1 -0
- data/test/test_real_world.rb +6 -1
- data/test/test_shutdown.rb +40 -0
- metadata +23 -8
- data/lib/tep/json.rb +0 -572
- data/lib/tep/url.rb +0 -161
data/lib/tep/presence.rb
CHANGED
|
@@ -211,21 +211,21 @@ module Tep
|
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
# Flat-JSON wire format for a diff event. `kind` is one of
|
|
214
|
-
# "join" / "leave" / "status".
|
|
214
|
+
# "join" / "leave" / "status". SpinelKit::Json's flat-object
|
|
215
215
|
# extractors handle this on the client side (or any
|
|
216
216
|
# JSON-aware peer).
|
|
217
217
|
def self.encode_diff(kind, entry)
|
|
218
218
|
"{" +
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
219
|
+
SpinelKit::Json.encode_pair_str("kind", kind) + "," +
|
|
220
|
+
SpinelKit::Json.encode_pair_str("topic", entry.topic) + "," +
|
|
221
|
+
SpinelKit::Json.encode_pair_str("principal", entry.principal_id) + "," +
|
|
222
|
+
SpinelKit::Json.encode_pair_str("ekind", entry.kind.to_s) + "," +
|
|
223
|
+
SpinelKit::Json.encode_pair_str("agent_id", entry.agent_id) + "," +
|
|
224
|
+
SpinelKit::Json.encode_pair_int("fd", entry.fd) + "," +
|
|
225
|
+
SpinelKit::Json.encode_pair_int("since", entry.since) + "," +
|
|
226
|
+
SpinelKit::Json.encode_pair_str("state", entry.status_state.to_s) + "," +
|
|
227
|
+
SpinelKit::Json.encode_pair_str("note", entry.status_note) + "," +
|
|
228
|
+
SpinelKit::Json.encode_pair_int("until_ts", entry.status_until) +
|
|
229
229
|
"}"
|
|
230
230
|
end
|
|
231
231
|
|
|
@@ -265,325 +265,29 @@ module Tep
|
|
|
265
265
|
swept
|
|
266
266
|
end
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
#
|
|
285
|
-
# Returns 0 on success, -1 on connect / schema failure.
|
|
286
|
-
def self.enable_pg_mirror(conninfo)
|
|
287
|
-
conn = PG::Connection.new(conninfo)
|
|
288
|
-
if conn.pgh < 0
|
|
289
|
-
return -1
|
|
290
|
-
end
|
|
291
|
-
# exec raises PG::Error on failure now; degrade gracefully
|
|
292
|
-
# (close + return -1) rather than letting it escape the worker.
|
|
293
|
-
begin
|
|
294
|
-
r = conn.exec(Tep::Presence.schema_sql)
|
|
295
|
-
r.clear
|
|
296
|
-
# Heartbeat table for the prune-stale-workers path (#47).
|
|
297
|
-
r = conn.exec(Tep::Presence.worker_schema_sql)
|
|
298
|
-
r.clear
|
|
299
|
-
rescue PG::Error
|
|
300
|
-
conn.finish
|
|
301
|
-
return -1
|
|
302
|
-
end
|
|
303
|
-
Tep::APP.set_presence_pg_conn(conn)
|
|
304
|
-
worker_id = Sock.sphttp_getpid.to_s + "-" + Time.now.to_i.to_s
|
|
305
|
-
Tep::APP.set_presence_pg_worker_id(worker_id)
|
|
306
|
-
Tep::APP.set_presence_pg_enabled(1)
|
|
307
|
-
# Drop any rows from a prior worker that managed to leave
|
|
308
|
-
# stale entries with this same worker_id (unlikely thanks
|
|
309
|
-
# to the boot-epoch suffix, but defensive). Best-effort.
|
|
310
|
-
Tep::Presence.mirror_exec(
|
|
311
|
-
"DELETE FROM tep_presence WHERE worker_id = $1",
|
|
312
|
-
[worker_id])
|
|
313
|
-
# Register this worker's heartbeat row immediately. Apps
|
|
314
|
-
# refresh it periodically via Tep::Presence.heartbeat;
|
|
315
|
-
# prune_stale_workers deletes rows whose heartbeat is stale.
|
|
316
|
-
Tep::Presence.heartbeat
|
|
317
|
-
0
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def self.disable_pg_mirror
|
|
321
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
322
|
-
return 0
|
|
323
|
-
end
|
|
324
|
-
# Best-effort cleanup -- swallow PG errors (we're tearing the
|
|
325
|
-
# mirror down regardless) and still finish + disable below.
|
|
326
|
-
begin
|
|
327
|
-
r = Tep::APP.presence_pg_conn.exec_params(
|
|
328
|
-
"DELETE FROM tep_presence WHERE worker_id = $1",
|
|
329
|
-
[Tep::APP.presence_pg_worker_id])
|
|
330
|
-
r.clear
|
|
331
|
-
# Remove the heartbeat row so prune_stale_workers doesn't
|
|
332
|
-
# see this worker as live after we're gone.
|
|
333
|
-
r = Tep::APP.presence_pg_conn.exec_params(
|
|
334
|
-
"DELETE FROM tep_presence_worker WHERE worker_id = $1",
|
|
335
|
-
[Tep::APP.presence_pg_worker_id])
|
|
336
|
-
r.clear
|
|
337
|
-
rescue PG::Error
|
|
338
|
-
# swallow -- shutting the mirror down anyway
|
|
339
|
-
end
|
|
340
|
-
Tep::APP.presence_pg_conn.finish
|
|
341
|
-
Tep::APP.set_presence_pg_enabled(0)
|
|
342
|
-
0
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# CREATE TABLE statement, kept here so apps that want to
|
|
346
|
-
# provision the schema separately (migration runners, etc.)
|
|
347
|
-
# can grab the canonical DDL. Idempotent via IF NOT EXISTS.
|
|
348
|
-
def self.schema_sql
|
|
349
|
-
"CREATE TABLE IF NOT EXISTS tep_presence (" +
|
|
350
|
-
"worker_id TEXT NOT NULL, " +
|
|
351
|
-
"topic TEXT NOT NULL, " +
|
|
352
|
-
"fd INTEGER NOT NULL, " +
|
|
353
|
-
"principal_id TEXT NOT NULL, " +
|
|
354
|
-
"kind TEXT NOT NULL, " +
|
|
355
|
-
"agent_id TEXT NOT NULL, " +
|
|
356
|
-
"since_ts BIGINT NOT NULL, " +
|
|
357
|
-
"status_state TEXT NOT NULL, " +
|
|
358
|
-
"status_note TEXT NOT NULL, " +
|
|
359
|
-
"status_until BIGINT NOT NULL, " +
|
|
360
|
-
"PRIMARY KEY (worker_id, topic, fd)" +
|
|
361
|
-
")"
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
# Heartbeat table -- one row per worker that's mirroring
|
|
365
|
-
# presence right now. Used by prune_stale_workers to identify
|
|
366
|
-
# crashed workers (no heartbeat updates in N seconds) and
|
|
367
|
-
# garbage-collect their orphan tep_presence rows.
|
|
368
|
-
def self.worker_schema_sql
|
|
369
|
-
"CREATE TABLE IF NOT EXISTS tep_presence_worker (" +
|
|
370
|
-
"worker_id TEXT PRIMARY KEY, " +
|
|
371
|
-
"last_seen_ts BIGINT NOT NULL" +
|
|
372
|
-
")"
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
# Refresh this worker's heartbeat row to the current Unix
|
|
376
|
-
# timestamp. Apps call this periodically (typical: from a
|
|
377
|
-
# before-filter, a Tep::Job tick, or an explicit timer fiber)
|
|
378
|
-
# so prune_stale_workers can tell live workers from crashed
|
|
379
|
-
# ones. No-op when the PG mirror isn't enabled, or when the
|
|
380
|
-
# mirror was opened on a different process and we're the
|
|
381
|
-
# post-fork child (worker_id is empty until enable_pg_mirror
|
|
382
|
-
# runs locally).
|
|
383
|
-
#
|
|
384
|
-
# Returns 1 if the heartbeat row was upserted, 0 if the call
|
|
385
|
-
# short-circuited (mirror disabled or no worker_id).
|
|
386
|
-
def self.heartbeat
|
|
387
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
388
|
-
return 0
|
|
389
|
-
end
|
|
390
|
-
wid = Tep::APP.presence_pg_worker_id
|
|
391
|
-
if wid.length == 0
|
|
392
|
-
return 0
|
|
393
|
-
end
|
|
394
|
-
begin
|
|
395
|
-
r = Tep::APP.presence_pg_conn.exec_params(
|
|
396
|
-
"INSERT INTO tep_presence_worker (worker_id, last_seen_ts) " +
|
|
397
|
-
"VALUES ($1, $2) " +
|
|
398
|
-
"ON CONFLICT (worker_id) DO UPDATE SET " +
|
|
399
|
-
" last_seen_ts = EXCLUDED.last_seen_ts",
|
|
400
|
-
[wid, Time.now.to_i.to_s])
|
|
401
|
-
r.clear
|
|
402
|
-
rescue PG::Error
|
|
403
|
-
return 0
|
|
404
|
-
end
|
|
405
|
-
1
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
# Prune crashed-worker rows. Deletes:
|
|
409
|
-
# 1. tep_presence_worker rows whose last_seen_ts is older than
|
|
410
|
-
# ttl_seconds (the worker's heartbeat is stale).
|
|
411
|
-
# 2. tep_presence rows whose worker_id has no surviving
|
|
412
|
-
# heartbeat (orphans left by the crashed worker).
|
|
413
|
-
#
|
|
414
|
-
# Apps call this periodically -- the canonical shape is a
|
|
415
|
-
# before-filter on a "/health" route that internal monitoring
|
|
416
|
-
# hits every 30s, or a Tep::Job that fires from a cron-like
|
|
417
|
-
# tick. Returns the number of tep_presence rows deleted.
|
|
418
|
-
#
|
|
419
|
-
# ttl_seconds should be at least 3x the app's typical
|
|
420
|
-
# heartbeat interval so a transient slow response doesn't
|
|
421
|
-
# evict a live worker. Default callers pass 90 (assumes 30s
|
|
422
|
-
# heartbeats).
|
|
423
|
-
def self.prune_stale_workers(ttl_seconds)
|
|
424
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
425
|
-
return 0
|
|
426
|
-
end
|
|
427
|
-
cutoff = Time.now.to_i - ttl_seconds
|
|
428
|
-
conn = Tep::APP.presence_pg_conn
|
|
429
|
-
begin
|
|
430
|
-
# Drop dead heartbeats first; the second DELETE then walks
|
|
431
|
-
# the worker_id space that's still alive.
|
|
432
|
-
r1 = conn.exec_params(
|
|
433
|
-
"DELETE FROM tep_presence_worker WHERE last_seen_ts < $1",
|
|
434
|
-
[cutoff.to_s])
|
|
435
|
-
r1.clear
|
|
436
|
-
# Now drop presence rows whose worker_id isn't in the live
|
|
437
|
-
# heartbeat table. NOT IN handles both crashed-and-pruned
|
|
438
|
-
# workers and workers that never registered (legacy rows
|
|
439
|
-
# from before this prune feature shipped).
|
|
440
|
-
r2 = conn.exec(
|
|
441
|
-
"DELETE FROM tep_presence " +
|
|
442
|
-
"WHERE worker_id NOT IN (SELECT worker_id FROM tep_presence_worker)")
|
|
443
|
-
n = r2.cmd_tuples
|
|
444
|
-
r2.clear
|
|
445
|
-
rescue PG::Error
|
|
446
|
-
return 0
|
|
447
|
-
end
|
|
448
|
-
n
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
# Best-effort mirror write: run an exec_params on the mirror conn
|
|
452
|
-
# and swallow any PG::Error. The PG mirror is advisory -- local
|
|
453
|
-
# presence is authoritative -- so a transient mirror failure must
|
|
454
|
-
# never propagate into the caller's request now that exec raises
|
|
455
|
-
# (matz/spinel#627 + #1041). Always returns 0.
|
|
456
|
-
def self.mirror_exec(sql, params)
|
|
457
|
-
begin
|
|
458
|
-
r = Tep::APP.presence_pg_conn.exec_params(sql, params)
|
|
459
|
-
r.clear
|
|
460
|
-
rescue PG::Error
|
|
461
|
-
# swallow -- advisory mirror, local presence is authoritative
|
|
462
|
-
end
|
|
463
|
-
0
|
|
464
|
-
end
|
|
465
|
-
|
|
466
|
-
# Mirror a track to PG. Called from track() when the PG
|
|
467
|
-
# mirror is enabled.
|
|
468
|
-
def self.mirror_insert(entry)
|
|
469
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
470
|
-
return 0
|
|
471
|
-
end
|
|
472
|
-
Tep::Presence.mirror_exec(
|
|
473
|
-
"INSERT INTO tep_presence " +
|
|
474
|
-
"(worker_id, topic, fd, principal_id, kind, agent_id, " +
|
|
475
|
-
" since_ts, status_state, status_note, status_until) " +
|
|
476
|
-
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) " +
|
|
477
|
-
"ON CONFLICT (worker_id, topic, fd) DO UPDATE SET " +
|
|
478
|
-
" principal_id = EXCLUDED.principal_id, " +
|
|
479
|
-
" kind = EXCLUDED.kind, " +
|
|
480
|
-
" agent_id = EXCLUDED.agent_id, " +
|
|
481
|
-
" since_ts = EXCLUDED.since_ts, " +
|
|
482
|
-
" status_state = EXCLUDED.status_state, " +
|
|
483
|
-
" status_note = EXCLUDED.status_note, " +
|
|
484
|
-
" status_until = EXCLUDED.status_until",
|
|
485
|
-
[
|
|
486
|
-
Tep::APP.presence_pg_worker_id,
|
|
487
|
-
entry.topic,
|
|
488
|
-
entry.fd.to_s,
|
|
489
|
-
entry.principal_id,
|
|
490
|
-
entry.kind.to_s,
|
|
491
|
-
entry.agent_id,
|
|
492
|
-
entry.since.to_s,
|
|
493
|
-
entry.status_state.to_s,
|
|
494
|
-
entry.status_note,
|
|
495
|
-
entry.status_until.to_s
|
|
496
|
-
])
|
|
497
|
-
end
|
|
498
|
-
|
|
499
|
-
# Mirror an untrack to PG.
|
|
500
|
-
def self.mirror_delete(topic, fd)
|
|
501
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
502
|
-
return 0
|
|
503
|
-
end
|
|
504
|
-
Tep::Presence.mirror_exec(
|
|
505
|
-
"DELETE FROM tep_presence " +
|
|
506
|
-
"WHERE worker_id = $1 AND topic = $2 AND fd = $3",
|
|
507
|
-
[Tep::APP.presence_pg_worker_id, topic, fd.to_s])
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
# Mirror a status update.
|
|
511
|
-
def self.mirror_status(topic, fd, state, note, until_ts)
|
|
512
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
513
|
-
return 0
|
|
514
|
-
end
|
|
515
|
-
Tep::Presence.mirror_exec(
|
|
516
|
-
"UPDATE tep_presence " +
|
|
517
|
-
"SET status_state = $4, status_note = $5, status_until = $6 " +
|
|
518
|
-
"WHERE worker_id = $1 AND topic = $2 AND fd = $3",
|
|
519
|
-
[Tep::APP.presence_pg_worker_id, topic, fd.to_s,
|
|
520
|
-
state.to_s, note, until_ts.to_s])
|
|
521
|
-
end
|
|
268
|
+
# ---- PG mirror (cross-worker visibility) ----
|
|
269
|
+
#
|
|
270
|
+
# The presence PG mirror is OPT-IN (#216). enable_pg_mirror /
|
|
271
|
+
# disable_pg_mirror / heartbeat / prune_stale_workers / mirror_exec /
|
|
272
|
+
# list_global / count_global / the schema DDL, and the REAL bodies of
|
|
273
|
+
# the mirror_* hooks below, live in lib/tep/pg.rb and only compile
|
|
274
|
+
# into apps that `require "tep/pg"`. Core Presence keeps no PG
|
|
275
|
+
# reference, so a non-PG app DCEs the libpq closure entirely.
|
|
276
|
+
#
|
|
277
|
+
# track / untrack / set_status call these mirror_* hooks
|
|
278
|
+
# unconditionally; the core definitions are no-ops. `require
|
|
279
|
+
# "tep/pg"` redefines them (last-definition-wins) with the real
|
|
280
|
+
# exec_params writes.
|
|
281
|
+
def self.mirror_insert(entry)
|
|
282
|
+
0
|
|
283
|
+
end
|
|
522
284
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
# snapshots -- mutating them doesn't write back to PG.
|
|
527
|
-
def self.list_global(topic)
|
|
528
|
-
result = [Tep::PresenceEntry.new("", "", :human, "", -1, 0)]
|
|
529
|
-
result.delete_at(0)
|
|
530
|
-
if Tep::APP.presence_pg_enabled == 0
|
|
531
|
-
return result
|
|
532
|
-
end
|
|
533
|
-
begin
|
|
534
|
-
r = Tep::APP.presence_pg_conn.exec_params(
|
|
535
|
-
"SELECT principal_id, kind, agent_id, fd, since_ts, " +
|
|
536
|
-
" status_state, status_note, status_until " +
|
|
537
|
-
"FROM tep_presence WHERE topic = $1 ORDER BY since_ts",
|
|
538
|
-
[topic])
|
|
539
|
-
rescue PG::Error
|
|
540
|
-
return result
|
|
541
|
-
end
|
|
542
|
-
i = 0
|
|
543
|
-
n = r.ntuples
|
|
544
|
-
while i < n
|
|
545
|
-
kind_sym = :human
|
|
546
|
-
if r.getvalue(i, 1) == "agent_for"
|
|
547
|
-
kind_sym = :agent_for
|
|
548
|
-
end
|
|
549
|
-
state_sym = :available
|
|
550
|
-
sstr = r.getvalue(i, 5)
|
|
551
|
-
if sstr == "busy"
|
|
552
|
-
state_sym = :busy
|
|
553
|
-
elsif sstr == "blocked"
|
|
554
|
-
state_sym = :blocked
|
|
555
|
-
end
|
|
556
|
-
e = Tep::PresenceEntry.new(
|
|
557
|
-
topic,
|
|
558
|
-
r.getvalue(i, 0),
|
|
559
|
-
kind_sym,
|
|
560
|
-
r.getvalue(i, 2),
|
|
561
|
-
r.getvalue(i, 3).to_i,
|
|
562
|
-
r.getvalue(i, 4).to_i)
|
|
563
|
-
e.status_state = state_sym
|
|
564
|
-
e.status_note = r.getvalue(i, 6)
|
|
565
|
-
e.status_until = r.getvalue(i, 7).to_i
|
|
566
|
-
result.push(e)
|
|
567
|
-
i += 1
|
|
568
|
-
end
|
|
569
|
-
r.clear
|
|
570
|
-
result
|
|
571
|
-
end
|
|
285
|
+
def self.mirror_delete(topic, fd)
|
|
286
|
+
0
|
|
287
|
+
end
|
|
572
288
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
end
|
|
577
|
-
begin
|
|
578
|
-
r = Tep::APP.presence_pg_conn.exec_params(
|
|
579
|
-
"SELECT count(*) FROM tep_presence WHERE topic = $1",
|
|
580
|
-
[topic])
|
|
581
|
-
rescue PG::Error
|
|
582
|
-
return 0
|
|
583
|
-
end
|
|
584
|
-
n = r.getvalue(0, 0).to_i
|
|
585
|
-
r.clear
|
|
586
|
-
n
|
|
587
|
-
end
|
|
289
|
+
def self.mirror_status(topic, fd, state, note, until_ts)
|
|
290
|
+
0
|
|
291
|
+
end
|
|
588
292
|
end
|
|
589
293
|
end
|
data/lib/tep/proxy.rb
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# end
|
|
13
13
|
#
|
|
14
14
|
# def after_forward(req, ures, res)
|
|
15
|
-
#
|
|
15
|
+
# LOGGER.info("upstream " + ures.status.to_s) # LOGGER = SpinelKit::Log.new
|
|
16
16
|
# 0
|
|
17
17
|
# end
|
|
18
18
|
# end
|
|
@@ -246,7 +246,7 @@ module Tep
|
|
|
246
246
|
# An LLM gateway typically overrides this as:
|
|
247
247
|
#
|
|
248
248
|
# def stream_request?(req)
|
|
249
|
-
#
|
|
249
|
+
# SpinelKit::Json.get_bool(req.raw_body, "stream")
|
|
250
250
|
# end
|
|
251
251
|
def stream_request?(req)
|
|
252
252
|
false
|
|
@@ -298,10 +298,10 @@ module Tep
|
|
|
298
298
|
res.set_status(413)
|
|
299
299
|
res.headers["Content-Type"] = "application/json"
|
|
300
300
|
err_body = "{\"error\":{" +
|
|
301
|
-
|
|
301
|
+
SpinelKit::Json.encode_pair_str("message",
|
|
302
302
|
"request body exceeds proxy cap of " +
|
|
303
303
|
@max_request_body_bytes.to_s + " bytes") + "," +
|
|
304
|
-
|
|
304
|
+
SpinelKit::Json.encode_pair_str("type", "payload_too_large") +
|
|
305
305
|
"}}"
|
|
306
306
|
res.set_body(err_body)
|
|
307
307
|
return err_body
|
|
@@ -378,10 +378,10 @@ module Tep
|
|
|
378
378
|
res.set_status(502)
|
|
379
379
|
res.headers["Content-Type"] = "application/json"
|
|
380
380
|
err_body = "{\"error\":{" +
|
|
381
|
-
|
|
381
|
+
SpinelKit::Json.encode_pair_str("message",
|
|
382
382
|
"upstream response body exceeds proxy cap of " +
|
|
383
383
|
@max_response_body_bytes.to_s + " bytes") + "," +
|
|
384
|
-
|
|
384
|
+
SpinelKit::Json.encode_pair_str("type", "upstream_body_too_large") +
|
|
385
385
|
"}}"
|
|
386
386
|
res.set_body(err_body)
|
|
387
387
|
return err_body
|
|
@@ -424,7 +424,7 @@ module Tep
|
|
|
424
424
|
# 502 and returns "" without starting a stream.
|
|
425
425
|
def start_streaming_forward(req, res, ureq)
|
|
426
426
|
url = pick_upstream(req) + ureq.path
|
|
427
|
-
parts =
|
|
427
|
+
parts = SpinelKit::Url.split_url(url)
|
|
428
428
|
if parts["scheme"] != "http"
|
|
429
429
|
res.set_status(502)
|
|
430
430
|
return ""
|
data/lib/tep/request.rb
CHANGED
data/lib/tep/response.rb
CHANGED
|
@@ -88,7 +88,7 @@ module Tep
|
|
|
88
88
|
# (path, expires, max-age, domain, samesite, httponly, secure).
|
|
89
89
|
# Empty `opts` is fine: just writes "name=value".
|
|
90
90
|
def set_cookie(name, value, opts)
|
|
91
|
-
line = name + "=" + Url.escape(value)
|
|
91
|
+
line = name + "=" + SpinelKit::Url.escape(value)
|
|
92
92
|
if opts.length > 0
|
|
93
93
|
opts.each do |k, v|
|
|
94
94
|
if v.length == 0
|
data/lib/tep/router.rb
CHANGED
data/lib/tep/session.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Tep
|
|
|
46
46
|
if !Tep.timing_safe_eq(sig, expect)
|
|
47
47
|
return false
|
|
48
48
|
end
|
|
49
|
-
Url.parse_query(payload).each do |k, v|
|
|
49
|
+
SpinelKit::Url.parse_query(payload).each do |k, v|
|
|
50
50
|
@data[k] = v
|
|
51
51
|
end
|
|
52
52
|
true
|
|
@@ -61,7 +61,7 @@ module Tep
|
|
|
61
61
|
if !first
|
|
62
62
|
payload = payload + "&"
|
|
63
63
|
end
|
|
64
|
-
payload = payload + Url.escape(k) + "=" + Url.escape(v)
|
|
64
|
+
payload = payload + SpinelKit::Url.escape(k) + "=" + SpinelKit::Url.escape(v)
|
|
65
65
|
first = false
|
|
66
66
|
end
|
|
67
67
|
payload + "." + Crypto.sp_crypto_hmac_sha256_hex(secret, payload)
|
data/lib/tep/version.rb
CHANGED