david 0.4.5 → 0.5.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.
- checksums.yaml +4 -4
- data/.travis.yml +10 -6
- data/Gemfile +7 -3
- data/Gemfile.lock +180 -145
- data/README.md +17 -5
- data/david.gemspec +5 -5
- data/lib/david.rb +2 -1
- data/lib/david/etsi/mandatory/roda.rb +36 -0
- data/lib/david/observe.rb +21 -14
- data/lib/david/railties/middleware.rb +1 -0
- data/lib/david/registry.rb +1 -1
- data/lib/david/resource_discovery_proxy.rb +1 -1
- data/lib/david/server/multicast.rb +1 -1
- data/lib/david/server/utility.rb +2 -1
- data/lib/david/version.rb +2 -2
- data/lib/rack/handler/david.rb +4 -4
- data/spec/app_config_spec.rb +2 -1
- data/spec/dummy/app/controllers/etsis_controller.rb +1 -2
- data/spec/dummy/app/controllers/tests_controller.rb +2 -2
- data/spec/interop/mandatory_spec.rb +1 -0
- data/spec/interop/optional_spec.rb +1 -1
- data/spec/mapping_spec.rb +1 -1
- data/spec/observe_spec.rb +15 -15
- data/spec/perf/server_perf_spec.rb +1 -1
- data/spec/resource_discovery_spec.rb +3 -3
- data/spec/server_spec.rb +16 -16
- data/spec/spec_helper.rb +17 -6
- metadata +18 -31
- data/spec/dummy/config/initializers/assets.rb +0 -8
data/README.md
CHANGED
@@ -7,11 +7,10 @@
|
|
7
7
|
[](https://codeclimate.com/github/nning/david)
|
8
8
|
|
9
9
|
David is a CoAP server with Rack interface to bring the illustrious family of
|
10
|
-
Rack compatible web frameworks into the Internet of Things.
|
11
|
-
|
12
|
-
tested with MRI >= 1.9, JRuby, and Rubinius.
|
10
|
+
Rack compatible web frameworks into the Internet of Things. It is tested with
|
11
|
+
MRI >= 1.9, JRuby, and Rubinius.
|
13
12
|
|
14
|
-
##
|
13
|
+
## Quick Start
|
15
14
|
|
16
15
|
Just include David in your Gemfile!
|
17
16
|
|
@@ -21,8 +20,21 @@ It will hook into Rack and make itself the default handler, so running `rails
|
|
21
20
|
s` starts David. If you want to start WEBrick for example, you can do so by
|
22
21
|
executing `rails s webrick`.
|
23
22
|
|
23
|
+
**For now, you have to remove the `web-console` gem from the Gemfile (which is
|
24
|
+
HTTP specific anyway) if you use Rails/David in CoAP only mode.** You probably
|
25
|
+
also want to disable [CSRF
|
26
|
+
protection](http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html)
|
27
|
+
by removing the `protect_from_forgery` line from
|
28
|
+
`app/controllers/application_controller.rb` (or use `:null_session` if you know
|
29
|
+
what you are doing).
|
30
|
+
|
31
|
+
The [`coap-rails-dummy`](https://github.com/nning/coap-rails-dummy) repository
|
32
|
+
documents [changes to a newly generated Ruby on Rails application for a quick
|
33
|
+
start](https://github.com/nning/coap-rails-dummy/compare/initial...master).
|
34
|
+
|
24
35
|
After the server is started, the Rails application is available at
|
25
|
-
`coap://[::1]:3000/` by default.
|
36
|
+
`coap://[::1]:3000/` by default. (Although you have to set a route for `/` in
|
37
|
+
`config/routes.rb`, of course.)
|
26
38
|
|
27
39
|
[Copper](https://addons.mozilla.org/de/firefox/addon/copper-270430/) is a CoAP
|
28
40
|
client for Firefox and can be used for development. The [Ruby coap
|
data/david.gemspec
CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
21
21
|
s.require_paths = ['lib']
|
22
22
|
|
23
|
-
s.add_runtime_dependency 'celluloid', '
|
24
|
-
s.add_runtime_dependency 'celluloid-io', '
|
25
|
-
s.add_runtime_dependency 'coap', '
|
26
|
-
s.add_runtime_dependency 'rack', '~>
|
23
|
+
s.add_runtime_dependency 'celluloid', '~> 0.17.3'
|
24
|
+
s.add_runtime_dependency 'celluloid-io', '~> 0.17.3'
|
25
|
+
s.add_runtime_dependency 'coap', '~> 0.1'
|
26
|
+
s.add_runtime_dependency 'rack', '~> 2.0'
|
27
27
|
|
28
|
-
s.add_development_dependency 'rake'
|
28
|
+
s.add_development_dependency 'rake', '~> 0'
|
29
29
|
s.add_development_dependency 'rspec', '~> 3.2'
|
30
30
|
end
|
data/lib/david.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
module David::ETSI::Mandatory
|
2
|
+
class Roda < ::Roda
|
3
|
+
plugin :all_verbs
|
4
|
+
plugin :default_headers, 'Content-Type' => 'text/plain'
|
5
|
+
|
6
|
+
route do |r|
|
7
|
+
r.get 'test' do
|
8
|
+
response.status = 2.05
|
9
|
+
end
|
10
|
+
|
11
|
+
r.post :test do
|
12
|
+
response.status = 2.01
|
13
|
+
end
|
14
|
+
|
15
|
+
r.put :test do
|
16
|
+
response.status = 2.04
|
17
|
+
end
|
18
|
+
|
19
|
+
r.delete :test do
|
20
|
+
response.status = 2.02
|
21
|
+
end
|
22
|
+
|
23
|
+
r.on 'seg1' do
|
24
|
+
r.on 'seg2' do
|
25
|
+
r.get 'seg3' do
|
26
|
+
response.status = 2.05
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
r.get 'query' do
|
32
|
+
response.status = 2.05
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/david/observe.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module David
|
2
|
-
class Observe
|
2
|
+
class Observe
|
3
|
+
extend Forwardable
|
4
|
+
|
3
5
|
include Actor
|
4
6
|
|
5
|
-
|
6
|
-
alias_method :_include?, :include?
|
7
|
+
def_delegators :@store, :first, :keys, :size
|
7
8
|
|
8
9
|
def initialize(tick_interval = 3)
|
9
10
|
@tick_interval = tick_interval
|
11
|
+
@store = {}
|
12
|
+
|
10
13
|
async.run
|
11
14
|
|
12
15
|
log.debug('Observe initialized')
|
@@ -15,34 +18,38 @@ module David
|
|
15
18
|
def add(exchange, env, etag)
|
16
19
|
exchange.message.options.delete(:observe)
|
17
20
|
|
18
|
-
|
21
|
+
@store[[exchange.host, exchange.token]] ||=
|
19
22
|
[0, exchange, env, etag, Time.now.to_i]
|
20
23
|
end
|
21
24
|
|
22
25
|
def delete(exchange)
|
23
|
-
|
26
|
+
@store.delete([exchange.host, exchange.token])
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(key)
|
30
|
+
@store[key]
|
24
31
|
end
|
25
32
|
|
26
33
|
def include?(exchange)
|
27
|
-
|
34
|
+
@store.include?([exchange.host, exchange.token])
|
28
35
|
end
|
29
36
|
|
30
37
|
def to_s
|
31
|
-
|
38
|
+
@store.map { |k, v| [*k, v[2]['PATH_INFO'], v[0]].inspect }.join(', ')
|
32
39
|
end
|
33
40
|
|
34
41
|
private
|
35
42
|
|
36
43
|
def bump(key, n, response)
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
@store[key][0] = n
|
45
|
+
@store[key][3] = response.options[:etag]
|
46
|
+
@store[key][4] = Time.now.to_i
|
40
47
|
end
|
41
48
|
|
42
49
|
# TODO If ETag did not change but max-age of last notification is expired,
|
43
50
|
# return empty 2.03.
|
44
51
|
def handle_update(key)
|
45
|
-
n, exchange, env, etag =
|
52
|
+
n, exchange, env, etag = @store[key]
|
46
53
|
n += 1
|
47
54
|
|
48
55
|
response, options = server.respond(exchange, env)
|
@@ -86,12 +93,12 @@ module David
|
|
86
93
|
end
|
87
94
|
|
88
95
|
def tick(fiber = true)
|
89
|
-
unless
|
96
|
+
unless @store.empty?
|
90
97
|
log.debug('Observe tick')
|
91
|
-
log.debug(
|
98
|
+
log.debug(@store)
|
92
99
|
end
|
93
100
|
|
94
|
-
|
101
|
+
@store.each_key do |key|
|
95
102
|
if fiber
|
96
103
|
async.handle_update(key)
|
97
104
|
else
|
data/lib/david/registry.rb
CHANGED
@@ -17,7 +17,7 @@ module David
|
|
17
17
|
|
18
18
|
def observe
|
19
19
|
# Supervision is only initialized from here in tests.
|
20
|
-
Observe.
|
20
|
+
Observe.supervise(as: :observe) if Celluloid::Actor[:observe].nil?
|
21
21
|
Celluloid::Actor[:observe]
|
22
22
|
end
|
23
23
|
|
@@ -36,7 +36,7 @@ module David
|
|
36
36
|
|
37
37
|
# http://lists.apple.com/archives/darwin-kernel/2014/Mar/msg00012.html
|
38
38
|
if OS.osx?
|
39
|
-
ifname =
|
39
|
+
ifname = `route get default`.match(/interface: ([a-z0-9]*)$/)[1]
|
40
40
|
ifindex = Socket.if_nametoindex(ifname)
|
41
41
|
end
|
42
42
|
|
data/lib/david/server/utility.rb
CHANGED
@@ -5,8 +5,9 @@ module David
|
|
5
5
|
|
6
6
|
# This can only use each on body and currently does not support streaming.
|
7
7
|
def body_to_string(body)
|
8
|
+
p body
|
8
9
|
s = ''
|
9
|
-
body.each { |line| s << line << "\r\n" }
|
10
|
+
body.each { |line| s << line.to_s << "\r\n" }
|
10
11
|
body.close if body.respond_to?(:close)
|
11
12
|
s.chomp
|
12
13
|
end
|
data/lib/david/version.rb
CHANGED
data/lib/rack/handler/david.rb
CHANGED
@@ -2,13 +2,13 @@ module Rack
|
|
2
2
|
module Handler
|
3
3
|
class David
|
4
4
|
def self.run(app, options={})
|
5
|
-
g = Celluloid::
|
5
|
+
g = Celluloid::Supervision::Container.run!
|
6
6
|
|
7
|
-
g.
|
8
|
-
g.
|
7
|
+
g.supervise(as: :server, type: ::David::Server, args: [app, options])
|
8
|
+
g.supervise(as: :gc, type: ::David::GarbageCollector)
|
9
9
|
|
10
10
|
unless options[:Observe] == 'false'
|
11
|
-
g.
|
11
|
+
g.supervise(as: :observe, type: ::David::Observe)
|
12
12
|
end
|
13
13
|
|
14
14
|
begin
|
data/spec/app_config_spec.rb
CHANGED
@@ -15,7 +15,8 @@ describe AppConfig do
|
|
15
15
|
it { expect(method.call(nil)).to eq(nil) }
|
16
16
|
it { expect(method.call('::')).to eq('::') }
|
17
17
|
it { expect(method.call('::1')).to eq('::1') }
|
18
|
-
it { expect(method.call('
|
18
|
+
it { expect(method.call('127.0.0.1')).to eq('127.0.0.1') }
|
19
|
+
it { expect(%w[::1 127.0.0.1]).to include(method.call('localhost')) }
|
19
20
|
end
|
20
21
|
|
21
22
|
describe '#choose_port' do
|
data/spec/mapping_spec.rb
CHANGED
data/spec/observe_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
Celluloid.logger = ENV['DEBUG'].nil? ? nil : Logger.new($stdout)
|
4
4
|
|
5
5
|
describe Observe do
|
6
|
-
let!(:observe) { Observe.
|
6
|
+
let!(:observe) { Observe.supervise(as: :observe) }
|
7
7
|
|
8
8
|
# TODO Replace this with factory.
|
9
9
|
before do
|
@@ -33,7 +33,7 @@ describe Observe do
|
|
33
33
|
let!(:add) { subject.add(*dummy1) }
|
34
34
|
|
35
35
|
let!(:key) { [dummy1[0].host, dummy1[0].token] }
|
36
|
-
let!(:value) { subject
|
36
|
+
let!(:value) { subject.get(key) }
|
37
37
|
|
38
38
|
it '#to_s' do
|
39
39
|
s = '["127.0.0.1", ' + dummy1[0].token.to_s + ', "/", 0]'
|
@@ -131,16 +131,16 @@ describe Observe do
|
|
131
131
|
it 'shall change entry' do
|
132
132
|
subject.send(:bump, key, n, response)
|
133
133
|
|
134
|
-
expect(subject
|
135
|
-
expect(subject
|
136
|
-
expect(subject
|
134
|
+
expect(subject.get(key)[0]).to eq(n)
|
135
|
+
expect(subject.get(key)[3]).to eq(response.options[:etag])
|
136
|
+
expect(subject.get(key)[4]).to be <= Time.now.to_i
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
140
|
describe '#handle_update' do
|
141
141
|
let(:port) { random_port }
|
142
142
|
|
143
|
-
let!(:server) { supervised_server(:Port => port) }
|
143
|
+
let!(:server) { supervised_server(:Host => '0.0.0.0', :Port => port) }
|
144
144
|
|
145
145
|
context 'error (4.04)' do
|
146
146
|
let!(:key) { [dummy1[0].host, dummy1[0].token] }
|
@@ -152,7 +152,7 @@ describe Observe do
|
|
152
152
|
end
|
153
153
|
|
154
154
|
it 'delete' do
|
155
|
-
expect(subject
|
155
|
+
expect(subject.get(key)).to eq(nil)
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
@@ -166,9 +166,9 @@ describe Observe do
|
|
166
166
|
end
|
167
167
|
|
168
168
|
it 'bumped' do
|
169
|
-
expect(subject
|
170
|
-
expect(subject
|
171
|
-
expect(subject
|
169
|
+
expect(subject.get(key)[0]).to eq(1)
|
170
|
+
expect(subject.get(key)[3]).to eq(dummy2[0].message.options[:etag])
|
171
|
+
expect(subject.get(key)[4]).to be <= Time.now.to_i
|
172
172
|
end
|
173
173
|
end
|
174
174
|
end
|
@@ -176,7 +176,7 @@ describe Observe do
|
|
176
176
|
describe '#tick' do
|
177
177
|
let(:port) { random_port }
|
178
178
|
|
179
|
-
let!(:server) { supervised_server(:Port => port) }
|
179
|
+
let!(:server) { supervised_server(:Host => '0.0.0.0', :Port => port) }
|
180
180
|
|
181
181
|
context 'update (2.05)' do
|
182
182
|
let!(:key) { [dummy2[0].host, dummy2[0].token] }
|
@@ -188,9 +188,9 @@ describe Observe do
|
|
188
188
|
end
|
189
189
|
|
190
190
|
it 'bumped' do
|
191
|
-
expect(subject
|
192
|
-
expect(subject
|
193
|
-
expect(subject
|
191
|
+
expect(subject.get(key)[0]).to eq(1)
|
192
|
+
expect(subject.get(key)[3]).to eq(dummy2[0].message.options[:etag])
|
193
|
+
expect(subject.get(key)[4]).to be <= Time.now.to_i
|
194
194
|
end
|
195
195
|
end
|
196
196
|
end
|
@@ -208,7 +208,7 @@ describe Observe do
|
|
208
208
|
|
209
209
|
@t1 = Thread.start do
|
210
210
|
client.observe \
|
211
|
-
'/value',
|
211
|
+
'/value', localhost, nil,
|
212
212
|
->(s, m) { @answers << m }
|
213
213
|
end
|
214
214
|
|
@@ -16,7 +16,7 @@ describe Server, 'performance', performance: true do
|
|
16
16
|
|
17
17
|
# Stolen from thin.
|
18
18
|
it "should handle GET request in less than #{max1 = 0.0045} seconds" do
|
19
|
-
expect(Benchmark.realtime { client.get('/',
|
19
|
+
expect(Benchmark.realtime { client.get('/', localhost) }).to be < max1
|
20
20
|
end
|
21
21
|
|
22
22
|
after do
|
@@ -12,7 +12,7 @@ describe David::ResourceDiscovery do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
context 'ordinary request' do
|
15
|
-
subject { client.get('/.well-known/core',
|
15
|
+
subject { client.get('/.well-known/core', localhost) }
|
16
16
|
|
17
17
|
context 'response' do
|
18
18
|
let(:links) { CoRE::Link.parse_multiple(subject.payload) }
|
@@ -37,7 +37,7 @@ describe David::ResourceDiscovery do
|
|
37
37
|
|
38
38
|
context 'filtered request' do
|
39
39
|
context 'match' do
|
40
|
-
subject { client.get('/.well-known/core?href=new',
|
40
|
+
subject { client.get('/.well-known/core?href=new', localhost) }
|
41
41
|
|
42
42
|
context 'response' do
|
43
43
|
let(:links) { CoRE::Link.parse_multiple(subject.payload) }
|
@@ -50,7 +50,7 @@ describe David::ResourceDiscovery do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
context 'no match' do
|
53
|
-
subject { client.get('/.well-known/core?href=foo',
|
53
|
+
subject { client.get('/.well-known/core?href=foo', localhost) }
|
54
54
|
|
55
55
|
context 'response' do
|
56
56
|
let(:links) { CoRE::Link.parse_multiple(subject.payload) }
|