david 0.3.0.pre → 0.3.0

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +24 -0
  4. data/Gemfile +11 -7
  5. data/Gemfile.lock +155 -19
  6. data/README.md +21 -3
  7. data/Rakefile +5 -0
  8. data/TODO.md +73 -0
  9. data/benchmarks/Gemfile +4 -0
  10. data/benchmarks/Gemfile.lock +31 -0
  11. data/benchmarks/rps.rb +20 -0
  12. data/bin/david +9 -4
  13. data/config.ru +4 -0
  14. data/david.gemspec +10 -4
  15. data/experiments/mcast.rb +37 -0
  16. data/experiments/structs.rb +45 -0
  17. data/{test.rb → experiments/test.rb} +0 -0
  18. data/lib/david.rb +15 -4
  19. data/lib/david/actor.rb +18 -0
  20. data/lib/david/garbage_collector.rb +35 -0
  21. data/lib/david/observe.rb +102 -0
  22. data/lib/david/rails/action_controller/base.rb +11 -0
  23. data/lib/david/railties/config.rb +20 -1
  24. data/lib/david/railties/middleware.rb +18 -6
  25. data/lib/david/request.rb +80 -0
  26. data/lib/david/resource_discovery.rb +92 -0
  27. data/lib/david/resource_discovery_proxy.rb +13 -0
  28. data/lib/david/server.rb +72 -27
  29. data/lib/david/server/constants.rb +48 -0
  30. data/lib/david/server/deduplication.rb +21 -0
  31. data/lib/david/server/mapping.rb +64 -12
  32. data/lib/david/server/multicast.rb +54 -0
  33. data/lib/david/server/options.rb +32 -0
  34. data/lib/david/server/respond.rb +146 -0
  35. data/lib/david/server/utility.rb +1 -6
  36. data/lib/david/show_exceptions.rb +52 -0
  37. data/lib/david/version.rb +2 -1
  38. data/lib/rack/handler/david.rb +16 -6
  39. data/lib/rack/hello_world.rb +23 -0
  40. data/spec/dummy/Rakefile +6 -0
  41. data/spec/dummy/app/assets/images/.keep +0 -0
  42. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  43. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  44. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  45. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/mailers/.keep +0 -0
  48. data/spec/dummy/app/models/.keep +0 -0
  49. data/spec/dummy/app/models/concerns/.keep +0 -0
  50. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  51. data/spec/dummy/bin/bundle +3 -0
  52. data/spec/dummy/bin/rails +4 -0
  53. data/spec/dummy/bin/rake +4 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +29 -0
  56. data/spec/dummy/config/boot.rb +5 -0
  57. data/spec/dummy/config/database.yml +25 -0
  58. data/spec/dummy/config/environment.rb +5 -0
  59. data/spec/dummy/config/environments/development.rb +37 -0
  60. data/spec/dummy/config/environments/production.rb +78 -0
  61. data/spec/dummy/config/environments/test.rb +39 -0
  62. data/spec/dummy/config/initializers/assets.rb +8 -0
  63. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  65. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  66. data/spec/dummy/config/initializers/inflections.rb +16 -0
  67. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  68. data/spec/dummy/config/initializers/session_store.rb +3 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/dummy/config/locales/en.yml +23 -0
  71. data/spec/dummy/config/routes.rb +58 -0
  72. data/spec/dummy/config/secrets.yml +22 -0
  73. data/spec/dummy/db/test.sqlite3 +0 -0
  74. data/spec/dummy/lib/assets/.keep +0 -0
  75. data/spec/dummy/log/.keep +0 -0
  76. data/spec/dummy/public/404.html +67 -0
  77. data/spec/dummy/public/422.html +67 -0
  78. data/spec/dummy/public/500.html +66 -0
  79. data/spec/dummy/public/favicon.ico +0 -0
  80. data/spec/guerilla_rack_handler_spec.rb +16 -0
  81. data/spec/mapping_spec.rb +56 -0
  82. data/spec/observe_spec.rb +111 -0
  83. data/spec/perf/server_perf_spec.rb +15 -9
  84. data/spec/resource_discovery_spec.rb +65 -0
  85. data/spec/server_spec.rb +306 -0
  86. data/spec/spec_helper.rb +43 -1
  87. data/spec/utility_spec.rb +8 -0
  88. metadata +195 -38
  89. data/lib/david/server/response.rb +0 -124
  90. data/lib/david/well_known.rb +0 -59
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Handler do
4
+ describe '.default' do
5
+ context 'default' do
6
+ it { expect(Rack::Handler.default).to eq(Rack::Handler::David) }
7
+ end
8
+
9
+ context 'env' do
10
+ it 'RACK_HANDLER' do
11
+ ENV['RACK_HANDLER'] = 'webrick'
12
+ expect(Rack::Handler.default).to eq(Rack::Handler::WEBrick)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ include Server::Mapping
4
+
5
+ describe Server::Mapping do
6
+ context 'etag_to_coap' do
7
+ context '16 byte hex as string (from Rails for example)' do
8
+ it '0 in first 8 byte' do
9
+ expect(etag_to_coap({'ETag' => ([0]*32).join})).to eq(0)
10
+ expect(etag_to_coap({'ETag' => ([0]*16 + [1]*16).join})).to eq(0)
11
+ end
12
+
13
+ it '>0 in first 8 byte' do
14
+ expect(etag_to_coap({'ETag' => ([0]*15 + [1]*17).join})).to eq(1)
15
+ expect(etag_to_coap({'ETag' => (['f']*16 + [0]*16).join})).to eq(2**64-1)
16
+ expect(etag_to_coap({'ETag' => '2246fd11002a6bcad940fe5d76a48333'})).to eq(2469939695118347210)
17
+ expect(etag_to_coap({'ETag' => 'W/"2246fd11002a6bcad940fe5d76a48333"'})).to eq(2469939695118347210)
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'location_to_coap' do
23
+ it { expect(location_to_coap({})).to eq(nil) }
24
+ it { expect(location_to_coap({'Location' => nil})).to eq(nil) }
25
+ it { expect(location_to_coap({'Location' => '/rd'})).to eq(%w[rd]) }
26
+ it { expect(location_to_coap({'Location' => '/rd/1'})).to eq(%w[rd 1]) }
27
+ it { expect(location_to_coap({'Location' => '/'})).to eq(nil) }
28
+ it { expect(location_to_coap({'Location' => '///'})).to eq(nil) }
29
+ end
30
+
31
+ context 'code_to_coap' do
32
+ it { expect(code_to_coap(nil)).to eq([0, 0]) }
33
+ it { expect(code_to_coap(200)).to eq([2, 5]) }
34
+ it { expect(code_to_coap(205)).to eq([2, 5]) }
35
+ it { expect(code_to_coap(2.05)).to eq([2, 5]) }
36
+
37
+ context 'message' do
38
+ let(:port) { random_port }
39
+ let(:client) do
40
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1)
41
+ end
42
+
43
+ let!(:server) { supervised_server(:Port => port) }
44
+
45
+ subject { client.get('/code', '::1') }
46
+
47
+ it 'should be 2.05' do
48
+ expect(subject).to be_a(CoAP::Message)
49
+ expect(subject.ver).to eq(1)
50
+ expect(subject.tt).to eq(:ack)
51
+ expect(subject.mcode).to eq([2, 5])
52
+ expect(subject.payload).to eq('')
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ Celluloid.logger = ENV['DEBUG'].nil? ? nil : Logger.new($stdout)
4
+
5
+ describe Observe do
6
+ let!(:observe) { Observe.supervise_as(:observe) }
7
+
8
+ # TODO Replace this with factory.
9
+ before do
10
+ [:@request1, :@request2].each do |var|
11
+ token = SecureRandom.random_number(0xff)
12
+ options = { uri_path: [], token: token }
13
+
14
+ message = CoAP::Message.new(:con, :get, nil, '', options)
15
+ request = Request.new('127.0.0.1', CoAP::PORT, message)
16
+
17
+ instance_variable_set(var, request)
18
+ end
19
+ end
20
+
21
+ let(:dummy1) { [@request1, {'PATH_INFO' => '/'}, '1'] }
22
+ let(:dummy2) { [@request2, {'PATH_INFO' => '/'}, '1'] }
23
+
24
+ subject { Celluloid::Actor[:observe] }
25
+
26
+ describe '#add' do
27
+ let!(:add) { subject.add(*dummy1) }
28
+
29
+ let!(:key) { [dummy1[0].host, dummy1[0].token] }
30
+ let!(:value) { subject[key] }
31
+
32
+ context 'key' do
33
+ it 'presence' do
34
+ expect(subject.size).to eq(1)
35
+ expect(subject.keys.first).to eq(key)
36
+ end
37
+ end
38
+
39
+ # [n, request, env, etag, timestamp]
40
+ context 'value' do
41
+ let!(:time) { Time.now.to_i }
42
+
43
+ it 'type' do
44
+ expect(add).to be_a(Array)
45
+ expect(add.size).to eq(5)
46
+ expect(value).to be_a(Array)
47
+ end
48
+
49
+ it 'identity' do
50
+ expect(value[1..3]).to eq(dummy1[0..2])
51
+ end
52
+
53
+ it 'observe number' do
54
+ expect(value[0]).to be_a(Integer)
55
+ expect(value[0]).to eq(0)
56
+ end
57
+
58
+ it 'timestamp' do
59
+ expect(value[4]).to be_a(Integer)
60
+ expect(value[4]).to be <= time
61
+ expect(value[4]).to be > time - 2
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#delete' do
67
+ before do
68
+ subject.add(*dummy1)
69
+ subject.add(*dummy2)
70
+ subject.delete(dummy1[0])
71
+ end
72
+
73
+ context 'entry' do
74
+ let(:entry) { subject.first.flatten }
75
+
76
+ it 'count' do
77
+ expect(subject.size).to eq(1)
78
+ end
79
+
80
+ it 'identity' do
81
+ expect(entry.size).to eq(7)
82
+
83
+ expect(entry[0]).to eq(dummy2[0].host)
84
+ expect(entry[1]).to eq(dummy2[0].token)
85
+
86
+ expect(entry[3..5]).to eq(dummy2)
87
+ expect(entry[6]).to be_a(Integer)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#include?' do
93
+ before do
94
+ subject.add(*dummy1)
95
+ end
96
+
97
+ context 'entry' do
98
+ it 'true' do
99
+ expect(subject.include?(dummy1[0])).to be(true)
100
+ end
101
+
102
+ it 'false' do
103
+ expect(subject.include?(dummy2[0])).to be(false)
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '#tick' do
109
+ # Couldn't get mocking to work decently.
110
+ end
111
+ end
@@ -1,19 +1,25 @@
1
1
  require 'spec_helper'
2
2
  require 'benchmark'
3
3
 
4
- describe Server, 'performance' do
5
- before do
6
- @server = David::Server.supervise_as \
7
- :david,
8
- ->(e) { [200, { 'Content-Length' => 0 }, ['']] },
9
- {:Host => '::1', :Port => CoRE::CoAP::PORT, :Log => 'none'}
4
+ describe Server, 'performance', performance: true do
5
+ let(:port) { random_port }
6
+
7
+ subject! do
8
+ supervised_server \
9
+ :Port => port, :Block => false, :Multicast => false, :Log => debug,
10
+ :app => ->(e) { [200, { 'Content-Length' => 0 }, ['']] }
11
+ end
12
+
13
+ let(:client) do
14
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.05)
10
15
  end
11
16
 
12
- it "should handle GET request in less than #{max1 = 0.0025} seconds" do
13
- expect(Benchmark.realtime { CoRE::CoAP::Client.new.get('/', '::1') }).to be < max1
17
+ # Stolen from thin.
18
+ it "should handle GET request in less than #{max1 = 0.0045} seconds" do
19
+ expect(Benchmark.realtime { client.get('/', '::1') }).to be < max1
14
20
  end
15
21
 
16
22
  after do
17
- @server.terminate
23
+ subject.terminate
18
24
  end
19
25
  end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe David::ResourceDiscovery do
4
+ let(:port) { random_port }
5
+
6
+ let(:client) do
7
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1)
8
+ end
9
+
10
+ let!(:server) do
11
+ supervised_server(:Port => port, :Log => debug, :app => Dummy::Application)
12
+ end
13
+
14
+ context 'ordinary request' do
15
+ subject { client.get('/.well-known/core', '::1') }
16
+
17
+ context 'response' do
18
+ let(:links) { CoRE::Link.parse_multiple(subject.payload) }
19
+
20
+ it 'valid' do
21
+ expect(subject).to be_a(CoAP::Message)
22
+ expect(subject.ver).to eq(1)
23
+ expect(subject.tt).to eq(:ack)
24
+ expect(subject.mcode).to eq([2, 5])
25
+ end
26
+
27
+ it 'CoRE::Link' do
28
+ expect(links.size).to eq(4)
29
+ expect(links.map(&:uri).uniq.size).to eq(links.size)
30
+
31
+ links.each do |link|
32
+ expect(link.uri).to match(/^\/things/)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ context 'filtered request' do
39
+ context 'match' do
40
+ subject { client.get('/.well-known/core?href=new', '::1') }
41
+
42
+ context 'response' do
43
+ let(:links) { CoRE::Link.parse_multiple(subject.payload) }
44
+
45
+ it 'CoRE::Link' do
46
+ expect(links.size).to eq(1)
47
+ expect(links.first.uri).to eq('/things/new')
48
+ end
49
+ end
50
+ end
51
+
52
+ context 'no match' do
53
+ subject { client.get('/.well-known/core?href=foo', '::1') }
54
+
55
+ context 'response' do
56
+ let(:links) { CoRE::Link.parse_multiple(subject.payload) }
57
+
58
+ it 'empty' do
59
+ expect(links.size).to eq(0)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,306 @@
1
+ require 'spec_helper'
2
+ require 'benchmark'
3
+
4
+ describe Server do
5
+ let(:port) { random_port }
6
+ let(:client) do
7
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1)
8
+ end
9
+
10
+ let!(:server) { supervised_server(:Port => port) }
11
+
12
+ context 'ordinary request' do
13
+ subject { client.get('/hello', '::1') }
14
+
15
+ it 'should be plausible' do
16
+ expect(subject).to be_a(CoAP::Message)
17
+ expect(subject.ver).to eq(1)
18
+ expect(subject.tt).to eq(:ack)
19
+ expect(subject.mcode).to eq([2, 5])
20
+ expect(subject.payload).to eq('Hello World!')
21
+ end
22
+ end
23
+
24
+ context 'missing resource' do
25
+ subject { client.get('/404', '::1') }
26
+
27
+ it 'should be 4.04' do
28
+ expect(subject).to be_a(CoAP::Message)
29
+ expect(subject.ver).to eq(1)
30
+ expect(subject.tt).to eq(:ack)
31
+ expect(subject.mcode).to eq([4, 4])
32
+ expect(subject.payload).to eq('')
33
+ end
34
+ end
35
+
36
+ context 'unsupported method' do
37
+ subject { client.delete('/hello', '::1') }
38
+
39
+ it 'should be 4.05' do
40
+ expect(subject).to be_a(CoAP::Message)
41
+ expect(subject.ver).to eq(1)
42
+ expect(subject.tt).to eq(:ack)
43
+ expect(subject.mcode).to eq([4, 5])
44
+ expect(subject.payload).to eq('')
45
+ end
46
+ end
47
+
48
+ # See https://tools.ietf.org/html/rfc7252#section-12.8
49
+ context 'multicast' do
50
+ let(:client) do
51
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1,
52
+ tt: :non)
53
+ end
54
+
55
+ # -A INPUT -m pkttype --pkt-type multicast -d ff02::fd -j ACCEPT
56
+ context 'ipv6', multicast: :ipv6 do
57
+ ['ff02::1', 'ff02::fd', 'ff05::fd'].each do |address|
58
+ context address do
59
+ subject { client.get('/hello', address) }
60
+
61
+ it 'should be 2.05' do
62
+ expect(subject).to be_a(CoAP::Message)
63
+ expect(subject.ver).to eq(1)
64
+ expect(subject.tt).to eq(:non)
65
+ expect(subject.mcode).to eq([2, 5])
66
+ expect(subject.payload).to eq('Hello World!')
67
+ end
68
+ end
69
+ end
70
+
71
+ context '4.04' do
72
+ subject { client.get('/404', 'ff02::fd') }
73
+
74
+ it 'should timeout' do
75
+ expect { subject }.to raise_error(Timeout::Error)
76
+ end
77
+ end
78
+
79
+ context 'con' do
80
+ let(:client) do
81
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1,
82
+ tt: :con)
83
+ end
84
+
85
+ subject { client.get('/hello', 'ff02::fd') }
86
+
87
+ it 'should timeout' do
88
+ expect { subject }.to raise_error(Timeout::Error)
89
+ end
90
+ end
91
+ end
92
+
93
+ # -A INPUT -m pkttype --pkt-type multicast -d 224.0.1.187 -j ACCEPT
94
+ context 'ipv4', multicast: :ipv4 do
95
+ let!(:server) do
96
+ supervised_server(:Host => '0.0.0.0', :Port => port)
97
+ end
98
+
99
+ ['224.0.0.1', '224.0.1.187'].each do |address|
100
+ context address do
101
+ subject { client.get('/hello', address) }
102
+
103
+ it 'should be 2.05' do
104
+ expect(subject).to be_a(CoAP::Message)
105
+ expect(subject.ver).to eq(1)
106
+ expect(subject.tt).to eq(:non)
107
+ expect(subject.mcode).to eq([2, 5])
108
+ expect(subject.payload).to eq('Hello World!')
109
+ end
110
+ end
111
+ end
112
+
113
+ context '4.04' do
114
+ subject { client.get('/404', '224.0.1.187') }
115
+
116
+ it 'should timeout' do
117
+ expect { subject }.to raise_error(Timeout::Error)
118
+ end
119
+ end
120
+
121
+ context 'con' do
122
+ let(:client) do
123
+ CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1,
124
+ tt: :con)
125
+ end
126
+
127
+ subject { client.get('/hello', '224.0.1.187') }
128
+
129
+ it 'should timeout' do
130
+ expect { subject }.to raise_error(Timeout::Error)
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ context 'block' do
137
+ context 'block2.more set' do
138
+ subject { client.get('/', '::1', nil, nil, block2: 14) }
139
+
140
+ it 'should be an error' do
141
+ expect(subject).to be_a(CoAP::Message)
142
+ expect(subject.ver).to eq(1)
143
+ expect(subject.tt).to eq(:ack)
144
+ expect(subject.mcode).to eq([4, 5])
145
+ expect(subject.payload).to eq('')
146
+ end
147
+ end
148
+
149
+ context 'non existent' do
150
+ let(:client) do
151
+ CoAP::Client.new \
152
+ port: port,
153
+ retransmit: false,
154
+ recv_timeout: 0.1
155
+ end
156
+
157
+ subject { client.get('/hello', '::1', nil, nil, block2: 16) }
158
+
159
+ it 'should be an error' do
160
+ expect { subject }.to raise_error(Timeout::Error)
161
+ end
162
+ end
163
+
164
+ context 'transfer' do
165
+ subject do
166
+ [0, 16].map do |x|
167
+ client.get('/block', '::1', nil, nil, block2: x)
168
+ end
169
+ end
170
+
171
+ it { expect(subject[0].mcode).to eq([2, 5]) }
172
+ it { expect(subject[0].payload.bytesize).to eq(16) }
173
+
174
+ it { expect(subject[1].mcode).to eq([2, 5]) }
175
+ it { expect(subject[1].payload.bytesize).to eq(1) }
176
+ end
177
+ end
178
+
179
+ context 'accept' do
180
+ context 'unset' do
181
+ subject { client.get('/hello', '::1') }
182
+
183
+ it 'should return resource' do
184
+ expect(subject).to be_a(CoAP::Message)
185
+ expect(subject.ver).to eq(1)
186
+ expect(subject.tt).to eq(:ack)
187
+ expect(subject.mcode).to eq([2, 5])
188
+ expect(subject.payload).to eq('Hello World!')
189
+ expect(subject.options[:content_format]).to eq(0)
190
+ end
191
+ end
192
+
193
+ context 'default' do
194
+ subject { client.get('/echo/accept', '::1') }
195
+
196
+ it 'should return default' do
197
+ expect(subject).to be_a(CoAP::Message)
198
+ expect(subject.ver).to eq(1)
199
+ expect(subject.tt).to eq(:ack)
200
+ expect(subject.mcode).to eq([2, 5])
201
+ expect(subject.payload).to eq('')
202
+ expect(subject.options[:content_format]).to eq(50)
203
+ end
204
+ end
205
+
206
+ context 'right' do
207
+ subject { client.get('/echo/accept', '::1', nil, nil, accept: 40) }
208
+
209
+ it 'should echo' do
210
+ expect(subject).to be_a(CoAP::Message)
211
+ expect(subject.ver).to eq(1)
212
+ expect(subject.tt).to eq(:ack)
213
+ expect(subject.mcode).to eq([2, 5])
214
+ expect(subject.payload).to eq('')
215
+ expect(subject.options[:content_format]).to eq(40)
216
+ end
217
+ end
218
+
219
+ context 'wrong' do
220
+ subject { client.get('/hello', '::1', nil, nil, accept: 40) }
221
+
222
+ it 'should be an error' do
223
+ expect(subject).to be_a(CoAP::Message)
224
+ expect(subject.ver).to eq(1)
225
+ expect(subject.tt).to eq(:ack)
226
+ expect(subject.mcode).to eq([4, 6])
227
+ expect(subject.payload).to eq('')
228
+ end
229
+ end
230
+ end
231
+
232
+ context 'deduplication' do
233
+ context 'duplicates' do
234
+ let(:a) do
235
+ ([0]*3).map { client.get('/value', '::1', nil, nil, mid: 1, token: 1) }
236
+ end
237
+
238
+ it 'matching mid' do
239
+ expect(a[0].mid).to eq(a[1].mid)
240
+ expect(a[1].mid).to eq(a[2].mid)
241
+ end
242
+
243
+ it 'matching token' do
244
+ expect(a[0].options[:token]).to eq(a[1].options[:token])
245
+ expect(a[1].options[:token]).to eq(a[2].options[:token])
246
+ end
247
+
248
+ it 'matching payload' do
249
+ expect(a[0].payload).to eq(a[1].payload)
250
+ expect(a[1].payload).to eq(a[2].payload)
251
+ end
252
+ end
253
+
254
+ context 'different' do
255
+ let(:a) do
256
+ ([0]*3).map { client.get('/value', '::1') }
257
+ end
258
+
259
+ it 'matching mid' do
260
+ expect(a[0].mid).not_to eq(a[1].mid)
261
+ expect(a[1].mid).not_to eq(a[2].mid)
262
+ end
263
+
264
+ it 'matching token' do
265
+ expect(a[0].options[:token]).not_to eq(a[1].options[:token])
266
+ expect(a[1].options[:token]).not_to eq(a[2].options[:token])
267
+ end
268
+
269
+ it 'matching payload' do
270
+ expect(a[0].payload).not_to eq(a[1].payload)
271
+ expect(a[1].payload).not_to eq(a[2].payload)
272
+ end
273
+ end
274
+ end
275
+
276
+ context 'options' do
277
+ describe 'default_to_true' do
278
+ let(:method) do
279
+ ->(*args) { Server::Options.send(:default_to_true, *args) }
280
+ end
281
+
282
+ it { expect(method.call(:block, nil)).to eq(true) }
283
+ it { expect(method.call(:block, true)).to eq(true) }
284
+ it { expect(method.call(:block, 'true')).to eq(true) }
285
+
286
+ it { expect(method.call(:block, false)).to eq(false) }
287
+ it { expect(method.call(:block, 'false')).to eq(false) }
288
+ end
289
+ end
290
+
291
+ context 'proxy' do
292
+ subject { client.get('/', '::1', nil, nil, proxy_uri: 'coap://[::1]/') }
293
+
294
+ it 'should return 5.05' do
295
+ expect(subject).to be_a(CoAP::Message)
296
+ expect(subject.ver).to eq(1)
297
+ expect(subject.tt).to eq(:ack)
298
+ expect(subject.mcode).to eq([5, 5])
299
+ expect(subject.payload).to eq('')
300
+ end
301
+ end
302
+
303
+ after do
304
+ server.terminate
305
+ end
306
+ end