coap 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +19 -0
- data/LICENSE +21 -0
- data/README.md +64 -0
- data/Rakefile +9 -0
- data/bin/client +42 -0
- data/coap.gemspec +25 -0
- data/lib/coap.rb +34 -0
- data/lib/coap/block.rb +45 -0
- data/lib/coap/client.rb +364 -0
- data/lib/coap/coap.rb +273 -0
- data/lib/coap/message.rb +187 -0
- data/lib/coap/mysocket.rb +81 -0
- data/lib/coap/observer.rb +41 -0
- data/lib/coap/version.rb +3 -0
- data/lib/misc/hexdump.rb +17 -0
- data/test/coap_test_helper.rb +2 -0
- data/test/disabled_econotag_blck.rb +33 -0
- data/test/test_client.rb +311 -0
- data/test/test_message.rb +127 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: abcc50ad6fb9b06b1453ed668b7197fc98c3f667
|
4
|
+
data.tar.gz: 925d09866ea1e4b965e8433cb1d68352761ad305
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8030c1f4ab64dd80543f3f592d68b101d73c2569b08278ecad0d4fe779dd42366b978520e341ae1bca7c1a215023f2905192bd060af6d1c3759a78348da6cf04
|
7
|
+
data.tar.gz: 906b51fbf6be4b37c9ae36cafc35d118de15e98f237ea56bab1a0dd36d453595e7bb82bb1333776ca4d306d0425fdb0e31e9aa12a46738821342b6fccb89a782
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Simon Frerichs, Carsten Bormann
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/coap.png)](http://badge.fury.io/rb/coap)
|
2
|
+
[![Dependency Status](https://gemnasium.com/SmallLars/coap.png)](https://gemnasium.com/SmallLars/coap)
|
3
|
+
[![Build Status](https://travis-ci.org/SmallLars/coap.png?branch=master)](https://travis-ci.org/SmallLars/coap)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/SmallLars/coap/badge.png?branch=master)](https://coveralls.io/r/SmallLars/coap)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/SmallLars/coap.png)](https://codeclimate.com/github/SmallLars/coap)
|
6
|
+
|
7
|
+
# Gem CoAP
|
8
|
+
|
9
|
+
Ruby Gem for RFC 7252 - Constrained Application Protocol (CoAP)<br>
|
10
|
+
Based on code by Carsten Bormann<br>
|
11
|
+
|
12
|
+
The Constrained Application Protocol (CoAP) is a specialized web
|
13
|
+
transfer protocol for use with constrained nodes and constrained
|
14
|
+
(e.g., low-power, lossy) networks. The nodes often have 8-bit
|
15
|
+
microcontrollers with small amounts of ROM and RAM, while constrained
|
16
|
+
networks such as 6LoWPAN often have high packet error rates and a
|
17
|
+
typical throughput of 10s of kbit/s. The protocol is designed for
|
18
|
+
machine-to-machine (M2M) applications such as smart energy and
|
19
|
+
building automation.<br>
|
20
|
+
|
21
|
+
|
22
|
+
Implemented Version: RFC (http://tools.ietf.org/html/rfc7252)<br>
|
23
|
+
|
24
|
+
Additionally implemented:
|
25
|
+
|
26
|
+
- Blockwise Transfer http://tools.ietf.org/html/draft-ietf-core-block-14
|
27
|
+
- Observe http://tools.ietf.org/html/draft-ietf-core-observe-13
|
28
|
+
|
29
|
+
## Install
|
30
|
+
Add this line to your application's Gemfile:
|
31
|
+
|
32
|
+
gem 'coap'
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
|
36
|
+
$ bundle
|
37
|
+
|
38
|
+
Or install it yourself as:
|
39
|
+
|
40
|
+
$ gem install coap
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
### In your ruby/rails application
|
45
|
+
|
46
|
+
|
47
|
+
client = CoAP::Client.new
|
48
|
+
answer = client.get('coap.me', 5683, '/hello')
|
49
|
+
p answer.payload
|
50
|
+
|
51
|
+
Lot's of examples are stored in test/test_client.rb<br>
|
52
|
+
Code is commented in rdoc format
|
53
|
+
|
54
|
+
### Command Line Client (very limited)
|
55
|
+
|
56
|
+
./client get coap://coap.me:5683/.well-known/core
|
57
|
+
|
58
|
+
## Testing
|
59
|
+
|
60
|
+
rake test
|
61
|
+
|
62
|
+
## Copyright
|
63
|
+
Copyright (C) 2014 Simon Frerichs. See LICENSE.txt for further details.<br>
|
64
|
+
Based on code by Carsten Bormann
|
data/Rakefile
ADDED
data/bin/client
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Usage: client get coap://coap.me:5683/.well-known/core
|
3
|
+
# Usage: client observe coap://vs0.inf.ethz.ch:5683/obs
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
6
|
+
|
7
|
+
require 'coap'
|
8
|
+
|
9
|
+
def observe_callback(data, socket)
|
10
|
+
pp socket.inspect if $DEBUG
|
11
|
+
pp data.inspect if $DEBUG
|
12
|
+
end
|
13
|
+
|
14
|
+
PAYLOAD = 'CLILorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nuncLorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nuncCLI'
|
15
|
+
|
16
|
+
METHODS = %w(get post put delete observe)
|
17
|
+
|
18
|
+
if ARGV.size >= 2 && METHODS.include?(ARGV[0])
|
19
|
+
client = CoAP::Client.new
|
20
|
+
$DEBUG = true
|
21
|
+
$WIREDUMP = true
|
22
|
+
require 'pp'
|
23
|
+
|
24
|
+
case ARGV[0]
|
25
|
+
when 'get'
|
26
|
+
pp client.get_by_url(ARGV[1])
|
27
|
+
|
28
|
+
when 'post'
|
29
|
+
pp client.post_by_url(ARGV[1], PAYLOAD)
|
30
|
+
|
31
|
+
when 'put'
|
32
|
+
pp client.put_by_url(ARGV[1], PAYLOAD)
|
33
|
+
|
34
|
+
when 'delete'
|
35
|
+
pp client.delete_by_url(ARGV[1])
|
36
|
+
|
37
|
+
when 'observe'
|
38
|
+
pp client.observe_by_url(ARGV[1], {}, method(:observe_callback))
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
exit
|
data/coap.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'coap/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "coap"
|
8
|
+
spec.version = Coap::VERSION
|
9
|
+
spec.authors = ["Carsten Bormann","Simon Frerichs"]
|
10
|
+
spec.email = ["morpheus@morphhome.net"]
|
11
|
+
spec.summary = %q{coap gem.}
|
12
|
+
spec.description = %q{coap \o/ gem.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "TODO"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_dependency "resolv-ipv6favor"
|
24
|
+
# spec.add_dependency "codtls"
|
25
|
+
end
|
data/lib/coap.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module CoAP
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def logger
|
6
|
+
if @logger.nil?
|
7
|
+
@logger = Logger.new(STDOUT)
|
8
|
+
@logger.level = Logger::WARN
|
9
|
+
end
|
10
|
+
|
11
|
+
@logger
|
12
|
+
end
|
13
|
+
def logger= logger
|
14
|
+
@logger.close unless @logger.nil?
|
15
|
+
|
16
|
+
@logger = logger
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'logger'
|
21
|
+
require 'socket'
|
22
|
+
require 'resolv-ipv6favor'
|
23
|
+
require 'ipaddr'
|
24
|
+
require 'timeout'
|
25
|
+
#require 'CoDTLS'
|
26
|
+
|
27
|
+
require 'coap/coap.rb'
|
28
|
+
require 'coap/message.rb'
|
29
|
+
require 'coap/block.rb'
|
30
|
+
require 'coap/mysocket.rb'
|
31
|
+
require 'coap/observer.rb'
|
32
|
+
require 'coap/client.rb'
|
33
|
+
|
34
|
+
require 'misc/hexdump.rb'
|
data/lib/coap/block.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module CoAP
|
2
|
+
# CoAP client library
|
3
|
+
class Block
|
4
|
+
def self.initialize(num, more, szx)
|
5
|
+
@logger = CoAP.logger
|
6
|
+
|
7
|
+
{ more: more, szx: szx, num: num }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.encode(num, more, szx)
|
11
|
+
block = szx | more << 3 | num << 4
|
12
|
+
|
13
|
+
@logger.debug '### CoAP Block encode ###'
|
14
|
+
@logger.debug block
|
15
|
+
@logger.debug '### CoAP Block encode ###'
|
16
|
+
|
17
|
+
block
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.encode_hash(blockHash)
|
21
|
+
blockHash[:more] ? more = 1 : more = 0
|
22
|
+
block = blockHash[:szx] | more << 3 | blockHash[:num] << 4
|
23
|
+
|
24
|
+
@logger.debug '### CoAP Block encode ###'
|
25
|
+
@logger.debug block
|
26
|
+
@logger.debug '### CoAP Block encode ###'
|
27
|
+
|
28
|
+
block
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.decode(blockOption)
|
32
|
+
more = blockOption != nil && (blockOption & 8) === 8
|
33
|
+
szx = blockOption != nil ? blockOption & 7 : 0
|
34
|
+
num = blockOption != nil ? blockOption >> 4 : 0
|
35
|
+
|
36
|
+
@logger.debug '### CoAP Block decode ###'
|
37
|
+
@logger.debug 'm: ' + more.to_s
|
38
|
+
@logger.debug 'szx: ' + szx.to_s
|
39
|
+
@logger.debug 'num: ' + num.to_s
|
40
|
+
@logger.debug '### CoAP Block decode ###'
|
41
|
+
|
42
|
+
{ more: more, szx: szx, num: num }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/coap/client.rb
ADDED
@@ -0,0 +1,364 @@
|
|
1
|
+
module CoAP
|
2
|
+
|
3
|
+
# CoAP client library
|
4
|
+
class Client
|
5
|
+
|
6
|
+
# attr_writer
|
7
|
+
#
|
8
|
+
# @param max_payload optional payload size, default 256
|
9
|
+
# @param max_retransmit optional retransmit count, default 4
|
10
|
+
# @param ack_timeout optional ack_timeout, default 2
|
11
|
+
# @param hostname dest optional hostname
|
12
|
+
# @param port dest optional port
|
13
|
+
attr_writer :max_payload, :max_retransmit, :ack_timeout, :hostname, :port
|
14
|
+
|
15
|
+
# new arguments
|
16
|
+
#
|
17
|
+
# @param max_payload optional payload size, default 256
|
18
|
+
# @param max_retransmit optional retransmit count, default 4
|
19
|
+
# @param ack_timeout optional ack_timeout, default 2
|
20
|
+
# @param hostname dest optional hostname
|
21
|
+
# @param port dest optional port
|
22
|
+
def initialize(max_payload = 256, max_retransmit = 4, ack_timeout = 2, hostname = nil, port = nil)
|
23
|
+
@max_payload = max_payload
|
24
|
+
@max_retransmit = max_retransmit
|
25
|
+
@ack_timeout = ack_timeout
|
26
|
+
|
27
|
+
@hostname = hostname
|
28
|
+
@port = port
|
29
|
+
|
30
|
+
# dont change the following stuff
|
31
|
+
@retry_count = 0
|
32
|
+
|
33
|
+
@MySocket = MySocket.new
|
34
|
+
@MySocket.socket_type = UDPSocket
|
35
|
+
@MySocket.ack_timeout = @ack_timeout
|
36
|
+
|
37
|
+
@logger = CoAP.logger
|
38
|
+
end
|
39
|
+
|
40
|
+
# Enable DTLS Socket
|
41
|
+
# Needs CoDTLS Gem
|
42
|
+
def use_dtls
|
43
|
+
@MySocket = MySocket.new
|
44
|
+
@MySocket.socket_type = CoDTLS::SecureSocket
|
45
|
+
@MySocket.ack_timeout = @ack_timeout
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def chunk(string, size)
|
51
|
+
chunks = []
|
52
|
+
string.bytes.each_slice(size) { |i| chunks << i.pack('C*') }
|
53
|
+
chunks
|
54
|
+
# string.scan(/.{1,#{size}}/)
|
55
|
+
end
|
56
|
+
|
57
|
+
def decode_url(url)
|
58
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
59
|
+
|
60
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
61
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
62
|
+
|
63
|
+
url_decoded
|
64
|
+
end
|
65
|
+
|
66
|
+
# simple coap get
|
67
|
+
#
|
68
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
69
|
+
# @param options coap options
|
70
|
+
#
|
71
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
72
|
+
def get_by_url(url, options = {})
|
73
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
74
|
+
|
75
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
76
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
77
|
+
|
78
|
+
get(url_decoded[0], url_decoded[1], url_decoded[2], options)
|
79
|
+
end
|
80
|
+
|
81
|
+
# simple coap get
|
82
|
+
#
|
83
|
+
# @param hostname hostname or ip address
|
84
|
+
# @param port port to connect to
|
85
|
+
# @param uri eg /.well-known/core
|
86
|
+
# @param options coap options
|
87
|
+
#
|
88
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
89
|
+
def get(hostname, port, uri, options = {})
|
90
|
+
@retry_count = 0
|
91
|
+
client(hostname, port, uri, :get, nil, options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# coap post
|
95
|
+
#
|
96
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
97
|
+
# @param payload payload which should be send
|
98
|
+
# @param options coap options
|
99
|
+
#
|
100
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
101
|
+
def post_by_url(url, payload, options = {})
|
102
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
103
|
+
|
104
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
105
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
106
|
+
|
107
|
+
post(url_decoded[0], url_decoded[1], url_decoded[2], payload, options)
|
108
|
+
end
|
109
|
+
|
110
|
+
# coap post
|
111
|
+
#
|
112
|
+
# @param hostname hostname or ip address
|
113
|
+
# @param port port to connect to
|
114
|
+
# @param uri eg /.well-known/core
|
115
|
+
# @param payload payload which should be send
|
116
|
+
# @param options coap options
|
117
|
+
#
|
118
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
119
|
+
def post(hostname, port, uri, payload, options = {})
|
120
|
+
@retry_count = 0
|
121
|
+
client(hostname, port, uri, :post, payload, options)
|
122
|
+
end
|
123
|
+
|
124
|
+
# coap put
|
125
|
+
#
|
126
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
127
|
+
# @param payload payload which should be send
|
128
|
+
# @param options coap options
|
129
|
+
#
|
130
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
131
|
+
def put_by_url(url, payload, options = {})
|
132
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
133
|
+
|
134
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
135
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
136
|
+
|
137
|
+
put(url_decoded[0], url_decoded[1], url_decoded[2], payload, options)
|
138
|
+
end
|
139
|
+
|
140
|
+
# coap put
|
141
|
+
#
|
142
|
+
# @param hostname hostname or ip address
|
143
|
+
# @param port port to connect to
|
144
|
+
# @param uri eg /.well-known/core
|
145
|
+
# @param payload payload which should be send
|
146
|
+
# @param options coap options
|
147
|
+
#
|
148
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
149
|
+
def put(hostname, port, uri, payload, options = {})
|
150
|
+
@retry_count = 0
|
151
|
+
client(hostname, port, uri, :put, payload, options)
|
152
|
+
end
|
153
|
+
|
154
|
+
# coap delete
|
155
|
+
#
|
156
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
157
|
+
# @param options coap options
|
158
|
+
#
|
159
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
160
|
+
def delete_by_url(hostname, port, uri, options = {})
|
161
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
162
|
+
|
163
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
164
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
165
|
+
|
166
|
+
delete(url_decoded[0], url_decoded[1], url_decoded[2], options)
|
167
|
+
end
|
168
|
+
|
169
|
+
# coap delete
|
170
|
+
#
|
171
|
+
# @param hostname hostname or ip address
|
172
|
+
# @param port port to connect to
|
173
|
+
# @param uri eg /.well-known/core
|
174
|
+
# @param options coap options
|
175
|
+
#
|
176
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
177
|
+
def delete(hostname, port, uri, options = {})
|
178
|
+
@retry_count = 0
|
179
|
+
client(hostname, port, uri, :delete, nil, options)
|
180
|
+
end
|
181
|
+
|
182
|
+
# coap observe
|
183
|
+
#
|
184
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
185
|
+
# @param options coap options
|
186
|
+
# @param callback method to call with the observe data eg. observe_callback(payload, socket)
|
187
|
+
#
|
188
|
+
def observe_by_url(hostname, port, uri, callback, options = {})
|
189
|
+
url_decoded = CoAP.scheme_and_authority_decode(url)
|
190
|
+
|
191
|
+
@logger.debug 'url decoded: ' + url_decoded.inspect
|
192
|
+
fail ArgumentError, 'Invalid URL' if url_decoded.nil?
|
193
|
+
|
194
|
+
observe(url_decoded[0], url_decoded[1], url_decoded[2], callback, options)
|
195
|
+
end
|
196
|
+
|
197
|
+
# coap observe
|
198
|
+
#
|
199
|
+
# @param hostname hostname or ip address
|
200
|
+
# @param port port to connect to
|
201
|
+
# @param uri eg /.well-known/core
|
202
|
+
# @param options coap options
|
203
|
+
# @param callback method to call with the observe data eg. observe_callback(payload, socket)
|
204
|
+
#
|
205
|
+
def observe(hostname, port, uri, callback, options = {})
|
206
|
+
options[:observe] = 0
|
207
|
+
client(hostname, port, uri, :get, nil, options, callback)
|
208
|
+
end
|
209
|
+
|
210
|
+
# custom client for special use
|
211
|
+
#
|
212
|
+
# @param url coap scheme + authority url eg coap://coap.me:5683/time
|
213
|
+
# @param method string get post put delete etc
|
214
|
+
# @param payload message payload
|
215
|
+
# @param options coap options
|
216
|
+
#
|
217
|
+
# @return CoAP::Message #<struct CoAP::Message ver=1, tt=:ack, mcode=[2, 5], mid=866, options={:max_age=>60, :token=>150, :etag=>[16135160785136240028], :content_format=>40, :block2=>226}, payload=\"\\\";ct=50,</5>;rt=\\\"5\\\";ct=50\">"
|
218
|
+
def custom(url, method, payload = nil, options = {})
|
219
|
+
|
220
|
+
url_decoded = decode_url url
|
221
|
+
client(url_decoded[0], url_decoded[1], url_decoded[2], method.to_sym, payload, options)
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def client(hostname, port, uri, method, payload, options, observe_callback = nil)
|
228
|
+
|
229
|
+
# set hostname + port only one time on multiple requests
|
230
|
+
hostname.nil? ? (hostname = @hostname unless @hostname.nil?) : @hostname = hostname
|
231
|
+
port.nil? ? (port = @port unless @port.nil?) : @port = port
|
232
|
+
|
233
|
+
# Error handling for paramaters
|
234
|
+
fail ArgumentError, 'hostname missing' if hostname.nil? || hostname.empty?
|
235
|
+
fail ArgumentError, 'port missing' if port.nil?
|
236
|
+
fail ArgumentError, 'port must ba an integer' unless port.is_a? Integer
|
237
|
+
fail ArgumentError, 'uri missing' if uri.nil? || uri.empty?
|
238
|
+
fail ArgumentError, 'payload must be a string' unless payload.is_a? String unless payload.nil?
|
239
|
+
fail ArgumentError, 'payload shouldnt be empty ' if payload.empty? unless payload.nil?
|
240
|
+
|
241
|
+
# generate random message id
|
242
|
+
mid = Random.rand(999)
|
243
|
+
token = Random.rand(256)
|
244
|
+
szx = Math.log2(@max_payload).floor - 4
|
245
|
+
|
246
|
+
# initialize block 2 with payload size
|
247
|
+
block2 = Block.initialize(0, false, szx)
|
248
|
+
|
249
|
+
# initialize block1 if set
|
250
|
+
block1 = options[:block1].nil? ? Block.initialize(0, false, szx) : Block.decode(options[:block1])
|
251
|
+
|
252
|
+
# initialize chunks if payload size > max_payload
|
253
|
+
chunks = chunk(payload, 2**((Math.log2(@max_payload).floor - 4) + 4)) unless payload.nil?
|
254
|
+
|
255
|
+
# create coap message struct
|
256
|
+
message = Message.new(:con, method, mid, nil, {})
|
257
|
+
# , block2: Block.encode_hash(block2)
|
258
|
+
message.options = { uri_path: CoAP.path_decode(uri), token: token }
|
259
|
+
message.options[:block2] = Block.encode_hash(block2) if @max_payload != 256 # temp fix to disable early negotation
|
260
|
+
message.payload = payload unless payload.nil?
|
261
|
+
|
262
|
+
### initialize stuff end
|
263
|
+
|
264
|
+
# if chunks.size 1 > we need to use block1
|
265
|
+
if !payload.nil? and chunks.size > 1
|
266
|
+
# increase block number
|
267
|
+
block1[:num] += 1 unless options[:block1].nil?
|
268
|
+
# more ?
|
269
|
+
block1[:more] = chunks.size > block1[:num] + 1
|
270
|
+
chunks.size > block1[:num] + 1 ? message.options.delete(:block2) : block1[:more] = false
|
271
|
+
# set final payload
|
272
|
+
message.payload = chunks[block1[:num]]
|
273
|
+
# set message option
|
274
|
+
message.options[:block1] = Block.encode_hash(block1)
|
275
|
+
end
|
276
|
+
|
277
|
+
# preserve user options
|
278
|
+
message.options[:block2] = options[:block2] unless options[:block2] == nil
|
279
|
+
message.options[:observe] = options[:observe] unless options[:observe] == nil
|
280
|
+
message.options.merge(options)
|
281
|
+
|
282
|
+
# debug functions
|
283
|
+
@logger.debug '### CoAP Send Data ###'
|
284
|
+
#@logger.debug message.to_s.hexdump
|
285
|
+
@logger.debug message.inspect
|
286
|
+
@logger.debug '### CoAP Send Data ###'
|
287
|
+
|
288
|
+
# connect via udp/dtls
|
289
|
+
@MySocket.connect hostname, port
|
290
|
+
@MySocket.send message.to_wire, 0
|
291
|
+
|
292
|
+
# send message + retry
|
293
|
+
begin
|
294
|
+
recv_data = @MySocket.receive
|
295
|
+
rescue Timeout::Error
|
296
|
+
@retry_count += 1
|
297
|
+
raise 'Retry Timeout ' + @retry_count.to_s if @retry_count > @max_retransmit
|
298
|
+
return client(hostname, port, uri, method, payload, options, observe_callback)
|
299
|
+
end
|
300
|
+
|
301
|
+
# parse recv data
|
302
|
+
recv_parsed = CoAP.parse(recv_data[0].force_encoding('BINARY'))
|
303
|
+
|
304
|
+
# debug functions
|
305
|
+
@logger.debug '### CoAP Received Data ###'
|
306
|
+
#@logger.debug recv_parsed.to_s.hexdump
|
307
|
+
@logger.debug recv_parsed.inspect
|
308
|
+
@logger.debug '### CoAP Received Data ###'
|
309
|
+
|
310
|
+
# payload is not fully transmitted
|
311
|
+
if block1[:more]
|
312
|
+
fail 'Max Recursion' if @retry_count > 10
|
313
|
+
return client(hostname, port, uri, method, payload, message.options)
|
314
|
+
end
|
315
|
+
|
316
|
+
# separate ?
|
317
|
+
if recv_parsed.tt == :ack && recv_parsed.payload.empty? && recv_parsed.mid == mid && recv_parsed.mcode[0] == 0 && recv_parsed.mcode[1] == 0
|
318
|
+
@logger.debug '### SEPARATE REQUEST ###'
|
319
|
+
|
320
|
+
# wait for answer ...
|
321
|
+
recv_data = @MySocket.receive(600, @retry_count)
|
322
|
+
recv_parsed = CoAP.parse(recv_data[0].force_encoding('BINARY'))
|
323
|
+
|
324
|
+
# debug functions
|
325
|
+
@logger.debug '### CoAP SEPARAT Data ###'
|
326
|
+
#@logger.debug recv_parsed.to_s.hexdump
|
327
|
+
@logger.debug recv_parsed.inspect
|
328
|
+
@logger.debug '### CoAP SEPARAT Data ###'
|
329
|
+
|
330
|
+
if recv_parsed.tt == :con
|
331
|
+
message = Message.new(:ack, 0, recv_parsed.mid, nil, {})
|
332
|
+
message.options = { token: recv_parsed.options[:token] }
|
333
|
+
@MySocket.send message.to_wire, 0
|
334
|
+
end
|
335
|
+
|
336
|
+
@logger.debug '### SEPARATE REQUEST END ###'
|
337
|
+
end
|
338
|
+
|
339
|
+
# test for more block2 payload
|
340
|
+
block2 = Block.decode(recv_parsed.options[:block2])
|
341
|
+
|
342
|
+
if block2[:more]
|
343
|
+
block2[:num] += 1
|
344
|
+
|
345
|
+
options.delete(:block1) # end block1
|
346
|
+
options[:block2] = Block.encode_hash(block2)
|
347
|
+
fail 'Max Recursion' if @retry_count > 50
|
348
|
+
local_recv_parsed = client(hostname, port, uri, method, nil, options)
|
349
|
+
recv_parsed.payload << local_recv_parsed.payload unless local_recv_parsed.nil?
|
350
|
+
end
|
351
|
+
|
352
|
+
# do we need to observe?
|
353
|
+
if recv_parsed.options[:observe]
|
354
|
+
@Observer = CoAP::Observer.new
|
355
|
+
@Observer.observe(recv_parsed, recv_data, observe_callback, @MySocket)
|
356
|
+
end
|
357
|
+
|
358
|
+
# this is bad
|
359
|
+
fail ArgumentError, 'wrong token returned' if recv_parsed.options[:token] != token # create own error class
|
360
|
+
|
361
|
+
recv_parsed
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|