david 0.3.0 → 0.4.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/.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
|