coap 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in coap.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,19 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ coap (0.0.8)
5
+ resolv-ipv6favor
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ rake (10.1.0)
11
+ resolv-ipv6favor (0.0.0)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.5)
18
+ coap!
19
+ rake
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
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
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
@@ -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