pantry 0.0.0 → 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 +4 -4
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +15 -0
- data/Guardfile +16 -0
- data/LICENSE +20 -0
- data/README.md +53 -0
- data/Rakefile +18 -0
- data/Vagrantfile +86 -0
- data/bin/pantry +11 -0
- data/bin/pantry-client +38 -0
- data/bin/pantry-server +33 -0
- data/dist/client.yml +79 -0
- data/dist/server.yml +56 -0
- data/dist/upstart/pantry-client.conf +12 -0
- data/dist/upstart/pantry-server.conf +12 -0
- data/doc/message_packet.dot +19 -0
- data/doc/message_packet.dot.png +0 -0
- data/doc/network_topology.dot +42 -0
- data/doc/network_topology.dot.png +0 -0
- data/lib/celluloid_zmq_patches.rb +16 -0
- data/lib/opt_parse_plus.rb +184 -0
- data/lib/pantry.rb +197 -0
- data/lib/pantry/cli.rb +154 -0
- data/lib/pantry/client.rb +131 -0
- data/lib/pantry/client_info.rb +34 -0
- data/lib/pantry/client_registry.rb +104 -0
- data/lib/pantry/command.rb +194 -0
- data/lib/pantry/command_handler.rb +53 -0
- data/lib/pantry/command_line.rb +115 -0
- data/lib/pantry/commands/create_client.rb +30 -0
- data/lib/pantry/commands/download_directory.rb +35 -0
- data/lib/pantry/commands/echo.rb +32 -0
- data/lib/pantry/commands/edit_application.rb +60 -0
- data/lib/pantry/commands/register_client.rb +38 -0
- data/lib/pantry/commands/status.rb +78 -0
- data/lib/pantry/commands/sync_directory.rb +50 -0
- data/lib/pantry/commands/update_application.rb +45 -0
- data/lib/pantry/commands/upload_file.rb +68 -0
- data/lib/pantry/communication.rb +20 -0
- data/lib/pantry/communication/client.rb +75 -0
- data/lib/pantry/communication/client_filter.rb +117 -0
- data/lib/pantry/communication/file_service.rb +125 -0
- data/lib/pantry/communication/file_service/file_progress.rb +164 -0
- data/lib/pantry/communication/file_service/receive_file.rb +97 -0
- data/lib/pantry/communication/file_service/send_file.rb +74 -0
- data/lib/pantry/communication/publish_socket.rb +20 -0
- data/lib/pantry/communication/reading_socket.rb +89 -0
- data/lib/pantry/communication/receive_socket.rb +23 -0
- data/lib/pantry/communication/security.rb +44 -0
- data/lib/pantry/communication/security/authentication.rb +98 -0
- data/lib/pantry/communication/security/curve_key_store.rb +120 -0
- data/lib/pantry/communication/security/curve_security.rb +70 -0
- data/lib/pantry/communication/security/null_security.rb +32 -0
- data/lib/pantry/communication/send_socket.rb +19 -0
- data/lib/pantry/communication/serialize_message.rb +84 -0
- data/lib/pantry/communication/server.rb +97 -0
- data/lib/pantry/communication/subscribe_socket.rb +33 -0
- data/lib/pantry/communication/wait_list.rb +45 -0
- data/lib/pantry/communication/writing_socket.rb +46 -0
- data/lib/pantry/config.rb +182 -0
- data/lib/pantry/file_editor.rb +67 -0
- data/lib/pantry/logger.rb +78 -0
- data/lib/pantry/message.rb +134 -0
- data/lib/pantry/multi_command.rb +36 -0
- data/lib/pantry/server.rb +132 -0
- data/lib/pantry/test/acceptance.rb +83 -0
- data/lib/pantry/test/support/fake_fs.rb +31 -0
- data/lib/pantry/test/support/matchers.rb +13 -0
- data/lib/pantry/test/support/minitest.rb +13 -0
- data/lib/pantry/test/support/mock_ui.rb +23 -0
- data/lib/pantry/test/unit.rb +13 -0
- data/lib/pantry/ui.rb +68 -0
- data/lib/pantry/version.rb +3 -0
- data/pantry.gemspec +40 -0
- data/test/acceptance/cli/error_handling_test.rb +7 -0
- data/test/acceptance/cli/execute_command_on_clients_test.rb +32 -0
- data/test/acceptance/cli/request_info_from_server_test.rb +44 -0
- data/test/acceptance/communication/client_requests_info_from_server_test.rb +28 -0
- data/test/acceptance/communication/heartbeat_test.rb +19 -0
- data/test/acceptance/communication/pub_sub_communication_test.rb +53 -0
- data/test/acceptance/communication/security_test.rb +117 -0
- data/test/acceptance/communication/server_requests_info_from_client_test.rb +41 -0
- data/test/acceptance/test_helper.rb +25 -0
- data/test/fixtures/config.yml +22 -0
- data/test/fixtures/empty.yml +2 -0
- data/test/fixtures/file_to_upload +3 -0
- data/test/root_dir/.gitkeep +0 -0
- data/test/unit/cli_test.rb +173 -0
- data/test/unit/client_registry_test.rb +61 -0
- data/test/unit/client_test.rb +128 -0
- data/test/unit/command_handler_test.rb +79 -0
- data/test/unit/command_line_test.rb +5 -0
- data/test/unit/command_test.rb +206 -0
- data/test/unit/commands/create_client_test.rb +25 -0
- data/test/unit/commands/download_directory_test.rb +58 -0
- data/test/unit/commands/echo_test.rb +22 -0
- data/test/unit/commands/edit_application_test.rb +84 -0
- data/test/unit/commands/register_client_test.rb +41 -0
- data/test/unit/commands/status_test.rb +81 -0
- data/test/unit/commands/sync_directory_test.rb +75 -0
- data/test/unit/commands/update_application_test.rb +35 -0
- data/test/unit/commands/upload_file_test.rb +51 -0
- data/test/unit/communication/client_filter_test.rb +262 -0
- data/test/unit/communication/client_test.rb +99 -0
- data/test/unit/communication/file_service/receive_file_test.rb +214 -0
- data/test/unit/communication/file_service/send_file_test.rb +110 -0
- data/test/unit/communication/file_service_test.rb +56 -0
- data/test/unit/communication/publish_socket_test.rb +19 -0
- data/test/unit/communication/reading_socket_test.rb +110 -0
- data/test/unit/communication/receive_socket_test.rb +20 -0
- data/test/unit/communication/security/authentication_test.rb +97 -0
- data/test/unit/communication/security/curve_key_store_test.rb +110 -0
- data/test/unit/communication/security/curve_security_test.rb +44 -0
- data/test/unit/communication/security/null_security_test.rb +15 -0
- data/test/unit/communication/security_test.rb +49 -0
- data/test/unit/communication/send_socket_test.rb +19 -0
- data/test/unit/communication/serialize_message_test.rb +128 -0
- data/test/unit/communication/server_test.rb +106 -0
- data/test/unit/communication/subscribe_socket_test.rb +46 -0
- data/test/unit/communication/wait_list_test.rb +60 -0
- data/test/unit/communication/writing_socket_test.rb +46 -0
- data/test/unit/config_test.rb +150 -0
- data/test/unit/logger_test.rb +79 -0
- data/test/unit/message_test.rb +179 -0
- data/test/unit/multi_command_test.rb +45 -0
- data/test/unit/opt_parse_plus_test.rb +218 -0
- data/test/unit/pantry_test.rb +82 -0
- data/test/unit/server_test.rb +166 -0
- data/test/unit/test_helper.rb +25 -0
- data/test/unit/ui_test.rb +58 -0
- metadata +389 -13
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
class FileService
|
|
4
|
+
|
|
5
|
+
# Informational object for keeping track of file upload progress and
|
|
6
|
+
# important information.
|
|
7
|
+
class UploadInfo
|
|
8
|
+
|
|
9
|
+
# Identity of the Receiver we're sending a file to
|
|
10
|
+
attr_accessor :receiver_uuid
|
|
11
|
+
|
|
12
|
+
# The file session identity from the Receiver
|
|
13
|
+
attr_accessor :file_uuid
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@finish_future = Celluloid::Future.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Block and wait for the file upload to finish
|
|
20
|
+
def wait_for_finish(timeout = nil)
|
|
21
|
+
@finish_future.value(timeout)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def finished!
|
|
25
|
+
@finish_future.signal(OpenStruct.new(:value => self))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Sending-side version of UploadInfo
|
|
31
|
+
class SendingFile < UploadInfo
|
|
32
|
+
attr_reader :path, :file
|
|
33
|
+
|
|
34
|
+
def initialize(file_path, receiver_uuid, file_uuid)
|
|
35
|
+
super()
|
|
36
|
+
@path = file_path
|
|
37
|
+
@file_uuid = file_uuid
|
|
38
|
+
@file = File.open(@path, "r")
|
|
39
|
+
|
|
40
|
+
@receiver_uuid = receiver_uuid
|
|
41
|
+
|
|
42
|
+
@file_size = @file.size
|
|
43
|
+
@total_bytes_sent = 0
|
|
44
|
+
|
|
45
|
+
Pantry.ui.progress_start(@file_size)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def read(offset, bytes_to_read)
|
|
49
|
+
@total_bytes_sent += bytes_to_read
|
|
50
|
+
Pantry.ui.progress_step(bytes_to_read)
|
|
51
|
+
|
|
52
|
+
@file.seek(offset)
|
|
53
|
+
@file.read(bytes_to_read)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def finished!
|
|
57
|
+
Pantry.ui.progress_finish
|
|
58
|
+
|
|
59
|
+
@file.close
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def finished?
|
|
64
|
+
@total_bytes_sent == @file_size || @file.closed?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Receiving-side version of UploadInfo
|
|
70
|
+
# Can be configured with a completion block that will be executed once the
|
|
71
|
+
# file has been fully received and checksum verified.
|
|
72
|
+
class ReceivingFile < UploadInfo
|
|
73
|
+
|
|
74
|
+
# Location of the tempfile containing the contents of the uploaded file
|
|
75
|
+
attr_reader :uploaded_path
|
|
76
|
+
|
|
77
|
+
attr_reader :file_size, :checksum, :uploaded_path
|
|
78
|
+
attr_accessor :sender_uuid
|
|
79
|
+
|
|
80
|
+
def initialize(file_size, checksum, chunk_size, pipeline_size)
|
|
81
|
+
super()
|
|
82
|
+
@file_uuid = SecureRandom.uuid
|
|
83
|
+
@file_size = file_size
|
|
84
|
+
@checksum = checksum
|
|
85
|
+
|
|
86
|
+
@chunk_size = chunk_size
|
|
87
|
+
@pipeline_size = pipeline_size
|
|
88
|
+
|
|
89
|
+
@uploaded_file = Tempfile.new(file_uuid)
|
|
90
|
+
@uploaded_path = @uploaded_file.path
|
|
91
|
+
|
|
92
|
+
@next_requested_file_offset = 0
|
|
93
|
+
@current_pipeline_size = 0
|
|
94
|
+
|
|
95
|
+
@chunk_count = (@file_size.to_f / @chunk_size.to_f).ceil
|
|
96
|
+
@requested_chunks = 0
|
|
97
|
+
@received_chunks = 0
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def on_complete(&block)
|
|
101
|
+
@completion_block = block
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def chunks_to_fetch(&block)
|
|
105
|
+
chunks_to_fill_pipeline = [
|
|
106
|
+
(@pipeline_size - @current_pipeline_size),
|
|
107
|
+
@chunk_count - @requested_chunks
|
|
108
|
+
].min
|
|
109
|
+
|
|
110
|
+
chunks_to_fill_pipeline.times do
|
|
111
|
+
block.call(@next_requested_file_offset, @chunk_size)
|
|
112
|
+
|
|
113
|
+
@next_requested_file_offset += @chunk_size
|
|
114
|
+
@current_pipeline_size += 1
|
|
115
|
+
@requested_chunks += 1
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def write_chunk(offset, size, data)
|
|
120
|
+
@current_pipeline_size -= 1
|
|
121
|
+
@received_chunks += 1
|
|
122
|
+
|
|
123
|
+
@uploaded_file.seek(offset)
|
|
124
|
+
@uploaded_file.write(data)
|
|
125
|
+
|
|
126
|
+
if @received_chunks == @chunk_count
|
|
127
|
+
@uploaded_file.close
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def finished!
|
|
132
|
+
@uploaded_file.close
|
|
133
|
+
|
|
134
|
+
if @completion_block && valid?
|
|
135
|
+
begin
|
|
136
|
+
@completion_block.call
|
|
137
|
+
rescue => ex
|
|
138
|
+
Pantry.logger.debug("[Receive File] Error running completion block #{ex.inspect}")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
super
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def complete?
|
|
146
|
+
@uploaded_file.closed?
|
|
147
|
+
end
|
|
148
|
+
alias finished? complete?
|
|
149
|
+
|
|
150
|
+
def valid?
|
|
151
|
+
return @is_valid if defined?(@is_valid)
|
|
152
|
+
uploaded_checksum = Pantry.file_checksum(@uploaded_file.path)
|
|
153
|
+
@is_valid = (uploaded_checksum == @checksum)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def remove
|
|
157
|
+
@uploaded_file.unlink
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# Chunk file receiving tool that implements the protocol as described here
|
|
5
|
+
# http://zguide.zeromq.org/page:all#Transferring-Files
|
|
6
|
+
#
|
|
7
|
+
# In short, this tool requests chunks in a pipeline flow, writing out
|
|
8
|
+
# the received chunks to the file system at the given path.
|
|
9
|
+
class FileService::ReceiveFile
|
|
10
|
+
include Celluloid
|
|
11
|
+
|
|
12
|
+
attr_accessor :pipeline_size, :chunk_size
|
|
13
|
+
|
|
14
|
+
def initialize(service, chunk_size: 250_000, pipeline_size: 10)
|
|
15
|
+
@service = service
|
|
16
|
+
|
|
17
|
+
@chunk_size = chunk_size
|
|
18
|
+
@pipeline_size = pipeline_size
|
|
19
|
+
|
|
20
|
+
@receiving = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def receive_file(file_size, checksum)
|
|
24
|
+
FileService::ReceivingFile.new(
|
|
25
|
+
file_size, checksum, chunk_size, pipeline_size
|
|
26
|
+
).tap do |info|
|
|
27
|
+
@receiving[info.file_uuid] = info
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def receive_message(from_identity, message)
|
|
32
|
+
if current_file = @receiving[message.to]
|
|
33
|
+
current_file.sender_uuid = from_identity
|
|
34
|
+
else
|
|
35
|
+
return
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
case message.body[0]
|
|
39
|
+
when "START"
|
|
40
|
+
Pantry.logger.debug("[Receive File] Received START message #{message.inspect}")
|
|
41
|
+
fill_the_pipeline(current_file, message)
|
|
42
|
+
when "CHUNK"
|
|
43
|
+
Pantry.logger.debug("[Receive File] Received CHUNK message #{message.metadata}")
|
|
44
|
+
process_chunk(current_file, message)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
|
|
50
|
+
def fill_the_pipeline(current_file, message)
|
|
51
|
+
current_file.chunks_to_fetch do |offset, size|
|
|
52
|
+
Pantry.logger.debug("[Receive File] Fetching #{offset} x #{size} for #{current_file.file_uuid}")
|
|
53
|
+
send_message(current_file, "FETCH", offset, size)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def process_chunk(current_file, message)
|
|
58
|
+
chunk_offset = message[:chunk_offset]
|
|
59
|
+
chunk_size = message[:chunk_size]
|
|
60
|
+
chunk_data = message.body[1]
|
|
61
|
+
|
|
62
|
+
current_file.write_chunk(chunk_offset, chunk_size, chunk_data)
|
|
63
|
+
|
|
64
|
+
if current_file.complete?
|
|
65
|
+
finalize_file(current_file)
|
|
66
|
+
else
|
|
67
|
+
fill_the_pipeline(current_file, message)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def finalize_file(current_file)
|
|
72
|
+
if current_file.valid?
|
|
73
|
+
Pantry.logger.debug("[Receive File] File #{current_file.file_uuid} finished")
|
|
74
|
+
send_message(current_file, "FINISH")
|
|
75
|
+
else
|
|
76
|
+
Pantry.logger.debug("[Receive File] File #{current_file.file_uuid} did not upload successfully")
|
|
77
|
+
current_file.remove
|
|
78
|
+
send_message(current_file, "ERROR", "Checksum did not match the uploaded file")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
current_file.finished!
|
|
82
|
+
@receiving.delete(current_file.file_uuid)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def send_message(current_file, *body)
|
|
86
|
+
message = Pantry::Message.new
|
|
87
|
+
message.to = current_file.file_uuid
|
|
88
|
+
|
|
89
|
+
body.each {|part| message << part }
|
|
90
|
+
|
|
91
|
+
@service.send_message(current_file.sender_uuid, message)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# Chunk file sending tool that implements the protocol as described here
|
|
5
|
+
# http://zguide.zeromq.org/page:all#Transferring-Files
|
|
6
|
+
#
|
|
7
|
+
# As this actor receives chunk requests from the Receiver, it reads that chunk
|
|
8
|
+
# from the given file and sends it along.
|
|
9
|
+
class FileService::SendFile
|
|
10
|
+
include Celluloid
|
|
11
|
+
|
|
12
|
+
def initialize(service)
|
|
13
|
+
@service = service
|
|
14
|
+
@sending = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def send_file(file_path, receiver_uuid, file_uuid)
|
|
18
|
+
sender_info = FileService::SendingFile.new(file_path, receiver_uuid, file_uuid)
|
|
19
|
+
|
|
20
|
+
@sending[file_uuid] = sender_info
|
|
21
|
+
send_message(sender_info, "START")
|
|
22
|
+
|
|
23
|
+
sender_info
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def receive_message(from_identity, message)
|
|
27
|
+
current_file_info = @sending[message.to]
|
|
28
|
+
return unless current_file_info
|
|
29
|
+
|
|
30
|
+
case message.body[0]
|
|
31
|
+
when "FETCH"
|
|
32
|
+
Pantry.logger.debug("[Send File] FETCH requested #{message.inspect}")
|
|
33
|
+
fetch_and_return_chunk(current_file_info, message)
|
|
34
|
+
when "FINISH"
|
|
35
|
+
Pantry.logger.debug("[Send File] FINISHED cleaning up for #{message.inspect}")
|
|
36
|
+
clean_up(current_file_info, message)
|
|
37
|
+
when "ERROR"
|
|
38
|
+
Pantry.logger.debug("[Send File] ERROR #{message.inspect}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
def fetch_and_return_chunk(current_file, message)
|
|
45
|
+
chunk_offset = message.body[1].to_i
|
|
46
|
+
chunk_size = message.body[2].to_i
|
|
47
|
+
|
|
48
|
+
chunk = current_file.read(chunk_offset, chunk_size)
|
|
49
|
+
|
|
50
|
+
send_message(current_file, ["CHUNK", chunk], chunk_offset: chunk_offset, chunk_size: chunk_size)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def clean_up(current_file, message)
|
|
54
|
+
current_file.finished!
|
|
55
|
+
@sending.delete(message.to)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def send_message(sender_info, body, metadata = {})
|
|
59
|
+
message = Pantry::Message.new
|
|
60
|
+
message.to = sender_info.file_uuid
|
|
61
|
+
|
|
62
|
+
[body].flatten.each {|part| message << part }
|
|
63
|
+
|
|
64
|
+
metadata.each do |key, value|
|
|
65
|
+
message[key] = value
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@service.send_message(sender_info.receiver_uuid, message)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# The PublishSocket handles the Publish side of Pub/Sub using
|
|
5
|
+
# a 0MQ PUB socket. Messages can be published to all listening clients
|
|
6
|
+
# or can be filtered to certain clients using a ClientFilter.
|
|
7
|
+
# See SubscribeSocket for the receiving end.
|
|
8
|
+
class PublishSocket < WritingSocket
|
|
9
|
+
|
|
10
|
+
def build_socket
|
|
11
|
+
Celluloid::ZMQ::PubSocket.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def open_socket(socket)
|
|
15
|
+
socket.bind("tcp://#{host}:#{port}")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# Base class of all sockets that read messages from ZMQ.
|
|
5
|
+
# Not meant for direct use, please use one of the subclasses for specific
|
|
6
|
+
# functionality.
|
|
7
|
+
class ReadingSocket
|
|
8
|
+
include Celluloid::ZMQ
|
|
9
|
+
finalizer :shutdown
|
|
10
|
+
|
|
11
|
+
attr_reader :host, :port
|
|
12
|
+
|
|
13
|
+
def initialize(host, port, security)
|
|
14
|
+
@host = host
|
|
15
|
+
@port = port
|
|
16
|
+
@listener = nil
|
|
17
|
+
@security = security
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_listener(listener)
|
|
21
|
+
@listener = listener
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def open
|
|
25
|
+
@socket = build_socket
|
|
26
|
+
Communication.configure_socket(@socket)
|
|
27
|
+
@security.configure_socket(@socket)
|
|
28
|
+
open_socket(@socket)
|
|
29
|
+
|
|
30
|
+
@running = true
|
|
31
|
+
self.async.process_messages
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def build_socket
|
|
35
|
+
raise "Implement the socket setup."
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def open_socket(socket)
|
|
39
|
+
raise "Connect / Bind the socket built in #build_socket"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def shutdown
|
|
43
|
+
@running = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Some ZMQ socket types include the source as the first packet of a message.
|
|
47
|
+
# We need to know if the socket in question does this so we can properly
|
|
48
|
+
# build the Message coming in.
|
|
49
|
+
def has_source_header?
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def process_messages
|
|
56
|
+
while @running
|
|
57
|
+
process_next_message
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
@socket.close
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def process_next_message
|
|
64
|
+
next_message = []
|
|
65
|
+
|
|
66
|
+
# Drop the ZMQ given source packet, it's extraneous for our purposes
|
|
67
|
+
if has_source_header?
|
|
68
|
+
@socket.read
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
next_message << @socket.read
|
|
72
|
+
|
|
73
|
+
while @socket.more_parts?
|
|
74
|
+
next_message << @socket.read
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
async.handle_message(
|
|
78
|
+
SerializeMessage.from_zeromq(next_message)
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def handle_message(message)
|
|
83
|
+
@listener.handle_message(message) if @listener
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# The ReceiveSocket receives communication from Clients via the
|
|
5
|
+
# Dealer / Router socket pair. This class is the Server's Router side.
|
|
6
|
+
class ReceiveSocket < ReadingSocket
|
|
7
|
+
|
|
8
|
+
def build_socket
|
|
9
|
+
Celluloid::ZMQ::RouterSocket.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def open_socket(socket)
|
|
13
|
+
socket.bind("tcp://#{host}:#{port}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def has_source_header?
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|