coldwire-rails 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 268be717f39cc2dcb3927c27c22256afcc81981facc029f9be277ef9c22c1ee0
4
- data.tar.gz: 03cc56cf120523536b582180dcb8366fc9194ca3ada5dca136abd44761cf7afb
3
+ metadata.gz: e48a15a424535143cc620dbbe65eec4a367c9e1feaa60addc104587ee835c836
4
+ data.tar.gz: b85fc7cfc8b80db94b14e994afbb258cc997d0b161986738e80c33a8a49704d5
5
5
  SHA512:
6
- metadata.gz: d2bf7e4ef38d0b7f3e25483995663263468f092904810a9aa48186c0dff1abfd5429890ff0aadc628fd14730d287da327e05daac39d07d92f0c66507f10fc4a2
7
- data.tar.gz: edea984f52957b16f2fd2f0664c9bcf262cf58735679b646b6c345730a4df65524f36e3a4fa3a8ff32ed17a697f0b030330ee670ac860a3fc0f3d06d6aefa120
6
+ metadata.gz: 543c39e2d9999a45ce8041d4c87cb2853908056611bdc189d49e0fde87309373260131f433f92715d29943fcd44eb1d429aa6f270ef639287c0d45ce86787f11
7
+ data.tar.gz: 31bc9a01f008bd987c79700058b49682cae9d1020e873bf9aab03949fe9d15d9619c420c7b5c9efe7032a6b07eddaa090b54d43adecd9cf2f503983d4e702632
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [0.3.0]
6
+
7
+ - **`cache_origins` is `cacheable_hosts`**, and takes bare hosts: `"tiles.example.com"` rather
8
+ than `"https://tiles.example.com"`. The scheme was never carrying information — a worker
9
+ runs only on a secure page, and a secure page cannot fetch `http` — so it was a required
10
+ prefix with exactly one possible value. Requests are matched on a URL's `host`, so a port
11
+ belongs where it is not the default and `localhost:3001` matches that port and no other.
12
+ A scheme raises at boot, naming what to write instead.
13
+
14
+ ## [0.2.0]
15
+
16
+ - **Garbage collection** is on by default. `config.garbage_collection` periodically sweeps the cache, deleting entries that haven't been accessed for `max_age` (default: 60 days), and if the cache grows over `max_size` (default: 250 MB), it continues pruning the least recently accessed until the cache fits. Sweeps run only when confirmed online by pinging `probe_path`, since deletions are irreversible. Cached archives and offline page assets are never collected, and the age of an entry is renewed anytime it or its referenced subresources are accessed or stored. The size ceiling can be adjusted in the offline settings page and is remembered per device.
17
+ - The garbage collector runs safely and automatically; you generally do not need to configure it. But you can tune `max_age`, `max_size`, and `interval` to fit your app's needs.
18
+
19
+
3
20
  ## [0.1.0]
4
21
 
5
22
  First release. The API may still change before 1.0.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -21,7 +21,7 @@ const CACHE_AS_YOU_GO = compileRules(<%= raw Coldwire.cache_rules(Coldwire.confi
21
21
  const NEVER_CACHE = compileRules(<%= raw Coldwire.cache_rules(Coldwire.config.never_cache).to_json %>)
22
22
  // Origins besides ours that may be cached at all, and the URLs whose byte ranges are cached
23
23
  // piece by piece.
24
- const CACHE_ORIGINS = <%= raw Coldwire.config.cache_origins.to_json %>
24
+ const CACHEABLE_HOSTS = <%= raw Coldwire.config.cacheable_hosts.to_json %>
25
25
  const CACHE_RANGES = compileRules(<%= raw Coldwire.cache_rules(Coldwire.config.cache_ranges).to_json %>)
26
26
  // A range entry is stored under its own key, with the range in the query and the archive's
27
27
  // total size in a header — the two things needed to rebuild a 206 that was never storable.
@@ -36,7 +36,7 @@ Coldwire.configure do |config|
36
36
  config.never_cache = []
37
37
  config.never_intercept = [ "/up" ]
38
38
 
39
- config.cache_origins = []
39
+ config.cacheable_hosts = []
40
40
  config.cache_ranges = []
41
41
  config.cache_archives = []
42
42
 
@@ -71,7 +71,7 @@ down with it.
71
71
  | [`cache_as_you_go`](#cache_as_you_go) | `["/*"]` | Pages stored as somebody browses. `/*` is everything |
72
72
  | [`never_cache`](#never_cache) | `[]` | Never stored, by any route in. The one veto |
73
73
  | [`never_intercept`](#never_intercept) | `["/up"]` | Paths the worker does not touch at all |
74
- | [`cache_origins`](#cache_origins) | `[]` | Other origins the worker may cache |
74
+ | [`cacheable_hosts`](#cacheable_hosts) | `[]` | Other hosts the worker may cache |
75
75
  | [`cache_ranges`](#cache_ranges) | `[]` | URLs whose `Range` requests are cached piece by piece |
76
76
  | [`cache_archives`](#cache_archives) | `[]` | Large files somebody can choose to download |
77
77
  | [`ignore_query_params`](#ignore_query_params) | `true` | Treat `/map` and `/map?zoom=9` as one page |
@@ -497,22 +497,28 @@ config.never_cache = [ %r{^/admin(/|$)}, %r{^/users/[^/]+/edit$} ]
497
497
 
498
498
  ---
499
499
 
500
- ## `cache_origins`
500
+ ## `cacheable_hosts`
501
501
 
502
502
  **Default:** `[]`
503
503
 
504
- Origins besides your own that the worker may cache. Each has to send CORS headers naming
505
- your app, or the response arrives opaque — status 0, no headers, no readable body — and
506
- there is nothing worth storing. Ranged sources must also expose `Content-Range`.
504
+ The hosts besides your own that the worker may cache. Each has to send CORS headers naming
505
+ your app, or the response arrives opaque — status 0, no headers, no readable body — and there
506
+ is nothing worth storing. Ranged sources must also expose `Content-Range`.
507
507
 
508
508
  ```ruby
509
- config.cache_origins = [ "https://tiles.example.com" ]
509
+ config.cacheable_hosts = [ "tiles.example.com", "localhost:3001" ]
510
510
  ```
511
511
 
512
- Bare origins only: a scheme and a host, no path, no trailing slash. Anything else raises at
513
- boot, because a malformed origin silently fails to match a request's origin.
512
+ Just the host. No scheme: a worker runs only on a secure page, and a secure page cannot fetch
513
+ `http`, so there was never a second scheme for one to tell apart. Writing one raises at boot
514
+ rather than being stripped — a scheme quietly accepted is a config that looks migrated and is
515
+ not, and the error names what to write instead.
514
516
 
515
- Cross-origin requests are passed through unless the origin is listed here.
517
+ A port only where it is not the default. A request is matched on its URL's `host`, against
518
+ exactly what you wrote: `localhost:3001` matches that port and no other, and a subdomain is a
519
+ different host.
520
+
521
+ Requests to anywhere else are passed straight through — not intercepted, not stored.
516
522
 
517
523
  ---
518
524
 
@@ -530,7 +536,7 @@ config.cache_ranges = [ "/tiles/*", %r{\.pmtiles$} ]
530
536
 
531
537
  Patterns match the URL path, same as the other lists — not the full URL. A cross-origin
532
538
  tile at `https://tiles.example.com/basemap.pmtiles` is allowed only when that origin is in
533
- [`cache_origins`](#cache_origins) *and* its path matches a rule here.
539
+ [`cacheable_hosts`](#cacheable_hosts) *and* its path matches a rule here.
534
540
 
535
541
  This pairs with [`cache_archives`](#cache_archives): `cache_ranges` caches the slices
536
542
  actually read, so the places you have already opened work offline, and downloading the
@@ -561,7 +567,7 @@ Files arrive in 8 MB chunks, which is what makes a dropped connection cost secon
561
567
  of the whole download. A `Range` request against a downloaded archive is answered by
562
568
  slicing the chunks.
563
569
 
564
- If the file lives on another origin, list that origin in [`cache_origins`](#cache_origins).
570
+ If the file lives on another host, list it in [`cacheable_hosts`](#cacheable_hosts).
565
571
 
566
572
  ---
567
573
 
data/docs/setup.md CHANGED
@@ -176,7 +176,7 @@ app-bound mode and takes service workers with it.
176
176
  ## Optional next steps
177
177
 
178
178
  - Restrict what browsing stores with [`cache_as_you_go`](configuration.md#cache_as_you_go)
179
- - Nominate other origins or `Range` URLs with [`cache_origins`](configuration.md#cache_origins)
179
+ - Nominate other hosts or `Range` URLs with [`cacheable_hosts`](configuration.md#cacheable_hosts)
180
180
  and [`cache_ranges`](configuration.md#cache_ranges)
181
181
  - Offer large files for download with [`cache_archives`](configuration.md#cache_archives)
182
182
  - Override the offline fallback by creating
@@ -88,13 +88,19 @@ module Coldwire
88
88
  # cached pages hold whatever the previous session could see.
89
89
  attr_writer :cache_identity
90
90
 
91
- # Origins besides your own that the worker may cache. Each has to send CORS headers naming
91
+ # The hosts besides your own that the worker may cache. Each has to send CORS headers naming
92
92
  # your app, or the response arrives opaque — status 0, no headers, no readable body — and
93
93
  # there is nothing worth storing. Ranged sources must also expose Content-Range.
94
- attr_reader :cache_origins
94
+ #
95
+ # config.cacheable_hosts = [ "tiles.example.com" ]
96
+ #
97
+ # A hostname, with a port only where it is not the default — which is what a URL's `host`
98
+ # reads as. No scheme: a worker runs only on a secure page, and a secure page cannot fetch
99
+ # http, so there was never a second scheme for one to tell apart.
100
+ attr_reader :cacheable_hosts
95
101
 
96
- def cache_origins=(origins)
97
- @cache_origins = Array(origins).map { |origin| validate_origin(origin) }
102
+ def cacheable_hosts=(hosts)
103
+ @cacheable_hosts = Array(hosts).map { |host| validate_host(host) }
98
104
  end
99
105
 
100
106
  # URLs whose Range requests are cached piece by piece, keyed by the range — for a large
@@ -278,7 +284,7 @@ module Coldwire
278
284
  @register_if = -> { true }
279
285
  @caching_enabled_by_default = true
280
286
  @cache_identity = -> { nil }
281
- @cache_origins = []
287
+ @cacheable_hosts = []
282
288
  @cache_ranges = []
283
289
  @cache_archives = []
284
290
  end
@@ -330,19 +336,27 @@ module Coldwire
330
336
 
331
337
  # An origin and nothing more: no path, no trailing slash. Anything else silently fails to
332
338
  # match a request's origin, which is the same quiet failure as a malformed path pattern.
333
- def validate_origin(origin)
334
- value = origin.to_s
335
-
336
- begin
337
- uri = URI.parse(value)
338
- rescue URI::InvalidURIError
339
- uri = nil
339
+ # A host, and a port only where it is not the default — which is exactly what a URL's
340
+ # `host` reads as, so the worker compares what you wrote against what it is handed.
341
+ HOST = /\A[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*(?::\d+)?\z/
342
+ SCHEME = %r{\A[a-z][a-z0-9+.\-]*://}
343
+
344
+ # Raised rather than quietly stripped. Everyone arriving here is renaming `cache_origins`,
345
+ # and a scheme silently accepted is a config that looks migrated and is not — the next
346
+ # person to read it learns the wrong shape.
347
+ def validate_host(host)
348
+ value = host.to_s.strip.downcase
349
+
350
+ if value.match?(SCHEME)
351
+ raise ArgumentError,
352
+ "Coldwire cacheable_hosts takes a host with no scheme — " \
353
+ "#{value.sub(SCHEME, '').chomp('/').inspect} rather than #{host.inspect}"
340
354
  end
341
355
 
342
- unless uri&.scheme && uri.host && uri.path.to_s.empty? && uri.query.nil?
356
+ unless value.match?(HOST)
343
357
  raise ArgumentError,
344
- "Coldwire cache_origins takes bare origins like " \
345
- "\"https://tiles.example.com\": #{origin.inspect}"
358
+ "Coldwire cacheable_hosts takes bare hosts like " \
359
+ "\"tiles.example.com\": #{host.inspect}"
346
360
  end
347
361
 
348
362
  value
@@ -3,7 +3,7 @@ function shouldHandle(request) {
3
3
  if (request.method !== "GET") return false
4
4
 
5
5
  const url = new URL(request.url)
6
- if (!cacheableOrigin(url)) return false
6
+ if (!cacheableHost(url)) return false
7
7
 
8
8
  // A Range request cannot be stored as it arrives — cache.put refuses a 206 — so it is
9
9
  // stored as a 200 under a key naming the range, and answered with a 206 built here. Only
@@ -16,8 +16,8 @@ function shouldHandle(request) {
16
16
 
17
17
  // Our own origin, plus any the host app has nominated. A worker sees every request a page
18
18
  // makes, and caching other people's responses uninvited is not its business.
19
- function cacheableOrigin(url) {
20
- return url.origin === self.location.origin || CACHE_ORIGINS.includes(url.origin)
19
+ function cacheableHost(url) {
20
+ return url.origin === self.location.origin || CACHEABLE_HOSTS.includes(url.host)
21
21
  }
22
22
 
23
23
  function matchesPath(url, paths) {
@@ -83,7 +83,7 @@ function isAutoCacheable(request) {
83
83
 
84
84
  // A nominated origin is the opt-in; the path lists describe this app's own surfaces and say
85
85
  // nothing useful about somebody else's.
86
- if (url.origin !== self.location.origin) return CACHE_ORIGINS.includes(url.origin)
86
+ if (url.origin !== self.location.origin) return CACHEABLE_HOSTS.includes(url.host)
87
87
 
88
88
  if (isNeverCached(url)) return false
89
89
 
@@ -43,7 +43,7 @@ function urlsFromHtml(html, pageUrl) {
43
43
  // Any origin we are allowed to cache, not just our own. A page whose map library
44
44
  // comes off a CDN is not offline-ready without it: precaching the page and skipping
45
45
  // the script it cannot run without leaves a blank screen and a full cache.
46
- if (!cacheableOrigin(url)) return
46
+ if (!cacheableHost(url)) return
47
47
  if (matchesPath(url, NEVER_INTERCEPT)) return
48
48
  urls.add(url.href)
49
49
  } catch {}
@@ -54,8 +54,9 @@ Coldwire.configure do |config|
54
54
  # Never intercepted, so these fail outright offline. Coldwire's own routes are added for you.
55
55
  config.never_intercept = [ "/up" ] # probe_path is added for you
56
56
 
57
- # Origins besides your own the worker may cache, and URLs whose Range requests it caches.
58
- config.cache_origins = []
57
+ # The hosts besides your own the worker may cache, and URLs whose Range requests it caches.
58
+ # No scheme — "tiles.example.com" and a port only where it is not the default.
59
+ config.cacheable_hosts = []
59
60
  config.cache_ranges = []
60
61
 
61
62
  # Large files somebody can download for offline use. Nothing downloads on its own.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coldwire-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noreaster Group, Stuart Yamartino