takagi 0.1.0 → 1.1.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/.rubocop.yml +70 -7
- data/.yard/templates/default/layout/html/layout.erb +34 -0
- data/AGENTS.md +16 -0
- data/CHANGELOG.md +158 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +590 -23
- data/ROADMAP.md +55 -0
- data/Rakefile +4 -4
- data/Steepfile +39 -0
- data/bin/takagi-dev +159 -0
- data/docs/FIRST_PLUGIN_GUIDE.md +224 -0
- data/docs/HOOKS.md +31 -0
- data/examples/client_lifecycle_example.rb +118 -0
- data/examples/cloud_gateway_app.rb +217 -0
- data/examples/nested_api_app.rb +258 -0
- data/examples/simple_device_app.rb +71 -0
- data/examples/takagi.yml +138 -0
- data/lib/takagi/application.rb +256 -0
- data/lib/takagi/base/middleware_management.rb +39 -0
- data/lib/takagi/base/plugin_management.rb +75 -0
- data/lib/takagi/base/reactor_management.rb +104 -0
- data/lib/takagi/base/server_lifecycle.rb +156 -0
- data/lib/takagi/base.rb +103 -11
- data/lib/takagi/branding.rb +88 -0
- data/lib/takagi/cbor/decoder.rb +385 -0
- data/lib/takagi/cbor/encoder.rb +260 -0
- data/lib/takagi/cbor/error.rb +17 -0
- data/lib/takagi/cbor/version.rb +9 -0
- data/lib/takagi/client/response.rb +236 -0
- data/lib/takagi/client.rb +265 -0
- data/lib/takagi/client_base.rb +204 -0
- data/lib/takagi/coap/code_helpers.rb +190 -0
- data/lib/takagi/coap/registries/base.rb +165 -0
- data/lib/takagi/coap/registries/content_format.rb +71 -0
- data/lib/takagi/coap/registries/message_type.rb +69 -0
- data/lib/takagi/coap/registries/method.rb +38 -0
- data/lib/takagi/coap/registries/option.rb +71 -0
- data/lib/takagi/coap/registries/response.rb +93 -0
- data/lib/takagi/coap/registries/signaling.rb +34 -0
- data/lib/takagi/coap/signaling.rb +10 -0
- data/lib/takagi/coap.rb +37 -0
- data/lib/takagi/composite_router.rb +186 -0
- data/lib/takagi/config.rb +337 -0
- data/lib/takagi/controller/resource_allocator.rb +164 -0
- data/lib/takagi/controller/thread_pool.rb +144 -0
- data/lib/takagi/controller.rb +319 -0
- data/lib/takagi/core/attribute_set.rb +128 -0
- data/lib/takagi/discovery/core_link_format.rb +137 -0
- data/lib/takagi/errors.rb +536 -0
- data/lib/takagi/event_bus/address_prefix.rb +142 -0
- data/lib/takagi/event_bus/async_executor.rb +235 -0
- data/lib/takagi/event_bus/coap_bridge.rb +208 -0
- data/lib/takagi/event_bus/future.rb +153 -0
- data/lib/takagi/event_bus/lru_cache.rb +157 -0
- data/lib/takagi/event_bus/message_buffer.rb +237 -0
- data/lib/takagi/event_bus/observer_cleanup.rb +110 -0
- data/lib/takagi/event_bus/scope.rb +74 -0
- data/lib/takagi/event_bus.rb +594 -0
- data/lib/takagi/helpers.rb +88 -0
- data/lib/takagi/hooks.rb +82 -0
- data/lib/takagi/initializer.rb +18 -0
- data/lib/takagi/logger.rb +15 -6
- data/lib/takagi/message/base.rb +155 -0
- data/lib/takagi/message/deduplication_cache.rb +84 -0
- data/lib/takagi/message/inbound.rb +147 -0
- data/lib/takagi/message/outbound.rb +223 -0
- data/lib/takagi/message/request.rb +158 -0
- data/lib/takagi/message/retransmission_manager.rb +193 -0
- data/lib/takagi/middleware/authentication.rb +19 -0
- data/lib/takagi/middleware/caching.rb +23 -0
- data/lib/takagi/middleware/debugging.rb +16 -0
- data/lib/takagi/middleware/logging.rb +14 -0
- data/lib/takagi/middleware/metrics.rb +440 -0
- data/lib/takagi/middleware/rate_limiting.rb +24 -0
- data/lib/takagi/middleware_stack.rb +166 -0
- data/lib/takagi/network/base.rb +76 -0
- data/lib/takagi/network/framing/tcp.rb +222 -0
- data/lib/takagi/network/framing/udp.rb +110 -0
- data/lib/takagi/network/registry.rb +72 -0
- data/lib/takagi/network/tcp.rb +60 -0
- data/lib/takagi/network/tcp_sender.rb +21 -0
- data/lib/takagi/network/udp.rb +61 -0
- data/lib/takagi/network/udp_sender.rb +20 -0
- data/lib/takagi/observable/emitter.rb +62 -0
- data/lib/takagi/observable/reactor.rb +488 -0
- data/lib/takagi/observable/registry.rb +122 -0
- data/lib/takagi/observe_registry.rb +10 -0
- data/lib/takagi/observer/client.rb +68 -0
- data/lib/takagi/observer/registry.rb +137 -0
- data/lib/takagi/observer/sender.rb +39 -0
- data/lib/takagi/observer/watcher.rb +43 -0
- data/lib/takagi/plugin.rb +313 -0
- data/lib/takagi/profiles.rb +176 -0
- data/lib/takagi/reactor.rb +23 -0
- data/lib/takagi/reactor_registry.rb +64 -0
- data/lib/takagi/registry/base.rb +268 -0
- data/lib/takagi/response_builder.rb +141 -0
- data/lib/takagi/router/metadata_extractor.rb +133 -0
- data/lib/takagi/router/route_matcher.rb +83 -0
- data/lib/takagi/router.rb +284 -25
- data/lib/takagi/serialization/base.rb +102 -0
- data/lib/takagi/serialization/cbor_serializer.rb +92 -0
- data/lib/takagi/serialization/json_serializer.rb +96 -0
- data/lib/takagi/serialization/octet_stream_serializer.rb +82 -0
- data/lib/takagi/serialization/registry.rb +187 -0
- data/lib/takagi/serialization/text_serializer.rb +87 -0
- data/lib/takagi/serialization.rb +117 -0
- data/lib/takagi/server/multi.rb +41 -0
- data/lib/takagi/server/registry.rb +71 -0
- data/lib/takagi/server/tcp.rb +249 -0
- data/lib/takagi/server/udp.rb +139 -0
- data/lib/takagi/server/udp_worker.rb +174 -0
- data/lib/takagi/server.rb +1 -31
- data/lib/takagi/server_registry.rb +10 -0
- data/lib/takagi/tcp_client.rb +142 -0
- data/lib/takagi/version.rb +2 -1
- data/lib/takagi.rb +24 -3
- data/sig/takagi/application.rbs +48 -0
- data/sig/takagi/base/middleware_management.rbs +33 -0
- data/sig/takagi/base/reactor_management.rbs +52 -0
- data/sig/takagi/base/server_lifecycle.rbs +54 -0
- data/sig/takagi/base.rbs +48 -0
- data/sig/takagi/cbor/decoder.rbs +171 -0
- data/sig/takagi/cbor/encoder.rbs +146 -0
- data/sig/takagi/cbor/error.rbs +19 -0
- data/sig/takagi/cbor/version.rbs +7 -0
- data/sig/takagi/client/response.rbs +148 -0
- data/sig/takagi/client.rbs +119 -0
- data/sig/takagi/client_base.rbs +135 -0
- data/sig/takagi/coap/code_helpers.rbs +91 -0
- data/sig/takagi/coap/registries/base.rbs +95 -0
- data/sig/takagi/coap/registries/content_format.rbs +47 -0
- data/sig/takagi/coap/registries/message_type.rbs +53 -0
- data/sig/takagi/coap/registries/method.rbs +27 -0
- data/sig/takagi/coap/registries/option.rbs +43 -0
- data/sig/takagi/coap/registries/response.rbs +52 -0
- data/sig/takagi/coap.rbs +24 -0
- data/sig/takagi/composite_router.rbs +46 -0
- data/sig/takagi/config.rbs +134 -0
- data/sig/takagi/controller.rbs +73 -0
- data/sig/takagi/core/attribute_set.rbs +57 -0
- data/sig/takagi/discovery/core_link_format.rbs +50 -0
- data/sig/takagi/event_bus/address_prefix.rbs +78 -0
- data/sig/takagi/event_bus/async_executor.rbs +88 -0
- data/sig/takagi/event_bus/coap_bridge.rbs +93 -0
- data/sig/takagi/event_bus/future.rbs +78 -0
- data/sig/takagi/event_bus/lru_cache.rbs +86 -0
- data/sig/takagi/event_bus/message_buffer.rbs +133 -0
- data/sig/takagi/event_bus/observer_cleanup.rbs +62 -0
- data/sig/takagi/event_bus.rbs +320 -0
- data/sig/takagi/helpers.rbs +34 -0
- data/sig/takagi/initializer.rbs +9 -0
- data/sig/takagi/logger.rbs +17 -0
- data/sig/takagi/message/base.rbs +64 -0
- data/sig/takagi/message/deduplication_cache.rbs +49 -0
- data/sig/takagi/message/inbound.rbs +76 -0
- data/sig/takagi/message/outbound.rbs +48 -0
- data/sig/takagi/message/request.rbs +32 -0
- data/sig/takagi/message/retransmission_manager.rbs +76 -0
- data/sig/takagi/middleware/authentication.rbs +11 -0
- data/sig/takagi/middleware/caching.rbs +13 -0
- data/sig/takagi/middleware/debugging.rbs +9 -0
- data/sig/takagi/middleware/logging.rbs +7 -0
- data/sig/takagi/middleware/metrics.rbs +15 -0
- data/sig/takagi/middleware/rate_limiting.rbs +13 -0
- data/sig/takagi/middleware_stack.rbs +69 -0
- data/sig/takagi/network/tcp_sender.rbs +10 -0
- data/sig/takagi/network/udp_sender.rbs +14 -0
- data/sig/takagi/observe_registry.rbs +36 -0
- data/sig/takagi/observer/client.rbs +36 -0
- data/sig/takagi/observer/sender.rbs +12 -0
- data/sig/takagi/observer/watcher.rbs +18 -0
- data/sig/takagi/profiles.rbs +33 -0
- data/sig/takagi/reactor.rbs +20 -0
- data/sig/takagi/reactor_registry.rbs +14 -0
- data/sig/takagi/response_builder.rbs +12 -0
- data/sig/takagi/router/metadata_extractor.rbs +71 -0
- data/sig/takagi/router/route_matcher.rbs +43 -0
- data/sig/takagi/router.rbs +166 -0
- data/sig/takagi/serialization.rbs +32 -0
- data/sig/takagi/server/multi.rbs +16 -0
- data/sig/takagi/server/tcp.rbs +42 -0
- data/sig/takagi/server/udp.rbs +52 -0
- data/sig/takagi/server/udp_worker.rbs +42 -0
- data/sig/takagi/server.rbs +4 -0
- data/sig/takagi/server_registry.rbs +71 -0
- data/sig/takagi/tcp_client.rbs +23 -0
- data/sig/takagi/version.rbs +5 -0
- data/takagi.gemspec +37 -35
- metadata +204 -31
- data/.idea/.gitignore +0 -8
- data/.idea/misc.xml +0 -4
- data/.idea/modules.xml +0 -8
- data/.idea/takagi.iml +0 -81
- data/.idea/vcs.xml +0 -6
- data/lib/takagi/message.rb +0 -75
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# Base class for extensible constant registries.
|
|
7
|
+
#
|
|
8
|
+
# Provides a pattern for registering CoAP protocol constants with:
|
|
9
|
+
# - Numeric value
|
|
10
|
+
# - Human-readable name
|
|
11
|
+
# - Symbol accessor (optional)
|
|
12
|
+
# - Thread-safe storage
|
|
13
|
+
#
|
|
14
|
+
# This allows plugins to extend the protocol without modifying core code.
|
|
15
|
+
#
|
|
16
|
+
# @example Creating a custom registry
|
|
17
|
+
# class MyRegistry < Registries::Base
|
|
18
|
+
# register(1, 'First Value', :first)
|
|
19
|
+
# register(2, 'Second Value', :second)
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# MyRegistry::FIRST # => 1
|
|
23
|
+
# MyRegistry.name_for(1) # => "First Value"
|
|
24
|
+
# MyRegistry.all # => {1 => "First Value", 2 => "Second Value"}
|
|
25
|
+
require 'takagi/hooks'
|
|
26
|
+
|
|
27
|
+
class Base
|
|
28
|
+
def self.inherited(subclass)
|
|
29
|
+
super
|
|
30
|
+
subclass.instance_variable_set(:@mutex, Mutex.new)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Register a new constant in the registry
|
|
35
|
+
#
|
|
36
|
+
# @param value [Integer] Numeric value of the constant
|
|
37
|
+
# @param name [String] Human-readable name
|
|
38
|
+
# @param symbol [Symbol, nil] Optional symbol for constant access
|
|
39
|
+
# @param rfc [String, nil] Optional RFC reference
|
|
40
|
+
#
|
|
41
|
+
# @example
|
|
42
|
+
# register(69, '2.05 Content', :content, rfc: 'RFC 7252 §5.9.1.4')
|
|
43
|
+
def register(value, name, symbol = nil, rfc: nil)
|
|
44
|
+
@mutex.synchronize do
|
|
45
|
+
registry[value] = {
|
|
46
|
+
name: name,
|
|
47
|
+
symbol: symbol,
|
|
48
|
+
rfc: rfc
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Create constant if symbol provided
|
|
52
|
+
if symbol
|
|
53
|
+
const_name = symbol.to_s.upcase
|
|
54
|
+
const_set(const_name, value) unless const_defined?(const_name, false)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Store reverse lookup
|
|
58
|
+
reverse_registry[name] = value if name
|
|
59
|
+
reverse_registry[symbol] = value if symbol
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Takagi::Hooks.emit(
|
|
63
|
+
:coap_registry_registered,
|
|
64
|
+
registry: self,
|
|
65
|
+
value: value,
|
|
66
|
+
name: name,
|
|
67
|
+
symbol: symbol,
|
|
68
|
+
rfc: rfc
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Get human-readable name for a value
|
|
73
|
+
#
|
|
74
|
+
# @param value [Integer] The numeric value
|
|
75
|
+
# @return [String, nil] The name, or nil if not found
|
|
76
|
+
def name_for(value)
|
|
77
|
+
@mutex.synchronize { registry[value]&.dig(:name) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get numeric value for a name or symbol
|
|
81
|
+
#
|
|
82
|
+
# @param key [String, Symbol] The name or symbol
|
|
83
|
+
# @return [Integer, nil] The value, or nil if not found
|
|
84
|
+
def value_for(key)
|
|
85
|
+
@mutex.synchronize { reverse_registry[key] }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Get RFC reference for a value
|
|
89
|
+
#
|
|
90
|
+
# @param value [Integer] The numeric value
|
|
91
|
+
# @return [String, nil] The RFC reference, or nil if not found
|
|
92
|
+
def rfc_for(value)
|
|
93
|
+
@mutex.synchronize { registry[value]&.dig(:rfc) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Check if a value is registered
|
|
97
|
+
#
|
|
98
|
+
# @param value [Integer] The value to check
|
|
99
|
+
# @return [Boolean] true if registered
|
|
100
|
+
def registered?(value)
|
|
101
|
+
@mutex.synchronize { registry.key?(value) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Get all registered constants
|
|
105
|
+
#
|
|
106
|
+
# @return [Hash] Map of value => name
|
|
107
|
+
def all
|
|
108
|
+
@mutex.synchronize { registry.transform_values { |info| info[:name] } }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Iterate over registered values
|
|
112
|
+
#
|
|
113
|
+
# @yield [Integer] each registered numeric value
|
|
114
|
+
# @return [Enumerator] if no block given
|
|
115
|
+
def each_value(&block)
|
|
116
|
+
return enum_for(:each_value) unless block_given?
|
|
117
|
+
|
|
118
|
+
# Get snapshot to avoid holding lock during iteration
|
|
119
|
+
snapshot = @mutex.synchronize { registry.keys.dup }
|
|
120
|
+
snapshot.each(&block)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Get all registered values
|
|
124
|
+
#
|
|
125
|
+
# @return [Array<Integer>] Array of all values
|
|
126
|
+
def values
|
|
127
|
+
@mutex.synchronize { registry.keys.dup }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Get registry metadata for a value
|
|
131
|
+
#
|
|
132
|
+
# @param value [Integer] The numeric value
|
|
133
|
+
# @return [Hash, nil] Full metadata hash
|
|
134
|
+
def metadata_for(value)
|
|
135
|
+
@mutex.synchronize { registry[value]&.dup }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Clear all registrations (useful for testing)
|
|
139
|
+
def clear!
|
|
140
|
+
@mutex.synchronize do
|
|
141
|
+
registry.clear
|
|
142
|
+
reverse_registry.clear
|
|
143
|
+
# Remove constants (carefully)
|
|
144
|
+
constants(false).each do |const_name|
|
|
145
|
+
remove_const(const_name) if const_name =~ /^[A-Z_]+$/
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
Takagi::Hooks.emit(:coap_registry_cleared, registry: self)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def registry
|
|
155
|
+
@registry ||= {}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reverse_registry
|
|
159
|
+
@reverse_registry ||= {}
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Content-Format Registry (RFC 7252 §12.3)
|
|
7
|
+
#
|
|
8
|
+
# Extensible registry for CoAP content-format codes.
|
|
9
|
+
# Plugins can register custom content formats without modifying core code.
|
|
10
|
+
#
|
|
11
|
+
# @example Using predefined formats
|
|
12
|
+
# Takagi::CoAP::Registries::ContentFormat::JSON # => 50
|
|
13
|
+
# Takagi::CoAP::Registries::ContentFormat::TEXT_PLAIN # => 0
|
|
14
|
+
#
|
|
15
|
+
# @example Registering a custom format
|
|
16
|
+
# Takagi::CoAP::Registries::ContentFormat.register(65000, 'application/custom', :custom)
|
|
17
|
+
# Takagi::CoAP::Registries::ContentFormat::CUSTOM # => 65000
|
|
18
|
+
#
|
|
19
|
+
# @example Looking up format names
|
|
20
|
+
# Takagi::CoAP::Registries::ContentFormat.name_for(50) # => "application/json"
|
|
21
|
+
class ContentFormat < Base
|
|
22
|
+
# RFC 7252 §12.3 - Content-Format Registry
|
|
23
|
+
register(0, 'text/plain', :text_plain, rfc: 'RFC 7252 §12.3')
|
|
24
|
+
register(40, 'application/link-format', :link_format, rfc: 'RFC 6690')
|
|
25
|
+
register(41, 'application/xml', :xml, rfc: 'RFC 7252 §12.3')
|
|
26
|
+
register(42, 'application/octet-stream', :octet_stream, rfc: 'RFC 7252 §12.3')
|
|
27
|
+
register(47, 'application/exi', :exi, rfc: 'RFC 7252 §12.3')
|
|
28
|
+
register(50, 'application/json', :json, rfc: 'RFC 7252 §12.3')
|
|
29
|
+
register(60, 'application/cbor', :cbor, rfc: 'RFC 7049')
|
|
30
|
+
|
|
31
|
+
# Get MIME type for a content-format code
|
|
32
|
+
# @param code [Integer] Content-format code
|
|
33
|
+
# @return [String, nil] MIME type
|
|
34
|
+
def self.mime_type_for(code)
|
|
35
|
+
name_for(code)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get content-format code for a MIME type
|
|
39
|
+
# @param mime_type [String] MIME type
|
|
40
|
+
# @return [Integer, nil] Content-format code
|
|
41
|
+
def self.code_for_mime(mime_type)
|
|
42
|
+
value_for(mime_type)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if format is JSON-based
|
|
46
|
+
# @param code [Integer] Content-format code
|
|
47
|
+
# @return [Boolean] true if JSON format
|
|
48
|
+
def self.json?(code)
|
|
49
|
+
mime = mime_type_for(code)
|
|
50
|
+
mime&.include?('json') || false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Check if format is CBOR-based
|
|
54
|
+
# @param code [Integer] Content-format code
|
|
55
|
+
# @return [Boolean] true if CBOR format
|
|
56
|
+
def self.cbor?(code)
|
|
57
|
+
mime = mime_type_for(code)
|
|
58
|
+
mime&.include?('cbor') || false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Check if format is text-based
|
|
62
|
+
# @param code [Integer] Content-format code
|
|
63
|
+
# @return [Boolean] true if text format
|
|
64
|
+
def self.text?(code)
|
|
65
|
+
mime = mime_type_for(code)
|
|
66
|
+
mime&.start_with?('text/') || false
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Message Type Registry (RFC 7252 §3)
|
|
7
|
+
#
|
|
8
|
+
# Extensible registry for CoAP message types.
|
|
9
|
+
#
|
|
10
|
+
# @example Using predefined types
|
|
11
|
+
# Takagi::CoAP::Registries::MessageType::CONFIRMABLE # => 0
|
|
12
|
+
# Takagi::CoAP::Registries::MessageType::ACK # => 2
|
|
13
|
+
#
|
|
14
|
+
# @example Looking up type names
|
|
15
|
+
# Takagi::CoAP::Registries::MessageType.name_for(0) # => "Confirmable"
|
|
16
|
+
class MessageType < Base
|
|
17
|
+
# RFC 7252 §3 - Message Format
|
|
18
|
+
register(0, 'Confirmable', :confirmable, rfc: 'RFC 7252 §3')
|
|
19
|
+
register(1, 'Non-confirmable', :non_confirmable, rfc: 'RFC 7252 §3')
|
|
20
|
+
register(2, 'Acknowledgement', :acknowledgement, rfc: 'RFC 7252 §3')
|
|
21
|
+
register(3, 'Reset', :reset, rfc: 'RFC 7252 §3')
|
|
22
|
+
|
|
23
|
+
# Aliases for convenience
|
|
24
|
+
CON = CONFIRMABLE
|
|
25
|
+
NON = NON_CONFIRMABLE
|
|
26
|
+
ACK = ACKNOWLEDGEMENT
|
|
27
|
+
RST = RESET
|
|
28
|
+
|
|
29
|
+
# Check if type is confirmable
|
|
30
|
+
# @param type [Integer] Message type
|
|
31
|
+
# @return [Boolean] true if confirmable
|
|
32
|
+
def self.confirmable?(type)
|
|
33
|
+
type == CONFIRMABLE
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check if type is non-confirmable
|
|
37
|
+
# @param type [Integer] Message type
|
|
38
|
+
# @return [Boolean] true if non-confirmable
|
|
39
|
+
def self.non_confirmable?(type)
|
|
40
|
+
type == NON_CONFIRMABLE
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if type is acknowledgement
|
|
44
|
+
# @param type [Integer] Message type
|
|
45
|
+
# @return [Boolean] true if acknowledgement
|
|
46
|
+
def self.acknowledgement?(type)
|
|
47
|
+
type == ACKNOWLEDGEMENT
|
|
48
|
+
end
|
|
49
|
+
class << self
|
|
50
|
+
alias ack? acknowledgement?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Check if type is reset
|
|
54
|
+
# @param type [Integer] Message type
|
|
55
|
+
# @return [Boolean] true if reset
|
|
56
|
+
def self.reset?(type)
|
|
57
|
+
type == RESET
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Check if type is valid
|
|
61
|
+
# @param type [Integer] Message type
|
|
62
|
+
# @return [Boolean] true if valid
|
|
63
|
+
def self.valid?(type)
|
|
64
|
+
(0..3).include?(type)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Method Code Registry (RFC 7252 §12.1.1)
|
|
7
|
+
#
|
|
8
|
+
# Extensible registry for CoAP request method codes.
|
|
9
|
+
# Plugins can register custom methods without modifying core code.
|
|
10
|
+
#
|
|
11
|
+
# @example Using predefined methods
|
|
12
|
+
# Takagi::CoAP::Registries::Method::GET # => 1
|
|
13
|
+
# Takagi::CoAP::Registries::Method::POST # => 2
|
|
14
|
+
#
|
|
15
|
+
# @example Registering a custom method
|
|
16
|
+
# Takagi::CoAP::Registries::Method.register(5, 'FETCH', :fetch, rfc: 'RFC 8132')
|
|
17
|
+
# Takagi::CoAP::Registries::Method::FETCH # => 5
|
|
18
|
+
#
|
|
19
|
+
# @example Looking up method names
|
|
20
|
+
# Takagi::CoAP::Registries::Method.name_for(1) # => "GET"
|
|
21
|
+
class Method < Base
|
|
22
|
+
# RFC 7252 §5.8 - Method Codes
|
|
23
|
+
register(0, 'EMPTY', :empty, rfc: 'RFC 7252 §5.8')
|
|
24
|
+
register(1, 'GET', :get, rfc: 'RFC 7252 §5.8.1')
|
|
25
|
+
register(2, 'POST', :post, rfc: 'RFC 7252 §5.8.2')
|
|
26
|
+
register(3, 'PUT', :put, rfc: 'RFC 7252 §5.8.3')
|
|
27
|
+
register(4, 'DELETE', :delete, rfc: 'RFC 7252 §5.8.4')
|
|
28
|
+
|
|
29
|
+
# Check if code is a valid method code
|
|
30
|
+
# @param code [Integer] Code to check
|
|
31
|
+
# @return [Boolean] true if valid method
|
|
32
|
+
def self.valid?(code)
|
|
33
|
+
(0..31).include?(code) && registered?(code)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Option Number Registry (RFC 7252 §5.10)
|
|
7
|
+
#
|
|
8
|
+
# Extensible registry for CoAP option numbers.
|
|
9
|
+
# Plugins can register custom options without modifying core code.
|
|
10
|
+
#
|
|
11
|
+
# @example Using predefined options
|
|
12
|
+
# Takagi::CoAP::Registries::Option::URI_PATH # => 11
|
|
13
|
+
# Takagi::CoAP::Registries::Option::CONTENT_FORMAT # => 12
|
|
14
|
+
#
|
|
15
|
+
# @example Registering a custom option
|
|
16
|
+
# Takagi::CoAP::Registries::Option.register(65000, 'Custom-Option', :custom_option)
|
|
17
|
+
# Takagi::CoAP::Registries::Option::CUSTOM_OPTION # => 65000
|
|
18
|
+
#
|
|
19
|
+
# @example Looking up option names
|
|
20
|
+
# Takagi::CoAP::Registries::Option.name_for(11) # => "Uri-Path"
|
|
21
|
+
class Option < Base
|
|
22
|
+
# RFC 7252 §5.10.1 - Option Definitions
|
|
23
|
+
register(1, 'If-Match', :if_match, rfc: 'RFC 7252 §5.10.8.1')
|
|
24
|
+
register(3, 'Uri-Host', :uri_host, rfc: 'RFC 7252 §5.10.1')
|
|
25
|
+
register(4, 'ETag', :etag, rfc: 'RFC 7252 §5.10.6')
|
|
26
|
+
register(5, 'If-None-Match', :if_none_match, rfc: 'RFC 7252 §5.10.8.2')
|
|
27
|
+
register(6, 'Observe', :observe, rfc: 'RFC 7641 §2')
|
|
28
|
+
register(7, 'Uri-Port', :uri_port, rfc: 'RFC 7252 §5.10.1')
|
|
29
|
+
register(8, 'Location-Path', :location_path, rfc: 'RFC 7252 §5.10.7')
|
|
30
|
+
register(11, 'Uri-Path', :uri_path, rfc: 'RFC 7252 §5.10.1')
|
|
31
|
+
register(12, 'Content-Format', :content_format, rfc: 'RFC 7252 §5.10.3')
|
|
32
|
+
register(14, 'Max-Age', :max_age, rfc: 'RFC 7252 §5.10.5')
|
|
33
|
+
register(15, 'Uri-Query', :uri_query, rfc: 'RFC 7252 §5.10.2')
|
|
34
|
+
register(17, 'Accept', :accept, rfc: 'RFC 7252 §5.10.4')
|
|
35
|
+
register(20, 'Location-Query', :location_query, rfc: 'RFC 7252 §5.10.7')
|
|
36
|
+
register(35, 'Proxy-Uri', :proxy_uri, rfc: 'RFC 7252 §5.10.2')
|
|
37
|
+
register(39, 'Proxy-Scheme', :proxy_scheme, rfc: 'RFC 7252 §5.10.2')
|
|
38
|
+
register(60, 'Size1', :size1, rfc: 'RFC 7252 §5.10.9')
|
|
39
|
+
|
|
40
|
+
# Check if an option is critical
|
|
41
|
+
# Critical options must be understood by the recipient
|
|
42
|
+
# @param number [Integer] Option number
|
|
43
|
+
# @return [Boolean] true if critical
|
|
44
|
+
def self.critical?(number)
|
|
45
|
+
number.allbits?(1)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Check if an option is unsafe to forward
|
|
49
|
+
# @param number [Integer] Option number
|
|
50
|
+
# @return [Boolean] true if unsafe
|
|
51
|
+
def self.unsafe?(number)
|
|
52
|
+
number.allbits?(2)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Check if an option has NoCacheKey property
|
|
56
|
+
# @param number [Integer] Option number
|
|
57
|
+
# @return [Boolean] true if NoCacheKey
|
|
58
|
+
def self.no_cache_key?(number)
|
|
59
|
+
(number & 0x1E) == 0x1C
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Check if option number is valid
|
|
63
|
+
# @param number [Integer] Option number
|
|
64
|
+
# @return [Boolean] true if valid
|
|
65
|
+
def self.valid?(number)
|
|
66
|
+
number.between?(0, 65_535)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Response Code Registry (RFC 7252 §12.1.2)
|
|
7
|
+
#
|
|
8
|
+
# Extensible registry for CoAP response codes.
|
|
9
|
+
# Plugins can register custom response codes without modifying core code.
|
|
10
|
+
#
|
|
11
|
+
# @example Using predefined codes
|
|
12
|
+
# Takagi::CoAP::Registries::Response::CONTENT # => 69 (2.05)
|
|
13
|
+
# Takagi::CoAP::Registries::Response::NOT_FOUND # => 132 (4.04)
|
|
14
|
+
#
|
|
15
|
+
# @example Registering a custom code
|
|
16
|
+
# Takagi::CoAP::Registries::Response.register(231, '7.07 Custom', :custom)
|
|
17
|
+
# Takagi::CoAP::Registries::Response::CUSTOM # => 231
|
|
18
|
+
#
|
|
19
|
+
# @example Looking up code names
|
|
20
|
+
# Takagi::CoAP::Registries::Response.name_for(69) # => "2.05 Content"
|
|
21
|
+
class Response < Base
|
|
22
|
+
# Success 2.xx (RFC 7252 §5.9.1)
|
|
23
|
+
register(65, '2.01 Created', :created, rfc: 'RFC 7252 §5.9.1.1')
|
|
24
|
+
register(66, '2.02 Deleted', :deleted, rfc: 'RFC 7252 §5.9.1.2')
|
|
25
|
+
register(67, '2.03 Valid', :valid, rfc: 'RFC 7252 §5.9.1.3')
|
|
26
|
+
register(68, '2.04 Changed', :changed, rfc: 'RFC 7252 §5.9.1.4')
|
|
27
|
+
register(69, '2.05 Content', :content, rfc: 'RFC 7252 §5.9.1.5')
|
|
28
|
+
|
|
29
|
+
# Client Error 4.xx (RFC 7252 §5.9.2)
|
|
30
|
+
register(128, '4.00 Bad Request', :bad_request, rfc: 'RFC 7252 §5.9.2.1')
|
|
31
|
+
register(129, '4.01 Unauthorized', :unauthorized, rfc: 'RFC 7252 §5.9.2.2')
|
|
32
|
+
register(130, '4.02 Bad Option', :bad_option, rfc: 'RFC 7252 §5.9.2.3')
|
|
33
|
+
register(131, '4.03 Forbidden', :forbidden, rfc: 'RFC 7252 §5.9.2.4')
|
|
34
|
+
register(132, '4.04 Not Found', :not_found, rfc: 'RFC 7252 §5.9.2.5')
|
|
35
|
+
register(133, '4.05 Method Not Allowed', :method_not_allowed, rfc: 'RFC 7252 §5.9.2.6')
|
|
36
|
+
register(134, '4.06 Not Acceptable', :not_acceptable, rfc: 'RFC 7252 §5.9.2.7')
|
|
37
|
+
register(140, '4.12 Precondition Failed', :precondition_failed, rfc: 'RFC 7252 §5.9.2.9')
|
|
38
|
+
register(141, '4.13 Request Entity Too Large', :request_entity_too_large, rfc: 'RFC 7252 §5.9.2.10')
|
|
39
|
+
register(143, '4.15 Unsupported Content-Format', :unsupported_content_format, rfc: 'RFC 7252 §5.9.2.11')
|
|
40
|
+
|
|
41
|
+
# Server Error 5.xx (RFC 7252 §5.9.3)
|
|
42
|
+
register(160, '5.00 Internal Server Error', :internal_server_error, rfc: 'RFC 7252 §5.9.3.1')
|
|
43
|
+
register(161, '5.01 Not Implemented', :not_implemented, rfc: 'RFC 7252 §5.9.3.2')
|
|
44
|
+
register(162, '5.02 Bad Gateway', :bad_gateway, rfc: 'RFC 7252 §5.9.3.3')
|
|
45
|
+
register(163, '5.03 Service Unavailable', :service_unavailable, rfc: 'RFC 7252 §5.9.3.4')
|
|
46
|
+
register(164, '5.04 Gateway Timeout', :gateway_timeout, rfc: 'RFC 7252 §5.9.3.5')
|
|
47
|
+
register(165, '5.05 Proxying Not Supported', :proxying_not_supported, rfc: 'RFC 7252 §5.9.3.6')
|
|
48
|
+
|
|
49
|
+
# Get the response class (2, 4, 5, etc.)
|
|
50
|
+
# @param code [Integer] Response code
|
|
51
|
+
# @return [Integer] Class number
|
|
52
|
+
def self.class_for(code)
|
|
53
|
+
code / 32
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Check if code is a success (2.xx)
|
|
57
|
+
# @param code [Integer] Response code
|
|
58
|
+
# @return [Boolean] true if success
|
|
59
|
+
def self.success?(code)
|
|
60
|
+
class_for(code) == 2
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check if code is a client error (4.xx)
|
|
64
|
+
# @param code [Integer] Response code
|
|
65
|
+
# @return [Boolean] true if client error
|
|
66
|
+
def self.client_error?(code)
|
|
67
|
+
class_for(code) == 4
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Check if code is a server error (5.xx)
|
|
71
|
+
# @param code [Integer] Response code
|
|
72
|
+
# @return [Boolean] true if server error
|
|
73
|
+
def self.server_error?(code)
|
|
74
|
+
class_for(code) == 5
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Check if code is any error (4.xx or 5.xx)
|
|
78
|
+
# @param code [Integer] Response code
|
|
79
|
+
# @return [Boolean] true if error
|
|
80
|
+
def self.error?(code)
|
|
81
|
+
client_error?(code) || server_error?(code)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Check if code is a valid response code
|
|
85
|
+
# @param code [Integer] Code to check
|
|
86
|
+
# @return [Boolean] true if valid response code
|
|
87
|
+
def self.valid?(code)
|
|
88
|
+
code.between?(64, 191) && registered?(code)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
module CoAP
|
|
5
|
+
module Registries
|
|
6
|
+
# CoAP Signaling Code Registry (RFC 8323 §5)
|
|
7
|
+
#
|
|
8
|
+
# Registry for CoAP over TCP/TLS signaling messages.
|
|
9
|
+
# Signaling messages use codes in the 7.xx range.
|
|
10
|
+
#
|
|
11
|
+
# @example Using signaling codes
|
|
12
|
+
# Takagi::CoAP::Registries::Signaling::CSM # => 225 (7.01)
|
|
13
|
+
# Takagi::CoAP::Registries::Signaling::PING # => 226 (7.02)
|
|
14
|
+
#
|
|
15
|
+
# @example Looking up signaling message names
|
|
16
|
+
# Takagi::CoAP::Registries::Signaling.name_for(225) # => "7.01 CSM"
|
|
17
|
+
class Signaling < Base
|
|
18
|
+
# RFC 8323 §5.3 - Signaling Codes
|
|
19
|
+
register(225, '7.01 CSM', :csm, rfc: 'RFC 8323 §5.3.1') # Capabilities and Settings Message
|
|
20
|
+
register(226, '7.02 Ping', :ping, rfc: 'RFC 8323 §5.3.2') # Ping
|
|
21
|
+
register(227, '7.03 Pong', :pong, rfc: 'RFC 8323 §5.3.3') # Pong
|
|
22
|
+
register(228, '7.04 Release', :release, rfc: 'RFC 8323 §5.3.4') # Release
|
|
23
|
+
register(229, '7.05 Abort', :abort, rfc: 'RFC 8323 §5.3.5') # Abort
|
|
24
|
+
|
|
25
|
+
# Check if code is a signaling code
|
|
26
|
+
# @param code [Integer] Code to check
|
|
27
|
+
# @return [Boolean] true if signaling code (7.xx)
|
|
28
|
+
def self.signaling?(code)
|
|
29
|
+
(224..255).include?(code) && registered?(code)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/takagi/coap.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Takagi
|
|
4
|
+
# CoAP Protocol implementation following RFC 7252 and related RFCs.
|
|
5
|
+
#
|
|
6
|
+
# This module provides a registry-based system for CoAP protocol constants.
|
|
7
|
+
# Plugins and extensions can register additional constants without modifying
|
|
8
|
+
# core code by using the registries under {Takagi::CoAP::Registries}.
|
|
9
|
+
#
|
|
10
|
+
# @example Registering a custom response code
|
|
11
|
+
# Takagi::CoAP::Registries::Response.register(231, '7.01 Custom Code', :custom_code)
|
|
12
|
+
#
|
|
13
|
+
# @example Using registered constants
|
|
14
|
+
# code = Takagi::CoAP::Registries::Response::CONTENT # => 69
|
|
15
|
+
# Takagi::CoAP::Registries::Response.name_for(69) # => "2.05 Content"
|
|
16
|
+
module CoAP
|
|
17
|
+
# Default CoAP port (RFC 7252 §6.1)
|
|
18
|
+
DEFAULT_PORT = 5683
|
|
19
|
+
|
|
20
|
+
# Default CoAPS (secure) port (RFC 7252 §6.2)
|
|
21
|
+
DEFAULT_SECURE_PORT = 5684
|
|
22
|
+
|
|
23
|
+
# CoAP version (RFC 7252 §3)
|
|
24
|
+
VERSION = 1
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Load registries
|
|
29
|
+
require_relative 'coap/registries/base'
|
|
30
|
+
require_relative 'coap/registries/method'
|
|
31
|
+
require_relative 'coap/registries/response'
|
|
32
|
+
require_relative 'coap/registries/option'
|
|
33
|
+
require_relative 'coap/registries/content_format'
|
|
34
|
+
require_relative 'coap/registries/message_type'
|
|
35
|
+
require_relative 'coap/registries/signaling'
|
|
36
|
+
|
|
37
|
+
require_relative 'coap/code_helpers'
|