david 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +2 -2
- data/Gemfile +8 -2
- data/Gemfile.lock +84 -44
- data/README.md +87 -5
- data/benchmarks/Gemfile +1 -0
- data/benchmarks/Gemfile.lock +3 -1
- data/benchmarks/coapbench.sh +20 -0
- data/benchmarks/quick.sh +2 -0
- data/benchmarks/rackup/Gemfile +10 -0
- data/benchmarks/rackup/Gemfile.lock +159 -0
- data/benchmarks/rackup/grape.ru +18 -0
- data/benchmarks/rackup/rack.ru +7 -0
- data/benchmarks/rackup/rails.ru +14 -0
- data/benchmarks/rps.rb +14 -2
- data/benchmarks/stress.sh +17 -0
- data/david.gemspec +0 -3
- data/experiments/Gemfile +6 -0
- data/experiments/concurrency/Gemfile +3 -0
- data/experiments/concurrency/stub.rb +88 -0
- data/experiments/hash_key.rb +64 -0
- data/experiments/string_concat.rb +21 -0
- data/experiments/symbol_to_proc.rb +15 -0
- data/experiments/thread_safe.rb +40 -0
- data/lib/david.rb +8 -4
- data/lib/david/actor.rb +1 -11
- data/lib/david/app_config.rb +112 -0
- data/lib/david/exchange.rb +124 -0
- data/lib/david/fake_logger.rb +11 -0
- data/lib/david/garbage_collector.rb +9 -17
- data/lib/david/guerilla/rack/utils.rb +18 -0
- data/lib/david/interop.rb +4 -0
- data/lib/david/interop/mandatory_etsi.rb +4 -0
- data/lib/david/interop/mandatory_etsi/grape.rb +37 -0
- data/lib/david/interop/mandatory_etsi/hobbit.rb +30 -0
- data/lib/david/interop/mandatory_etsi/nyny.rb +36 -0
- data/lib/david/interop/mandatory_etsi/rack.rb +26 -0
- data/lib/david/interop/mandatory_etsi/sinatra.rb +36 -0
- data/lib/david/observe.rb +24 -29
- data/lib/david/rails/action_controller/base.rb +9 -7
- data/lib/david/railties/config.rb +4 -4
- data/lib/david/registry.rb +22 -0
- data/lib/david/server.rb +56 -56
- data/lib/david/server/constants.rb +1 -0
- data/lib/david/server/mapping.rb +43 -11
- data/lib/david/server/mid_cache.rb +29 -0
- data/lib/david/server/multicast.rb +7 -5
- data/lib/david/server/respond.rb +53 -43
- data/lib/david/server/utility.rb +2 -1
- data/lib/david/show_exceptions.rb +12 -8
- data/lib/david/transmitter.rb +44 -0
- data/lib/david/trap.rb +10 -0
- data/lib/david/version.rb +1 -1
- data/lib/rack/handler/david.rb +6 -10
- data/lib/rack/hello_world.rb +25 -0
- data/spec/app_config_spec.rb +56 -0
- data/spec/dummy/app/controllers/etsis_controller.rb +26 -0
- data/spec/dummy/app/controllers/tests_controller.rb +9 -0
- data/spec/dummy/config/application.rb +4 -6
- data/spec/dummy/config/environments/development.rb +2 -2
- data/spec/dummy/config/environments/test.rb +2 -2
- data/spec/dummy/config/routes.rb +13 -53
- data/spec/interop/mandatory_spec.rb +100 -0
- data/spec/observe_spec.rb +8 -7
- data/spec/resource_discovery_spec.rb +3 -3
- data/spec/server_spec.rb +60 -15
- data/spec/spec_helper.rb +21 -2
- metadata +40 -33
- data/lib/david/request.rb +0 -80
- data/lib/david/server/deduplication.rb +0 -21
- data/lib/david/server/options.rb +0 -79
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module ActionController
|
2
|
+
class Base
|
3
|
+
def self.discoverable(options)
|
4
|
+
discovery_actor.register(self, options)
|
5
|
+
end
|
5
6
|
|
6
|
-
|
7
|
+
protected
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
def self.discovery_actor
|
10
|
+
Celluloid::Actor[:discovery]
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
@@ -4,19 +4,19 @@ module David
|
|
4
4
|
config.coap = ActiveSupport::OrderedOptions.new
|
5
5
|
|
6
6
|
# Blockwise transfer
|
7
|
-
config.coap.block =
|
7
|
+
config.coap.block = nil
|
8
8
|
|
9
9
|
# Transparent JSON<>CBOR conversion
|
10
|
-
config.coap.cbor =
|
10
|
+
config.coap.cbor = nil
|
11
11
|
|
12
12
|
# Default Content-Type if HTTP_ACCEPT is empty
|
13
13
|
config.coap.default_format = nil
|
14
14
|
|
15
15
|
# Multicast
|
16
|
-
config.coap.multicast =
|
16
|
+
config.coap.multicast = nil
|
17
17
|
|
18
18
|
# Observe
|
19
|
-
config.coap.observe =
|
19
|
+
config.coap.observe = nil
|
20
20
|
|
21
21
|
# david as default Rack handler (`rails s` starts david)
|
22
22
|
config.coap.only = true
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module David
|
2
|
+
module Registry
|
3
|
+
protected
|
4
|
+
|
5
|
+
def log
|
6
|
+
@log ||= Celluloid.logger
|
7
|
+
@log ||= ::Logger.new(nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
def gc
|
11
|
+
Celluloid::Actor[:gc]
|
12
|
+
end
|
13
|
+
|
14
|
+
def observe
|
15
|
+
Celluloid::Actor[:observe]
|
16
|
+
end
|
17
|
+
|
18
|
+
def server
|
19
|
+
Celluloid::Actor[:server]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/david/server.rb
CHANGED
@@ -1,103 +1,103 @@
|
|
1
|
-
require 'david/
|
1
|
+
require 'david/app_config'
|
2
|
+
require 'david/server/mid_cache'
|
2
3
|
require 'david/server/multicast'
|
3
|
-
require 'david/server/options'
|
4
4
|
require 'david/server/respond'
|
5
5
|
|
6
6
|
module David
|
7
7
|
class Server
|
8
8
|
include Celluloid::IO
|
9
|
-
include CoAP::Coding
|
10
9
|
|
11
|
-
include
|
10
|
+
include MidCache
|
12
11
|
include Multicast
|
13
|
-
include Options
|
14
12
|
include Respond
|
15
13
|
|
16
|
-
attr_reader :
|
14
|
+
attr_reader :socket
|
17
15
|
|
18
16
|
finalizer :shutdown
|
19
17
|
|
20
18
|
def initialize(app, options)
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@logger = choose(:logger, options[:Log])
|
25
|
-
@mcast = choose(:mcast, options[:Multicast])
|
26
|
-
@observe = choose(:observe, options[:Observe])
|
27
|
-
@port = options[:Port].to_i
|
19
|
+
@app = app.respond_to?(:new) ? app.new : app
|
20
|
+
@mid_cache = {}
|
21
|
+
@options = AppConfig.new(options)
|
28
22
|
|
29
|
-
|
23
|
+
host, port = @options.values_at(:Host, :Port)
|
30
24
|
|
31
|
-
|
25
|
+
log.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
|
26
|
+
log.info "Starting on [#{host}]:#{port}"
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
logger.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
|
36
|
-
logger.info "Starting on [#{@host}]:#{@port}"
|
37
|
-
|
38
|
-
@ipv6 = IPAddr.new(@host).ipv6?
|
39
|
-
af = @ipv6 ? ::Socket::AF_INET6 : ::Socket::AF_INET
|
28
|
+
af = ipv6? ? ::Socket::AF_INET6 : ::Socket::AF_INET
|
40
29
|
|
41
30
|
# Actually Celluloid::IO::UDPSocket.
|
42
31
|
@socket = UDPSocket.new(af)
|
43
|
-
multicast_initialize if @
|
44
|
-
@socket.bind(
|
32
|
+
multicast_initialize! if @options[:Multicast]
|
33
|
+
@socket.bind(host, port)
|
34
|
+
end
|
45
35
|
|
46
|
-
|
36
|
+
def run
|
37
|
+
loop do
|
38
|
+
if defined?(JRuby) || defined?(Rubinius)
|
39
|
+
dispatch(*@socket.recvfrom(1152))
|
40
|
+
else
|
41
|
+
begin
|
42
|
+
dispatch(*@socket.to_io.recvmsg_nonblock)
|
43
|
+
rescue ::IO::WaitReadable
|
44
|
+
Celluloid::IO.wait_readable(@socket)
|
45
|
+
retry
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
47
49
|
end
|
48
50
|
|
49
51
|
private
|
50
52
|
|
51
|
-
def
|
53
|
+
def dispatch(*args)
|
52
54
|
data, sender, _, anc = args
|
53
55
|
|
54
|
-
if defined?(JRuby)
|
56
|
+
if defined?(JRuby) || defined?(Rubinius)
|
55
57
|
port, _, host = sender[1..3]
|
56
58
|
else
|
57
59
|
host, port = sender.ip_address, sender.ip_port
|
58
60
|
end
|
59
61
|
|
60
|
-
message
|
61
|
-
|
62
|
+
message = CoAP::Message.parse(data)
|
63
|
+
exchange = Exchange.new(host, port, message, anc)
|
64
|
+
|
65
|
+
return if !exchange.non? && exchange.multicast?
|
62
66
|
|
63
|
-
|
64
|
-
|
65
|
-
return if !request.non? && request.multicast?
|
67
|
+
log.info('<- ' + exchange.to_s)
|
68
|
+
log.debug(message.inspect)
|
66
69
|
|
67
|
-
|
68
|
-
|
70
|
+
key = exchange.key
|
71
|
+
cached = cache_get(key)
|
69
72
|
|
70
|
-
if
|
71
|
-
|
72
|
-
|
73
|
+
if exchange.response? && !cached.nil?
|
74
|
+
cache_delete(key)
|
75
|
+
elsif exchange.request?
|
76
|
+
handle_request(exchange, key, cached)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_request(exchange, key, cached)
|
81
|
+
if exchange.con? && !cached.nil? #&& !exchange.idempotent?
|
82
|
+
response = cached[0]
|
83
|
+
log.debug("dedup cache hit #{exchange.mid}")
|
73
84
|
else
|
74
|
-
response,
|
85
|
+
response, _ = respond(exchange)
|
75
86
|
end
|
76
87
|
|
77
88
|
unless response.nil?
|
78
|
-
|
89
|
+
@socket.send(response.to_wire, 0, exchange.host, exchange.port)
|
79
90
|
|
80
|
-
|
81
|
-
|
91
|
+
exchange.message = response if log.info?
|
92
|
+
log.info('-> ' + exchange.to_s)
|
93
|
+
log.debug(response.inspect)
|
82
94
|
|
83
|
-
|
84
|
-
cache_response(request, response)
|
95
|
+
cache_add(key, response) if response.tt == :ack
|
85
96
|
end
|
86
97
|
end
|
87
98
|
|
88
|
-
def
|
89
|
-
|
90
|
-
if defined?(JRuby)
|
91
|
-
async.handle_input(*@socket.recvfrom(1152))
|
92
|
-
else
|
93
|
-
begin
|
94
|
-
async.handle_input(*@socket.to_io.recvmsg_nonblock)
|
95
|
-
rescue ::IO::WaitReadable
|
96
|
-
Celluloid::IO.wait_readable(@socket)
|
97
|
-
retry
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
99
|
+
def ipv6?
|
100
|
+
IPAddr.new(@options[:Host]).ipv6?
|
101
101
|
end
|
102
102
|
|
103
103
|
def shutdown
|
@@ -27,6 +27,7 @@ module David
|
|
27
27
|
# Freeze CoAP specific env keys.
|
28
28
|
COAP_VERSION = 'coap.version'.freeze
|
29
29
|
COAP_MULTICAST = 'coap.multicast'.freeze
|
30
|
+
COAP_CBOR = 'coap.cbor'.freeze
|
30
31
|
COAP_DTLS = 'coap.dtls'.freeze
|
31
32
|
COAP_DTLS_ID = 'coap.dtls.id'.freeze
|
32
33
|
COAP_DTLS_NOSEC = 'NoSec'.freeze
|
data/lib/david/server/mapping.rb
CHANGED
@@ -20,32 +20,41 @@ module David
|
|
20
20
|
511 => 500,
|
21
21
|
}.freeze
|
22
22
|
|
23
|
+
HTTP_TO_COAP_CODES_MINIMAL = {
|
24
|
+
200 => 205,
|
25
|
+
}.freeze
|
26
|
+
|
23
27
|
protected
|
24
28
|
|
25
29
|
def accept_to_http(request)
|
26
30
|
if request.accept.nil?
|
27
|
-
@
|
31
|
+
@options[:DefaultFormat]
|
28
32
|
else
|
29
33
|
CoAP::Registry.convert_content_format(request.accept)
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
def body_to_cbor(
|
34
|
-
JSON.parse(
|
37
|
+
def body_to_cbor(json)
|
38
|
+
JSON.parse(json).to_cbor
|
35
39
|
end
|
36
40
|
|
37
|
-
def
|
38
|
-
if
|
39
|
-
|
41
|
+
def body_to_json(cbor)
|
42
|
+
if cbor.is_a?(String)
|
43
|
+
CBOR.load(cbor).to_json
|
44
|
+
else
|
45
|
+
cbor.to_json
|
40
46
|
end
|
47
|
+
end
|
41
48
|
|
42
|
-
|
43
|
-
|
49
|
+
def code_to_coap(code)
|
50
|
+
set_http_to_coap_codes!
|
44
51
|
|
45
|
-
|
46
|
-
b = code - (a * 100)
|
52
|
+
return float_to_array(code) if code.is_a?(Float)
|
47
53
|
|
48
|
-
|
54
|
+
code = code.to_i
|
55
|
+
code = @http_to_coap_codes[code] if @http_to_coap_codes[code]
|
56
|
+
|
57
|
+
int_to_array(code)
|
49
58
|
end
|
50
59
|
|
51
60
|
def etag_to_coap(headers, bytes = 8)
|
@@ -63,6 +72,19 @@ module David
|
|
63
72
|
end
|
64
73
|
end
|
65
74
|
|
75
|
+
def float_to_array(float)
|
76
|
+
[float.to_i, (float * 100 % 100).round]
|
77
|
+
end
|
78
|
+
|
79
|
+
def int_to_array(int)
|
80
|
+
int = int.to_i
|
81
|
+
|
82
|
+
a = int / 100
|
83
|
+
b = int - (a * 100)
|
84
|
+
|
85
|
+
[a, b]
|
86
|
+
end
|
87
|
+
|
66
88
|
def location_to_coap(headers)
|
67
89
|
l = headers[HTTP_LOCATION].split('/').reject(&:empty?)
|
68
90
|
return l.empty? ? nil : l
|
@@ -79,6 +101,16 @@ module David
|
|
79
101
|
def method_to_http(method)
|
80
102
|
method.to_s.upcase
|
81
103
|
end
|
104
|
+
|
105
|
+
def set_http_to_coap_codes!
|
106
|
+
@http_to_coap_codes ||= begin
|
107
|
+
if @options && @options[:MinimalMapping]
|
108
|
+
HTTP_TO_COAP_CODES_MINIMAL
|
109
|
+
else
|
110
|
+
HTTP_TO_COAP_CODES
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
82
114
|
end
|
83
115
|
end
|
84
116
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module David
|
2
|
+
module MidCache
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:attr_reader, :mid_cache)
|
5
|
+
end
|
6
|
+
|
7
|
+
def cache
|
8
|
+
@mid_cache
|
9
|
+
end
|
10
|
+
|
11
|
+
def cache_add(key, message)
|
12
|
+
@mid_cache[key] = [message, Time.now.to_i]
|
13
|
+
end
|
14
|
+
|
15
|
+
def cache_clean!(timeout)
|
16
|
+
now = Time.now.to_i
|
17
|
+
@mid_cache.delete_if { |_, v| now - v[1] >= timeout }
|
18
|
+
log.debug(@mid_cache.map { |k, v| v[0].to_s })
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache_delete(key)
|
22
|
+
@mid_cache.delete(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache_get(key)
|
26
|
+
@mid_cache[key]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -2,10 +2,10 @@ module David
|
|
2
2
|
class Server
|
3
3
|
# See https://tools.ietf.org/html/rfc7252#section-12.8
|
4
4
|
module Multicast
|
5
|
-
def multicast_initialize
|
5
|
+
def multicast_initialize!
|
6
6
|
@socket.to_io.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
|
7
7
|
|
8
|
-
if
|
8
|
+
if ipv6?
|
9
9
|
maddrs = ['ff02::fd', 'ff05::fd']
|
10
10
|
maddrs << 'ff02::1' if OS.osx? # OSX needs ff02::1 explicitly joined.
|
11
11
|
maddrs.each { |maddr| multicast_listen_ipv6(maddr) }
|
@@ -18,12 +18,14 @@ module David
|
|
18
18
|
setsockopts_ipv4
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
log.debug "Joined multicast groups: #{maddrs.join(', ')}"
|
22
22
|
rescue Errno::ENODEV, Errno::EADDRNOTAVAIL
|
23
|
-
|
24
|
-
@
|
23
|
+
log.warn 'Multicast initialization failure: Device not found.'
|
24
|
+
@options[:Multicast] = false
|
25
25
|
end
|
26
26
|
|
27
|
+
private
|
28
|
+
|
27
29
|
def multicast_listen_ipv4(address)
|
28
30
|
mreq = IPAddr.new(address).hton + IPAddr.new('0.0.0.0').hton
|
29
31
|
@socket.to_io.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
|
data/lib/david/server/respond.rb
CHANGED
@@ -5,35 +5,51 @@ require 'david/server/utility'
|
|
5
5
|
module David
|
6
6
|
class Server
|
7
7
|
module Respond
|
8
|
+
include CoAP::Coding
|
9
|
+
|
8
10
|
include Constants
|
9
11
|
include Mapping
|
12
|
+
include Registry
|
10
13
|
include Utility
|
11
14
|
|
12
|
-
def respond(
|
13
|
-
block_enabled = @
|
15
|
+
def respond(exchange, env = nil)
|
16
|
+
block_enabled = @options[:Block] && exchange.get?
|
14
17
|
|
15
18
|
if block_enabled
|
16
19
|
# Fail if m set.
|
17
|
-
if
|
18
|
-
return error(
|
20
|
+
if exchange.block.more && !exchange.multicast?
|
21
|
+
return error(exchange, 4.05)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
return error(
|
25
|
+
return error(exchange, 5.05) if exchange.proxy?
|
26
|
+
|
27
|
+
env ||= basic_env(exchange)
|
28
|
+
|
29
|
+
if @options[:CBOR] && exchange.cbor?
|
30
|
+
begin
|
31
|
+
cbor = CBOR.load(exchange.message.payload)
|
32
|
+
|
33
|
+
body = body_to_json(cbor)
|
34
|
+
body = body.force_encoding('ASCII-8BIT') # Rack::Lint insisted...
|
23
35
|
|
24
|
-
|
36
|
+
env[COAP_CBOR] = cbor
|
37
|
+
env[CONTENT_LENGTH] = body.bytesize
|
38
|
+
env[CONTENT_TYPE] = CONTENT_TYPE_JSON
|
39
|
+
env[RACK_INPUT] = StringIO.new(body)
|
40
|
+
rescue EOFError, CBOR::MalformedFormatError
|
41
|
+
end
|
42
|
+
end
|
25
43
|
|
26
44
|
code, headers, body = @app.call(env)
|
27
45
|
|
28
|
-
# No error responses on multicast
|
29
|
-
return if
|
46
|
+
# No error responses on multicast exchanges.
|
47
|
+
return if exchange.multicast? && !(200..299).include?(code)
|
30
48
|
|
31
49
|
ct = headers[HTTP_CONTENT_TYPE]
|
32
50
|
body = body_to_string(body)
|
33
51
|
|
34
|
-
|
35
|
-
|
36
|
-
if @cbor && ct == 'application/json'
|
52
|
+
if @options[:CBOR] && ct == CONTENT_TYPE_JSON
|
37
53
|
begin
|
38
54
|
body = body_to_cbor(body)
|
39
55
|
ct = CONTENT_TYPE_CBOR
|
@@ -41,37 +57,35 @@ module David
|
|
41
57
|
end
|
42
58
|
end
|
43
59
|
|
44
|
-
# No response on
|
45
|
-
return if block_enabled && !
|
60
|
+
# No response on exchange for non-existent block.
|
61
|
+
return if block_enabled && !exchange.block.included_by?(body)
|
46
62
|
|
47
63
|
cf = CoAP::Registry.convert_content_format(ct)
|
48
64
|
etag = etag_to_coap(headers, 4)
|
49
65
|
loc = location_to_coap(headers)
|
50
66
|
ma = max_age_to_coap(headers)
|
51
67
|
mcode = code_to_coap(code)
|
52
|
-
size = headers[HTTP_CONTENT_LENGTH].to_i
|
53
68
|
|
54
69
|
# App returned cf different from accept
|
55
|
-
return error(
|
70
|
+
return error(exchange, 4.06) if exchange.accept && exchange.accept != cf
|
56
71
|
|
57
|
-
response = initialize_response(
|
72
|
+
response = initialize_response(exchange, mcode)
|
58
73
|
|
59
74
|
response.options[:content_format] = cf
|
60
75
|
response.options[:etag] = etag
|
61
76
|
response.options[:location_path] = loc unless loc.nil?
|
62
77
|
response.options[:max_age] = ma.to_i unless ma.nil?
|
63
78
|
|
64
|
-
if @
|
79
|
+
if @options[:Observe] && handle_observe(exchange, env, etag)
|
65
80
|
response.options[:observe] = 0
|
66
81
|
end
|
67
82
|
|
68
83
|
if block_enabled
|
69
|
-
block =
|
84
|
+
block = exchange.block.dup
|
70
85
|
block.set_more!(body)
|
71
86
|
|
72
87
|
response.payload = block.chunk(body)
|
73
88
|
response.options[:block2] = block.encode
|
74
|
-
# response.options[:size2] = size if size != 0
|
75
89
|
else
|
76
90
|
response.payload = body
|
77
91
|
end
|
@@ -81,22 +95,22 @@ module David
|
|
81
95
|
|
82
96
|
private
|
83
97
|
|
84
|
-
def basic_env(
|
85
|
-
m =
|
98
|
+
def basic_env(exchange)
|
99
|
+
m = exchange.message
|
86
100
|
|
87
101
|
{
|
88
|
-
REMOTE_ADDR =>
|
89
|
-
REMOTE_PORT =>
|
102
|
+
REMOTE_ADDR => exchange.host,
|
103
|
+
REMOTE_PORT => exchange.port.to_s,
|
90
104
|
REQUEST_METHOD => method_to_http(m.mcode),
|
91
105
|
SCRIPT_NAME => EMPTY_STRING,
|
92
106
|
PATH_INFO => path_encode(m.options[:uri_path]),
|
93
107
|
QUERY_STRING => query_encode(m.options[:uri_query])
|
94
108
|
.gsub(/^\?/, ''),
|
95
|
-
SERVER_NAME => @
|
96
|
-
SERVER_PORT => @
|
109
|
+
SERVER_NAME => @options[:Host],
|
110
|
+
SERVER_PORT => @options[:Port].to_s,
|
97
111
|
CONTENT_LENGTH => m.payload.bytesize.to_s,
|
98
112
|
CONTENT_TYPE => EMPTY_STRING,
|
99
|
-
HTTP_ACCEPT => accept_to_http(
|
113
|
+
HTTP_ACCEPT => accept_to_http(exchange),
|
100
114
|
RACK_VERSION => [1, 2],
|
101
115
|
RACK_URL_SCHEME => RACK_URL_SCHEME_HTTP,
|
102
116
|
RACK_INPUT => StringIO.new(m.payload),
|
@@ -104,42 +118,38 @@ module David
|
|
104
118
|
RACK_MULTITHREAD => true,
|
105
119
|
RACK_MULTIPROCESS => true,
|
106
120
|
RACK_RUN_ONCE => false,
|
107
|
-
RACK_LOGGER => @
|
121
|
+
RACK_LOGGER => @options[:Log],
|
108
122
|
COAP_VERSION => 1,
|
109
|
-
COAP_MULTICAST =>
|
123
|
+
COAP_MULTICAST => exchange.multicast?,
|
110
124
|
COAP_DTLS => COAP_DTLS_NOSEC,
|
111
125
|
COAP_DTLS_ID => EMPTY_STRING,
|
112
126
|
}
|
113
127
|
end
|
114
128
|
|
115
|
-
def error(
|
116
|
-
[initialize_response(
|
129
|
+
def error(exchange, mcode)
|
130
|
+
[initialize_response(exchange, mcode), retransmit: false]
|
117
131
|
end
|
118
132
|
|
119
|
-
def handle_observe(
|
120
|
-
return unless
|
133
|
+
def handle_observe(exchange, env, etag)
|
134
|
+
return unless exchange.get? && exchange.observe?
|
121
135
|
|
122
|
-
if
|
123
|
-
observe.add(
|
136
|
+
if exchange.message.options[:observe] == 0
|
137
|
+
observe.add(exchange, env, etag)
|
124
138
|
true
|
125
139
|
else
|
126
|
-
observe.delete(
|
140
|
+
observe.delete(exchange)
|
127
141
|
false
|
128
142
|
end
|
129
143
|
end
|
130
144
|
|
131
|
-
def initialize_response(
|
132
|
-
type =
|
145
|
+
def initialize_response(exchange, mcode = 2.05)
|
146
|
+
type = exchange.con? ? :ack : :non
|
133
147
|
|
134
148
|
CoAP::Message.new \
|
135
149
|
tt: type,
|
136
150
|
mcode: mcode,
|
137
|
-
mid:
|
138
|
-
token:
|
139
|
-
end
|
140
|
-
|
141
|
-
def observe
|
142
|
-
Celluloid::Actor[:observe]
|
151
|
+
mid: exchange.message.mid || SecureRandom.random_number(0xffff),
|
152
|
+
token: exchange.token
|
143
153
|
end
|
144
154
|
end
|
145
155
|
end
|