async-http-cache 0.4.5 → 0.4.6
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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +80 -0
- data/context/index.yaml +12 -0
- data/lib/async/http/cache/body.rb +11 -4
- data/lib/async/http/cache/general.rb +21 -2
- data/lib/async/http/cache/response.rb +13 -1
- data/lib/async/http/cache/store/memory.rb +15 -1
- data/lib/async/http/cache/store/vary.rb +20 -1
- data/lib/async/http/cache/store.rb +4 -1
- data/lib/async/http/cache/version.rb +5 -2
- data/license.md +1 -1
- data/readme.md +58 -27
- data/releases.md +81 -0
- data.tar.gz.sig +0 -0
- metadata +8 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ae1d78b87d12a34eb5217c0cf6a49d6ecd81c5322790d563568a48e951733fe
|
4
|
+
data.tar.gz: 44a915c95e0eced20d783f702179b701c1ba07678bc62e3201d8adce1b5d204c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2faf1e2713d8f273e9b938b6dfd01ece309533a8b96173306fc93c1cd02ca84305eada3dc99967d27605f6fca369bef6c57a745361882243a6e519a32987c48
|
7
|
+
data.tar.gz: ec998fd4509ec25c908c176ce894904eb7e3515073b7a750e56ac47022d6c336c4d6d0437ee2be1e9fa3bdc5c7f53868028a4dd82395691e2fc15d5e72437786
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
This guide explains how to get started with `async-http-cache`, a cache middleware for `Async::HTTP` clients and servers.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the gem to your project:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
$ bundle add async-http-cache
|
11
|
+
```
|
12
|
+
|
13
|
+
## Core Concepts
|
14
|
+
|
15
|
+
`async-http-cache` has several core concepts:
|
16
|
+
|
17
|
+
- A {Async::HTTP::Cache::General} which represents the main cache middleware that implements a general shared cache according to RFC 9111.
|
18
|
+
- A cache store system that handles the actual storage and retrieval of cached responses.
|
19
|
+
- Cache-aware request and response handling that respects HTTP caching headers.
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The cache middleware can be used with both `Async::HTTP` clients and servers to provide transparent HTTP caching.
|
24
|
+
|
25
|
+
### Client Side Caching
|
26
|
+
|
27
|
+
Here's how to use the cache with an HTTP client:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require "async"
|
31
|
+
require "async/http"
|
32
|
+
require "async/http/cache"
|
33
|
+
|
34
|
+
endpoint = Async::HTTP::Endpoint.parse("https://www.oriontransfer.co.nz")
|
35
|
+
client = Async::HTTP::Client.new(endpoint)
|
36
|
+
cache = Async::HTTP::Cache::General.new(client)
|
37
|
+
|
38
|
+
Async do
|
39
|
+
2.times do
|
40
|
+
response = cache.get("/products/index")
|
41
|
+
puts response.inspect
|
42
|
+
# <Async::HTTP::Protocol::HTTP2::Response ...>
|
43
|
+
# <Async::HTTP::Cache::Response ...>
|
44
|
+
response.finish
|
45
|
+
end
|
46
|
+
ensure
|
47
|
+
cache.close
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
In this example:
|
52
|
+
|
53
|
+
1. We create an HTTP client as usual
|
54
|
+
2. We wrap it with `Async::HTTP::Cache::General` to add caching capability
|
55
|
+
3. The first request will hit the remote server
|
56
|
+
4. The second identical request will be served from the cache (if the response is cacheable)
|
57
|
+
|
58
|
+
### Caching Behavior
|
59
|
+
|
60
|
+
The cache middleware automatically handles:
|
61
|
+
|
62
|
+
- **Cacheable Methods**: Only `GET` and `HEAD` requests are cached
|
63
|
+
- **Cache Headers**: Respects `Cache-Control`, `Expires`, and other standard HTTP caching headers
|
64
|
+
- **Response Codes**: Only certain response codes (200, 203, 300, 301, 302, 404, 410) are cached
|
65
|
+
- **Request Validation**: Requests with authorization headers, cookies, or request bodies are not cached
|
66
|
+
|
67
|
+
### Important Notes on Vary Header
|
68
|
+
|
69
|
+
The `vary` header creates a headache for proxy implementations, because it creates a combinatorial explosion of cache keys, even if the content is the same. Try to avoid it unless absolutely necessary.
|
70
|
+
|
71
|
+
## Custom Cache Stores
|
72
|
+
|
73
|
+
By default, the cache uses an in-memory store, but you can provide custom storage backends:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# Use a custom store
|
77
|
+
cache = Async::HTTP::Cache::General.new(client, store: MyCustomStore.new)
|
78
|
+
```
|
79
|
+
|
80
|
+
The store must implement the interface defined by {Async::HTTP::Cache::Store}.
|
data/context/index.yaml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
3
|
+
---
|
4
|
+
description: Standard-compliant cache for async-http.
|
5
|
+
metadata:
|
6
|
+
documentation_uri: https://socketry.github.io/async-http-cache/
|
7
|
+
source_code_uri: https://github.com/socketry/async-http-cache.git
|
8
|
+
files:
|
9
|
+
- path: getting-started.md
|
10
|
+
title: Getting Started
|
11
|
+
description: This guide explains how to get started with `async-http-cache`, a cache
|
12
|
+
middleware for `Async::HTTP` clients and servers.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require "protocol/http/body/rewindable"
|
7
7
|
require "protocol/http/body/completable"
|
@@ -13,10 +13,17 @@ require "console/event/failure"
|
|
13
13
|
module Async
|
14
14
|
module HTTP
|
15
15
|
module Cache
|
16
|
+
# Provides utilities for wrapping HTTP response bodies with caching capabilities.
|
16
17
|
module Body
|
17
18
|
TRAILER = "trailer"
|
18
19
|
ETAG = "etag"
|
19
20
|
|
21
|
+
# Wrap a response body with caching functionality, including ETag generation and completion handling.
|
22
|
+
# @parameter response [Protocol::HTTP::Response] The HTTP response to wrap.
|
23
|
+
# @yields {|response, body| ...} The block to execute when caching is complete.
|
24
|
+
# @parameter response [Protocol::HTTP::Response] The wrapped response.
|
25
|
+
# @parameter body [Protocol::HTTP::Body::Buffered, nil] The buffered response body.
|
26
|
+
# @returns [Protocol::HTTP::Response] The original response, potentially with modified headers.
|
20
27
|
def self.wrap(response, &block)
|
21
28
|
if body = response.body
|
22
29
|
if body.empty?
|
@@ -27,13 +34,13 @@ module Async
|
|
27
34
|
rewindable = ::Protocol::HTTP::Body::Rewindable.wrap(response)
|
28
35
|
|
29
36
|
unless response.headers.include?(ETAG)
|
37
|
+
# Add the etag header to the trailers:
|
38
|
+
response.headers.add(TRAILER, ETAG)
|
39
|
+
|
30
40
|
# Compute a digest and add it to the response headers:
|
31
41
|
::Protocol::HTTP::Body::Digestable.wrap(response) do |wrapper|
|
32
42
|
response.headers.add(ETAG, wrapper.etag)
|
33
43
|
end
|
34
|
-
|
35
|
-
# Ensure the etag is listed as a trailer:
|
36
|
-
response.headers.add(TRAILER, ETAG)
|
37
44
|
end
|
38
45
|
|
39
46
|
# Wrap the response with the callback:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2022, by Colin Kelley.
|
6
6
|
|
7
7
|
require "set"
|
@@ -37,6 +37,9 @@ module Async
|
|
37
37
|
410 => true # Gone
|
38
38
|
}.freeze
|
39
39
|
|
40
|
+
# Initialize a new cache middleware instance.
|
41
|
+
# @parameter app [Object] The downstream application or middleware.
|
42
|
+
# @parameter store [Store] The cache store to use for persistence.
|
40
43
|
def initialize(app, store: Store.default)
|
41
44
|
super(app)
|
42
45
|
|
@@ -48,18 +51,25 @@ module Async
|
|
48
51
|
attr :count
|
49
52
|
attr :store
|
50
53
|
|
54
|
+
# Close the cache and clean up resources.
|
51
55
|
def close
|
52
56
|
@store.close
|
53
57
|
ensure
|
54
58
|
super
|
55
59
|
end
|
56
60
|
|
61
|
+
# Generate a cache key for the given request.
|
62
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request.
|
63
|
+
# @returns [Array] A cache key array containing authority, method, and path.
|
57
64
|
def key(request)
|
58
65
|
@store.normalize(request)
|
59
66
|
|
60
67
|
[request.authority, request.method, request.path]
|
61
68
|
end
|
62
69
|
|
70
|
+
# Determine if a request is cacheable based on method, headers, and body.
|
71
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request to check.
|
72
|
+
# @returns [Boolean] True if the request can be cached.
|
63
73
|
def cacheable_request?(request)
|
64
74
|
# We don't support caching requests which have a request body:
|
65
75
|
if request.body
|
@@ -88,6 +98,9 @@ module Async
|
|
88
98
|
return true
|
89
99
|
end
|
90
100
|
|
101
|
+
# Check if response headers allow caching.
|
102
|
+
# @parameter headers [Protocol::HTTP::Headers] The response headers to check.
|
103
|
+
# @returns [Boolean] True if headers permit caching.
|
91
104
|
def cacheable_response_headers?(headers)
|
92
105
|
if cache_control = headers[CACHE_CONTROL]
|
93
106
|
if cache_control.no_store? || cache_control.private?
|
@@ -100,10 +113,13 @@ module Async
|
|
100
113
|
Console.logger.debug(self) {"Cannot cache response with set-cookie header!"}
|
101
114
|
return false
|
102
115
|
end
|
103
|
-
|
116
|
+
|
104
117
|
return true
|
105
118
|
end
|
106
119
|
|
120
|
+
# Determine if a response is cacheable based on status code and headers.
|
121
|
+
# @parameter response [Protocol::HTTP::Response] The HTTP response to check.
|
122
|
+
# @returns [Boolean] True if the response can be cached.
|
107
123
|
def cacheable_response?(response)
|
108
124
|
# At this point, we know response.status and response.headers.
|
109
125
|
# But we don't know response.body or response.headers.trailer.
|
@@ -162,6 +178,9 @@ module Async
|
|
162
178
|
end
|
163
179
|
end
|
164
180
|
|
181
|
+
# Process an HTTP request, checking cache first and storing responses when appropriate.
|
182
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request to process.
|
183
|
+
# @returns [Protocol::HTTP::Response] Either a cached response or a fresh response from upstream.
|
165
184
|
def call(request)
|
166
185
|
cache_control = request.headers[CACHE_CONTROL]
|
167
186
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require "protocol/http/response"
|
7
7
|
require "async/clock"
|
@@ -9,12 +9,16 @@ require "async/clock"
|
|
9
9
|
module Async
|
10
10
|
module HTTP
|
11
11
|
module Cache
|
12
|
+
# Represents a cached HTTP response with cache-specific metadata and functionality.
|
12
13
|
class Response < ::Protocol::HTTP::Response
|
13
14
|
CACHE_CONTROL = "cache-control"
|
14
15
|
ETAG = "etag"
|
15
16
|
|
16
17
|
X_CACHE = "x-cache"
|
17
18
|
|
19
|
+
# Initialize a new cached response.
|
20
|
+
# @parameter response [Protocol::HTTP::Response] The original HTTP response.
|
21
|
+
# @parameter body [Protocol::HTTP::Body] The response body.
|
18
22
|
def initialize(response, body)
|
19
23
|
@generated_at = Async::Clock.now
|
20
24
|
|
@@ -34,20 +38,28 @@ module Async
|
|
34
38
|
|
35
39
|
attr :generated_at
|
36
40
|
|
41
|
+
# Get the ETag header value for this cached response.
|
42
|
+
# @returns [String, nil] The ETag value or nil if not present.
|
37
43
|
def etag
|
38
44
|
@etag ||= @headers[ETAG]
|
39
45
|
end
|
40
46
|
|
47
|
+
# Calculate the age of this cached response in seconds.
|
48
|
+
# @returns [Float] The number of seconds since the response was generated.
|
41
49
|
def age
|
42
50
|
Async::Clock.now - @generated_at
|
43
51
|
end
|
44
52
|
|
53
|
+
# Check if this cached response has expired based on its max-age.
|
54
|
+
# @returns [Boolean, nil] True if expired, false if still valid, nil if no max-age set.
|
45
55
|
def expired?
|
46
56
|
if @max_age
|
47
57
|
self.age > @max_age
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
61
|
+
# Create a duplicate of this cached response with independent body and headers.
|
62
|
+
# @returns [Response] A new Response instance with duplicated body and headers.
|
51
63
|
def dup
|
52
64
|
dup = super
|
53
65
|
|
@@ -1,13 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Async
|
7
7
|
module HTTP
|
8
8
|
module Cache
|
9
9
|
module Store
|
10
|
+
# Represents an in-memory cache store with automatic pruning of expired entries.
|
10
11
|
class Memory
|
12
|
+
# Initialize a new in-memory cache store.
|
13
|
+
# @parameter limit [Integer] Maximum number of entries to store.
|
14
|
+
# @parameter maximum_size [Integer] Maximum size in bytes for individual cached responses.
|
15
|
+
# @parameter prune_interval [Integer] Interval in seconds between automatic pruning operations.
|
11
16
|
def initialize(limit: 1024, maximum_size: 1024*64, prune_interval: 60)
|
12
17
|
@index = {}
|
13
18
|
@limit = limit
|
@@ -42,6 +47,7 @@ module Async
|
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
50
|
+
# Close the cache store and stop background pruning.
|
45
51
|
def close
|
46
52
|
@gardener.stop
|
47
53
|
end
|
@@ -51,6 +57,10 @@ module Async
|
|
51
57
|
IF_NONE_MATCH = "if-none-match"
|
52
58
|
NOT_MODIFIED = ::Protocol::HTTP::Response[304]
|
53
59
|
|
60
|
+
# Look up a cached response for the given key and request.
|
61
|
+
# @parameter key [Array] The cache key to look up.
|
62
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request.
|
63
|
+
# @returns [Protocol::HTTP::Response, nil] The cached response or nil if not found/expired.
|
54
64
|
def lookup(key, request)
|
55
65
|
if response = @index[key]
|
56
66
|
if response.expired?
|
@@ -77,6 +87,10 @@ module Async
|
|
77
87
|
end
|
78
88
|
end
|
79
89
|
|
90
|
+
# Insert a response into the cache if it meets size and limit constraints.
|
91
|
+
# @parameter key [Array] The cache key.
|
92
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request.
|
93
|
+
# @parameter response [Protocol::HTTP::Response] The HTTP response to cache.
|
80
94
|
def insert(key, request, response)
|
81
95
|
if @index.size < @limit
|
82
96
|
length = response.body&.length
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Async
|
7
7
|
module HTTP
|
@@ -10,18 +10,25 @@ module Async
|
|
10
10
|
VARY = "vary"
|
11
11
|
ACCEPT_ENCODING = "accept-encoding"
|
12
12
|
|
13
|
+
# Represents a cache store wrapper that handles HTTP Vary header functionality.
|
13
14
|
class Vary
|
15
|
+
# Initialize a new Vary store wrapper.
|
16
|
+
# @parameter delegate [Store] The underlying cache store to delegate to.
|
17
|
+
# @parameter vary [Hash] Initial vary header mappings.
|
14
18
|
def initialize(delegate, vary = {})
|
15
19
|
@delegate = delegate
|
16
20
|
@vary = vary
|
17
21
|
end
|
18
22
|
|
23
|
+
# Close the vary store and its delegate.
|
19
24
|
def close
|
20
25
|
@delegate.close
|
21
26
|
end
|
22
27
|
|
23
28
|
attr :delegate
|
24
29
|
|
30
|
+
# Normalize request headers to reduce cache key variations.
|
31
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request to normalize.
|
25
32
|
def normalize(request)
|
26
33
|
if accept_encoding = request.headers[ACCEPT_ENCODING]
|
27
34
|
if accept_encoding.include?("gzip")
|
@@ -32,10 +39,18 @@ module Async
|
|
32
39
|
end
|
33
40
|
end
|
34
41
|
|
42
|
+
# Generate vary-specific key components from request headers.
|
43
|
+
# @parameter headers [Protocol::HTTP::Headers] The request headers.
|
44
|
+
# @parameter vary [Array] Array of header names that affect caching.
|
45
|
+
# @returns [Array] Array of header values for the vary keys.
|
35
46
|
def key_for(headers, vary)
|
36
47
|
vary.map{|key| headers[key]}
|
37
48
|
end
|
38
49
|
|
50
|
+
# Look up a cached response, accounting for vary headers.
|
51
|
+
# @parameter key [Array] The base cache key.
|
52
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request.
|
53
|
+
# @returns [Protocol::HTTP::Response, nil] The cached response or nil if not found.
|
39
54
|
def lookup(key, request)
|
40
55
|
if vary = @vary[key]
|
41
56
|
# We should provide user-supported normalization here:
|
@@ -45,6 +60,10 @@ module Async
|
|
45
60
|
return @delegate.lookup(key, request)
|
46
61
|
end
|
47
62
|
|
63
|
+
# Insert a response into the cache, handling vary headers appropriately.
|
64
|
+
# @parameter key [Array] The base cache key.
|
65
|
+
# @parameter request [Protocol::HTTP::Request] The HTTP request.
|
66
|
+
# @parameter response [Protocol::HTTP::Response] The HTTP response to cache.
|
48
67
|
def insert(key, request, response)
|
49
68
|
if vary = response.headers[VARY]&.sort
|
50
69
|
@vary[key] = vary
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "store/memory"
|
7
7
|
require_relative "store/vary"
|
@@ -9,7 +9,10 @@ require_relative "store/vary"
|
|
9
9
|
module Async
|
10
10
|
module HTTP
|
11
11
|
module Cache
|
12
|
+
# Provides cache storage implementations and utilities.
|
12
13
|
module Store
|
14
|
+
# Create a default cache store with Vary support over an in-memory store.
|
15
|
+
# @returns [Store::Vary] A default cache store instance.
|
13
16
|
def self.default
|
14
17
|
Vary.new(Memory.new)
|
15
18
|
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
|
+
# @namespace
|
6
7
|
module Async
|
8
|
+
# @namespace
|
7
9
|
module HTTP
|
10
|
+
# @namespace
|
8
11
|
module Cache
|
9
|
-
VERSION = "0.4.
|
12
|
+
VERSION = "0.4.6"
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -6,33 +6,64 @@ Provides a cache middleware for `Async::HTTP` clients and servers.
|
|
6
6
|
|
7
7
|
## Usage
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
9
|
+
Please see the [project documentation](https://socketry.github.io/async-http-cache/) for more details.
|
10
|
+
|
11
|
+
- [Getting Started](https://socketry.github.io/async-http-cache/guides/getting-started/index) - This guide explains how to get started with `async-http-cache`, a cache middleware for `Async::HTTP` clients and servers.
|
12
|
+
|
13
|
+
## Releases
|
14
|
+
|
15
|
+
Please see the [project releases](https://socketry.github.io/async-http-cache/releases/index) for all releases.
|
16
|
+
|
17
|
+
### v0.4.6
|
18
|
+
|
19
|
+
- Improved documentation and agent context.
|
20
|
+
|
21
|
+
### v0.4.5
|
22
|
+
|
23
|
+
- Modernized gem structure and dependencies.
|
24
|
+
|
25
|
+
### v0.4.4
|
26
|
+
|
27
|
+
- Modernized gem structure and test suite.
|
28
|
+
- Added reference to RFC 9111 in documentation.
|
29
|
+
- Improved cache-ability checks by moving response validation to `General` class.
|
30
|
+
|
31
|
+
### v0.4.3
|
32
|
+
|
33
|
+
- Improved `Memory` store with configurable limits and pruning intervals.
|
34
|
+
- Enhanced cache-control header handling for `no-store` and `private` directives.
|
35
|
+
- Fixed spelling of `cacheable?` method (was `cachable?`).
|
36
|
+
- Optimized cacheable response code checks using hash table lookup.
|
37
|
+
- Added external test suite for validation.
|
38
|
+
|
39
|
+
### v0.4.2
|
40
|
+
|
41
|
+
- Improved memory cache gardener task to be transient with proper annotations.
|
42
|
+
|
43
|
+
### v0.4.1
|
44
|
+
|
45
|
+
- Updated to use `Console.logger` for consistent logging.
|
46
|
+
- Modernized gem structure.
|
47
|
+
|
48
|
+
### v0.4.0
|
49
|
+
|
50
|
+
- **Breaking**: Renamed `trailers` to `trailer` for consistency with HTTP specifications.
|
51
|
+
- Updated dependency on `async-http`.
|
52
|
+
|
53
|
+
### v0.3.0
|
54
|
+
|
55
|
+
- Updated dependencies to latest versions.
|
56
|
+
|
57
|
+
### v0.2.0
|
58
|
+
|
59
|
+
- Added support for ETag validation with `if-none-match` header handling.
|
60
|
+
- Improved streaming response handling with trailing ETag generation.
|
61
|
+
- Enhanced trailer support for body digest calculation.
|
62
|
+
- Removed support for end-of-life Ruby versions.
|
63
|
+
|
64
|
+
### v0.1.5
|
65
|
+
|
66
|
+
- Fixed handling of responses with `nil` body length to prevent caching errors.
|
36
67
|
|
37
68
|
## Contributing
|
38
69
|
|
data/releases.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Releases
|
2
|
+
|
3
|
+
## v0.4.6
|
4
|
+
|
5
|
+
- Improved documentation and agent context.
|
6
|
+
|
7
|
+
## v0.4.5
|
8
|
+
|
9
|
+
- Modernized gem structure and dependencies.
|
10
|
+
|
11
|
+
## v0.4.4
|
12
|
+
|
13
|
+
- Modernized gem structure and test suite.
|
14
|
+
- Added reference to RFC 9111 in documentation.
|
15
|
+
- Improved cache-ability checks by moving response validation to `General` class.
|
16
|
+
|
17
|
+
## v0.4.3
|
18
|
+
|
19
|
+
- Improved `Memory` store with configurable limits and pruning intervals.
|
20
|
+
- Enhanced cache-control header handling for `no-store` and `private` directives.
|
21
|
+
- Fixed spelling of `cacheable?` method (was `cachable?`).
|
22
|
+
- Optimized cacheable response code checks using hash table lookup.
|
23
|
+
- Added external test suite for validation.
|
24
|
+
|
25
|
+
## v0.4.2
|
26
|
+
|
27
|
+
- Improved memory cache gardener task to be transient with proper annotations.
|
28
|
+
|
29
|
+
## v0.4.1
|
30
|
+
|
31
|
+
- Updated to use `Console.logger` for consistent logging.
|
32
|
+
- Modernized gem structure.
|
33
|
+
|
34
|
+
## v0.4.0
|
35
|
+
|
36
|
+
- **Breaking**: Renamed `trailers` to `trailer` for consistency with HTTP specifications.
|
37
|
+
- Updated dependency on `async-http`.
|
38
|
+
|
39
|
+
## v0.3.0
|
40
|
+
|
41
|
+
- Updated dependencies to latest versions.
|
42
|
+
|
43
|
+
## v0.2.0
|
44
|
+
|
45
|
+
- Added support for ETag validation with `if-none-match` header handling.
|
46
|
+
- Improved streaming response handling with trailing ETag generation.
|
47
|
+
- Enhanced trailer support for body digest calculation.
|
48
|
+
- Removed support for end-of-life Ruby versions.
|
49
|
+
|
50
|
+
## v0.1.5
|
51
|
+
|
52
|
+
- Fixed handling of responses with `nil` body length to prevent caching errors.
|
53
|
+
|
54
|
+
## v0.1.4
|
55
|
+
|
56
|
+
- Fixed caching behavior for `HEAD` requests to handle empty bodies correctly.
|
57
|
+
|
58
|
+
## v0.1.3
|
59
|
+
|
60
|
+
- Updated dependencies.
|
61
|
+
|
62
|
+
## v0.1.2
|
63
|
+
|
64
|
+
- Improved handling of malformed or missing `cache-control` headers.
|
65
|
+
|
66
|
+
## v0.1.1
|
67
|
+
|
68
|
+
- Added automatic cache pruning with background gardener task.
|
69
|
+
- Improved `vary` header handling with better key generation.
|
70
|
+
- Enhanced request `cache-control` header checking.
|
71
|
+
- Added detailed cache statistics logging.
|
72
|
+
- Improved error logging for response failures.
|
73
|
+
|
74
|
+
## v0.1.0
|
75
|
+
|
76
|
+
- Initial release with HTTP caching middleware.
|
77
|
+
- Support for `GET` and `HEAD` request caching.
|
78
|
+
- In-memory cache store with configurable limits.
|
79
|
+
- Vary header support for content negotiation.
|
80
|
+
- Cache-control directive compliance.
|
81
|
+
- Response validation based on status codes and headers.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-http-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -38,7 +38,7 @@ cert_chain:
|
|
38
38
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
39
39
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
40
40
|
-----END CERTIFICATE-----
|
41
|
-
date:
|
41
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
42
42
|
dependencies:
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: async-http
|
@@ -58,6 +58,8 @@ executables: []
|
|
58
58
|
extensions: []
|
59
59
|
extra_rdoc_files: []
|
60
60
|
files:
|
61
|
+
- context/getting-started.md
|
62
|
+
- context/index.yaml
|
61
63
|
- lib/async/http/cache.rb
|
62
64
|
- lib/async/http/cache/body.rb
|
63
65
|
- lib/async/http/cache/general.rb
|
@@ -68,10 +70,12 @@ files:
|
|
68
70
|
- lib/async/http/cache/version.rb
|
69
71
|
- license.md
|
70
72
|
- readme.md
|
73
|
+
- releases.md
|
71
74
|
homepage: https://github.com/socketry/async-http-cache
|
72
75
|
licenses:
|
73
76
|
- MIT
|
74
77
|
metadata:
|
78
|
+
documentation_uri: https://socketry.github.io/async-http-cache/
|
75
79
|
source_code_uri: https://github.com/socketry/async-http-cache.git
|
76
80
|
rdoc_options: []
|
77
81
|
require_paths:
|
@@ -80,14 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
84
|
requirements:
|
81
85
|
- - ">="
|
82
86
|
- !ruby/object:Gem::Version
|
83
|
-
version: '3.
|
87
|
+
version: '3.2'
|
84
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
89
|
requirements:
|
86
90
|
- - ">="
|
87
91
|
- !ruby/object:Gem::Version
|
88
92
|
version: '0'
|
89
93
|
requirements: []
|
90
|
-
rubygems_version: 3.6.
|
94
|
+
rubygems_version: 3.6.9
|
91
95
|
specification_version: 4
|
92
96
|
summary: Standard-compliant cache for async-http.
|
93
97
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|