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