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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +2 -2
  4. data/Gemfile +8 -2
  5. data/Gemfile.lock +84 -44
  6. data/README.md +87 -5
  7. data/benchmarks/Gemfile +1 -0
  8. data/benchmarks/Gemfile.lock +3 -1
  9. data/benchmarks/coapbench.sh +20 -0
  10. data/benchmarks/quick.sh +2 -0
  11. data/benchmarks/rackup/Gemfile +10 -0
  12. data/benchmarks/rackup/Gemfile.lock +159 -0
  13. data/benchmarks/rackup/grape.ru +18 -0
  14. data/benchmarks/rackup/rack.ru +7 -0
  15. data/benchmarks/rackup/rails.ru +14 -0
  16. data/benchmarks/rps.rb +14 -2
  17. data/benchmarks/stress.sh +17 -0
  18. data/david.gemspec +0 -3
  19. data/experiments/Gemfile +6 -0
  20. data/experiments/concurrency/Gemfile +3 -0
  21. data/experiments/concurrency/stub.rb +88 -0
  22. data/experiments/hash_key.rb +64 -0
  23. data/experiments/string_concat.rb +21 -0
  24. data/experiments/symbol_to_proc.rb +15 -0
  25. data/experiments/thread_safe.rb +40 -0
  26. data/lib/david.rb +8 -4
  27. data/lib/david/actor.rb +1 -11
  28. data/lib/david/app_config.rb +112 -0
  29. data/lib/david/exchange.rb +124 -0
  30. data/lib/david/fake_logger.rb +11 -0
  31. data/lib/david/garbage_collector.rb +9 -17
  32. data/lib/david/guerilla/rack/utils.rb +18 -0
  33. data/lib/david/interop.rb +4 -0
  34. data/lib/david/interop/mandatory_etsi.rb +4 -0
  35. data/lib/david/interop/mandatory_etsi/grape.rb +37 -0
  36. data/lib/david/interop/mandatory_etsi/hobbit.rb +30 -0
  37. data/lib/david/interop/mandatory_etsi/nyny.rb +36 -0
  38. data/lib/david/interop/mandatory_etsi/rack.rb +26 -0
  39. data/lib/david/interop/mandatory_etsi/sinatra.rb +36 -0
  40. data/lib/david/observe.rb +24 -29
  41. data/lib/david/rails/action_controller/base.rb +9 -7
  42. data/lib/david/railties/config.rb +4 -4
  43. data/lib/david/registry.rb +22 -0
  44. data/lib/david/server.rb +56 -56
  45. data/lib/david/server/constants.rb +1 -0
  46. data/lib/david/server/mapping.rb +43 -11
  47. data/lib/david/server/mid_cache.rb +29 -0
  48. data/lib/david/server/multicast.rb +7 -5
  49. data/lib/david/server/respond.rb +53 -43
  50. data/lib/david/server/utility.rb +2 -1
  51. data/lib/david/show_exceptions.rb +12 -8
  52. data/lib/david/transmitter.rb +44 -0
  53. data/lib/david/trap.rb +10 -0
  54. data/lib/david/version.rb +1 -1
  55. data/lib/rack/handler/david.rb +6 -10
  56. data/lib/rack/hello_world.rb +25 -0
  57. data/spec/app_config_spec.rb +56 -0
  58. data/spec/dummy/app/controllers/etsis_controller.rb +26 -0
  59. data/spec/dummy/app/controllers/tests_controller.rb +9 -0
  60. data/spec/dummy/config/application.rb +4 -6
  61. data/spec/dummy/config/environments/development.rb +2 -2
  62. data/spec/dummy/config/environments/test.rb +2 -2
  63. data/spec/dummy/config/routes.rb +13 -53
  64. data/spec/interop/mandatory_spec.rb +100 -0
  65. data/spec/observe_spec.rb +8 -7
  66. data/spec/resource_discovery_spec.rb +3 -3
  67. data/spec/server_spec.rb +60 -15
  68. data/spec/spec_helper.rb +21 -2
  69. metadata +40 -33
  70. data/lib/david/request.rb +0 -80
  71. data/lib/david/server/deduplication.rb +0 -21
  72. data/lib/david/server/options.rb +0 -79
@@ -1,11 +1,13 @@
1
- class ActionController::Base
2
- def self.discoverable(options)
3
- discovery_actor.register(self, options)
4
- end
1
+ module ActionController
2
+ class Base
3
+ def self.discoverable(options)
4
+ discovery_actor.register(self, options)
5
+ end
5
6
 
6
- protected
7
+ protected
7
8
 
8
- def self.discovery_actor
9
- Celluloid::Actor[:discovery]
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 = true
7
+ config.coap.block = nil
8
8
 
9
9
  # Transparent JSON<>CBOR conversion
10
- config.coap.cbor = false
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 = true
16
+ config.coap.multicast = nil
17
17
 
18
18
  # Observe
19
- config.coap.observe = true
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/server/deduplication'
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 Deduplication
10
+ include MidCache
12
11
  include Multicast
13
- include Options
14
12
  include Respond
15
13
 
16
- attr_reader :logger, :socket
14
+ attr_reader :socket
17
15
 
18
16
  finalizer :shutdown
19
17
 
20
18
  def initialize(app, options)
21
- @block = choose(:block, options[:Block])
22
- @cbor = choose(:cbor, options[:CBOR])
23
- @host = choose(:host, options[:Host])
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
- @app = app.respond_to?(:new) ? app.new : app
23
+ host, port = @options.values_at(:Host, :Port)
30
24
 
31
- @default_format = choose(:default_format, options[:DefaultFormat])
25
+ log.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
26
+ log.info "Starting on [#{host}]:#{port}"
32
27
 
33
- @dedup_cache = {}
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 @mcast
44
- @socket.bind(@host, @port)
32
+ multicast_initialize! if @options[:Multicast]
33
+ @socket.bind(host, port)
34
+ end
45
35
 
46
- async.run
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 handle_input(*args)
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 = CoAP::Message.parse(data)
61
- request = Request.new(host, port, message, anc)
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
- return unless request.con? || request.non?
64
- return unless request.valid_method?
65
- return if !request.non? && request.multicast?
67
+ log.info('<- ' + exchange.to_s)
68
+ log.debug(message.inspect)
66
69
 
67
- logger.info "[#{host}]:#{port}: #{message} (block #{request.block.num})"
68
- logger.debug message.inspect
70
+ key = exchange.key
71
+ cached = cache_get(key)
69
72
 
70
- if request.con? && duplicate?(request) #&& !request.idempotent?
71
- response, options = cached_response(request)
72
- logger.debug "(mid:#{request.mid} duplicate, response cached)"
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, options = respond(request)
85
+ response, _ = respond(exchange)
75
86
  end
76
87
 
77
88
  unless response.nil?
78
- logger.debug response.inspect
89
+ @socket.send(response.to_wire, 0, exchange.host, exchange.port)
79
90
 
80
- CoAP::Transmission.send(response, host, port,
81
- options.merge(socket: @socket))
91
+ exchange.message = response if log.info?
92
+ log.info('-> ' + exchange.to_s)
93
+ log.debug(response.inspect)
82
94
 
83
- request.options = options
84
- cache_response(request, response)
95
+ cache_add(key, response) if response.tt == :ack
85
96
  end
86
97
  end
87
98
 
88
- def run
89
- loop do
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
@@ -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
- @default_format
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(body)
34
- JSON.parse(body).to_cbor
37
+ def body_to_cbor(json)
38
+ JSON.parse(json).to_cbor
35
39
  end
36
40
 
37
- def code_to_coap(code)
38
- if code.is_a?(Float)
39
- return [code.to_i, (code * 100 % 100).round]
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
- code = code.to_i
43
- code = HTTP_TO_COAP_CODES[code] if HTTP_TO_COAP_CODES[code]
49
+ def code_to_coap(code)
50
+ set_http_to_coap_codes!
44
51
 
45
- a = code / 100
46
- b = code - (a * 100)
52
+ return float_to_array(code) if code.is_a?(Float)
47
53
 
48
- [a, b]
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 @ipv6
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
- logger.debug "Joined multicast groups: #{maddrs.join(', ')}"
21
+ log.debug "Joined multicast groups: #{maddrs.join(', ')}"
22
22
  rescue Errno::ENODEV, Errno::EADDRNOTAVAIL
23
- logger.warn 'Multicast initialization failure: Device not found.'
24
- @mcast = false
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)
@@ -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(request, env = nil)
13
- block_enabled = @block && request.get?
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 request.block.more && !request.multicast?
18
- return error(request, 4.05)
20
+ if exchange.block.more && !exchange.multicast?
21
+ return error(exchange, 4.05)
19
22
  end
20
23
  end
21
24
 
22
- return error(request, 5.05) if request.proxy?
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
- env ||= basic_env(request)
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 requests.
29
- return if request.multicast? && !(200..299).include?(code)
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
- body.close if body.respond_to?(:close)
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 request for non-existent block.
45
- return if block_enabled && !request.block.included_by?(body)
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(request, 4.06) if request.accept && request.accept != cf
70
+ return error(exchange, 4.06) if exchange.accept && exchange.accept != cf
56
71
 
57
- response = initialize_response(request, mcode)
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 @observe && handle_observe(request, env, etag)
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 = request.block.dup
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(request)
85
- m = request.message
98
+ def basic_env(exchange)
99
+ m = exchange.message
86
100
 
87
101
  {
88
- REMOTE_ADDR => request.host,
89
- REMOTE_PORT => request.port.to_s,
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 => @host,
96
- SERVER_PORT => @port.to_s,
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(request),
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 => @logger,
121
+ RACK_LOGGER => @options[:Log],
108
122
  COAP_VERSION => 1,
109
- COAP_MULTICAST => request.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(request, mcode)
116
- [initialize_response(request, mcode), retransmit: false]
129
+ def error(exchange, mcode)
130
+ [initialize_response(exchange, mcode), retransmit: false]
117
131
  end
118
132
 
119
- def handle_observe(request, env, etag)
120
- return unless request.get? && request.observe?
133
+ def handle_observe(exchange, env, etag)
134
+ return unless exchange.get? && exchange.observe?
121
135
 
122
- if request.message.options[:observe] == 0
123
- observe.add(request, env, etag)
136
+ if exchange.message.options[:observe] == 0
137
+ observe.add(exchange, env, etag)
124
138
  true
125
139
  else
126
- observe.delete(request)
140
+ observe.delete(exchange)
127
141
  false
128
142
  end
129
143
  end
130
144
 
131
- def initialize_response(request, mcode = 2.00)
132
- type = request.con? ? :ack : :non
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: request.message.mid || SecureRandom.random_number(0xffff),
138
- token: request.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