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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +5 -2
- data/CHANGES.md +24 -7
- data/CONTRIBUTING.md +25 -0
- data/Gemfile +24 -22
- data/Guardfile +2 -2
- data/README.md +34 -4
- data/Rakefile +7 -7
- data/examples/parallel_requests_with_celluloid.rb +2 -2
- data/http.gemspec +12 -12
- data/lib/http.rb +11 -10
- data/lib/http/cache.rb +146 -0
- data/lib/http/cache/headers.rb +100 -0
- data/lib/http/cache/null_cache.rb +13 -0
- data/lib/http/chainable.rb +14 -3
- data/lib/http/client.rb +64 -80
- data/lib/http/connection.rb +139 -0
- data/lib/http/content_type.rb +2 -2
- data/lib/http/errors.rb +7 -1
- data/lib/http/headers.rb +21 -8
- data/lib/http/headers/mixin.rb +1 -1
- data/lib/http/mime_type.rb +2 -2
- data/lib/http/mime_type/adapter.rb +2 -2
- data/lib/http/mime_type/json.rb +4 -4
- data/lib/http/options.rb +65 -74
- data/lib/http/redirector.rb +3 -3
- data/lib/http/request.rb +20 -13
- data/lib/http/request/caching.rb +95 -0
- data/lib/http/request/writer.rb +5 -5
- data/lib/http/response.rb +15 -9
- data/lib/http/response/body.rb +21 -8
- data/lib/http/response/caching.rb +142 -0
- data/lib/http/response/io_body.rb +63 -0
- data/lib/http/response/parser.rb +1 -1
- data/lib/http/response/status.rb +4 -12
- data/lib/http/response/status/reasons.rb +53 -53
- data/lib/http/response/string_body.rb +53 -0
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/cache/headers_spec.rb +77 -0
- data/spec/lib/http/cache_spec.rb +182 -0
- data/spec/lib/http/client_spec.rb +123 -95
- data/spec/lib/http/content_type_spec.rb +25 -25
- data/spec/lib/http/headers/mixin_spec.rb +8 -8
- data/spec/lib/http/headers_spec.rb +213 -173
- data/spec/lib/http/options/body_spec.rb +5 -5
- data/spec/lib/http/options/form_spec.rb +3 -3
- data/spec/lib/http/options/headers_spec.rb +7 -7
- data/spec/lib/http/options/json_spec.rb +3 -3
- data/spec/lib/http/options/merge_spec.rb +26 -22
- data/spec/lib/http/options/new_spec.rb +10 -10
- data/spec/lib/http/options/proxy_spec.rb +8 -8
- data/spec/lib/http/options_spec.rb +2 -2
- data/spec/lib/http/redirector_spec.rb +32 -32
- data/spec/lib/http/request/caching_spec.rb +133 -0
- data/spec/lib/http/request/writer_spec.rb +26 -26
- data/spec/lib/http/request_spec.rb +63 -58
- data/spec/lib/http/response/body_spec.rb +13 -13
- data/spec/lib/http/response/caching_spec.rb +201 -0
- data/spec/lib/http/response/io_body_spec.rb +35 -0
- data/spec/lib/http/response/status_spec.rb +25 -25
- data/spec/lib/http/response/string_body_spec.rb +35 -0
- data/spec/lib/http/response_spec.rb +64 -45
- data/spec/lib/http_spec.rb +103 -76
- data/spec/spec_helper.rb +10 -12
- data/spec/support/connection_reuse_shared.rb +100 -0
- data/spec/support/create_certs.rb +12 -12
- data/spec/support/dummy_server.rb +11 -11
- data/spec/support/dummy_server/servlet.rb +43 -31
- data/spec/support/proxy_server.rb +31 -25
- metadata +57 -8
- data/spec/support/example_server.rb +0 -30
- 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) {
|
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
|
12
|
-
context
|
11
|
+
describe "#initalize" do
|
12
|
+
context "when body is nil" do
|
13
13
|
let(:body) { nil }
|
14
14
|
|
15
|
-
it
|
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
|
21
|
-
let(:body) {
|
20
|
+
context "when body is a string" do
|
21
|
+
let(:body) { "string body" }
|
22
22
|
|
23
|
-
it
|
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
|
28
|
+
context "when body is an Enumerable" do
|
29
29
|
let(:body) { %w(bees cows) }
|
30
30
|
|
31
|
-
it
|
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
|
36
|
+
context "when body is not string, enumerable or nil" do
|
37
37
|
let(:body) { 123 }
|
38
38
|
|
39
|
-
it
|
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
|
46
|
-
context
|
45
|
+
describe "#stream" do
|
46
|
+
context "when body is Enumerable" do
|
47
47
|
let(:body) { %w(bees cows) }
|
48
|
-
let(:headers) { HTTP::Headers.coerce
|
48
|
+
let(:headers) { HTTP::Headers.coerce "Transfer-Encoding" => "chunked" }
|
49
49
|
|
50
|
-
it
|
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
|
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
|
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
|
66
|
-
let(:headers) { HTTP::Headers.coerce
|
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
|
72
|
-
let(:body) {
|
71
|
+
context "when body is a unicode String" do
|
72
|
+
let(:body) { "Привет, мир!" }
|
73
73
|
|
74
|
-
it
|
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
|
80
|
-
let(:headers) { HTTP::Headers.coerce
|
79
|
+
context "when Content-Length explicitly set" do
|
80
|
+
let(:headers) { HTTP::Headers.coerce "Content-Length" => 12 }
|
81
81
|
|
82
|
-
it
|
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 =>
|
3
|
-
let(:request_uri) {
|
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
|
7
|
+
it "includes HTTP::Headers::Mixin" do
|
8
8
|
expect(described_class).to include HTTP::Headers::Mixin
|
9
9
|
end
|
10
10
|
|
11
|
-
it
|
12
|
-
expect { HTTP::Request.new(:get,
|
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
|
16
|
+
it "provides a #scheme accessor" do
|
17
17
|
expect(request.scheme).to eq(:http)
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
20
|
+
it "provides a #verb accessor" do
|
21
21
|
expect(subject.verb).to eq(:get)
|
22
22
|
end
|
23
23
|
|
24
|
-
it
|
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
|
32
|
-
expect(subject[
|
31
|
+
it "sets given headers" do
|
32
|
+
expect(subject["Accept"]).to eq("text/html")
|
33
33
|
end
|
34
34
|
|
35
|
-
describe
|
36
|
-
subject { request[
|
35
|
+
describe "Host header" do
|
36
|
+
subject { request["Host"] }
|
37
37
|
|
38
|
-
context
|
39
|
-
it { is_expected.to eq
|
38
|
+
context "was not given" do
|
39
|
+
it { is_expected.to eq "example.com" }
|
40
40
|
|
41
|
-
context
|
42
|
-
let(:request_uri) {
|
43
|
-
it { is_expected.to eq
|
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
|
48
|
-
before { headers[:host] =
|
49
|
-
it { is_expected.to eq
|
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
|
54
|
-
subject { request[
|
53
|
+
describe "User-Agent header" do
|
54
|
+
subject { request["User-Agent"] }
|
55
55
|
|
56
|
-
context
|
56
|
+
context "was not given" do
|
57
57
|
it { is_expected.to eq HTTP::Request::USER_AGENT }
|
58
58
|
end
|
59
59
|
|
60
|
-
context
|
61
|
-
before { headers[:user_agent] =
|
62
|
-
it { is_expected.to eq
|
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
|
67
|
-
let(:headers) { {:accept =>
|
68
|
-
let(:proxy) { {:proxy_username =>
|
69
|
-
let(:body) {
|
70
|
-
let(:request) { HTTP::Request.new(:post,
|
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
|
72
|
+
subject(:redirected) { request.redirect "http://blog.example.com/" }
|
73
73
|
|
74
|
-
its(:uri) { is_expected.to eq URI.parse
|
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
|
81
|
-
expect(redirected[
|
80
|
+
it "presets new Host header" do
|
81
|
+
expect(redirected["Host"]).to eq "blog.example.com"
|
82
82
|
end
|
83
83
|
|
84
|
-
context
|
85
|
-
subject(:redirected) { request.redirect
|
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
|
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
|
94
|
-
expect(redirected[
|
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
|
99
|
-
subject(:redirected) { request.redirect
|
98
|
+
context "with relative URL given" do
|
99
|
+
subject(:redirected) { request.redirect "/blog" }
|
100
100
|
|
101
|
-
its(:uri) { is_expected.to eq URI.parse
|
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
|
108
|
-
expect(redirected[
|
107
|
+
it "keeps Host header" do
|
108
|
+
expect(redirected["Host"]).to eq "example.com"
|
109
109
|
end
|
110
110
|
|
111
|
-
context
|
112
|
-
let(:request) { HTTP::Request.new(:post,
|
113
|
-
its(:uri) { is_expected.to eq URI.parse
|
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
|
118
|
-
subject(:redirected) { request.redirect
|
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
|
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
|
127
|
-
expect(redirected[
|
126
|
+
it "keeps Host header" do
|
127
|
+
expect(redirected["Host"]).to eq "example.com"
|
128
128
|
end
|
129
129
|
|
130
|
-
context
|
131
|
-
let(:request) { HTTP::Request.new(:post,
|
132
|
-
its(:uri) { is_expected.to eq URI.parse
|
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
|
137
|
-
subject { request.redirect
|
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) { [
|
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
|
10
|
-
expect(subject.to_s).to eq
|
9
|
+
it "streams bodies from responses" do
|
10
|
+
expect(subject.to_s).to eq "Hello, World!"
|
11
11
|
end
|
12
12
|
|
13
|
-
context
|
14
|
-
let(:chunks) { [
|
13
|
+
context "when body empty" do
|
14
|
+
let(:chunks) { [""] }
|
15
15
|
|
16
|
-
it
|
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
|
22
|
-
context
|
23
|
-
it
|
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
|
30
|
-
it
|
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
|
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
|