Skip to content
This repository has been archived by the owner on Oct 11, 2023. It is now read-only.

Commit

Permalink
Merge b9cb809 into fe0594a
Browse files Browse the repository at this point in the history
  • Loading branch information
nmeum committed Jul 16, 2018
2 parents fe0594a + b9cb809 commit 8bf04db
Show file tree
Hide file tree
Showing 17 changed files with 104 additions and 38 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -7,6 +7,10 @@ end

gemspec

group :dtls do
gem 'tinydtls', platforms: :ruby
end

group :cbor do
gem 'cbor', platforms: :ruby
end
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Expand Up @@ -107,6 +107,7 @@ GEM
docile (1.1.5)
equalizer (0.0.11)
erubi (1.7.1)
ffi (1.9.25)
globalid (0.4.1)
activesupport (>= 4.2.0)
grape (1.0.3)
Expand Down Expand Up @@ -220,6 +221,8 @@ GEM
timers (4.1.2)
hitimes
tins (1.16.3)
tinydtls (0.1.0)
ffi (~> 1.9)
tzinfo (1.2.5)
thread_safe (~> 0.1)
virtus (1.0.5)
Expand Down Expand Up @@ -252,6 +255,7 @@ DEPENDENCIES
rspec-rails (~> 3.5.0)
ruby-prof
sinatra!
tinydtls

BUNDLED WITH
1.16.2
6 changes: 6 additions & 0 deletions lib/david.rb
Expand Up @@ -11,6 +11,12 @@ module David
$stderr << "`gem install cbor` for transparent JSON/CBOR conversion "
$stderr << "support.\n"
end

begin
require 'tinydtls'
rescue LoadError
$stderr << "`gem install tinydtls` for DTLs support.\n"
end
end

require 'celluloid/current'
Expand Down
12 changes: 11 additions & 1 deletion lib/david/app_config.rb
Expand Up @@ -3,14 +3,16 @@ class AppConfig < Hash
DEFAULT_OPTIONS = {
:Block => true,
:CBOR => false,
:DTLS => false,
:DefaultFormat => 'application/json',
:Host => ENV['RACK_ENV'] == 'development' ? '::1' : '::',
:Log => nil,
:MinimalMapping => false,
:Multicast => true,
:MulticastGroups => ['ff02::fd', 'ff05::fd'],
:Observe => true,
:Port => ::CoAP::PORT
:Port => ::CoAP::PORT,
:PortDTLS => 5684 # TODO
}

def initialize(hash = {})
Expand All @@ -32,6 +34,10 @@ def choose_cbor(value)
default_to_false(:cbor, value)
end

def choose_dtls(value)
default_to_false(:dtls, value)
end

def choose_defaultformat(value)
value = from_rails(:default_format)
return nil if value.nil?
Expand Down Expand Up @@ -82,6 +88,10 @@ def choose_port(value)
value.nil? ? nil : value.to_i
end

def choose_portdtls(value)
value.nil? ? nil: value.to_i
end

def default_to_false(key, value)
return true if value.to_s == 'true'

Expand Down
2 changes: 1 addition & 1 deletion lib/david/exchange.rb
@@ -1,5 +1,5 @@
module David
class Exchange < Struct.new(:host, :port, :message, :ancillary, :options)
class Exchange < Struct.new(:server, :host, :port, :message, :ancillary, :options)
include Registry

def accept
Expand Down
8 changes: 5 additions & 3 deletions lib/david/garbage_collector.rb
Expand Up @@ -18,9 +18,11 @@ def run
end

def tick
unless server.cache.empty?
log.debug('GarbageCollector tick')
server.cache_clean!(@timeout)
servers.each do |server|
unless server.cache.empty?
log.debug("GarbageCollector tick for #{server.class.name}")
server.cache_clean!(@timeout)
end
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/david/observe.rb
Expand Up @@ -52,7 +52,7 @@ def handle_update(key)
n, exchange, env, etag = @store[key]
n += 1

response, options = server.respond(exchange, env)
response, options = exchange.server.respond(exchange, env)

return if response.nil?

Expand Down Expand Up @@ -82,7 +82,7 @@ def handle_update(key)

def transmit(exchange, message, options)
begin
server.socket.send(message.to_wire, 0, exchange.host, exchange.port)
exchange.server.socket.send(message.to_wire, 0, exchange.host, exchange.port)
log.debug(message.inspect)
rescue Timeout::Error, RuntimeError, Errno::ENETUNREACH
end
Expand Down
15 changes: 12 additions & 3 deletions lib/david/registry.rb
Expand Up @@ -5,7 +5,7 @@ module Registry
protected

def log
@log ||= server.log
@log ||= Celluloid.logger
# In some tests no server actor is present
rescue NoMethodError
@log ||= FakeLogger.new
Expand All @@ -21,8 +21,17 @@ def observe
Celluloid::Actor[:observe]
end

def server
Celluloid::Actor[:server]
def server_udp
Celluloid::Actor[:server_udp]
end

def server_dtls
Celluloid::Actor[:server_dtls]
end

def servers
servers = [server_udp]
servers << server_dtls unless server_dtls.nil?
end
end
end
24 changes: 14 additions & 10 deletions lib/david/server.rb
Expand Up @@ -4,6 +4,8 @@
require 'david/server/multicast'
require 'david/server/respond'
require 'david/server/utility'
require 'david/server/coap'
require 'david/server/coaps'

module David
class Server
Expand All @@ -14,25 +16,25 @@ class Server
include Respond
include Utility

attr_reader :log, :socket
attr_reader :socket

finalizer :shutdown

def initialize(app, options)
@app = app.respond_to?(:new) ? app.new : app
@mid_cache = {}
@options = AppConfig.new(options)
@log = @options[:Log]

host, port = @options.values_at(:Host, :Port)
af = ipv6? ? ::Socket::AF_INET6 : ::Socket::AF_INET

log.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
log.info "Starting on coap://[#{host}]:#{port}"
@socket = create_socket(af)
secure = @socket.is_a? TinyDTLS::UDPSocket
host, port = @options.values_at(:Host, secure ? :PortDTLS : :Port)

af = ipv6? ? ::Socket::AF_INET6 : ::Socket::AF_INET
log.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
log.info "Starting on %s://[#{host}]:#{port}" %
(secure ? "coaps" : "coap")

# Actually Celluloid::IO::UDPSocket.
@socket = UDPSocket.new(af)
multicast_initialize! if @options[:Multicast]
@socket.bind(host, port)
end
Expand All @@ -41,6 +43,8 @@ def run
loop do
if jruby_or_rbx?
dispatch(*@socket.recvfrom(1152))
elsif @socket.is_a? TinyDTLS::UDPSocket
defer { dispatch(*@socket.recvmsg) }
else
begin
dispatch(*@socket.to_io.recvmsg_nonblock)
Expand Down Expand Up @@ -74,8 +78,8 @@ def dispatch(*args)
host, port = sender.ip_address, sender.ip_port
end

message = CoAP::Message.parse(data)
exchange = Exchange.new(host, port, message, anc)
message = ::CoAP::Message.parse(data)
exchange = Exchange.new(self, host, port, message, anc)

return if !exchange.non? && exchange.multicast?

Expand Down
9 changes: 9 additions & 0 deletions lib/david/server/coap.rb
@@ -0,0 +1,9 @@
module David
class Server
class CoAP < Server
def create_socket(af)
Celluloid::IO::UDPSocket.new(af)
end
end
end
end
12 changes: 12 additions & 0 deletions lib/david/server/coaps.rb
@@ -0,0 +1,12 @@
module David
class Server
class CoAPs < Server
def create_socket(af)
socket = TinyDTLS::UDPSocket.new(af)
socket.add_client("foobar", "foobar")

return socket
end
end
end
end
4 changes: 2 additions & 2 deletions lib/david/server/respond.rb
Expand Up @@ -60,7 +60,7 @@ def respond(exchange, env = nil)
# No response on exchange for non-existent block.
return if block_enabled && !exchange.block.included_by?(body)

cf = CoAP::Registry.convert_content_format(ct)
cf = ::CoAP::Registry.convert_content_format(ct)
etag = etag_to_coap(headers, 4)
loc = location_to_coap(headers)
ma = max_age_to_coap(headers)
Expand Down Expand Up @@ -145,7 +145,7 @@ def handle_observe(exchange, env, etag)
def initialize_response(exchange, mcode = 2.05)
type = exchange.con? ? :ack : :non

CoAP::Message.new \
::CoAP::Message.new \
tt: type,
mcode: mcode,
mid: exchange.message.mid || SecureRandom.random_number(0xffff),
Expand Down
2 changes: 1 addition & 1 deletion lib/david/transmitter.rb
Expand Up @@ -6,7 +6,7 @@ class Transmitter

def initialize(socket)
@log = Celluloid.logger
@socket = socket || server.socket
@socket = socket
end

# TODO Retransmissions
Expand Down
21 changes: 15 additions & 6 deletions lib/rack/handler/david.rb
Expand Up @@ -4,15 +4,22 @@ class David
def self.run(app, options={})
g = Celluloid::Supervision::Container.run!

g.supervise(as: :server, type: ::David::Server, args: [app, options])
g.supervise(as: :gc, type: ::David::GarbageCollector)
g.supervise(as: :server_udp, type: ::David::Server::CoAP, args: [app, options])
if options[:DTLS] == 'true'
g.supervise(as: :server_dtls, type: ::David::Server::CoAPs, args: [app, options])
end

g.supervise(as: :gc, type: ::David::GarbageCollector)
if options[:Observe] != 'false'
g.supervise(as: :observe, type: ::David::Observe)
end

begin
Celluloid::Actor[:server].run
if options[:DTLS] == 'true'
Celluloid::Actor[:server_dtls].async.run
end

Celluloid::Actor[:server_udp].run
rescue Interrupt
Celluloid.logger.info 'Terminated'
Celluloid.logger = nil
Expand All @@ -21,19 +28,21 @@ def self.run(app, options={})
end

def self.valid_options
host, port, maddrs =
AppConfig::DEFAULT_OPTIONS.values_at(:Host, :Port, :MulticastGroups)
host, port, dport, maddrs =
AppConfig::DEFAULT_OPTIONS.values_at(:Host, :Port, :PortDTLS, :MulticastGroups)

{
'Block=BOOLEAN' => 'Support for blockwise transfer (default: true)',
'CBOR=BOOLEAN' => 'Transparent JSON/CBOR conversion (default: false)',
'DTLS=BOOLEAN' => 'DTLS support (default: false)',
'DefaultFormat=F' => 'Content-Type if CoAP accept option on request is undefined',
'Host=HOST' => "Hostname to listen on (default: #{host})",
'Log=LOG' => 'Change logging (debug|none)',
'Multicast=BOOLEAN' => 'Multicast support (default: true)',
'MulticastGroups=ARRAY' => "Multicast groups (default: #{maddrs.join(', ')})",
'Observe=BOOLEAN' => 'Observe support (default: true)',
'Port=PORT' => "Port to listen on (default: #{port})"
'Port=PORT' => "UDP port to listen on (default: #{port})",
'PortDTLS=PORT' => "DTLS port to listen on (default: #{dport})"
}
end
end
Expand Down
9 changes: 3 additions & 6 deletions spec/observe_spec.rb
Expand Up @@ -7,13 +7,15 @@

# TODO Replace this with factory.
before do
server = supervised_server(:Host => '0.0.0.0', :Port => port)

[:@exchange1, :@exchange2].each do |var|
mid = SecureRandom.random_number(0xffff)
token = SecureRandom.random_number(0xff)
options = { uri_path: [], token: token }

message = CoAP::Message.new(:con, :get, mid, '', options)
exchange = Exchange.new('127.0.0.1', CoAP::PORT, message)
exchange = Exchange.new(server, '127.0.0.1', CoAP::PORT, message)

instance_variable_set(var, exchange)
end
Expand Down Expand Up @@ -140,8 +142,6 @@
describe '#handle_update' do
let(:port) { random_port }

let!(:server) { supervised_server(:Host => '0.0.0.0', :Port => port) }

context 'error (4.04)' do
let!(:key) { [dummy1[0].host, dummy1[0].token] }

Expand Down Expand Up @@ -176,8 +176,6 @@
describe '#tick' do
let(:port) { random_port }

let!(:server) { supervised_server(:Host => '0.0.0.0', :Port => port) }

context 'update (2.05)' do
let!(:key) { [dummy2[0].host, dummy2[0].token] }

Expand All @@ -198,7 +196,6 @@
describe 'integration' do
let(:port) { random_port }

let!(:server) { supervised_server(:Port => port) }
let!(:client) do
CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1)
end
Expand Down
2 changes: 1 addition & 1 deletion spec/server_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'benchmark'

describe Server do
describe Server::CoAP do
let(:port) { random_port }
let(:client) do
CoAP::Client.new(port: port, retransmit: false, recv_timeout: 0.1)
Expand Down
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
Expand Up @@ -63,7 +63,7 @@ def supervised_server(options)

g = Celluloid::Supervision::Container.run!

g.supervise(as: :server, type: ::David::Server,
g.supervise(as: :server_udp, type: ::David::Server::CoAP,
args: [app, defaults.merge(options)])

g.supervise(as: :gc, type: ::David::GarbageCollector)
Expand All @@ -72,7 +72,7 @@ def supervised_server(options)
g.supervise(as: :observe, type: ::David::Observe)
end

Celluloid::Actor[:server].async.run
Celluloid::Actor[:server_udp].async.run

g
end
Expand Down

0 comments on commit 8bf04db

Please sign in to comment.