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 +15 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +151 -0
- data/gossiperl_client.gemspec +44 -0
- data/lib/gossiperl_client.rb +5 -0
- data/lib/gossiperl_client/encryption/aes256.rb +44 -0
- data/lib/gossiperl_client/headers.rb +46 -0
- data/lib/gossiperl_client/messaging.rb +120 -0
- data/lib/gossiperl_client/overlay_worker.rb +73 -0
- data/lib/gossiperl_client/requirements.rb +15 -0
- data/lib/gossiperl_client/resolution.rb +38 -0
- data/lib/gossiperl_client/serialization/serializer.rb +128 -0
- data/lib/gossiperl_client/state.rb +73 -0
- data/lib/gossiperl_client/supervisor.rb +81 -0
- data/lib/gossiperl_client/thrift/gossiperl_constants.rb +15 -0
- data/lib/gossiperl_client/thrift/gossiperl_types.rb +378 -0
- data/lib/gossiperl_client/transport/udp.rb +52 -0
- data/lib/gossiperl_client/util/validation.rb +37 -0
- data/lib/gossiperl_client/version.rb +9 -0
- data/tests/process_tests.rb +63 -0
- data/tests/thrift_tests.rb +45 -0
- metadata +129 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
require 'socket'
|
3
|
+
module Gossiperl
|
4
|
+
module Client
|
5
|
+
module Transport
|
6
|
+
class Udp < Gossiperl::Client::Resolution
|
7
|
+
|
8
|
+
field :worker, Gossiperl::Client::OverlayWorker
|
9
|
+
field :socket, UDPSocket, nil
|
10
|
+
field :recv_buf_size, Fixnum, 16777216
|
11
|
+
|
12
|
+
field :serializer, Gossiperl::Client::Serialization::Serializer
|
13
|
+
field :encryption, Gossiperl::Client::Encryption::Aes256
|
14
|
+
|
15
|
+
def initialize worker
|
16
|
+
self.worker = worker
|
17
|
+
self.serializer = Gossiperl::Client::Serialization::Serializer.new
|
18
|
+
self.encryption = Gossiperl::Client::Encryption::Aes256.new( self.worker.options[:symkey].to_s )
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle &block
|
22
|
+
worker = Thread.new ({ :proto => self, :block => block }) do |args|
|
23
|
+
begin
|
24
|
+
args[:proto].socket = UDPSocket.new
|
25
|
+
args[:proto].socket.bind '127.0.0.1', args[:proto].worker.options[:client_port]
|
26
|
+
while args[:proto].worker.working
|
27
|
+
begin
|
28
|
+
data, address = args[:proto].socket.recvfrom args[:proto].recv_buf_size
|
29
|
+
decrypted = args[:proto].encryption.decrypt(data)
|
30
|
+
deserialized = args[:proto].serializer.deserialize(decrypted)
|
31
|
+
args[:block].call deserialized
|
32
|
+
rescue Exception => ex
|
33
|
+
args[:block].call({ :error => ex })
|
34
|
+
end
|
35
|
+
end
|
36
|
+
self.socket.close unless self.socket.nil?
|
37
|
+
args[:proto].worker.logger.debug("Stopping UDP services for client #{args[:proto].worker.options[:client_name]}.")
|
38
|
+
rescue Exception => e
|
39
|
+
args[:proto].worker.logger.error("Could not bind UDP service for client #{args[:proto].worker.options[:client_name]}.")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def send digest
|
45
|
+
serialized = self.serializer.serialize digest
|
46
|
+
encrypted = self.encryption.encrypt serialized
|
47
|
+
self.socket.send encrypted, 0, '127.0.0.1', self.worker.options[:overlay_port]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
module Gossiperl
|
3
|
+
module Client
|
4
|
+
module Util
|
5
|
+
|
6
|
+
class Validation
|
7
|
+
|
8
|
+
def self.validate_connect options
|
9
|
+
raise ArgumentError.new('Options must be a Hash.') unless options.kind_of?(Hash)
|
10
|
+
[ :overlay_name, :client_name, :client_secret, :overlay_port, :client_port, :symkey ].each {|opt|
|
11
|
+
raise ArgumentError.new("Required option #{opt} missing.") unless options.has_key?(opt)
|
12
|
+
}
|
13
|
+
[ :overlay_name, :client_name, :client_secret, :symkey ].each {|str_opt|
|
14
|
+
raise TypeError.new("Option #{str_opt} must be a String or Symbol.") unless [String, Symbol].include?( options[str_opt].class )
|
15
|
+
}
|
16
|
+
[ :overlay_port, :client_port ].each {|fixnum_opt|
|
17
|
+
raise TypeError.new("Option #{str_opt} must be a Fixnum.") unless [Fixnum].include?( options[fixnum_opt].class )
|
18
|
+
}
|
19
|
+
if options.has_key?(:thrift_window) and not options[:thrift_window].is_a?(Fixnum)
|
20
|
+
raise TypeError.new('Option thrift_window has to be a Fixnum.')
|
21
|
+
end
|
22
|
+
if options.has_key?(:logger) and not options[:logger].kind_of?(Logger)
|
23
|
+
raise TypeError.new('Option logger must be an instance of Logger.')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.validate_event_types event_types
|
28
|
+
event_types.each {|et|
|
29
|
+
raise TypeError.new("Event type #{et.inspect} must be a String or Symbol.") unless [String, Symbol].include?( et.class )
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/../lib/gossiperl_client/requirements.rb"
|
3
|
+
Shindo.tests('[Gossiperl] connect process') do
|
4
|
+
|
5
|
+
@supervisor = Gossiperl::Client::Supervisor.new
|
6
|
+
@options = {
|
7
|
+
:overlay_name => :gossiper_overlay_remote,
|
8
|
+
:overlay_port => 6666,
|
9
|
+
:client_port => 54321,
|
10
|
+
:client_name => :'ruby-client',
|
11
|
+
:client_secret => :'ruby-client-secret',
|
12
|
+
:symkey => :v3JElaRswYgxOt4b }
|
13
|
+
@subscriptions = [ :member_in, :digestForwardableTest ]
|
14
|
+
|
15
|
+
tests('success') do
|
16
|
+
|
17
|
+
tests('connect goes to connected').returns(true) do
|
18
|
+
Thread.new(@supervisor) do |sup|
|
19
|
+
sup.connect( @options ) do |event|
|
20
|
+
if event[:event] == :connected
|
21
|
+
self.logger.info "Connected to overlay #{event[:options][:overlay_name]}..."
|
22
|
+
elsif event[:event] == :disconnected
|
23
|
+
self.logger.info "Disconnected from overlay #{event[:options][:overlay_name]}..."
|
24
|
+
elsif event[:event] == :subscribed
|
25
|
+
self.logger.info "Received subscription confirmation for #{event[:details][:types]}"
|
26
|
+
elsif event[:event] == :unsubscribed
|
27
|
+
self.logger.info "Received unsubscription confirmation for #{event[:details][:types]}"
|
28
|
+
elsif event[:event] == :event
|
29
|
+
self.logger.info "Received member related event #{event[:details][:type]} for member #{event[:details][:member]}."
|
30
|
+
elsif event[:event] == :forwarded_ack
|
31
|
+
self.logger.info "Received confirmation of forwarded message. Message ID: #{event[:details][:reply_id]}"
|
32
|
+
elsif event[:event] == :forwarded
|
33
|
+
self.logger.info "Received forwarded digest #{event[:digest]} of type #{event[:digest_type]}"
|
34
|
+
elsif event[:event] == :failed
|
35
|
+
self.logger.info "Received an error from the client. Reason: #{event[:error]}."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
sleep 3
|
40
|
+
@supervisor.state( @options[:overlay_name] ) == :connected
|
41
|
+
end
|
42
|
+
|
43
|
+
tests('subscribe').returns(true) do
|
44
|
+
result = @supervisor.subscribe( @options[:overlay_name], @subscriptions ) == @subscriptions
|
45
|
+
sleep 3
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
tests('unsubscribe').returns(true) do
|
50
|
+
result = @supervisor.unsubscribe( @options[:overlay_name], @subscriptions ) == []
|
51
|
+
sleep 3
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
tests('disconnect').returns(true) do
|
56
|
+
@supervisor.disconnect( @options[:overlay_name] )
|
57
|
+
sleep 1.5
|
58
|
+
@supervisor.connections == {}
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/../lib/gossiperl_client/requirements.rb"
|
3
|
+
Shindo.tests('[Thrift] Serialize / deserialize') do
|
4
|
+
|
5
|
+
@digest = digest = ::Gossiperl::Client::Thrift::Digest.new
|
6
|
+
@digest.name = "test-client"
|
7
|
+
@digest.port = 54321
|
8
|
+
@digest.heartbeat = Time.now.to_i
|
9
|
+
@digest.secret = "test-client-secret"
|
10
|
+
@digest.id = "test-digest-id"
|
11
|
+
|
12
|
+
@enc_key = "SomeEncryptionKe"
|
13
|
+
|
14
|
+
tests('success') do
|
15
|
+
|
16
|
+
tests('serialize / deserialize').returns(true) do
|
17
|
+
envelope = Gossiperl::Client::Serialization::Serializer.new.serialize(@digest)
|
18
|
+
read = Gossiperl::Client::Serialization::Serializer.new.deserialize(envelope)
|
19
|
+
read.id == @digest.id
|
20
|
+
end
|
21
|
+
|
22
|
+
tests('encrypt / decrypt').returns(true) do
|
23
|
+
envelope = Gossiperl::Client::Serialization::Serializer.new.serialize(@digest)
|
24
|
+
encrypted = Gossiperl::Client::Encryption::Aes256.new(@enc_key).encrypt(envelope)
|
25
|
+
decrypted = Gossiperl::Client::Encryption::Aes256.new(@enc_key).decrypt(encrypted)
|
26
|
+
read = Gossiperl::Client::Serialization::Serializer.new.deserialize(decrypted)
|
27
|
+
read.id == @digest.id
|
28
|
+
end
|
29
|
+
|
30
|
+
tests('serialize / deserialize arbitrary').returns(true) do
|
31
|
+
serializer = Gossiperl::Client::Serialization::Serializer.new
|
32
|
+
serialized_data = serializer.serialize_arbitrary :digestForwardableTest, {
|
33
|
+
:string_property => { :value => "some string",
|
34
|
+
:type => :string,
|
35
|
+
:field_id => 1 },
|
36
|
+
:some_port => { :value => 1234567890,
|
37
|
+
:type => :i32,
|
38
|
+
:field_id => 2 },
|
39
|
+
}
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: !binary |-
|
3
|
+
Z29zc2lwZXJsX2NsaWVudA==
|
4
|
+
version: !ruby/object:Gem::Version
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- !binary |-
|
9
|
+
UmFkIEdydWNoYWxza2k=
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: !binary |-
|
17
|
+
dGhyaWZ0
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - !binary |-
|
21
|
+
Pj0=
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.2.0
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - !binary |-
|
29
|
+
Pj0=
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 0.9.2.0
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: !binary |-
|
34
|
+
c2hpbmRv
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
description: !binary |-
|
48
|
+
V29yayB3aXRoIGdvc3NpcGVybCBmcm9tIFJ1Ynku
|
49
|
+
email:
|
50
|
+
- !binary |-
|
51
|
+
cmFkZWtAZ3J1Y2hhbHNraS5jb20=
|
52
|
+
executables: []
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- !binary |-
|
57
|
+
R2VtZmlsZQ==
|
58
|
+
- !binary |-
|
59
|
+
UkVBRE1FLm1k
|
60
|
+
- !binary |-
|
61
|
+
TElDRU5TRQ==
|
62
|
+
- !binary |-
|
63
|
+
bGliL2dvc3NpcGVybF9jbGllbnQucmI=
|
64
|
+
- !binary |-
|
65
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvZW5jcnlwdGlvbi9hZXMyNTYucmI=
|
66
|
+
- !binary |-
|
67
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvc2VyaWFsaXphdGlvbi9zZXJpYWxpemVy
|
68
|
+
LnJi
|
69
|
+
- !binary |-
|
70
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvdGhyaWZ0L2dvc3NpcGVybF9jb25zdGFu
|
71
|
+
dHMucmI=
|
72
|
+
- !binary |-
|
73
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvdGhyaWZ0L2dvc3NpcGVybF90eXBlcy5y
|
74
|
+
Yg==
|
75
|
+
- !binary |-
|
76
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvdHJhbnNwb3J0L3VkcC5yYg==
|
77
|
+
- !binary |-
|
78
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvdXRpbC92YWxpZGF0aW9uLnJi
|
79
|
+
- !binary |-
|
80
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvaGVhZGVycy5yYg==
|
81
|
+
- !binary |-
|
82
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvbWVzc2FnaW5nLnJi
|
83
|
+
- !binary |-
|
84
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvb3ZlcmxheV93b3JrZXIucmI=
|
85
|
+
- !binary |-
|
86
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvcmVxdWlyZW1lbnRzLnJi
|
87
|
+
- !binary |-
|
88
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvcmVzb2x1dGlvbi5yYg==
|
89
|
+
- !binary |-
|
90
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvc3RhdGUucmI=
|
91
|
+
- !binary |-
|
92
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvc3VwZXJ2aXNvci5yYg==
|
93
|
+
- !binary |-
|
94
|
+
bGliL2dvc3NpcGVybF9jbGllbnQvdmVyc2lvbi5yYg==
|
95
|
+
- !binary |-
|
96
|
+
Z29zc2lwZXJsX2NsaWVudC5nZW1zcGVj
|
97
|
+
- !binary |-
|
98
|
+
dGVzdHMvcHJvY2Vzc190ZXN0cy5yYg==
|
99
|
+
- !binary |-
|
100
|
+
dGVzdHMvdGhyaWZ0X3Rlc3RzLnJi
|
101
|
+
homepage: !binary |-
|
102
|
+
aHR0cHM6Ly9naXRodWIuY29tL3JhZGVrZy9nb3NzaXBlcmwtY2xpZW50LXJ1
|
103
|
+
Ynk=
|
104
|
+
licenses: []
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- !binary |-
|
110
|
+
bGli
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.1.5
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: !binary |-
|
127
|
+
R29zc2lwZXJsIFJ1YnkgY2xpZW50
|
128
|
+
test_files: []
|
129
|
+
has_rdoc: false
|