gossiperl_client 0.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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzE0MjE3NzNmNjg1MWJhYzQwNTE2MDE1N2FmNzExMDc3ZTA3MDdiZQ==
5
+ data.tar.gz: !binary |-
6
+ NTBjODRjMzU1YjcxNjcyZGEyMTRkYThkMDYzNTY2OTczOTc2ZTI1MQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ Yzg0M2FhZGI4ODJmYjBiZDc2NzE0NzA3ZWYzODkzYzJlNDk2YzBiNDAzZmNm
10
+ MjI1YmFiZjQ3MTc5OTQ0NGEyZGYwY2Q0NjkxMGZiYTQzZTRiNTM1YmVkOTEw
11
+ NjljNzkzOTM1N2VlMThiZjdhYzYyZjFjM2JlMjFhZTY2Mjc4Yzg=
12
+ data.tar.gz: !binary |-
13
+ YmRiY2ZhOGU1MjExMTk3MzI3MzE1ZDc4MjQ3NjA2NGZmZmNiYzZmMjBmNDRl
14
+ NWQ5YWZkNzQzMTJhOGE1MzFhODAxYmE0YzY2NzBhOWIxZjQ4NTRhZjY4OTU2
15
+ ZjYzY2ZjODdlNjY1MDJkOTMxM2FmNzU5Y2Y2ZWMyYWZiYmZmZTA=
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+ gem 'thrift'
3
+ gem 'shindo'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Radoslaw Gruchalski <radek@gruchalski.com>
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # Ruby gossiperl client
2
+
3
+ Ruby [gossiperl](https://github.com/radekg/gossiperl) client library.
4
+
5
+ ## Installation
6
+
7
+ In your `Gemfile`:
8
+
9
+ gem 'gossiperl_client', :git => 'https://github.com/radekg/gossiperl-client-ruby.git'
10
+
11
+ ## Running
12
+
13
+ require 'gossiperl_client'
14
+ supervisor = ::Gossiperl::Client::Supervisor.new
15
+
16
+ ## Connecting to an overlay
17
+
18
+ supervisor.connect( :overlay_name => :your_overlay_name
19
+ :overlay_port => 6666,
20
+ :client_port => 54321,
21
+ :client_name => :your_client_name,
22
+ :client_secret => :your_client_secret,
23
+ :symkey => :symmetric_key )
24
+
25
+ It's also possible to connect with a block:
26
+
27
+ supervisor.connect( ... ) do |event|
28
+ if event[:event] == :connected
29
+ self.logger.info "Connected to overlay #{event[:options][:overlay_name]}..."
30
+ elsif event[:event] == :disconnected
31
+ self.logger.info "Disconnected from overlay #{event[:options][:overlay_name]}..."
32
+ elsif event[:event] == :subscribed
33
+ self.logger.info "Received subscription confirmation for #{event[:details][:types]}"
34
+ elsif event[:event] == :unsubscribed
35
+ self.logger.info "Received unsubscription confirmation for #{event[:details][:types]}"
36
+ elsif event[:event] == :event
37
+ self.logger.info "Received member related event #{event[:details][:type]} for member #{event[:details][:member]}."
38
+ elsif event[:event] == :forwarded_ack
39
+ self.logger.info "Received confirmation of forwarded message. Message ID: #{event[:details][:reply_id]}"
40
+ elsif event[:event] == :forwarded
41
+ self.logger.info "Received forwarded digest #{event[:digest]} of type #{event[:digest_type]}"
42
+ elsif event[:event] == :failed
43
+ self.logger.info "Received an error from the client. Reason: #{event[:error]}."
44
+ end
45
+ end
46
+
47
+ A client may be connected to multiple overlays.
48
+
49
+ ## Subscribing / unsubscribing
50
+
51
+ Subscribing:
52
+
53
+ supervisor.subscribe( :overlay_name, [ :event_1, :event_2, ... ] )
54
+
55
+ Unsubscribing:
56
+
57
+ supervisor.unsubscribe( :overlay_name, [ :event_1, :event_2, ... ] )
58
+
59
+ Or in a block:
60
+
61
+ self.subscribe( [ :event_1, :event_2, ... ] )
62
+ self.unsubscribe( [ :event_1, :event_2, ... ] )
63
+
64
+ ## Disconnecting from an overlay:
65
+
66
+ supervisor.disconnect( :overlay_name )
67
+
68
+ Or in a block:
69
+
70
+ self.stop
71
+
72
+ This will attempt a graceful exit from an overlay.
73
+
74
+ ## Additional operations
75
+
76
+ ### Checking current client state
77
+
78
+ supervisor.state( :overlay_name )
79
+
80
+ Or in a block:
81
+
82
+ self.current_state
83
+
84
+ ### Get the list of current subscriptions
85
+
86
+ supervisor.subscriptions( :overlay_name )
87
+
88
+ Or in a block:
89
+
90
+ self.state.subscriptions
91
+
92
+ ### Sending arbitrary digests
93
+
94
+
95
+ supervisor.send( :overlay_name, :digestType, {
96
+ :property => { :value => <value>, :type => <thrift-type-as-string>, :field_id => <field-order> }
97
+ } )
98
+
99
+ Or in a block:
100
+
101
+ self.send( :digestType, {
102
+ :property => { :value => <value>, :type => <thrift-type-as-string>, :field_id => <field-order> }
103
+ } )
104
+
105
+ Where `:type` is one of the Thrift types:
106
+
107
+ - `:stop`
108
+ - `:void`
109
+ - `:bool`
110
+ - `:byte`
111
+ - `:double`
112
+ - `:i16`
113
+ - `:i32`
114
+ - `:i64`
115
+ - `:string`
116
+ - `:struct`
117
+ - `:map`
118
+ - `:set`
119
+ - `:list`
120
+
121
+ And `:field_id` is a Thrift field ID.
122
+
123
+ ## Running tests
124
+
125
+ shindont tests
126
+
127
+ Tests assume an overlay with the details specified in the `tests/process_tests.rb` running.
128
+
129
+ ## License
130
+
131
+ The MIT License (MIT)
132
+
133
+ Copyright (c) 2014 Radoslaw Gruchalski <radek@gruchalski.com>
134
+
135
+ Permission is hereby granted, free of charge, to any person obtaining a copy
136
+ of this software and associated documentation files (the "Software"), to deal
137
+ in the Software without restriction, including without limitation the rights
138
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
139
+ copies of the Software, and to permit persons to whom the Software is
140
+ furnished to do so, subject to the following conditions:
141
+
142
+ The above copyright notice and this permission notice shall be included in
143
+ all copies or substantial portions of the Software.
144
+
145
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
146
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
147
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
148
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
149
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
150
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
151
+ THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # encoding: ascii-8bit
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "gossiperl_client/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "gossiperl_client"
7
+ s.version = Gossiperl::Client::Version::VERSION
8
+ s.has_rdoc = false
9
+ s.summary = "Gossiperl Ruby client"
10
+ s.description = "Work with gossiperl from Ruby."
11
+ s.authors = ["Rad Gruchalski"]
12
+ s.email = ["radek@gruchalski.com"]
13
+ s.homepage = "https://github.com/radekg/gossiperl-client-ruby"
14
+ s.require_paths = %w[lib]
15
+
16
+ s.add_dependency('thrift', '>=0.9.2.0')
17
+ s.add_development_dependency('shindo')
18
+
19
+ s.files = %w[
20
+ Gemfile
21
+ README.md
22
+ LICENSE
23
+ lib/gossiperl_client.rb
24
+ lib/gossiperl_client/encryption/aes256.rb
25
+ lib/gossiperl_client/serialization/serializer.rb
26
+ lib/gossiperl_client/thrift/gossiperl_constants.rb
27
+ lib/gossiperl_client/thrift/gossiperl_types.rb
28
+ lib/gossiperl_client/transport/udp.rb
29
+ lib/gossiperl_client/util/validation.rb
30
+ lib/gossiperl_client/headers.rb
31
+ lib/gossiperl_client/messaging.rb
32
+ lib/gossiperl_client/overlay_worker.rb
33
+ lib/gossiperl_client/requirements.rb
34
+ lib/gossiperl_client/resolution.rb
35
+ lib/gossiperl_client/state.rb
36
+ lib/gossiperl_client/supervisor.rb
37
+ lib/gossiperl_client/version.rb
38
+ gossiperl_client.gemspec
39
+ tests/process_tests.rb
40
+ tests/thrift_tests.rb
41
+ ]
42
+ s.test_files = s.files.select { |path| path =~ /^[tests]\/.*_[tests]\.rb/ }
43
+
44
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: ascii-8bit
2
+ require 'thrift'
3
+ require 'securerandom'
4
+ require 'logger'
5
+ require "#{File.expand_path(File.dirname(__FILE__))}/gossiperl_client/requirements.rb"
@@ -0,0 +1,44 @@
1
+ # encoding: ascii-8bit
2
+ module Gossiperl
3
+ module Client
4
+ module Encryption
5
+ class Aes256 < Gossiperl::Client::Resolution
6
+
7
+ field :key, Object
8
+
9
+ def initialize key_in
10
+ # setup key:
11
+ self.key = ::Digest::SHA256.digest(key_in)
12
+ end
13
+
14
+ def algorithm
15
+ 'AES-256-CBC'
16
+ end
17
+
18
+ def encrypt data
19
+ random_iv = OpenSSL::Cipher::Cipher.new(algorithm).random_iv
20
+ aes = ::OpenSSL::Cipher::Cipher.new(algorithm)
21
+ aes.encrypt
22
+ aes.key = self.key
23
+ aes.iv = random_iv
24
+ cipher = aes.update(data)
25
+ cipher << aes.final
26
+ random_iv + cipher
27
+ end
28
+
29
+ def decrypt cipher
30
+ iv = cipher[0...16]
31
+ cipher_data = cipher[16..-1]
32
+ decode_cipher = ::OpenSSL::Cipher::Cipher.new(algorithm)
33
+ decode_cipher.decrypt
34
+ decode_cipher.key = self.key
35
+ decode_cipher.padding = 0
36
+ decode_cipher.iv = iv
37
+ plain = decode_cipher.update(cipher_data)
38
+ plain << decode_cipher.final
39
+ plain
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: ascii-8bit
2
+ module Gossiperl
3
+ module Client
4
+
5
+ class Resolution; end
6
+
7
+ module Encryption
8
+ class Aes256 < Gossiperl::Client::Resolution; end
9
+ end
10
+
11
+ module Serialization
12
+ class Serializer; end
13
+ end
14
+
15
+ module Thrift
16
+ class DigestEnvelope; end
17
+ class DigestForwardedAck; end
18
+ class DigestError; end
19
+ class DigestExit; end
20
+ class DigestMember; end
21
+ class DigestSubscription; end
22
+ class Digest; end
23
+ class DigestAck; end
24
+ class DigestSubscriptions; end
25
+ class DigestSubscribe; end
26
+ class DigestSubscribeAck; end
27
+ class DigestUnsubscribe; end
28
+ class DigestUnsubscribeAck; end
29
+ class DigestEvent; end
30
+ end
31
+
32
+ module Transport
33
+ class Udp < Gossiperl::Client::Resolution; end
34
+ end
35
+
36
+ module Util
37
+ class Validation; end
38
+ end
39
+
40
+ class Messaging < Gossiperl::Client::Resolution; end
41
+ class OverlayWorker < Gossiperl::Client::Resolution; end
42
+ class State < Gossiperl::Client::Resolution; end
43
+ class Supervisor < Gossiperl::Client::Resolution; end
44
+
45
+ end
46
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: ascii-8bit
2
+ module Gossiperl
3
+ module Client
4
+ class Messaging < Gossiperl::Client::Resolution
5
+
6
+ field :worker, Gossiperl::Client::OverlayWorker
7
+ field :transport, Gossiperl::Client::Transport::Udp
8
+
9
+ def initialize worker, &block
10
+ self.worker = worker
11
+ @callback_block = block
12
+ end
13
+
14
+ def get_callback_block
15
+ @callback_block
16
+ end
17
+
18
+ def start
19
+ self.transport = Gossiperl::Client::Transport::Udp.new( self.worker )
20
+ if self.worker.options.has_key?(:thrift_window)
21
+ self.transport.recv_buf_size = self.worker.options[:thrift_window]
22
+ end
23
+ Thread.new(self) do |msg|
24
+ msg.transport.handle do |data|
25
+ if data.kind_of? Hash
26
+ if data.has_key?(:error)
27
+ msg.worker.process_event( { :event => :failed,
28
+ :error => data[:error] } )
29
+ elsif data.has_key?(:forward)
30
+ msg.worker.process_event( { :event => :forwarded,
31
+ :digest => data[:envelope],
32
+ :digest_type => data[:type] } )
33
+ msg.digest_forwarded_ack data[:envelope].id
34
+ else
35
+ msg.worker.process_event( { :event => :failed,
36
+ :error => { :unsupported_hash_response => data } } )
37
+ end
38
+ else
39
+ if data.is_a?( Gossiperl::Client::Thrift::Digest )
40
+ msg.digest_ack data
41
+ elsif data.is_a?( Gossiperl::Client::Thrift::DigestAck )
42
+ msg.worker.state.receive data
43
+ elsif data.is_a?( Gossiperl::Client::Thrift::DigestEvent )
44
+ msg.worker.process_event( { :event => :event,
45
+ :details => { :type => data.event_type,
46
+ :member => data.event_object,
47
+ :heartbeat => data.heartbeat } } )
48
+ elsif data.is_a?( Gossiperl::Client::Thrift::DigestSubscribeAck )
49
+ msg.worker.process_event( { :event => :subscribed,
50
+ :details => { :types => data.event_types.map{|item| item.to_sym},
51
+ :heartbeat => data.heartbeat } } )
52
+ elsif data.is_a?( Gossiperl::Client::Thrift::DigestUnsubscribeAck )
53
+ msg.worker.process_event( { :event => :unsubscribed,
54
+ :details => { :types => data.event_types.map{|item| item.to_sym},
55
+ :heartbeat => data.heartbeat } } )
56
+ elsif data.is_a?( Gossiperl::Client::Thrift::DigestForwardedAck )
57
+ msg.worker.process_event( { :event => :forwarded_ack,
58
+ :details => { :reply_id => data.reply_id } } )
59
+ else
60
+ msg.worker.process_event( { :event => :failed,
61
+ :error => { :unsupported_digest => data } } )
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def send digest
69
+ self.transport.send digest
70
+ end
71
+
72
+ def digest_ack digest
73
+ ack = ::Gossiperl::Client::Thrift::DigestAck.new
74
+ ack.name = self.worker.options[:client_name].to_s
75
+ ack.heartbeat = Time.now.to_i
76
+ ack.reply_id = digest.id
77
+ ack.membership = []
78
+ self.send ack
79
+ end
80
+
81
+ def digest_forwarded_ack digest_id
82
+ ack = ::Gossiperl::Client::Thrift::DigestForwardedAck.new
83
+ ack.name = self.worker.options[:client_name].to_s
84
+ ack.secret = self.worker.options[:client_secret].to_s
85
+ ack.reply_id = digest_id
86
+ self.send ack
87
+ end
88
+
89
+ def digest_subscribe event_types
90
+ digest = ::Gossiperl::Client::Thrift::DigestSubscribe.new
91
+ digest.name = self.worker.options[:client_name].to_s
92
+ digest.secret = self.worker.options[:client_secret].to_s
93
+ digest.id = SecureRandom.uuid.to_s
94
+ digest.heartbeat = Time.now.to_i
95
+ digest.event_types = event_types.map{|item| item.to_s}
96
+ self.send digest
97
+ end
98
+
99
+ def digest_unsubscribe event_types
100
+ digest = ::Gossiperl::Client::Thrift::DigestUnsubscribe.new
101
+ digest.name = self.worker.options[:client_name].to_s
102
+ digest.secret = self.worker.options[:client_secret].to_s
103
+ digest.id = SecureRandom.uuid.to_s
104
+ digest.heartbeat = Time.now.to_i
105
+ digest.event_types = event_types.map{|item| item.to_s}
106
+ self.send digest
107
+ end
108
+
109
+ def digest_exit
110
+ digest = ::Gossiperl::Client::Thrift::DigestExit.new
111
+ digest.name = self.worker.options[:client_name].to_s
112
+ digest.heartbeat = Time.now.to_i
113
+ digest.secret = self.worker.options[:client_secret].to_s
114
+ self.send digest
115
+ self.worker.working = false
116
+ end
117
+
118
+ end
119
+ end
120
+ end