gossiperl_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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