async-http-cache 0.1.5 → 0.2.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: cc8e38cbf05084350cc6d91e0f6f35cd612f14d3554b720c48ebb03abf3331ae
4
- data.tar.gz: 05b44c8cbc06f3ff26b9f9b7d2fa8643278d7067adb902d8ce4b11726f23f6a6
3
+ metadata.gz: c0819bedaf18e394d19ae5ac3e53fa5eb418f2166252b4a578c6087a3a5b4704
4
+ data.tar.gz: b9d593ee6e8ecef570603c486b04fd52aaaec9c227abe73118cfa0463a54c58d
5
5
  SHA512:
6
- metadata.gz: cd3729f3a2877372c7df040dd1798d6ef55e44688dbd9d8ae4d40e9ee4a0d4eba37d1cc53b0c6d512d3c1277bd9dc043b423c5a21b54d4085da827cf1481c753
7
- data.tar.gz: e3a1951d11b75c3da93f585bcb8cb4254e93d16d73282a9b1f713d4208c28de2bbb991b3305516db7cb47c2b6cd0df343d5116cec962792a73d94e9f8fe09f87
6
+ metadata.gz: fd1bf4dc1595e38f251094969895398b4b335380bb654cd7449197932f657f0aaa3d0f849d8f58846b1ee23ff317f5a6c14359c458e5c25c34630f521af4a620
7
+ data.tar.gz: dbdc2267bb3a61ce7e376bb4f76c37c81913b41a48b98eb220856b1b2352aac3e00a63191125404318c0880a8e83d7ef10a49eb8bdfc85ae87f2c2e047f864ed
@@ -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"
24
+ spec.add_dependency "async-http", "~> 0.51"
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
@@ -22,37 +22,48 @@
22
22
 
23
23
  require 'protocol/http/body/rewindable'
24
24
  require 'protocol/http/body/streamable'
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
31
+ TRAILERS = 'trailers'
32
+ ETAG = 'etag'
33
+
34
+ def self.wrap(response, &block)
35
+ if body = response.body
32
36
  if body.empty?
33
37
  # A body that is empty? at the outset, is immutable. This generally only applies to HEAD requests.
34
- yield message, body
38
+ yield response, body
35
39
  else
36
- # Create a rewindable body wrapping the message body:
37
- rewindable = ::Protocol::HTTP::Body::Rewindable.new(body)
40
+ # Insert a rewindable body so that we can cache the response body:
41
+ rewindable = ::Protocol::HTTP::Body::Rewindable.wrap(response)
38
42
 
39
- # Set the message body to the rewindable body:
40
- message.body = rewindable
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
41
52
 
42
- # Wrap the message with the callback:
43
- ::Protocol::HTTP::Body::Streamable.wrap(message) do |error|
53
+ # Wrap the response with the callback:
54
+ ::Protocol::HTTP::Body::Streamable.wrap(response) do |error|
44
55
  if error
45
56
  Async.logger.error(self) {error}
46
57
  else
47
- yield message, rewindable.buffered
58
+ yield response, rewindable.buffered
48
59
  end
49
60
  end
50
61
  end
51
62
  else
52
- yield message, nil
63
+ yield response, nil
53
64
  end
54
65
 
55
- return message
66
+ return response
56
67
  end
57
68
  end
58
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
@@ -23,7 +23,7 @@
23
23
  module Async
24
24
  module HTTP
25
25
  module Cache
26
- VERSION = "0.1.5"
26
+ VERSION = "0.2.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.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-24 00:00:00.000000000 Z
11
+ date: 2020-04-08 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'
19
+ version: '0.51'
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'
26
+ version: '0.51'
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
  - - ">="
@@ -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
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