xenon-routing 0.0.4
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 +7 -0
- data/.codeclimate.yml +18 -0
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +20 -0
- data/Guardfile +16 -0
- data/LICENSE +22 -0
- data/README.md +116 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/examples/hello_world/config.ru +3 -0
- data/examples/hello_world/hello_world.rb +27 -0
- data/xenon-http/lib/xenon/auth.rb +63 -0
- data/xenon-http/lib/xenon/errors.rb +5 -0
- data/xenon-http/lib/xenon/etag.rb +48 -0
- data/xenon-http/lib/xenon/headers.rb +112 -0
- data/xenon-http/lib/xenon/headers/accept.rb +34 -0
- data/xenon-http/lib/xenon/headers/accept_charset.rb +59 -0
- data/xenon-http/lib/xenon/headers/accept_encoding.rb +63 -0
- data/xenon-http/lib/xenon/headers/accept_language.rb +59 -0
- data/xenon-http/lib/xenon/headers/authorization.rb +50 -0
- data/xenon-http/lib/xenon/headers/cache_control.rb +56 -0
- data/xenon-http/lib/xenon/headers/content_type.rb +23 -0
- data/xenon-http/lib/xenon/headers/if_match.rb +53 -0
- data/xenon-http/lib/xenon/headers/if_modified_since.rb +22 -0
- data/xenon-http/lib/xenon/headers/if_none_match.rb +53 -0
- data/xenon-http/lib/xenon/headers/if_range.rb +45 -0
- data/xenon-http/lib/xenon/headers/if_unmodified_since.rb +22 -0
- data/xenon-http/lib/xenon/headers/user_agent.rb +65 -0
- data/xenon-http/lib/xenon/headers/www_authenticate.rb +71 -0
- data/xenon-http/lib/xenon/http.rb +7 -0
- data/xenon-http/lib/xenon/http_version.rb +3 -0
- data/xenon-http/lib/xenon/media_type.rb +162 -0
- data/xenon-http/lib/xenon/parsers/basic_rules.rb +86 -0
- data/xenon-http/lib/xenon/parsers/header_rules.rb +60 -0
- data/xenon-http/lib/xenon/parsers/media_type.rb +53 -0
- data/xenon-http/lib/xenon/quoted_string.rb +20 -0
- data/xenon-http/spec/spec_helper.rb +94 -0
- data/xenon-http/spec/xenon/etag_spec.rb +19 -0
- data/xenon-http/spec/xenon/headers/accept_charset_spec.rb +31 -0
- data/xenon-http/spec/xenon/headers/accept_encoding_spec.rb +40 -0
- data/xenon-http/spec/xenon/headers/accept_language_spec.rb +33 -0
- data/xenon-http/spec/xenon/headers/accept_spec.rb +54 -0
- data/xenon-http/spec/xenon/headers/authorization_spec.rb +47 -0
- data/xenon-http/spec/xenon/headers/cache_control_spec.rb +64 -0
- data/xenon-http/spec/xenon/headers/if_match_spec.rb +73 -0
- data/xenon-http/spec/xenon/headers/if_modified_since_spec.rb +19 -0
- data/xenon-http/spec/xenon/headers/if_none_match_spec.rb +79 -0
- data/xenon-http/spec/xenon/headers/if_range_spec.rb +45 -0
- data/xenon-http/spec/xenon/headers/if_unmodified_since_spec.rb +19 -0
- data/xenon-http/spec/xenon/headers/user_agent_spec.rb +67 -0
- data/xenon-http/spec/xenon/headers/www_authenticate_spec.rb +43 -0
- data/xenon-http/spec/xenon/media_type_spec.rb +267 -0
- data/xenon-http/xenon-http.gemspec +25 -0
- data/xenon-routing/lib/xenon/api.rb +118 -0
- data/xenon-routing/lib/xenon/marshallers.rb +48 -0
- data/xenon-routing/lib/xenon/request.rb +40 -0
- data/xenon-routing/lib/xenon/response.rb +29 -0
- data/xenon-routing/lib/xenon/routing.rb +6 -0
- data/xenon-routing/lib/xenon/routing/context.rb +35 -0
- data/xenon-routing/lib/xenon/routing/directives.rb +14 -0
- data/xenon-routing/lib/xenon/routing/header_directives.rb +32 -0
- data/xenon-routing/lib/xenon/routing/method_directives.rb +26 -0
- data/xenon-routing/lib/xenon/routing/param_directives.rb +22 -0
- data/xenon-routing/lib/xenon/routing/path_directives.rb +37 -0
- data/xenon-routing/lib/xenon/routing/route_directives.rb +51 -0
- data/xenon-routing/lib/xenon/routing/security_directives.rb +34 -0
- data/xenon-routing/lib/xenon/routing_version.rb +3 -0
- data/xenon-routing/spec/spec_helper.rb +94 -0
- data/xenon-routing/xenon-routing.gemspec +25 -0
- data/xenon.gemspec +26 -0
- metadata +145 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'xenon/headers/accept'
|
2
|
+
|
3
|
+
describe Xenon::Headers::Accept do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse a basic accept range' do
|
7
|
+
header = Xenon::Headers::Accept.parse('text/plain; q=0.5')
|
8
|
+
expect(header.media_ranges.size).to eq(1)
|
9
|
+
expect(header.media_ranges[0].to_s).to eq('text/plain; q=0.5')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can parse the first example from RFC 7231 § 5.3.2 with the right precedence' do
|
13
|
+
header = Xenon::Headers::Accept.parse('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')
|
14
|
+
expect(header.media_ranges.size).to eq(4)
|
15
|
+
expect(header.media_ranges[0].to_s).to eq('text/html')
|
16
|
+
expect(header.media_ranges[1].to_s).to eq('text/x-c')
|
17
|
+
expect(header.media_ranges[2].to_s).to eq('text/x-dvi; q=0.8')
|
18
|
+
expect(header.media_ranges[3].to_s).to eq('text/plain; q=0.5')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can parse the second example from RFC 7231 § 5.3.2 with the right precedence' do
|
22
|
+
header = Xenon::Headers::Accept.parse('text/*, text/plain, text/plain;format=flowed, */*')
|
23
|
+
expect(header.media_ranges.size).to eq(4)
|
24
|
+
expect(header.media_ranges[0].to_s).to eq('text/plain; format=flowed')
|
25
|
+
expect(header.media_ranges[1].to_s).to eq('text/plain')
|
26
|
+
expect(header.media_ranges[2].to_s).to eq('text/*')
|
27
|
+
expect(header.media_ranges[3].to_s).to eq('*/*')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can parse the third example from RFC 7231 § 5.3.2 with the right precedence' do
|
31
|
+
header = Xenon::Headers::Accept.parse('text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
|
32
|
+
expect(header.media_ranges.size).to eq(5)
|
33
|
+
expect(header.media_ranges[0].to_s).to eq('text/html; level=1')
|
34
|
+
expect(header.media_ranges[1].to_s).to eq('text/html; level=2; q=0.4')
|
35
|
+
expect(header.media_ranges[2].to_s).to eq('text/html; q=0.7')
|
36
|
+
expect(header.media_ranges[3].to_s).to eq('text/*; q=0.3')
|
37
|
+
expect(header.media_ranges[4].to_s).to eq('*/*; q=0.5')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#merge' do
|
42
|
+
it 'can merge two headers with the right precedence' do
|
43
|
+
h1 = Xenon::Headers::Accept.parse('text/plain; q=0.5, text/html')
|
44
|
+
h2 = Xenon::Headers::Accept.parse('text/x-c, text/x-dvi; q=0.8')
|
45
|
+
header = h1.merge(h2)
|
46
|
+
expect(header.media_ranges.size).to eq(4)
|
47
|
+
expect(header.media_ranges[0].to_s).to eq('text/html')
|
48
|
+
expect(header.media_ranges[1].to_s).to eq('text/x-c')
|
49
|
+
expect(header.media_ranges[2].to_s).to eq('text/x-dvi; q=0.8')
|
50
|
+
expect(header.media_ranges[3].to_s).to eq('text/plain; q=0.5')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'xenon/headers/authorization'
|
2
|
+
|
3
|
+
describe Xenon::Headers::Authorization do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse Basic credentials' do
|
7
|
+
header = Xenon::Headers::Authorization.parse('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==')
|
8
|
+
expect(header.credentials.class).to eq(Xenon::BasicCredentials)
|
9
|
+
expect(header.credentials.username).to eq('Aladdin')
|
10
|
+
expect(header.credentials.password).to eq('open sesame')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can parse Digest credentials' do
|
14
|
+
header = Xenon::Headers::Authorization.parse('Digest username="Mufasa"' +
|
15
|
+
', realm="testrealm@host.com"' +
|
16
|
+
', nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"' +
|
17
|
+
', uri="/dir/index.html"' +
|
18
|
+
', qop=auth' +
|
19
|
+
', nc=00000001' +
|
20
|
+
', cnonce="0a4f113b"' +
|
21
|
+
', response="6629fae49393a05397450978507c4ef1"' +
|
22
|
+
', opaque="5ccc069c403ebaf9f0171e9517f40e41"')
|
23
|
+
expect(header.credentials.class).to eq(Xenon::GenericCredentials)
|
24
|
+
expect(header.credentials.scheme).to eq('Digest')
|
25
|
+
expect(header.credentials.token).to eq(nil)
|
26
|
+
expect(header.credentials.params).to eq(
|
27
|
+
'username' => 'Mufasa',
|
28
|
+
'realm' => 'testrealm@host.com',
|
29
|
+
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
|
30
|
+
'uri' => '/dir/index.html',
|
31
|
+
'qop' => 'auth',
|
32
|
+
'nc' => '00000001',
|
33
|
+
'cnonce' => '0a4f113b',
|
34
|
+
'response' => '6629fae49393a05397450978507c4ef1',
|
35
|
+
'opaque' => '5ccc069c403ebaf9f0171e9517f40e41')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can parse Bearer credentials' do
|
39
|
+
header = Xenon::Headers::Authorization.parse('Bearer eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.')
|
40
|
+
expect(header.credentials.class).to eq(Xenon::GenericCredentials)
|
41
|
+
expect(header.credentials.scheme).to eq('Bearer')
|
42
|
+
expect(header.credentials.token).to eq('eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.')
|
43
|
+
expect(header.credentials.params).to eq({})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'xenon/headers/cache_control'
|
2
|
+
|
3
|
+
describe Xenon::Headers::CacheControl do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
['max-age', 'max-stale', 'min-fresh', 's-maxage'].each do |dir|
|
7
|
+
it "can parse the #{dir} directive" do
|
8
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}=5")
|
9
|
+
expect(header.directives.size).to eq(1)
|
10
|
+
expect(header.directives[0].to_s).to eq("#{dir}=5")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can parse the #{dir} directive with a quoted value" do # should not be sent by clients but is permitted
|
14
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}=\"5\"")
|
15
|
+
expect(header.directives.size).to eq(1)
|
16
|
+
expect(header.directives[0].to_s).to eq("#{dir}=5")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
['no-cache', 'no-store', 'no-transform', 'only-if-cached', 'must-revalidate', 'public', 'proxy-revalidate'].each do |dir|
|
21
|
+
it "can parse the #{dir} directive" do
|
22
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}")
|
23
|
+
expect(header.directives.size).to eq(1)
|
24
|
+
expect(header.directives[0].to_s).to eq("#{dir}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can parse the private directive with no field names" do
|
29
|
+
header = Xenon::Headers::CacheControl.parse('private')
|
30
|
+
expect(header.directives.size).to eq(1)
|
31
|
+
expect(header.directives[0].to_s).to eq('private')
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: private directive with field names
|
35
|
+
|
36
|
+
it 'can parse extension directives with quoted string values' do
|
37
|
+
header = Xenon::Headers::CacheControl.parse('ext="hello \"world\""')
|
38
|
+
expect(header.directives.size).to eq(1)
|
39
|
+
expect(header.directives[0].name).to eq('ext')
|
40
|
+
expect(header.directives[0].value).to eq("hello \"world\"")
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'can parse more complex directives' do
|
44
|
+
header = Xenon::Headers::CacheControl.parse('public, max-age=3600, must-revalidate')
|
45
|
+
expect(header.directives.size).to eq(3)
|
46
|
+
expect(header.directives[0].to_s).to eq('public')
|
47
|
+
expect(header.directives[1].to_s).to eq('max-age=3600')
|
48
|
+
expect(header.directives[2].to_s).to eq('must-revalidate')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context '#merge' do
|
53
|
+
it 'can merge two headers and maintain directive order' do
|
54
|
+
h1 = Xenon::Headers::CacheControl.parse('public, max-age=3600')
|
55
|
+
h2 = Xenon::Headers::CacheControl.parse('must-revalidate')
|
56
|
+
header = h1.merge(h2)
|
57
|
+
expect(header.directives.size).to eq(3)
|
58
|
+
expect(header.directives[0].to_s).to eq('public')
|
59
|
+
expect(header.directives[1].to_s).to eq('max-age=3600')
|
60
|
+
expect(header.directives[2].to_s).to eq('must-revalidate')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'xenon/headers/if_match'
|
2
|
+
|
3
|
+
describe Xenon::Headers::IfMatch do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse a single etag' do
|
7
|
+
header = Xenon::Headers::IfMatch.parse('"xyzzy"')
|
8
|
+
expect(header.etags.size).to eq(1)
|
9
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can parse multiple etags' do
|
13
|
+
header = Xenon::Headers::IfMatch.parse('"xyzzy", "r2d2xxxx", "c3piozzzz"')
|
14
|
+
expect(header.etags.size).to eq(3)
|
15
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
16
|
+
expect(header.etags[1]).to eq(Xenon::ETag.new('r2d2xxxx'))
|
17
|
+
expect(header.etags[2]).to eq(Xenon::ETag.new('c3piozzzz'))
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can parse a wildcard header' do
|
21
|
+
header = Xenon::Headers::IfMatch.parse('*')
|
22
|
+
expect(header.etags.size).to eq(0)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#merge' do
|
27
|
+
it 'can merge two headers and maintain etag order' do
|
28
|
+
h1 = Xenon::Headers::IfMatch.parse('"xyzzy", "r2d2xxxx"')
|
29
|
+
h2 = Xenon::Headers::IfMatch.parse('"c3piozzzz"')
|
30
|
+
header = h1.merge(h2)
|
31
|
+
expect(header.etags.size).to eq(3)
|
32
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
33
|
+
expect(header.etags[1]).to eq(Xenon::ETag.new('r2d2xxxx'))
|
34
|
+
expect(header.etags[2]).to eq(Xenon::ETag.new('c3piozzzz'))
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'raises a protocol error when trying to merge into a wildcard header' do
|
38
|
+
h1 = Xenon::Headers::IfMatch.parse('*')
|
39
|
+
h2 = Xenon::Headers::IfMatch.parse('"c3piozzzz"')
|
40
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises a protocol error when trying to merge a wildcard into a header' do
|
44
|
+
h1 = Xenon::Headers::IfMatch.parse('"xyzzy"')
|
45
|
+
h2 = Xenon::Headers::IfMatch.parse('*')
|
46
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises a protocol error when trying to merge two wildcard headers' do
|
50
|
+
h1 = Xenon::Headers::IfMatch.parse('*')
|
51
|
+
h2 = Xenon::Headers::IfMatch.parse('*')
|
52
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context '#to_s' do
|
57
|
+
it 'returns the string representation a single etag' do
|
58
|
+
header = Xenon::Headers::IfMatch.parse('"xyzzy"')
|
59
|
+
expect(header.to_s).to eq('"xyzzy"')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns the string representation of multiple etags' do
|
63
|
+
header = Xenon::Headers::IfMatch.parse('"xyzzy", "r2d2xxxx", "c3piozzzz"')
|
64
|
+
expect(header.to_s).to eq('"xyzzy", "r2d2xxxx", "c3piozzzz"')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns the string representation of a wildcard header' do
|
68
|
+
header = Xenon::Headers::IfMatch.wildcard
|
69
|
+
expect(header.to_s).to eq('*')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'xenon/headers/if_modified_since'
|
2
|
+
|
3
|
+
describe Xenon::Headers::IfModifiedSince do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse an http date' do
|
7
|
+
header = Xenon::Headers::IfModifiedSince.parse('Sat, 29 Oct 1994 19:43:31 GMT')
|
8
|
+
expect(header.date).to eq(Time.utc(1994, 10, 29, 19, 43, 31))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#to_s' do
|
13
|
+
it 'returns the http date format' do
|
14
|
+
header = Xenon::Headers::IfModifiedSince.new(Time.utc(1994, 10, 29, 19, 43, 31))
|
15
|
+
expect(header.to_s).to eq('Sat, 29 Oct 1994 19:43:31 GMT')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'xenon/headers/if_none_match'
|
2
|
+
|
3
|
+
describe Xenon::Headers::IfNoneMatch do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse a single strong etag' do
|
7
|
+
header = Xenon::Headers::IfNoneMatch.parse('"xyzzy"')
|
8
|
+
expect(header.etags.size).to eq(1)
|
9
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can parse a single weak etag' do
|
13
|
+
header = Xenon::Headers::IfNoneMatch.parse('W/"xyzzy"')
|
14
|
+
expect(header.etags.size).to eq(1)
|
15
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy', weak: true))
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can parse multiple etags' do
|
19
|
+
header = Xenon::Headers::IfNoneMatch.parse('"xyzzy", W/"r2d2xxxx", "c3piozzzz"')
|
20
|
+
expect(header.etags.size).to eq(3)
|
21
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
22
|
+
expect(header.etags[1]).to eq(Xenon::ETag.new('r2d2xxxx', weak: true))
|
23
|
+
expect(header.etags[2]).to eq(Xenon::ETag.new('c3piozzzz'))
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can parse a wildcard header' do
|
27
|
+
header = Xenon::Headers::IfNoneMatch.parse('*')
|
28
|
+
expect(header.etags.size).to eq(0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#merge' do
|
33
|
+
it 'can merge two headers and maintain etag order' do
|
34
|
+
h1 = Xenon::Headers::IfNoneMatch.parse('"xyzzy", W/"r2d2xxxx"')
|
35
|
+
h2 = Xenon::Headers::IfNoneMatch.parse('"c3piozzzz"')
|
36
|
+
header = h1.merge(h2)
|
37
|
+
expect(header.etags.size).to eq(3)
|
38
|
+
expect(header.etags[0]).to eq(Xenon::ETag.new('xyzzy'))
|
39
|
+
expect(header.etags[1]).to eq(Xenon::ETag.new('r2d2xxxx', weak: true))
|
40
|
+
expect(header.etags[2]).to eq(Xenon::ETag.new('c3piozzzz'))
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises a protocol error when trying to merge into a wildcard header' do
|
44
|
+
h1 = Xenon::Headers::IfNoneMatch.parse('*')
|
45
|
+
h2 = Xenon::Headers::IfNoneMatch.parse('"c3piozzzz"')
|
46
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises a protocol error when trying to merge a wildcard into a header' do
|
50
|
+
h1 = Xenon::Headers::IfNoneMatch.parse('"xyzzy"')
|
51
|
+
h2 = Xenon::Headers::IfNoneMatch.parse('*')
|
52
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises a protocol error when trying to merge two wildcard headers' do
|
56
|
+
h1 = Xenon::Headers::IfNoneMatch.parse('*')
|
57
|
+
h2 = Xenon::Headers::IfNoneMatch.parse('*')
|
58
|
+
expect { h1.merge(h2) }.to raise_error(Xenon::ProtocolError)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context '#to_s' do
|
63
|
+
it 'returns the string representation a single etag' do
|
64
|
+
header = Xenon::Headers::IfNoneMatch.parse('"xyzzy"')
|
65
|
+
expect(header.to_s).to eq('"xyzzy"')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns the string representation of multiple etags' do
|
69
|
+
header = Xenon::Headers::IfNoneMatch.parse('"xyzzy", W/"r2d2xxxx", "c3piozzzz"')
|
70
|
+
expect(header.to_s).to eq('"xyzzy", W/"r2d2xxxx", "c3piozzzz"')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns the string representation of a wildcard header' do
|
74
|
+
header = Xenon::Headers::IfNoneMatch.wildcard
|
75
|
+
expect(header.to_s).to eq('*')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'xenon/headers/if_range'
|
2
|
+
|
3
|
+
describe Xenon::Headers::IfRange do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse an http date' do
|
7
|
+
header = Xenon::Headers::IfRange.parse('Sat, 29 Oct 1994 19:43:31 GMT')
|
8
|
+
expect(header.date).to eq(Time.utc(1994, 10, 29, 19, 43, 31))
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'can parse an obsolete RFC 850 date' do
|
12
|
+
header = Xenon::Headers::IfRange.parse('Sunday, 06-Nov-94 08:49:37 GMT')
|
13
|
+
expect(header.date).to eq(Time.utc(1994, 11, 6, 8, 49, 37))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can parse an obsolete asctime date' do
|
17
|
+
header = Xenon::Headers::IfRange.parse('Sun Nov 6 08:49:37 1994')
|
18
|
+
expect(header.date).to eq(Time.utc(1994, 11, 6, 8, 49, 37))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can parse a strong etag' do
|
22
|
+
header = Xenon::Headers::IfRange.parse('"xyzzy"')
|
23
|
+
expect(header.etag).to_not be_nil
|
24
|
+
expect(header.etag.opaque_tag).to eq 'xyzzy'
|
25
|
+
expect(header.etag).to be_strong
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should raise a ProtocolError if the etag is weak' do
|
29
|
+
expect { Xenon::Headers::IfRange.parse('W/"xyzzy"') }.to raise_error Xenon::ProtocolError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#to_s' do
|
34
|
+
it 'returns the http date format for dates' do
|
35
|
+
header = Xenon::Headers::IfRange.new(Time.utc(1994, 10, 29, 19, 43, 31))
|
36
|
+
expect(header.to_s).to eq('Sat, 29 Oct 1994 19:43:31 GMT')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns the etag format for etags' do
|
40
|
+
header = Xenon::Headers::IfRange.new(Xenon::ETag.new('xyzzy'))
|
41
|
+
expect(header.to_s).to eq('"xyzzy"')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'xenon/headers/if_unmodified_since'
|
2
|
+
|
3
|
+
describe Xenon::Headers::IfUnmodifiedSince do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse an http date' do
|
7
|
+
header = Xenon::Headers::IfUnmodifiedSince.parse('Sat, 29 Oct 1994 19:43:31 GMT')
|
8
|
+
expect(header.date).to eq(Time.utc(1994, 10, 29, 19, 43, 31))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#to_s' do
|
13
|
+
it 'returns the http date format' do
|
14
|
+
header = Xenon::Headers::IfUnmodifiedSince.new(Time.utc(1994, 10, 29, 19, 43, 31))
|
15
|
+
expect(header.to_s).to eq('Sat, 29 Oct 1994 19:43:31 GMT')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'xenon/headers/user_agent'
|
2
|
+
|
3
|
+
describe Xenon::Headers::UserAgent do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse a user agent with a product name' do
|
7
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla')
|
8
|
+
expect(header.products.size).to eq(1)
|
9
|
+
expect(header.products[0].name).to eq('Mozilla')
|
10
|
+
expect(header.products[0].version).to be_nil
|
11
|
+
expect(header.products[0].comment).to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can parse a user agent with a product name and version' do
|
15
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla/5.0')
|
16
|
+
expect(header.products.size).to eq(1)
|
17
|
+
expect(header.products[0].name).to eq('Mozilla')
|
18
|
+
expect(header.products[0].version).to eq('5.0')
|
19
|
+
expect(header.products[0].comment).to be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can parse a user agent with a product name and comment' do
|
23
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla (Macintosh; Intel Mac OS X 10_10_2)')
|
24
|
+
expect(header.products.size).to eq(1)
|
25
|
+
expect(header.products[0].name).to eq('Mozilla')
|
26
|
+
expect(header.products[0].version).to be_nil
|
27
|
+
expect(header.products[0].comment).to eq('Macintosh; Intel Mac OS X 10_10_2')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can parse a user agent with a product name, version and comment' do
|
31
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2)')
|
32
|
+
expect(header.products.size).to eq(1)
|
33
|
+
expect(header.products[0].name).to eq('Mozilla')
|
34
|
+
expect(header.products[0].version).to eq('5.0')
|
35
|
+
expect(header.products[0].comment).to eq('Macintosh; Intel Mac OS X 10_10_2')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can parse a user agent with multiple comments' do
|
39
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla/5.0 (Macintosh) (Intel Mac OS X 10_10_2)')
|
40
|
+
expect(header.products.size).to eq(2)
|
41
|
+
expect(header.products[0].name).to eq('Mozilla')
|
42
|
+
expect(header.products[0].version).to eq('5.0')
|
43
|
+
expect(header.products[0].comment).to eq('Macintosh')
|
44
|
+
expect(header.products[1].name).to be_nil
|
45
|
+
expect(header.products[1].version).to be_nil
|
46
|
+
expect(header.products[1].comment).to eq('Intel Mac OS X 10_10_2')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'can parse a typical Chrome user agent' do
|
50
|
+
header = Xenon::Headers::UserAgent.parse('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36')
|
51
|
+
expect(header.products.size).to eq(4)
|
52
|
+
expect(header.products[0].name).to eq('Mozilla')
|
53
|
+
expect(header.products[0].version).to eq('5.0')
|
54
|
+
expect(header.products[0].comment).to eq('Macintosh; Intel Mac OS X 10_10_2')
|
55
|
+
expect(header.products[1].name).to eq('AppleWebKit')
|
56
|
+
expect(header.products[1].version).to eq('537.36')
|
57
|
+
expect(header.products[1].comment).to eq('KHTML, like Gecko')
|
58
|
+
expect(header.products[2].name).to eq('Chrome')
|
59
|
+
expect(header.products[2].version).to eq('41.0.2272.89')
|
60
|
+
expect(header.products[2].comment).to be_nil
|
61
|
+
expect(header.products[3].name).to eq('Safari')
|
62
|
+
expect(header.products[3].version).to eq('537.36')
|
63
|
+
expect(header.products[3].comment).to be_nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|