http 0.7.4 → 0.8.0.pre

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +5 -2
  4. data/CHANGES.md +24 -7
  5. data/CONTRIBUTING.md +25 -0
  6. data/Gemfile +24 -22
  7. data/Guardfile +2 -2
  8. data/README.md +34 -4
  9. data/Rakefile +7 -7
  10. data/examples/parallel_requests_with_celluloid.rb +2 -2
  11. data/http.gemspec +12 -12
  12. data/lib/http.rb +11 -10
  13. data/lib/http/cache.rb +146 -0
  14. data/lib/http/cache/headers.rb +100 -0
  15. data/lib/http/cache/null_cache.rb +13 -0
  16. data/lib/http/chainable.rb +14 -3
  17. data/lib/http/client.rb +64 -80
  18. data/lib/http/connection.rb +139 -0
  19. data/lib/http/content_type.rb +2 -2
  20. data/lib/http/errors.rb +7 -1
  21. data/lib/http/headers.rb +21 -8
  22. data/lib/http/headers/mixin.rb +1 -1
  23. data/lib/http/mime_type.rb +2 -2
  24. data/lib/http/mime_type/adapter.rb +2 -2
  25. data/lib/http/mime_type/json.rb +4 -4
  26. data/lib/http/options.rb +65 -74
  27. data/lib/http/redirector.rb +3 -3
  28. data/lib/http/request.rb +20 -13
  29. data/lib/http/request/caching.rb +95 -0
  30. data/lib/http/request/writer.rb +5 -5
  31. data/lib/http/response.rb +15 -9
  32. data/lib/http/response/body.rb +21 -8
  33. data/lib/http/response/caching.rb +142 -0
  34. data/lib/http/response/io_body.rb +63 -0
  35. data/lib/http/response/parser.rb +1 -1
  36. data/lib/http/response/status.rb +4 -12
  37. data/lib/http/response/status/reasons.rb +53 -53
  38. data/lib/http/response/string_body.rb +53 -0
  39. data/lib/http/version.rb +1 -1
  40. data/spec/lib/http/cache/headers_spec.rb +77 -0
  41. data/spec/lib/http/cache_spec.rb +182 -0
  42. data/spec/lib/http/client_spec.rb +123 -95
  43. data/spec/lib/http/content_type_spec.rb +25 -25
  44. data/spec/lib/http/headers/mixin_spec.rb +8 -8
  45. data/spec/lib/http/headers_spec.rb +213 -173
  46. data/spec/lib/http/options/body_spec.rb +5 -5
  47. data/spec/lib/http/options/form_spec.rb +3 -3
  48. data/spec/lib/http/options/headers_spec.rb +7 -7
  49. data/spec/lib/http/options/json_spec.rb +3 -3
  50. data/spec/lib/http/options/merge_spec.rb +26 -22
  51. data/spec/lib/http/options/new_spec.rb +10 -10
  52. data/spec/lib/http/options/proxy_spec.rb +8 -8
  53. data/spec/lib/http/options_spec.rb +2 -2
  54. data/spec/lib/http/redirector_spec.rb +32 -32
  55. data/spec/lib/http/request/caching_spec.rb +133 -0
  56. data/spec/lib/http/request/writer_spec.rb +26 -26
  57. data/spec/lib/http/request_spec.rb +63 -58
  58. data/spec/lib/http/response/body_spec.rb +13 -13
  59. data/spec/lib/http/response/caching_spec.rb +201 -0
  60. data/spec/lib/http/response/io_body_spec.rb +35 -0
  61. data/spec/lib/http/response/status_spec.rb +25 -25
  62. data/spec/lib/http/response/string_body_spec.rb +35 -0
  63. data/spec/lib/http/response_spec.rb +64 -45
  64. data/spec/lib/http_spec.rb +103 -76
  65. data/spec/spec_helper.rb +10 -12
  66. data/spec/support/connection_reuse_shared.rb +100 -0
  67. data/spec/support/create_certs.rb +12 -12
  68. data/spec/support/dummy_server.rb +11 -11
  69. data/spec/support/dummy_server/servlet.rb +43 -31
  70. data/spec/support/proxy_server.rb +31 -25
  71. metadata +57 -8
  72. data/spec/support/example_server.rb +0 -30
  73. data/spec/support/example_server/servlet.rb +0 -102
@@ -2,84 +2,84 @@
2
2
 
3
3
  RSpec.describe HTTP::Request::Writer do
4
4
  let(:io) { StringIO.new }
5
- let(:body) { '' }
5
+ let(:body) { "" }
6
6
  let(:headers) { HTTP::Headers.new }
7
- let(:headerstart) { 'GET /test HTTP/1.1' }
7
+ let(:headerstart) { "GET /test HTTP/1.1" }
8
8
 
9
9
  subject(:writer) { described_class.new(io, body, headers, headerstart) }
10
10
 
11
- describe '#initalize' do
12
- context 'when body is nil' do
11
+ describe "#initalize" do
12
+ context "when body is nil" do
13
13
  let(:body) { nil }
14
14
 
15
- it 'does not raise an error' do
15
+ it "does not raise an error" do
16
16
  expect { writer }.not_to raise_error
17
17
  end
18
18
  end
19
19
 
20
- context 'when body is a string' do
21
- let(:body) { 'string body' }
20
+ context "when body is a string" do
21
+ let(:body) { "string body" }
22
22
 
23
- it 'does not raise an error' do
23
+ it "does not raise an error" do
24
24
  expect { writer }.not_to raise_error
25
25
  end
26
26
  end
27
27
 
28
- context 'when body is an Enumerable' do
28
+ context "when body is an Enumerable" do
29
29
  let(:body) { %w(bees cows) }
30
30
 
31
- it 'does not raise an error' do
31
+ it "does not raise an error" do
32
32
  expect { writer }.not_to raise_error
33
33
  end
34
34
  end
35
35
 
36
- context 'when body is not string, enumerable or nil' do
36
+ context "when body is not string, enumerable or nil" do
37
37
  let(:body) { 123 }
38
38
 
39
- it 'raises an error' do
39
+ it "raises an error" do
40
40
  expect { writer }.to raise_error
41
41
  end
42
42
  end
43
43
  end
44
44
 
45
- describe '#stream' do
46
- context 'when body is Enumerable' do
45
+ describe "#stream" do
46
+ context "when body is Enumerable" do
47
47
  let(:body) { %w(bees cows) }
48
- let(:headers) { HTTP::Headers.coerce 'Transfer-Encoding' => 'chunked' }
48
+ let(:headers) { HTTP::Headers.coerce "Transfer-Encoding" => "chunked" }
49
49
 
50
- it 'writes a chunked request from an Enumerable correctly' do
50
+ it "writes a chunked request from an Enumerable correctly" do
51
51
  writer.stream
52
52
  expect(io.string).to end_with "\r\n4\r\nbees\r\n4\r\ncows\r\n0\r\n\r\n"
53
53
  end
54
54
 
55
- it 'writes Transfer-Encoding header only once' do
55
+ it "writes Transfer-Encoding header only once" do
56
56
  writer.stream
57
57
  expect(io.string).to start_with "#{headerstart}\r\nTransfer-Encoding: chunked\r\n\r\n"
58
58
  end
59
59
 
60
- context 'when Transfer-Encoding not set' do
60
+ context "when Transfer-Encoding not set" do
61
61
  let(:headers) { HTTP::Headers.new }
62
62
  specify { expect { writer.stream }.to raise_error }
63
63
  end
64
64
 
65
- context 'when Transfer-Encoding is not chunked' do
66
- let(:headers) { HTTP::Headers.coerce 'Transfer-Encoding' => 'gzip' }
65
+ context "when Transfer-Encoding is not chunked" do
66
+ let(:headers) { HTTP::Headers.coerce "Transfer-Encoding" => "gzip" }
67
67
  specify { expect { writer.stream }.to raise_error }
68
68
  end
69
69
  end
70
70
 
71
- context 'when body is a unicode String' do
72
- let(:body) { 'Привет, мир!' }
71
+ context "when body is a unicode String" do
72
+ let(:body) { "Привет, мир!" }
73
73
 
74
- it 'properly calculates Content-Length if needed' do
74
+ it "properly calculates Content-Length if needed" do
75
75
  writer.stream
76
76
  expect(io.string).to start_with "#{headerstart}\r\nContent-Length: 21\r\n\r\n"
77
77
  end
78
78
 
79
- context 'when Content-Length explicitly set' do
80
- let(:headers) { HTTP::Headers.coerce 'Content-Length' => 12 }
79
+ context "when Content-Length explicitly set" do
80
+ let(:headers) { HTTP::Headers.coerce "Content-Length" => 12 }
81
81
 
82
- it 'keeps given value' do
82
+ it "keeps given value" do
83
83
  writer.stream
84
84
  expect(io.string).to start_with "#{headerstart}\r\nContent-Length: 12\r\n\r\n"
85
85
  end
@@ -1,141 +1,146 @@
1
1
  RSpec.describe HTTP::Request do
2
- let(:headers) { {:accept => 'text/html'} }
3
- let(:request_uri) { 'http://example.com/' }
2
+ let(:headers) { {:accept => "text/html"} }
3
+ let(:request_uri) { "http://example.com/" }
4
4
 
5
5
  subject(:request) { HTTP::Request.new(:get, request_uri, headers) }
6
6
 
7
- it 'includes HTTP::Headers::Mixin' do
7
+ it "includes HTTP::Headers::Mixin" do
8
8
  expect(described_class).to include HTTP::Headers::Mixin
9
9
  end
10
10
 
11
- it 'requires URI to have scheme part' do
12
- expect { HTTP::Request.new(:get, 'example.com/') }.to \
11
+ it "requires URI to have scheme part" do
12
+ expect { HTTP::Request.new(:get, "example.com/") }.to \
13
13
  raise_error(HTTP::Request::UnsupportedSchemeError)
14
14
  end
15
15
 
16
- it 'provides a #scheme accessor' do
16
+ it "provides a #scheme accessor" do
17
17
  expect(request.scheme).to eq(:http)
18
18
  end
19
19
 
20
- it 'provides a #verb accessor' do
20
+ it "provides a #verb accessor" do
21
21
  expect(subject.verb).to eq(:get)
22
22
  end
23
23
 
24
- it 'provides a #__method__ method that outputs a deprecation warning and delegates to Object#method' do
24
+ it "provides a #__method__ method that outputs a deprecation warning and delegates to Object#method" do
25
25
  warning = capture_warning do
26
26
  expect(subject.__method__(:verb)).to eq(subject.method(:verb))
27
27
  end
28
28
  expect(warning).to match(/\[DEPRECATION\] HTTP::Request#__method__ is deprecated\. Use #method instead\.$/)
29
29
  end
30
30
 
31
- it 'sets given headers' do
32
- expect(subject['Accept']).to eq('text/html')
31
+ it "sets given headers" do
32
+ expect(subject["Accept"]).to eq("text/html")
33
33
  end
34
34
 
35
- describe 'Host header' do
36
- subject { request['Host'] }
35
+ describe "Host header" do
36
+ subject { request["Host"] }
37
37
 
38
- context 'was not given' do
39
- it { is_expected.to eq 'example.com' }
38
+ context "was not given" do
39
+ it { is_expected.to eq "example.com" }
40
40
 
41
- context 'and request URI has non-standard port' do
42
- let(:request_uri) { 'http://example.com:3000/' }
43
- it { is_expected.to eq 'example.com:3000' }
41
+ context "and request URI has non-standard port" do
42
+ let(:request_uri) { "http://example.com:3000/" }
43
+ it { is_expected.to eq "example.com:3000" }
44
44
  end
45
45
  end
46
46
 
47
- context 'was explicitly given' do
48
- before { headers[:host] = 'github.com' }
49
- it { is_expected.to eq 'github.com' }
47
+ context "was explicitly given" do
48
+ before { headers[:host] = "github.com" }
49
+ it { is_expected.to eq "github.com" }
50
50
  end
51
51
  end
52
52
 
53
- describe 'User-Agent header' do
54
- subject { request['User-Agent'] }
53
+ describe "User-Agent header" do
54
+ subject { request["User-Agent"] }
55
55
 
56
- context 'was not given' do
56
+ context "was not given" do
57
57
  it { is_expected.to eq HTTP::Request::USER_AGENT }
58
58
  end
59
59
 
60
- context 'was explicitly given' do
61
- before { headers[:user_agent] = 'MrCrawly/123' }
62
- it { is_expected.to eq 'MrCrawly/123' }
60
+ context "was explicitly given" do
61
+ before { headers[:user_agent] = "MrCrawly/123" }
62
+ it { is_expected.to eq "MrCrawly/123" }
63
63
  end
64
64
  end
65
65
 
66
- describe '#redirect' do
67
- let(:headers) { {:accept => 'text/html'} }
68
- let(:proxy) { {:proxy_username => 'douglas', :proxy_password => 'adams'} }
69
- let(:body) { 'The Ultimate Question' }
70
- let(:request) { HTTP::Request.new(:post, 'http://example.com/', headers, proxy, body) }
66
+ describe "#redirect" do
67
+ let(:headers) { {:accept => "text/html"} }
68
+ let(:proxy) { {:proxy_username => "douglas", :proxy_password => "adams"} }
69
+ let(:body) { "The Ultimate Question" }
70
+ let(:request) { HTTP::Request.new(:post, "http://example.com/", headers, proxy, body) }
71
71
 
72
- subject(:redirected) { request.redirect 'http://blog.example.com/' }
72
+ subject(:redirected) { request.redirect "http://blog.example.com/" }
73
73
 
74
- its(:uri) { is_expected.to eq URI.parse 'http://blog.example.com/' }
74
+ its(:uri) { is_expected.to eq URI.parse "http://blog.example.com/" }
75
75
 
76
76
  its(:verb) { is_expected.to eq request.verb }
77
77
  its(:body) { is_expected.to eq request.body }
78
78
  its(:proxy) { is_expected.to eq request.proxy }
79
79
 
80
- it 'presets new Host header' do
81
- expect(redirected['Host']).to eq 'blog.example.com'
80
+ it "presets new Host header" do
81
+ expect(redirected["Host"]).to eq "blog.example.com"
82
82
  end
83
83
 
84
- context 'with schema-less absolute URL given' do
85
- subject(:redirected) { request.redirect '//another.example.com/blog' }
84
+ context "with schema-less absolute URL given" do
85
+ subject(:redirected) { request.redirect "//another.example.com/blog" }
86
86
 
87
- its(:uri) { is_expected.to eq URI.parse 'http://another.example.com/blog' }
87
+ its(:uri) { is_expected.to eq URI.parse "http://another.example.com/blog" }
88
88
 
89
89
  its(:verb) { is_expected.to eq request.verb }
90
90
  its(:body) { is_expected.to eq request.body }
91
91
  its(:proxy) { is_expected.to eq request.proxy }
92
92
 
93
- it 'presets new Host header' do
94
- expect(redirected['Host']).to eq 'another.example.com'
93
+ it "presets new Host header" do
94
+ expect(redirected["Host"]).to eq "another.example.com"
95
95
  end
96
96
  end
97
97
 
98
- context 'with relative URL given' do
99
- subject(:redirected) { request.redirect '/blog' }
98
+ context "with relative URL given" do
99
+ subject(:redirected) { request.redirect "/blog" }
100
100
 
101
- its(:uri) { is_expected.to eq URI.parse 'http://example.com/blog' }
101
+ its(:uri) { is_expected.to eq URI.parse "http://example.com/blog" }
102
102
 
103
103
  its(:verb) { is_expected.to eq request.verb }
104
104
  its(:body) { is_expected.to eq request.body }
105
105
  its(:proxy) { is_expected.to eq request.proxy }
106
106
 
107
- it 'keeps Host header' do
108
- expect(redirected['Host']).to eq 'example.com'
107
+ it "keeps Host header" do
108
+ expect(redirected["Host"]).to eq "example.com"
109
109
  end
110
110
 
111
- context 'with original URI having non-standard port' do
112
- let(:request) { HTTP::Request.new(:post, 'http://example.com:8080/', headers, proxy, body) }
113
- its(:uri) { is_expected.to eq URI.parse 'http://example.com:8080/blog' }
111
+ context "with original URI having non-standard port" do
112
+ let(:request) { HTTP::Request.new(:post, "http://example.com:8080/", headers, proxy, body) }
113
+ its(:uri) { is_expected.to eq URI.parse "http://example.com:8080/blog" }
114
114
  end
115
115
  end
116
116
 
117
- context 'with relative URL that misses leading slash given' do
118
- subject(:redirected) { request.redirect 'blog' }
117
+ context "with relative URL that misses leading slash given" do
118
+ subject(:redirected) { request.redirect "blog" }
119
119
 
120
- its(:uri) { is_expected.to eq URI.parse 'http://example.com/blog' }
120
+ its(:uri) { is_expected.to eq URI.parse "http://example.com/blog" }
121
121
 
122
122
  its(:verb) { is_expected.to eq request.verb }
123
123
  its(:body) { is_expected.to eq request.body }
124
124
  its(:proxy) { is_expected.to eq request.proxy }
125
125
 
126
- it 'keeps Host header' do
127
- expect(redirected['Host']).to eq 'example.com'
126
+ it "keeps Host header" do
127
+ expect(redirected["Host"]).to eq "example.com"
128
128
  end
129
129
 
130
- context 'with original URI having non-standard port' do
131
- let(:request) { HTTP::Request.new(:post, 'http://example.com:8080/', headers, proxy, body) }
132
- its(:uri) { is_expected.to eq URI.parse 'http://example.com:8080/blog' }
130
+ context "with original URI having non-standard port" do
131
+ let(:request) { HTTP::Request.new(:post, "http://example.com:8080/", headers, proxy, body) }
132
+ its(:uri) { is_expected.to eq URI.parse "http://example.com:8080/blog" }
133
133
  end
134
134
  end
135
135
 
136
- context 'with new verb given' do
137
- subject { request.redirect 'http://blog.example.com/', :get }
136
+ context "with new verb given" do
137
+ subject { request.redirect "http://blog.example.com/", :get }
138
138
  its(:verb) { is_expected.to be :get }
139
139
  end
140
140
  end
141
+
142
+ describe "#caching" do
143
+ subject { request.caching }
144
+ it { is_expected.to be_a HTTP::Request::Caching }
145
+ end
141
146
  end
@@ -1,37 +1,37 @@
1
1
  RSpec.describe HTTP::Response::Body do
2
- let(:client) { double }
3
- let(:chunks) { ['Hello, ', 'World!'] }
2
+ let(:client) { double(:sequence_id => 0) }
3
+ let(:chunks) { ["Hello, ", "World!"] }
4
4
 
5
5
  before { allow(client).to receive(:readpartial) { chunks.shift } }
6
6
 
7
7
  subject(:body) { described_class.new client }
8
8
 
9
- it 'streams bodies from responses' do
10
- expect(subject.to_s).to eq 'Hello, World!'
9
+ it "streams bodies from responses" do
10
+ expect(subject.to_s).to eq "Hello, World!"
11
11
  end
12
12
 
13
- context 'when body empty' do
14
- let(:chunks) { [''] }
13
+ context "when body empty" do
14
+ let(:chunks) { [""] }
15
15
 
16
- it 'returns responds to empty? with true' do
16
+ it "returns responds to empty? with true" do
17
17
  expect(subject).to be_empty
18
18
  end
19
19
  end
20
20
 
21
- describe '#readpartial' do
22
- context 'with size given' do
23
- it 'passes value to underlying client' do
21
+ describe "#readpartial" do
22
+ context "with size given" do
23
+ it "passes value to underlying client" do
24
24
  expect(client).to receive(:readpartial).with(42)
25
25
  body.readpartial 42
26
26
  end
27
27
  end
28
28
 
29
- context 'without size given' do
30
- it 'does not blows up' do
29
+ context "without size given" do
30
+ it "does not blows up" do
31
31
  expect { body.readpartial }.to_not raise_error
32
32
  end
33
33
 
34
- it 'calls underlying client readpartial without specific size' do
34
+ it "calls underlying client readpartial without specific size" do
35
35
  expect(client).to receive(:readpartial).with no_args
36
36
  body.readpartial
37
37
  end
@@ -0,0 +1,201 @@
1
+ RSpec.describe HTTP::Response::Caching do
2
+ let(:cache_control) { "" }
3
+ let(:headers) { {"cache-control" => cache_control} }
4
+ let(:response) { HTTP::Response.new(200, "http/1.1", headers, "") }
5
+
6
+ subject(:caching_response) { described_class.new response }
7
+
8
+ describe "#cache_headers" do
9
+ subject { caching_response.cache_headers }
10
+ it { is_expected.to be_a HTTP::Cache::Headers }
11
+ end
12
+
13
+ it "allows requested_at to be set" do
14
+ subject.requested_at = Time.now
15
+ expect(subject.requested_at).to be_within(1).of(Time.now)
16
+ end
17
+
18
+ it "allows received_at to be set" do
19
+ subject.received_at = Time.now
20
+ expect(subject.received_at).to be_within(1).of(Time.now)
21
+ end
22
+
23
+ describe "basic 200 response w/ private cache control" do
24
+ let(:cache_control) { "private" }
25
+
26
+ it "is cacheable" do
27
+ expect(subject.cacheable?).to be_truthy
28
+ end
29
+
30
+ it "is not stale" do
31
+ expect(subject.stale?).to be_falsy
32
+ end
33
+
34
+ it "is not expired" do
35
+ expect(subject.expired?).to be_falsy
36
+ end
37
+
38
+ it "is expected to be 0 seconds old" do
39
+ expect(subject.current_age).to be_within(1).of(0)
40
+ end
41
+ end
42
+
43
+ describe "basic 200 response w/ public cache control" do
44
+ let(:cache_control) { "public" }
45
+
46
+ it "is cacheable" do
47
+ expect(subject.cacheable?).to be_truthy
48
+ end
49
+
50
+ it "is not stale" do
51
+ expect(subject.stale?).to be_falsy
52
+ end
53
+
54
+ it "is not expired" do
55
+ expect(subject.expired?).to be_falsy
56
+ end
57
+
58
+ it "is expected to be 0 seconds old" do
59
+ expect(subject.current_age).to be_within(1).of(0)
60
+ end
61
+ end
62
+
63
+ describe "basic 200 response w/ no-cache" do
64
+ let(:cache_control) { "no-cache" }
65
+
66
+ it "is not cacheable" do
67
+ expect(subject.cacheable?).to be_falsy
68
+ end
69
+
70
+ it "is not stale" do
71
+ expect(subject.stale?).to be_falsy
72
+ end
73
+
74
+ it "is not expired" do
75
+ expect(subject.expired?).to be_falsy
76
+ end
77
+
78
+ it "is expected to be 0 seconds old" do
79
+ expect(subject.current_age).to be_within(1).of(0)
80
+ end
81
+ end
82
+
83
+ describe "basic 200 response w/ no-store" do
84
+ let(:cache_control) { "no-store" }
85
+
86
+ it "is not cacheable" do
87
+ expect(subject.cacheable?).to be_falsy
88
+ end
89
+
90
+ it "is not stale" do
91
+ expect(subject.stale?).to be_falsy
92
+ end
93
+
94
+ it "is not expired" do
95
+ expect(subject.expired?).to be_falsy
96
+ end
97
+
98
+ it "is expected to be 0 seconds old" do
99
+ expect(subject.current_age).to be_within(1).of(0)
100
+ end
101
+ end
102
+
103
+ describe "basic 200 response w/ max age" do
104
+ let(:cache_control) { "max-age=100" }
105
+
106
+ it "is not cacheable" do
107
+ expect(subject.cacheable?).to be_truthy
108
+ end
109
+
110
+ it "is not stale" do
111
+ expect(subject.stale?).to be_falsy
112
+ end
113
+
114
+ it "is not expired" do
115
+ expect(subject.expired?).to be_falsy
116
+ end
117
+
118
+ it "is expected to be 0 seconds old" do
119
+ expect(subject.current_age).to be_within(1).of(0)
120
+ end
121
+ end
122
+
123
+ describe "basic 200 response w/ public & max age" do
124
+ let(:cache_control) { "public, max-age=100" }
125
+
126
+ it "is not cacheable" do
127
+ expect(subject.cacheable?).to be_truthy
128
+ end
129
+
130
+ it "is not stale" do
131
+ expect(subject.stale?).to be_falsy
132
+ end
133
+
134
+ it "is not expired" do
135
+ expect(subject.expired?).to be_falsy
136
+ end
137
+
138
+ it "is expected to be 0 seconds old" do
139
+ expect(subject.current_age).to be_within(1).of(0)
140
+ end
141
+
142
+ context "with age of max-age + 1 seconds" do
143
+ let(:headers) { {"cache-control" => cache_control, "age" => "101"} }
144
+
145
+ it "is stale" do
146
+ expect(subject.stale?).to be_truthy
147
+ end
148
+
149
+ it "is expired" do
150
+ expect(subject.expired?).to be_truthy
151
+ end
152
+
153
+ it "is expected to be max-age + 1 seconds old" do
154
+ expect(subject.current_age).to be_within(1).of(101)
155
+ end
156
+ end
157
+
158
+ context "after max-age + 1 seconds" do
159
+ before do
160
+ subject.received_at = subject.requested_at = (Time.now - 101)
161
+ end
162
+
163
+ it "is stale" do
164
+ expect(subject.stale?).to be_truthy
165
+ end
166
+
167
+ it "is expired" do
168
+ expect(subject.expired?).to be_truthy
169
+ end
170
+
171
+ it "is expected to be max-age + 1 seconds old" do
172
+ expect(subject.current_age).to be_within(1).of(101)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "basic 400 response " do
178
+ let(:response) { HTTP::Response.new(400, "http/1.1", {}, "") }
179
+
180
+ it "is not cacheable" do
181
+ expect(subject.cacheable?).to be_falsy
182
+ end
183
+
184
+ it "is not stale" do
185
+ expect(subject.stale?).to be_falsy
186
+ end
187
+
188
+ it "is not expired" do
189
+ expect(subject.expired?).to be_falsy
190
+ end
191
+
192
+ it "is expected to be 0 seconds old" do
193
+ expect(subject.current_age).to be_within(1).of(0)
194
+ end
195
+ end
196
+
197
+ describe "#caching" do
198
+ subject(:caching_response) { response.caching }
199
+ it { is_expected.to be_kind_of described_class }
200
+ end
201
+ end