tina4ruby 3.13.74 → 3.13.75
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/lib/tina4/rack_app.rb +71 -6
- data/lib/tina4/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7b6cfa07830b59619c790f1724802af5989b3013f7408aa47b35a8ff0dd6ca1
|
|
4
|
+
data.tar.gz: 6b107ed9e12d657b5b3ef8e8183a2cb05c1b7c2e3befcc35cd977b9ddabb63ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 966951da4e142e1083cb21b9123ae44f0f3c1e5750aef15f5f1d27c91fb1c2119e3212d6a26f560705a20ee341286b6b828d0d36dd0ddcbaaf0dc51264f9faa6
|
|
7
|
+
data.tar.gz: 831737c53898556428116fdf4992c50f1d658acf21f86209aebf00e09b691162b915504992a2f6a396ee0ff13f46720bc9d4896ef841f373a717e437f860a9d7
|
data/lib/tina4/rack_app.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "json"
|
|
3
3
|
require "securerandom"
|
|
4
|
+
require "time"
|
|
4
5
|
require "uri"
|
|
5
6
|
|
|
6
7
|
module Tina4
|
|
@@ -128,7 +129,7 @@ module Tina4
|
|
|
128
129
|
end
|
|
129
130
|
|
|
130
131
|
# Static files (only for non-API paths)
|
|
131
|
-
static_response = try_static(path)
|
|
132
|
+
static_response = try_static(path, env)
|
|
132
133
|
return static_response if static_response
|
|
133
134
|
end
|
|
134
135
|
|
|
@@ -386,30 +387,94 @@ module Tina4
|
|
|
386
387
|
final_response.to_rack
|
|
387
388
|
end
|
|
388
389
|
|
|
389
|
-
def try_static(path)
|
|
390
|
+
def try_static(path, env = nil)
|
|
390
391
|
return nil if path.include?("..")
|
|
391
392
|
|
|
392
393
|
@static_roots.each do |root|
|
|
393
394
|
full_path = File.join(root, path)
|
|
394
395
|
if File.file?(full_path)
|
|
395
|
-
return serve_static_file(full_path)
|
|
396
|
+
return serve_static_file(full_path, env)
|
|
396
397
|
end
|
|
397
398
|
|
|
398
399
|
# Only try index.html for directory-like paths
|
|
399
400
|
if path.end_with?("/") || !path.include?(".")
|
|
400
401
|
index_path = File.join(full_path, "index.html")
|
|
401
402
|
if File.file?(index_path)
|
|
402
|
-
return serve_static_file(index_path)
|
|
403
|
+
return serve_static_file(index_path, env)
|
|
403
404
|
end
|
|
404
405
|
end
|
|
405
406
|
end
|
|
406
407
|
nil
|
|
407
408
|
end
|
|
408
409
|
|
|
409
|
-
|
|
410
|
+
# Serve a static asset with cache-revalidation semantics (parity with the
|
|
411
|
+
# Python master). A static file may be cached by the browser but MUST be
|
|
412
|
+
# revalidated before use, so a redeployed asset reaches the browser on the
|
|
413
|
+
# next load without a manual hard refresh — and an unchanged asset costs a
|
|
414
|
+
# cheap 304 round-trip, not a re-download.
|
|
415
|
+
#
|
|
416
|
+
# Validators are derived from the file's mtime + size (a weak ETag) plus a
|
|
417
|
+
# Last-Modified date, so we never have to read/hash the bytes to build them.
|
|
418
|
+
# When the request already carries a matching validator (If-None-Match, or
|
|
419
|
+
# If-Modified-Since not older than the file) we answer a bare 304 with no
|
|
420
|
+
# body. Stdlib only — no new dependency.
|
|
421
|
+
def serve_static_file(full_path, env = nil)
|
|
410
422
|
ext = File.extname(full_path).downcase
|
|
411
423
|
content_type = Tina4::Response::MIME_TYPES[ext] || "application/octet-stream"
|
|
412
|
-
|
|
424
|
+
|
|
425
|
+
stat = File.stat(full_path)
|
|
426
|
+
etag = %(W/"#{stat.mtime.to_i.to_s(16)}-#{stat.size.to_s(16)}")
|
|
427
|
+
last_modified = stat.mtime.httpdate
|
|
428
|
+
|
|
429
|
+
headers = {
|
|
430
|
+
"content-type" => content_type,
|
|
431
|
+
"cache-control" => "no-cache, must-revalidate",
|
|
432
|
+
"etag" => etag,
|
|
433
|
+
"last-modified" => last_modified
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if env && static_not_modified?(env, etag, stat.mtime)
|
|
437
|
+
# 304 carries the validators (RFC 9110 §15.4.5) but NO body.
|
|
438
|
+
return [304, {
|
|
439
|
+
"cache-control" => "no-cache, must-revalidate",
|
|
440
|
+
"etag" => etag,
|
|
441
|
+
"last-modified" => last_modified
|
|
442
|
+
}, []]
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
[200, headers, [File.binread(full_path)]]
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Decide whether a conditional GET for a static asset can be answered 304.
|
|
449
|
+
# If-None-Match takes precedence over If-Modified-Since (RFC 9110 §13.1.3);
|
|
450
|
+
# ETag comparison is weak (a leading `W/` is ignored on either side).
|
|
451
|
+
def static_not_modified?(env, etag, mtime)
|
|
452
|
+
if_none_match = env["HTTP_IF_NONE_MATCH"]
|
|
453
|
+
if if_none_match && !if_none_match.empty?
|
|
454
|
+
return true if if_none_match.strip == "*"
|
|
455
|
+
|
|
456
|
+
want = weak_etag_value(etag)
|
|
457
|
+
return if_none_match.split(",").any? { |candidate| weak_etag_value(candidate) == want }
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
if_modified_since = env["HTTP_IF_MODIFIED_SINCE"]
|
|
461
|
+
if if_modified_since && !if_modified_since.empty?
|
|
462
|
+
begin
|
|
463
|
+
# HTTP dates have 1-second resolution — compare at whole seconds so a
|
|
464
|
+
# sub-second mtime never spuriously looks "newer" than the client copy.
|
|
465
|
+
return mtime.to_i <= Time.httpdate(if_modified_since).to_i
|
|
466
|
+
rescue ArgumentError
|
|
467
|
+
return false # Unparseable date — treat as no validator, serve the body.
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
false
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Normalise an ETag for weak comparison: drop a leading `W/` and surrounding
|
|
475
|
+
# whitespace so `W/"abc"` and `"abc"` compare equal.
|
|
476
|
+
def weak_etag_value(tag)
|
|
477
|
+
tag.to_s.strip.sub(/\AW\//, "")
|
|
413
478
|
end
|
|
414
479
|
|
|
415
480
|
def serve_swagger_ui
|
data/lib/tina4/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tina4ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.13.
|
|
4
|
+
version: 3.13.75
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tina4 Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|