async-http-cache 0.1.2 → 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: '08cba1514e099ffea868d2c6db065da163f3b71197dc0481be150177c8eef70f'
4
- data.tar.gz: f70d55aad88205d2d61d1f995e483fd8596ea3814cf2dcc2d09dd165665872b3
3
+ metadata.gz: 533b151a2683c0394fdab5d6948380152636f30f7eae85b5442a7dfe08c0039e
4
+ data.tar.gz: 002b42c3b6f1e3a871a870fd94b1697f0ca19c3761021224e9cc301a16aa8bbe
5
5
  SHA512:
6
- metadata.gz: 91623e4eba7d1028daeb40b132da3d92167f84981f590e810a2a8170a426a1b625a59ae886ee3f0a0cff7b6e44a5ff61eba62adc0ea786f093b9d1859e4c1f7d
7
- data.tar.gz: 72a68261b04e66422a81b4a9b9a6ff87937a0b72b42c7f68a6010e8e1021693e231bb4ef3bd80921e4aecf968e45f0111d49d4bdb331525ebf6a313732ad67d8
6
+ metadata.gz: bbee2bef91a871bb45d2161ee37a77d6db22690925e6894ed568b3617734eebe0996e77fd8cdfe74c34215857a21a3a6edc788cf7abb8288a9055f434d9e8b95
7
+ data.tar.gz: 396c077f925cc2359072f9da2244783c43eaa6a8cc87988dc79c1330f0ca5328b7b57a665fafc98d7718bc4dde14a08067133bee5786b6c7143a06fbd0f14b66
@@ -11,7 +11,6 @@ jobs:
11
11
  - macos
12
12
 
13
13
  ruby:
14
- - 2.4
15
14
  - 2.5
16
15
  - 2.6
17
16
  - 2.7
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in async-http-cache.gemspec
4
4
  gemspec
5
+
6
+ # gem "async-http", path: "../async-http"
7
+ # gem "protocol-http", path: "../protocol-http"
8
+ # gem "protocol-http1", path: "../protocol-http1"
@@ -21,13 +21,12 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "async-http"
25
- spec.add_dependency "protocol-http", "~> 0.14.4"
24
+ spec.add_dependency "async-http", "~> 0.53"
26
25
 
27
26
  spec.add_development_dependency "async-rspec", "~> 1.10"
28
27
 
29
28
  spec.add_development_dependency "covered"
30
29
  spec.add_development_dependency "bundler"
31
30
  spec.add_development_dependency "rspec"
32
- spec.add_development_dependency "rake"
31
+ spec.add_development_dependency "bake-bundler"
33
32
  end
@@ -21,33 +21,49 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  require 'protocol/http/body/rewindable'
24
- require 'protocol/http/body/streamable'
24
+ require 'protocol/http/body/completable'
25
+ require 'protocol/http/body/digestable'
25
26
 
26
27
  module Async
27
28
  module HTTP
28
29
  module Cache
29
30
  module Body
30
- def self.wrap(message, &block)
31
- if body = message.body
32
- # Create a rewindable body wrapping the message body:
33
- rewindable = ::Protocol::HTTP::Body::Rewindable.new(body)
34
-
35
- # Set the message body to the rewindable body:
36
- message.body = rewindable
37
-
38
- # Wrap the message with the callback:
39
- ::Protocol::HTTP::Body::Streamable.wrap(message) do |error|
40
- if error
41
- Async.logger.error(self) {error}
42
- else
43
- yield message, rewindable.buffered
31
+ TRAILERS = 'trailers'
32
+ ETAG = 'etag'
33
+
34
+ def self.wrap(response, &block)
35
+ if body = response.body
36
+ if body.empty?
37
+ # A body that is empty? at the outset, is immutable. This generally only applies to HEAD requests.
38
+ yield response, body
39
+ else
40
+ # Insert a rewindable body so that we can cache the response body:
41
+ rewindable = ::Protocol::HTTP::Body::Rewindable.wrap(response)
42
+
43
+ unless response.headers.include?(ETAG)
44
+ # Compute a digest and add it to the response headers:
45
+ ::Protocol::HTTP::Body::Digestable.wrap(response) do |wrapper|
46
+ response.headers.add(ETAG, wrapper.etag)
47
+ end
48
+
49
+ # Ensure the etag is listed as a trailer:
50
+ response.headers.add(TRAILERS, ETAG)
51
+ end
52
+
53
+ # Wrap the response with the callback:
54
+ ::Protocol::HTTP::Body::Completable.wrap(response) do |error|
55
+ if error
56
+ Async.logger.error(self) {error}
57
+ else
58
+ yield response, rewindable.buffered
59
+ end
44
60
  end
45
61
  end
46
62
  else
47
- yield message, nil
63
+ yield response, nil
48
64
  end
49
65
 
50
- return message
66
+ return response
51
67
  end
52
68
  end
53
69
  end
@@ -31,6 +31,7 @@ module Async
31
31
  module Cache
32
32
  class General < ::Protocol::HTTP::Middleware
33
33
  CACHE_CONTROL = 'cache-control'
34
+
34
35
  CONTENT_TYPE = 'content-type'
35
36
  AUTHORIZATION = 'authorization'
36
37
  COOKIE = 'cookie'
@@ -91,9 +92,17 @@ module Async
91
92
  return response
92
93
  end
93
94
 
94
- return Body.wrap(response) do |message, body|
95
+ if request.head? and body = response.body
96
+ unless body.empty?
97
+ Async.logger.warn(self) {"HEAD request resulted in non-empty body!"}
98
+
99
+ return response
100
+ end
101
+ end
102
+
103
+ return Body.wrap(response) do |response, body|
95
104
  Async.logger.debug(self) {"Updating cache for #{key}..."}
96
- @store.insert(key, request, Response.new(message, body))
105
+ @store.insert(key, request, Response.new(response, body))
97
106
  end
98
107
  end
99
108
 
@@ -107,7 +116,7 @@ module Async
107
116
  Async.logger.debug(self) {"Cache hit for #{key}..."}
108
117
  @count += 1
109
118
 
110
- # Create a dup of the response:
119
+ # Return the cached response:
111
120
  return response
112
121
  end
113
122
  end
@@ -29,6 +29,9 @@ module Async
29
29
  class Response < ::Protocol::HTTP::Response
30
30
  CACHE_CONTROL = 'cache-control'
31
31
  SET_COOKIE = 'set-cookie'
32
+ ETAG = 'etag'
33
+
34
+ X_CACHE = 'x-cache'
32
35
 
33
36
  def initialize(response, body)
34
37
  @generated_at = Async::Clock.now
@@ -36,16 +39,23 @@ module Async
36
39
  super(
37
40
  response.version,
38
41
  response.status,
39
- response.headers.dup,
42
+ response.headers.flatten,
40
43
  body,
41
44
  response.protocol
42
45
  )
43
46
 
44
47
  @max_age = @headers[CACHE_CONTROL]&.max_age
48
+ @etag = nil
49
+
50
+ @headers.set(X_CACHE, 'hit')
45
51
  end
46
52
 
47
53
  attr :generated_at
48
54
 
55
+ def etag
56
+ @etag ||= @headers[ETAG]
57
+ end
58
+
49
59
  def cachable?
50
60
  if cache_control = @headers[CACHE_CONTROL]
51
61
  if cache_control.private? || !cache_control.public?
@@ -64,6 +64,9 @@ module Async
64
64
 
65
65
  attr :index
66
66
 
67
+ IF_NONE_MATCH = 'if-none-match'
68
+ NOT_MODIFIED = ::Protocol::HTTP::Response[304]
69
+
67
70
  def lookup(key, request)
68
71
  if response = @index[key]
69
72
  if response.expired?
@@ -74,6 +77,12 @@ module Async
74
77
  return nil
75
78
  end
76
79
 
80
+ if etags = request.headers[IF_NONE_MATCH]
81
+ if etags.include?(response.etag)
82
+ return NOT_MODIFIED
83
+ end
84
+ end
85
+
77
86
  @hit += 1
78
87
 
79
88
  return response.dup
@@ -86,7 +95,8 @@ module Async
86
95
 
87
96
  def insert(key, request, response)
88
97
  if @index.size < @limit
89
- if response.body.length < 1024*64
98
+ length = response.body&.length
99
+ if length.nil? or length < 1024*64
90
100
  @index[key] = response
91
101
  end
92
102
  end
@@ -23,7 +23,7 @@
23
23
  module Async
24
24
  module HTTP
25
25
  module Cache
26
- VERSION = "0.1.2"
26
+ VERSION = "0.3.0"
27
27
  end
28
28
  end
29
29
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-http-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-14 00:00:00.000000000 Z
11
+ date: 2020-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: protocol-http
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: 0.14.4
19
+ version: '0.53'
34
20
  type: :runtime
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: 0.14.4
26
+ version: '0.53'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: async-rspec
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +81,7 @@ dependencies:
95
81
  - !ruby/object:Gem::Version
96
82
  version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
- name: rake
84
+ name: bake-bundler
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
87
  - - ">="
@@ -108,7 +94,7 @@ dependencies:
108
94
  - - ">="
109
95
  - !ruby/object:Gem::Version
110
96
  version: '0'
111
- description:
97
+ description:
112
98
  email:
113
99
  - samuel.williams@oriontransfer.co.nz
114
100
  executables: []
@@ -120,7 +106,6 @@ files:
120
106
  - ".rspec"
121
107
  - Gemfile
122
108
  - README.md
123
- - Rakefile
124
109
  - async-http-cache.gemspec
125
110
  - lib/async/http/cache.rb
126
111
  - lib/async/http/cache/body.rb
@@ -134,7 +119,7 @@ homepage: https://github.com/socketry/async-http-cache
134
119
  licenses:
135
120
  - MIT
136
121
  metadata: {}
137
- post_install_message:
122
+ post_install_message:
138
123
  rdoc_options: []
139
124
  require_paths:
140
125
  - lib
@@ -149,8 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
134
  - !ruby/object:Gem::Version
150
135
  version: '0'
151
136
  requirements: []
152
- rubygems_version: 3.0.6
153
- signing_key:
137
+ rubygems_version: 3.1.2
138
+ signing_key:
154
139
  specification_version: 4
155
140
  summary: Standard-compliant cache for async-http.
156
141
  test_files: []
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec