socketeer 0.0.17
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 +7 -0
- data/lib/socketeer.rb +1 -0
- data/lib/socketeer/handler.rb +61 -0
- data/lib/socketeer/message_collator.rb +27 -0
- data/lib/socketeer/message_handler.rb +19 -0
- data/lib/socketeer/message_transformer.rb +29 -0
- data/lib/socketeer/messenger.rb +28 -0
- data/lib/socketeer/passthrough.rb +46 -0
- data/lib/socketeer/pipeline.rb +19 -0
- data/lib/socketeer/selector_mix.rb +22 -0
- data/lib/socketeer/server.rb +96 -0
- data/lib/socketeer/socketeer.rb +71 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 52a6b2979493d0ddcd9da248609b3b4a6a90c125
|
4
|
+
data.tar.gz: 2f017474f0e6369fd09ffaf0eab93da420dcca9e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8be06b965b173dd97a4b0d3e23b7766019ec6579606e458a54045808fe5657fb737af6926fdcedee1e1e0f9a8c4b922ddcdeac558a3e62c9d1d03e05de4db77a
|
7
|
+
data.tar.gz: 924b6b92eb44b5b614d6f41ea7ba7a7f49fd7b994ed57a60e3dcbad502010ec8758056612be77b249a10593dfa17c45c1d5d39327f6b394aec7b394cf106edbb
|
data/lib/socketeer.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'socketeer/socketeer'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'selector_mix'
|
2
|
+
require_relative 'messenger'
|
3
|
+
|
4
|
+
class Handler
|
5
|
+
|
6
|
+
# PUSHES : raw received data
|
7
|
+
# PULLS : raw to send data
|
8
|
+
|
9
|
+
include Selectable
|
10
|
+
include Messenger
|
11
|
+
|
12
|
+
attr_reader :conn_id
|
13
|
+
|
14
|
+
def initialize socket, conn_id
|
15
|
+
@socket = socket
|
16
|
+
@conn_id = conn_id
|
17
|
+
register_monitor @socket, :r do |m|
|
18
|
+
read
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def cycle
|
23
|
+
cycle_data_in
|
24
|
+
cycle_data_out
|
25
|
+
end
|
26
|
+
|
27
|
+
def cycle_data_in
|
28
|
+
read
|
29
|
+
end
|
30
|
+
|
31
|
+
def cycle_data_out
|
32
|
+
1000.times do
|
33
|
+
m = pop_message
|
34
|
+
write m
|
35
|
+
return if m.nil?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def socket
|
40
|
+
@socket
|
41
|
+
end
|
42
|
+
|
43
|
+
def read
|
44
|
+
begin
|
45
|
+
# raises ex if no data to read
|
46
|
+
data = ''
|
47
|
+
loop do
|
48
|
+
data << @socket.read_nonblock(4096)
|
49
|
+
end
|
50
|
+
rescue
|
51
|
+
# no more datas
|
52
|
+
end
|
53
|
+
push_message data unless data.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
def write data
|
57
|
+
return if data.nil?
|
58
|
+
@socket.write data rescue false
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'messenger'
|
2
|
+
|
3
|
+
class MessageCollator
|
4
|
+
|
5
|
+
# PUSHES : full set of overall data (String)
|
6
|
+
# PULLS : pieces of overall data (String)
|
7
|
+
|
8
|
+
include Messenger
|
9
|
+
|
10
|
+
def initialize deliminator
|
11
|
+
@deliminator = deliminator
|
12
|
+
@buffer = ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def cycle
|
16
|
+
handle_data_in pop_message
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle_data_in data
|
20
|
+
return if data.nil?
|
21
|
+
@buffer += data
|
22
|
+
while @buffer.include? @deliminator
|
23
|
+
message_data, _, @buffer = @buffer.partition @deliminator
|
24
|
+
push_message message_data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'messenger'
|
2
|
+
|
3
|
+
class MessageHandler
|
4
|
+
|
5
|
+
# PUSHES :
|
6
|
+
# PULLS :
|
7
|
+
|
8
|
+
include Messenger
|
9
|
+
|
10
|
+
def initialize &handler
|
11
|
+
@handler = handler
|
12
|
+
end
|
13
|
+
|
14
|
+
def cycle
|
15
|
+
in_message = pop_message
|
16
|
+
out_message = @handler.call in_message unless in_message.nil?
|
17
|
+
push_message out_message
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'messenger'
|
2
|
+
|
3
|
+
class MessageTransformer
|
4
|
+
|
5
|
+
# PUSHES : transformed data (objs)
|
6
|
+
# PULLS : raw data (String)
|
7
|
+
|
8
|
+
include Messenger
|
9
|
+
|
10
|
+
def initialize &transformer
|
11
|
+
@transformer = transformer
|
12
|
+
end
|
13
|
+
|
14
|
+
def cycle
|
15
|
+
handle_data_in pop_message
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_data_in data
|
19
|
+
return if data.nil?
|
20
|
+
push_message transform data
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def transform data
|
26
|
+
@transformer.call data
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Messenger
|
4
|
+
|
5
|
+
attr_reader :in_queue, :out_queue
|
6
|
+
|
7
|
+
def bind_queues in_queue=nil, out_queue=nil
|
8
|
+
@in_queue ||= in_queue
|
9
|
+
@out_queue ||= out_queue
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def push_message message
|
15
|
+
return if message.nil?
|
16
|
+
out_queue << message unless out_queue.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def pop_message
|
20
|
+
# noblock
|
21
|
+
begin
|
22
|
+
return in_queue.deq true unless in_queue.nil?
|
23
|
+
rescue ThreadError
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative 'messenger'
|
2
|
+
|
3
|
+
class Passthrough
|
4
|
+
|
5
|
+
include Messenger
|
6
|
+
|
7
|
+
def initialize attr, messenger
|
8
|
+
@attr = attr
|
9
|
+
@messenger = messenger
|
10
|
+
@in_flight = []
|
11
|
+
bind_queues Queue.new, Queue.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def cycle
|
15
|
+
cycle_in_queue
|
16
|
+
cycle_out_queue
|
17
|
+
cycle_messenger
|
18
|
+
end
|
19
|
+
|
20
|
+
def cycle_in_queue
|
21
|
+
begin
|
22
|
+
in_message = in_queue.deq true
|
23
|
+
if in_message
|
24
|
+
@in_flight << in_message
|
25
|
+
@messenger.in_queue << in_message[@attr]
|
26
|
+
end
|
27
|
+
rescue ThreadError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cycle_out_queue
|
32
|
+
begin
|
33
|
+
out_message = @messenger.out_queue.deq true
|
34
|
+
if out_message
|
35
|
+
original_message = @in_flight.shift
|
36
|
+
original_message[@attr] = out_message
|
37
|
+
out_queue << original_message
|
38
|
+
end
|
39
|
+
rescue ThreadError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def cycle_messenger
|
44
|
+
@messenger.cycle if @messenger.respond_to? 'cycle'
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'messenger'
|
2
|
+
|
3
|
+
class Pipeline
|
4
|
+
def initialize *messengers
|
5
|
+
@messengers = messengers
|
6
|
+
end
|
7
|
+
def cycle
|
8
|
+
@messengers.each_cons(2) do |a, b|
|
9
|
+
a.cycle if a.respond_to? 'cycle'
|
10
|
+
begin
|
11
|
+
1000.times do
|
12
|
+
m = a.out_queue.deq true
|
13
|
+
b.in_queue << m
|
14
|
+
end
|
15
|
+
rescue ThreadError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Selectable
|
3
|
+
|
4
|
+
def cycle_selector
|
5
|
+
@selector.select(0.1) do |monitor|
|
6
|
+
monitor.callback.call monitor
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def register_monitor obj, mode, &callback
|
13
|
+
monitor = selector.register obj, mode
|
14
|
+
monitor.class.class_eval { attr_accessor :callback }
|
15
|
+
monitor.callback = callback
|
16
|
+
monitor
|
17
|
+
end
|
18
|
+
|
19
|
+
def selector
|
20
|
+
@selector ||= NIO::Selector.new
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'nio'
|
2
|
+
require 'socket'
|
3
|
+
require_relative 'selector_mix'
|
4
|
+
require_relative 'messenger'
|
5
|
+
|
6
|
+
class Server
|
7
|
+
|
8
|
+
# PUSHES : {:conn_id, :data}
|
9
|
+
# PULLS : {:conn_id, :data}
|
10
|
+
|
11
|
+
include Selectable
|
12
|
+
include Messenger
|
13
|
+
|
14
|
+
attr_accessor :host, :port
|
15
|
+
|
16
|
+
def initialize host='localhost', port=3123, handler=nil
|
17
|
+
|
18
|
+
@Handler = handler
|
19
|
+
@host = host
|
20
|
+
@port = port
|
21
|
+
|
22
|
+
@connections = {}
|
23
|
+
puts "BINDING: #{host}:#{port}"
|
24
|
+
@tcp_server = TCPServer.new host, port
|
25
|
+
register_monitor @tcp_server, :r do |monitor|
|
26
|
+
handle_socket_connect @tcp_server.accept
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def cycle
|
31
|
+
cycle_selector
|
32
|
+
cycle_connection_queues
|
33
|
+
cycle_inbound
|
34
|
+
cycle_handlers
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def cycle_handlers
|
40
|
+
@connections.each {|conn_id, handler| handler.cycle}
|
41
|
+
end
|
42
|
+
|
43
|
+
def cycle_inbound
|
44
|
+
1000.times do
|
45
|
+
msg = pop_message
|
46
|
+
return if msg.nil?
|
47
|
+
handle_new_message msg
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def cycle_connection_queues
|
52
|
+
@connections.each do |conn_id, handler|
|
53
|
+
# dont get backed up here
|
54
|
+
begin
|
55
|
+
1000.times do
|
56
|
+
handle_new_data conn_id, handler.out_queue.deq(true)
|
57
|
+
end
|
58
|
+
rescue ThreadError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_socket_connect socket
|
64
|
+
handler = create_handler socket
|
65
|
+
return handler.conn_id
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_handler socket
|
69
|
+
conn_id = get_conn_id socket
|
70
|
+
return unless @connections[conn_id].nil?
|
71
|
+
handler = @Handler.new socket, conn_id
|
72
|
+
# TODO: not ref Queue directly
|
73
|
+
handler.bind_queues Queue.new, Queue.new
|
74
|
+
@connections[conn_id] = handler
|
75
|
+
return handler
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_conn_id socket
|
79
|
+
socket.object_id
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_new_data conn_id, data
|
83
|
+
return if data.nil?
|
84
|
+
push_message :conn_id => conn_id, :data => data
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_new_message message
|
88
|
+
return if message.nil?
|
89
|
+
unless message[:conn_id].nil?
|
90
|
+
@connections[message[:conn_id]].in_queue << message[:data]
|
91
|
+
else
|
92
|
+
@connections.each { |_, c| c.in_queue << message[:data] }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require_relative 'server'
|
3
|
+
require_relative 'handler'
|
4
|
+
require_relative 'message_transformer'
|
5
|
+
require_relative 'message_collator'
|
6
|
+
require_relative 'message_handler'
|
7
|
+
require_relative 'passthrough'
|
8
|
+
require_relative 'pipeline'
|
9
|
+
|
10
|
+
class PrintingQueue < Queue
|
11
|
+
def << *args
|
12
|
+
r = super *args
|
13
|
+
return r
|
14
|
+
end
|
15
|
+
|
16
|
+
def deq *args
|
17
|
+
r = super
|
18
|
+
return r
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class IQueue < Queue
|
23
|
+
end
|
24
|
+
|
25
|
+
module Socketeer
|
26
|
+
|
27
|
+
def bind host, port, &callback
|
28
|
+
# will use the passed callback if provided, else calls handle_message
|
29
|
+
callback ||= proc { |m| handle_message m }
|
30
|
+
@message_handler = MessageHandler.new &callback
|
31
|
+
@server = Server.new host, port, Handler
|
32
|
+
@collator = MessageCollator.new "\n\n"
|
33
|
+
@in_message_transformer = MessageTransformer.new {|d| MessagePack.unpack d }
|
34
|
+
@out_message_transformer = MessageTransformer.new { |d|
|
35
|
+
MessagePack.pack(d) + "\n\n"
|
36
|
+
}
|
37
|
+
@server.bind_queues IQueue.new, IQueue.new
|
38
|
+
@collator.bind_queues IQueue.new, IQueue.new
|
39
|
+
@in_message_transformer.bind_queues IQueue.new, IQueue.new
|
40
|
+
@out_message_transformer.bind_queues IQueue.new, IQueue.new
|
41
|
+
@message_handler.bind_queues IQueue.new, IQueue.new
|
42
|
+
@pipeline = Pipeline.new(@server,
|
43
|
+
Passthrough.new(:data, @collator),
|
44
|
+
Passthrough.new(:data, @in_message_transformer),
|
45
|
+
@message_handler,
|
46
|
+
Passthrough.new(:data, @out_message_transformer),
|
47
|
+
@server)
|
48
|
+
[host, port]
|
49
|
+
end
|
50
|
+
|
51
|
+
def cycle
|
52
|
+
@pipeline.cycle
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def handle_message message
|
58
|
+
end
|
59
|
+
|
60
|
+
def send_message conn_id, message
|
61
|
+
@message_handler.out_queue << { conn_id: conn_id,
|
62
|
+
data: message }
|
63
|
+
end
|
64
|
+
|
65
|
+
def register_socket socket
|
66
|
+
@server.instance_eval do
|
67
|
+
handle_socket_connect socket
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: socketeer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.17
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robby Ranshous
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: msgpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nio4r
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: simple socket server for doing message passing
|
42
|
+
email: rranshous@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/socketeer.rb
|
48
|
+
- lib/socketeer/handler.rb
|
49
|
+
- lib/socketeer/message_collator.rb
|
50
|
+
- lib/socketeer/message_handler.rb
|
51
|
+
- lib/socketeer/message_transformer.rb
|
52
|
+
- lib/socketeer/messenger.rb
|
53
|
+
- lib/socketeer/passthrough.rb
|
54
|
+
- lib/socketeer/pipeline.rb
|
55
|
+
- lib/socketeer/selector_mix.rb
|
56
|
+
- lib/socketeer/server.rb
|
57
|
+
- lib/socketeer/socketeer.rb
|
58
|
+
homepage: http://oneinchmile.com
|
59
|
+
licenses: []
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.4.8
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: simple socket server
|
81
|
+
test_files: []
|