async-http-cache 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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