discovery 0.0.1

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: 204a4d851db30ec0c717303d933b3a2f02aa8ef4
4
+ data.tar.gz: fee2b6b4a7b55e80cf87f8a535c3ff1d42487c73
5
+ SHA512:
6
+ metadata.gz: 98eee54e5dd469906d625ab2a25f5040a1b58d2cbb36dcde8f048fb93e323660767e196314ecbd7c0a667c6bef79facab4000d51a813810a59cb4287444e9c0b
7
+ data.tar.gz: d8f6778c0b2a8b56a48ca568043acc770ef664c9a4522a6d6927868c924bd89c497e252e962e681d8654ad86f2e3887103500eb0691fbe2e8dbf9c019631054f
@@ -0,0 +1,54 @@
1
+
2
+ require_relative 'util/udp'
3
+ require 'timeout'
4
+
5
+ module Discovery
6
+ class UDP
7
+ @port = 0
8
+ @group = "0.0.0.0"
9
+
10
+ def self.new; raise NotImplementedError nil end
11
+
12
+ # Register a callback to be used in the event of a recognized beacon.
13
+ # Each non-nil response object for each UDP beacon will be yielded to it.
14
+ def self.listen &block
15
+ @blocks ||= []
16
+ (@blocks << block).uniq!
17
+
18
+ @thread ||= Thread.new do
19
+ loop do
20
+ @discoverables.values.each_with_object(next_beacon) do |blk,beacon|
21
+ obj = blk.call beacon
22
+ @blocks.each do |callback|
23
+ callback.call(obj)
24
+ end unless obj.nil?
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ # Return the info hash of the next DDDP beacon detected - blocking
31
+ # Optional :timeout keyword argument specifies maximum time to block
32
+ def self.next_beacon timeout:nil
33
+ @udp_rx ||= Util::UDP::RX.new(@group, @port)
34
+ beacon_to_hash(Timeout.timeout(timeout) { @udp_rx.gets })
35
+ end
36
+
37
+ # Convert the incoming beacon to a Hash containing all necessary info
38
+ # Implement to something nontrivial in the inherited module
39
+ def self.beacon_to_hash beacon
40
+ {beacon:beacon}
41
+ end
42
+
43
+ # Register a block to be used to convert a recognized
44
+ # beacon info-hash into a response object.
45
+ # The block should accept the info hash as an argument and
46
+ # return the response object or nil if the beacon isn't recognized.
47
+ class << self; attr_reader :discoverables; end
48
+ def self.register cls, &block
49
+ @discoverables ||= {}
50
+ @discoverables[cls] = block
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,115 @@
1
+
2
+ require 'socket'
3
+ require 'ipaddr'
4
+
5
+ module Discovery
6
+ module Util
7
+ module UDP
8
+
9
+ class << self; attr_accessor :max_length; end
10
+ @max_length = 1024 # default max payload length
11
+
12
+ class Xceiver
13
+ attr_reader :group, :port, :socket, :local_ip, :local_port
14
+
15
+ def self.new(*args)
16
+ if (self.class==Xceiver)
17
+ raise TypeError, "#{self.class} is an 'abstract class' only."\
18
+ " Inherit it; don't instantiate it!"; end
19
+ super
20
+ end
21
+
22
+ def initialize(group, port, **kwargs)
23
+ @group = group
24
+ @port = port
25
+ kwargs.each_pair { |k,v| instance_variable_set("@#{k}".to_sym, v) }
26
+ open
27
+ end
28
+
29
+ def open
30
+ @socket.close if @socket
31
+ @socket = UDPSocket.new
32
+ configure
33
+ @local_ip = Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
34
+ @local_port = @socket.addr[1]
35
+ return @socket
36
+ ensure
37
+ @finalizer ||= ObjectSpace.define_finalizer self, Proc.new { close }
38
+ end
39
+
40
+ def close
41
+ @socket.close if @socket
42
+ @socket = nil
43
+ end
44
+ end
45
+
46
+
47
+ class TX < Xceiver
48
+ def configure
49
+ # Set up for multicasting
50
+ @socket.setsockopt Socket::IPPROTO_IP,
51
+ Socket::IP_MULTICAST_TTL,
52
+ [1].pack('i')
53
+ # Bind to any available port
54
+ @socket.bind "0.0.0.0", (@bind_port or 0)
55
+ end
56
+
57
+ def puts(m)
58
+ max = UDP.max_length
59
+ if m.size > max
60
+ self.puts m[0...max]
61
+ self.puts m[max...m.size]
62
+ else
63
+ @socket.send(m, 0, @group, @port)
64
+ m
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ class RX < Xceiver
71
+ def configure
72
+ # Add membership to the multicast group
73
+ @socket.setsockopt Socket::IPPROTO_IP,
74
+ Socket::IP_ADD_MEMBERSHIP,
75
+ IPAddr.new(@group).hton + IPAddr.new("0.0.0.0").hton
76
+ # Don't prevent future listening peers on the same machine
77
+ @socket.setsockopt(Socket::SOL_SOCKET,
78
+ Socket::SO_REUSEADDR,
79
+ [1].pack('i')) unless @selfish
80
+ # Bind the socket to the specified port or any open port on the machine
81
+ @socket.bind Socket::INADDR_ANY, @port
82
+ end
83
+
84
+ def gets
85
+ msg, addrinfo = @socket.recvfrom(UDP.max_length)
86
+ msg.instance_variable_set :@source, addrinfo[3].to_s+':'+addrinfo[1].to_s
87
+ class << msg; attr_reader :source; end
88
+ msg
89
+ end
90
+
91
+ def test!(message="#{self}.test!")
92
+ tx = UDP::TX.new @group, @port
93
+ rx = self
94
+
95
+ outer_thread = Thread.current
96
+ passed = false
97
+ thr = Thread.new do
98
+ rx.gets
99
+ passed = true
100
+ outer_thread.wakeup
101
+ end
102
+ Thread.pass
103
+
104
+ tx.puts message
105
+ sleep 1 if thr.status
106
+ thr.kill
107
+ tx.close
108
+
109
+ passed
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
data/lib/discovery.rb ADDED
@@ -0,0 +1,2 @@
1
+
2
+ require_relative 'discovery/udp'
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: discovery
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joe McIlvain
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Device and service discovery.
28
+ email: joe.eli.mac@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/discovery.rb
34
+ - lib/discovery/util/udp.rb
35
+ - lib/discovery/udp.rb
36
+ homepage: https://github.com/jemc/discovery/
37
+ licenses:
38
+ - Copyright 2013 Joe McIlvain. MIT Licensed.
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.1.5
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: discovery
60
+ test_files: []