rxio 0.11.4 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +102 -30
- data/lib/rxio/client.rb +160 -0
- data/lib/rxio/handler_base.rb +8 -8
- data/lib/rxio/io_base.rb +79 -0
- data/lib/rxio/io_filters/bin_delim.rb +1 -0
- data/lib/rxio/service.rb +28 -71
- data/lib/rxio/version.rb +1 -1
- data/lib/rxio.rb +2 -1
- data/rxio.gemspec +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a192fa9381b7872090a16feb90e28fe3fe6c1994
|
4
|
+
data.tar.gz: 582612082630571b7ef3bd855620bedddd488e17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 934b55278bcd8237d83d79c94374b2cb4e0ecc01eb6321a91388e22fb73fecbb94db2abd122ad2b743cc9986dd85c5a6c25b5af23d1875c6c6a388b93a4294b4
|
7
|
+
data.tar.gz: 16939703ed1cc130e9f5b5be682a951890c67c12f1f3952d7f98ab0f698e80d3a45a75045b4390fe2c4360b5ac4a133d9fc7579ab3cc4ff10d5dc3e41ffa132e
|
data/README.md
CHANGED
@@ -23,26 +23,27 @@ gem install -V rxio
|
|
23
23
|
|
24
24
|
A simple service is created by spawning an instance of the *RxIO::Service* class, passing a *Handler Module* as argument to its constructor and calling its _run_ method.
|
25
25
|
|
26
|
-
|
26
|
+
A simple client is created by spawning an instance of the *RxIO::Client* class, passing a *Handler Module* as argument to its constructor and calling its _run_ method.
|
27
27
|
|
28
|
-
|
28
|
+
This will execute the service / client main loop. The handler module provides the specific protocol implementation through a set of methods.
|
29
29
|
|
30
|
-
|
30
|
+
The _run_ method on both *Service* & *Client* is blocking and will only return after the main loop has terminated.
|
31
|
+
A _stop_ method is also provided to request the main loop to terminate.
|
31
32
|
|
32
33
|
### Running in the background
|
33
34
|
|
34
|
-
While the _run_ / _stop_ methods offer a simple way to implement a single service within the current thread, some situations may require
|
35
|
+
While the _run_ / _stop_ methods offer a simple way to implement a single service/client within the current thread, some situations may require wrapping in a separate thread.
|
35
36
|
|
36
|
-
Thank to [Runify](https://rubygems.org/gems/runify), a dedicated interface is provided for exactly this: _startup_ / _shutdown_ / _restart_
|
37
|
+
Thank to [Runify](https://rubygems.org/gems/runify), a dedicated interface is provided for exactly this purpose: _startup_ / _shutdown_ / _restart_
|
37
38
|
|
38
39
|
#### *startup*
|
39
40
|
|
40
|
-
Calling this method will spawn a new thread around the _run_ method, effectively starting the service in the 'background'.
|
41
|
+
Calling this method will spawn a new thread around the _run_ method, effectively starting the service / client in the 'background'.
|
41
42
|
|
42
43
|
#### *shutdown*
|
43
44
|
|
44
|
-
Calling _shutdown_ will request the service to terminate and wait for the thread to complete (join).
|
45
|
-
Once this method returns, the service has completely terminated (thread dead, all clients disconnected).
|
45
|
+
Calling _shutdown_ will request the service / client to terminate and wait for the wrapper thread to complete (join).
|
46
|
+
Once this method returns, the service / client has completely terminated (thread dead, all sockets closed, all clients disconnected).
|
46
47
|
|
47
48
|
#### *restart*
|
48
49
|
|
@@ -50,52 +51,53 @@ This method is a simple shortcut to _shutdown_ immediately followed by _startup_
|
|
50
51
|
|
51
52
|
### Handler Interface
|
52
53
|
|
53
|
-
The following is a list of methods that should be implemented by the handler module in order to provide a valid interface to the RxIO::Service
|
54
|
+
The following is a list of methods that should be implemented by the handler module in order to provide a valid interface to the RxIO::Service and RxIO::Client classes that will use it.
|
54
55
|
|
55
|
-
#### *filter_input*
|
56
|
+
#### *filter_input* _endpoint_, _chunk_
|
56
57
|
|
57
|
-
This method is called any time a chunk of data is received
|
58
|
+
This method is called any time a chunk of data is received on the wire.
|
58
59
|
Its purpose is usually to filter protocol data and re-assemble messages.
|
59
|
-
Complete messages can be enqueued for processing by pushing them into the *
|
60
|
+
Complete messages can be enqueued for processing by pushing them into the *endpoint[:msgs]* array.
|
60
61
|
|
61
|
-
#### *handle_msg*
|
62
|
+
#### *handle_msg* _endpoint_, _msg_
|
62
63
|
|
63
|
-
Messages in the *
|
64
|
-
This is usually the entry point to most of the
|
64
|
+
Messages in the *endpoint[:msgs]* array are regularly de-queued and passed to the *handle_msg* method for handling.
|
65
|
+
This is usually the entry point to most of the business logic for services.
|
65
66
|
|
66
|
-
#### (OPTIONAL) *on_join*
|
67
|
+
#### (OPTIONAL) *on_join* _endpoint_
|
67
68
|
|
68
|
-
As soon as a
|
69
|
-
This allows the handler module to perform any necessary initialization tasks related to this
|
69
|
+
As soon as a endpoint is 'online' (Client is registered by the Service / Connection to Server is established by the Client), it is passed as argument to the _on_join_ method of the handler module (if available).
|
70
|
+
This allows the handler module to perform any necessary initialization tasks related to this endpoint.
|
70
71
|
|
71
|
-
#### (OPTIONAL) *on_drop*
|
72
|
+
#### (OPTIONAL) *on_drop* _endpoint_
|
72
73
|
|
73
|
-
Called by the service whenever a client
|
74
|
+
Called by the service / client whenever the connection to the server / a client has been lost (if available).
|
74
75
|
This method should release any resources used by the client.
|
75
76
|
|
76
|
-
#### (OPTIONAL) *send_msg*
|
77
|
+
#### (OPTIONAL) *send_msg* _endpoint_, _msg_
|
77
78
|
|
78
|
-
This method should
|
79
|
-
While not
|
79
|
+
This method should encapsulate the message supplied as argument according to the desired protocol, and then add the result to the output buffer.
|
80
|
+
While not absolutely required by the service or client classes, this method should be considered a best-practice for users to encapsulate data according to a protocol.
|
81
|
+
Also, if an implementation for _send_msg_ is provided in the Service Handler, Clients built around it will expose a shortcut 'send_msg' instance method.
|
80
82
|
|
81
83
|
### Handler Base
|
82
84
|
|
83
|
-
A base module is provided to facilitate implementation of new
|
85
|
+
A base module is provided to facilitate implementation of new protocols.
|
84
86
|
The *RxIO::HandlerBase* module should be extended by the service handler module.
|
85
87
|
It provides two simple I/O methods:
|
86
88
|
|
87
|
-
#### *write*
|
89
|
+
#### *write* _endpoint_, _*data_
|
88
90
|
|
89
|
-
Used to send one or more data chunks to
|
90
|
-
This method pushes the chunk(s) to the output buffer, to be later sent
|
91
|
+
Used to send one or more data chunks to an endpoint.
|
92
|
+
This method pushes the chunk(s) to the output buffer, to be later sent over the wire.
|
91
93
|
|
92
|
-
This method is *thread-safe* - while in most cases it will be called from within the main service loop (
|
94
|
+
This method is *thread-safe* - while in most cases it will be called from within the main service / client loop (often as a consequence of service logic in _handle_msg_), it is perfectly safe to call from any other thread. This is how most clients should behave. This can also be useful in situations where a service might generate messages for the client _outside_ of the normal request-response cycle (such as an event-subscription service).
|
93
95
|
|
94
96
|
Note: this method does not perform any encoding or transformation on the data. It is the user's responsibility to provide protocol encoding, usually through the _send_msg_ method.
|
95
97
|
|
96
|
-
#### *buffer_input*,
|
98
|
+
#### *buffer_input*, _endpoint_, _chunk_
|
97
99
|
|
98
|
-
Used to add a data chunk to the
|
100
|
+
Used to add a data chunk to the endpoint's input buffer.
|
99
101
|
|
100
102
|
### Provided Filters
|
101
103
|
|
@@ -217,6 +219,58 @@ es = EchoService.new
|
|
217
219
|
es.run
|
218
220
|
```
|
219
221
|
|
222
|
+
### A simple client for the echo service
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
#!/usr/bin/env ruby
|
226
|
+
|
227
|
+
require 'rxio'
|
228
|
+
|
229
|
+
# Echo Client Class
|
230
|
+
class EchoClient < RxIO::Client
|
231
|
+
|
232
|
+
# Defaults
|
233
|
+
DEFAULT_ADDR = '0.0.0.0'
|
234
|
+
DEFAULT_PORT = 4444
|
235
|
+
|
236
|
+
# Construct
|
237
|
+
def initialize
|
238
|
+
super DEFAULT_ADDR, DEFAULT_PORT, EchoHandler
|
239
|
+
end
|
240
|
+
|
241
|
+
# Echo Service Handler Module
|
242
|
+
module EchoHandler
|
243
|
+
|
244
|
+
# Extend BinDelim I/O Filter
|
245
|
+
extend RxIO::IOFilters::BinDelim
|
246
|
+
|
247
|
+
# Set Message Delimiter
|
248
|
+
msg_delim "\n"
|
249
|
+
|
250
|
+
# On Join
|
251
|
+
def self.on_join server
|
252
|
+
puts "Connected to server: #{server[:peer][:name]}:#{server[:peer][:port]}"
|
253
|
+
send_msg server, "Hello World!"
|
254
|
+
end
|
255
|
+
|
256
|
+
# On Drop
|
257
|
+
def self.on_drop server
|
258
|
+
puts "Connection dropped: #{server[:peer][:name]}:#{server[:peer][:port]}"
|
259
|
+
end
|
260
|
+
|
261
|
+
# Handle Message
|
262
|
+
def self.handle_msg server, msg
|
263
|
+
puts "Got response from server: #{server[:peer][:name]}:#{server[:peer][:port]} -> \"#{msg}\""
|
264
|
+
server[:client].stop # Done - Terminate the Client!
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Run the Client
|
270
|
+
ec = EchoClient.new
|
271
|
+
ec.run
|
272
|
+
```
|
273
|
+
|
220
274
|
### Running a service in the background
|
221
275
|
|
222
276
|
```ruby
|
@@ -235,6 +289,24 @@ es.shutdown
|
|
235
289
|
puts 'Service has terminated!'
|
236
290
|
```
|
237
291
|
|
292
|
+
### Running a client in the background
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
# ...
|
296
|
+
|
297
|
+
# Run the client in the background
|
298
|
+
puts 'Client is starting up...'
|
299
|
+
es = EchoService.new
|
300
|
+
es.startup
|
301
|
+
puts 'Client is online!'
|
302
|
+
|
303
|
+
# Do something while the client is running...
|
304
|
+
|
305
|
+
# Shutdown the client
|
306
|
+
es.shutdown
|
307
|
+
puts 'Client has terminated!'
|
308
|
+
```
|
309
|
+
|
238
310
|
## License
|
239
311
|
|
240
312
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/rxio/client.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# RxIO
|
2
|
+
# by Eresse <eresse@eresse.net>
|
3
|
+
|
4
|
+
# External Includes
|
5
|
+
require 'thread'
|
6
|
+
require 'socket'
|
7
|
+
require 'runify'
|
8
|
+
|
9
|
+
# Internal Includes
|
10
|
+
require 'rxio/io_base'
|
11
|
+
|
12
|
+
# RxIO Module
|
13
|
+
module RxIO
|
14
|
+
|
15
|
+
# Client Class
|
16
|
+
class Client
|
17
|
+
|
18
|
+
# Runify
|
19
|
+
include Runify
|
20
|
+
|
21
|
+
# I/O Base
|
22
|
+
include IOBase
|
23
|
+
|
24
|
+
# Reconnect Delay (seconds)
|
25
|
+
RECONNECT_DELAY = 1
|
26
|
+
|
27
|
+
# Select Timeout (Seconds)
|
28
|
+
SELECT_TIMEOUT = 0.1
|
29
|
+
|
30
|
+
# Construct
|
31
|
+
# Builds a *Client* around a given _service_handler_ module, set to connect to _addr_ on _port_.
|
32
|
+
# @param [String] addr
|
33
|
+
# @param [Fixnum] port
|
34
|
+
# @param [Module] service_handler
|
35
|
+
def initialize addr, port, service_handler
|
36
|
+
|
37
|
+
# Set Address & Port
|
38
|
+
@addr = addr
|
39
|
+
@port = port
|
40
|
+
|
41
|
+
# Set Service Handler Module
|
42
|
+
@service_handler = service_handler
|
43
|
+
|
44
|
+
# Set Socket
|
45
|
+
@sock = nil
|
46
|
+
|
47
|
+
# Client Hash
|
48
|
+
@client = {
|
49
|
+
client: self,
|
50
|
+
ibuf: '',
|
51
|
+
obuf: '',
|
52
|
+
lock: Mutex.new,
|
53
|
+
msgs: []
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Send Message
|
58
|
+
# Enqueues a Message to be sent to the server
|
59
|
+
# @param [String] msg
|
60
|
+
def send_msg msg
|
61
|
+
|
62
|
+
# Verify Implementation
|
63
|
+
raise "Method send_msg is not implemented by Service Handler #{@service_handler.name}" unless @service_handler.respond_to? :send_msg
|
64
|
+
|
65
|
+
# Send Message through Service Handler
|
66
|
+
@service_handler.send_msg @client, msg
|
67
|
+
end
|
68
|
+
|
69
|
+
# Run
|
70
|
+
# Executes the main client loop, taking care of I/O scheduling and message handling.
|
71
|
+
def run
|
72
|
+
|
73
|
+
# Update Loop
|
74
|
+
begin
|
75
|
+
|
76
|
+
# Update Service
|
77
|
+
update until @stop
|
78
|
+
rescue Exception => e
|
79
|
+
puts "[!] ERROR - RxIO Client Update failed - #{e.inspect}"
|
80
|
+
e.backtrace.each { |b| puts " - #{b}" }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Drop Socket
|
84
|
+
@sock.close if @sock
|
85
|
+
@sock = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Stop
|
89
|
+
# Requests the client loop to stop executing.
|
90
|
+
# The _run_ method should return shortly after calling _stop_
|
91
|
+
def stop
|
92
|
+
@stop = true
|
93
|
+
end
|
94
|
+
|
95
|
+
# Privates
|
96
|
+
private
|
97
|
+
|
98
|
+
# Update
|
99
|
+
# Serves as the client loop main method, performing I/O scheduling and message handling.
|
100
|
+
def update
|
101
|
+
|
102
|
+
# Ensure Socket is available
|
103
|
+
return unless ensure_sock
|
104
|
+
|
105
|
+
# Select
|
106
|
+
rd, wr, _er = IO.select [@sock], @client[:obuf].empty? ? [] : [@sock], [], SELECT_TIMEOUT
|
107
|
+
|
108
|
+
# Handle I/O
|
109
|
+
rd.each { |s| read_sock s } if rd
|
110
|
+
wr.each { |s| write_sock s } if wr
|
111
|
+
end
|
112
|
+
|
113
|
+
# Ensure Socket is available
|
114
|
+
# Re-creates the socket in the event of failure
|
115
|
+
def ensure_sock
|
116
|
+
|
117
|
+
# Check
|
118
|
+
return @sock if @sock
|
119
|
+
|
120
|
+
# Reconnect Delay (don't be an asshole...)
|
121
|
+
sleep RECONNECT_DELAY unless @sock
|
122
|
+
|
123
|
+
# Attempt reconnect
|
124
|
+
@sock = TCPSocket.new @addr, @port rescue nil
|
125
|
+
|
126
|
+
# Check Socket
|
127
|
+
return nil unless @sock
|
128
|
+
|
129
|
+
# Acquire Peer Address
|
130
|
+
peer = @sock.peeraddr
|
131
|
+
@client[:peer] = {
|
132
|
+
port: peer[1],
|
133
|
+
name: peer[2],
|
134
|
+
addr: peer[3]
|
135
|
+
}
|
136
|
+
|
137
|
+
# Notify Service Handler on success
|
138
|
+
@service_handler.on_join @client if @service_handler.respond_to? :on_join
|
139
|
+
|
140
|
+
# Return Result
|
141
|
+
@sock
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get Endpoint for Socket - Callback for IOBase
|
145
|
+
# Simply returns the Client Hash
|
146
|
+
# @param [TCPSocket] _s
|
147
|
+
# @return [Hash] The Client Hash
|
148
|
+
def get_endpoint_for_sock _s
|
149
|
+
@client
|
150
|
+
end
|
151
|
+
|
152
|
+
# On Drop - Callback for IOBase
|
153
|
+
# Kills the socket
|
154
|
+
# @param [Hash] _e
|
155
|
+
def on_drop _e
|
156
|
+
@sock.close rescue nil
|
157
|
+
@sock = nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/rxio/handler_base.rb
CHANGED
@@ -9,23 +9,23 @@ module RxIO
|
|
9
9
|
module HandlerBase
|
10
10
|
|
11
11
|
# Write
|
12
|
-
# Writes one or more chunks of data to the
|
13
|
-
# @param [Hash]
|
12
|
+
# Writes one or more chunks of data to the endpoint's output buffer (:obuf).
|
13
|
+
# @param [Hash] endpoint
|
14
14
|
# @param [String] data One or more chunks of data to be written to the ouput buffer
|
15
|
-
def write
|
15
|
+
def write endpoint, *data
|
16
16
|
|
17
17
|
# Add Data Chunks to Buffer
|
18
|
-
data.each { |c|
|
18
|
+
data.each { |c| endpoint[:lock].synchronize { endpoint[:obuf] << c } }
|
19
19
|
end
|
20
20
|
|
21
21
|
# Buffer Input Chunk
|
22
|
-
# Writes a chunk of data to the
|
23
|
-
# @param [Hash]
|
22
|
+
# Writes a chunk of data to the endpoint's input buffer (:ibuf).
|
23
|
+
# @param [Hash] endpoint
|
24
24
|
# @param [String] chunk
|
25
|
-
def buffer_input
|
25
|
+
def buffer_input endpoint, chunk
|
26
26
|
|
27
27
|
# Buffer Chunk
|
28
|
-
|
28
|
+
endpoint[:ibuf] << chunk
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/lib/rxio/io_base.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# RxIO
|
2
|
+
# by Eresse <eresse@eresse.net>
|
3
|
+
|
4
|
+
# RxIO Module
|
5
|
+
module RxIO
|
6
|
+
|
7
|
+
# I/O Base Module
|
8
|
+
module IOBase
|
9
|
+
|
10
|
+
# Chunk Size
|
11
|
+
CHUNK_SIZE = 1024
|
12
|
+
|
13
|
+
# Process Input
|
14
|
+
# Processes Input for an Endpoint, in the form of a data chunk
|
15
|
+
# @param [Hash] endpoint
|
16
|
+
# @param [String] chunk A chunk of data, as received by the socket
|
17
|
+
def process_input endpoint, chunk
|
18
|
+
|
19
|
+
# Pass through Service Handler Module
|
20
|
+
@service_handler.filter_input endpoint, chunk
|
21
|
+
|
22
|
+
# Process Messages
|
23
|
+
@service_handler.handle_msg endpoint, endpoint[:msgs].shift until endpoint[:msgs].empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Read Socket
|
27
|
+
# Attempts to read as many bytes as possible (up to CHUNK_SIZE) from a given socket _s_, passing the data chunks to _process_input_.
|
28
|
+
# @param [TCPSocket] s
|
29
|
+
def read_sock s
|
30
|
+
|
31
|
+
# Acquire Endpoint for Socket
|
32
|
+
e = get_endpoint_for_sock s
|
33
|
+
|
34
|
+
# Check Endpoint
|
35
|
+
return unless e
|
36
|
+
|
37
|
+
# Read Chunk from Socket
|
38
|
+
chunk = s.read_nonblock CHUNK_SIZE rescue nil
|
39
|
+
|
40
|
+
# Drop Endpoint & Abort on Error
|
41
|
+
return drop_endpoint e unless chunk
|
42
|
+
|
43
|
+
# Process Input
|
44
|
+
process_input e, chunk
|
45
|
+
end
|
46
|
+
|
47
|
+
# Write Socket
|
48
|
+
# Attempts to write as many bytes as possible to a given socket _s_ from the associated client's output buffer.
|
49
|
+
# @param [TCPSocket] s
|
50
|
+
def write_sock s
|
51
|
+
|
52
|
+
# Acquire Endpoint
|
53
|
+
e = get_endpoint_for_sock s
|
54
|
+
|
55
|
+
# Check Endpoint
|
56
|
+
return unless e
|
57
|
+
|
58
|
+
# Synchronize Endpoint
|
59
|
+
e[:lock].synchronize do
|
60
|
+
|
61
|
+
# Write as much as possible
|
62
|
+
size = (s.write_nonblock e[:obuf] rescue nil) || 0
|
63
|
+
e[:obuf].slice! 0, size if size > 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Drop Endpoint
|
68
|
+
# Notifies the Service Handler and Parent Implementation (in that order) of a dropped endpoint
|
69
|
+
# @param [Hash] endpoint
|
70
|
+
def drop_endpoint endpoint
|
71
|
+
|
72
|
+
# Notify Service Handler
|
73
|
+
@service_handler.on_drop endpoint if @service_handler.respond_to? :on_drop
|
74
|
+
|
75
|
+
# Drop Endpoint
|
76
|
+
on_drop endpoint
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/rxio/service.rb
CHANGED
@@ -4,6 +4,10 @@
|
|
4
4
|
# External Includes
|
5
5
|
require 'thread'
|
6
6
|
require 'socket'
|
7
|
+
require 'runify'
|
8
|
+
|
9
|
+
# Internal Includes
|
10
|
+
require 'rxio/io_base'
|
7
11
|
|
8
12
|
# RxIO Module
|
9
13
|
module RxIO
|
@@ -14,8 +18,11 @@ module RxIO
|
|
14
18
|
# Runify
|
15
19
|
include Runify
|
16
20
|
|
17
|
-
#
|
18
|
-
|
21
|
+
# I/O Base
|
22
|
+
include IOBase
|
23
|
+
|
24
|
+
# Select Timeout (Seconds)
|
25
|
+
SELECT_TIMEOUT = 0.1
|
19
26
|
|
20
27
|
# Construct
|
21
28
|
# Builds a *Service* around a given _service_handler_ module, set to listen for incoming connections @ _addr_ on _port_.
|
@@ -61,7 +68,7 @@ module RxIO
|
|
61
68
|
end
|
62
69
|
|
63
70
|
# Drop all Clients
|
64
|
-
@service_handler.on_drop @clients.
|
71
|
+
@service_handler.on_drop @clients.shift until @clients.empty? if @service_handler.respond_to? :on_drop
|
65
72
|
@cmap = {}
|
66
73
|
|
67
74
|
# Close all Sockets
|
@@ -90,24 +97,11 @@ module RxIO
|
|
90
97
|
ws = @clients.reject { |c| c[:lock].synchronize { c[:obuf].empty? } }.collect { |c| c[:sock] }
|
91
98
|
|
92
99
|
# Select Sockets
|
93
|
-
rd, wr = IO.select @socks, ws
|
100
|
+
rd, wr, _er = IO.select @socks, ws, [], SELECT_TIMEOUT
|
94
101
|
|
95
102
|
# Handle I/O
|
96
|
-
rd.each { |s| s == @serv ? acpt_sock(s) : read_sock(s) }
|
97
|
-
wr.each { |s| write_sock s }
|
98
|
-
end
|
99
|
-
|
100
|
-
# Process Input
|
101
|
-
# Processes Input from a client, in the form of a data chunk
|
102
|
-
# @param [Hash] client
|
103
|
-
# @param [String] chunk A chunk of data, as received by the socket
|
104
|
-
def process_input client, chunk
|
105
|
-
|
106
|
-
# Pass through Service Handler Module
|
107
|
-
@service_handler.filter_input client, chunk
|
108
|
-
|
109
|
-
# Process Messages
|
110
|
-
@service_handler.handle_msg client, client[:msgs].shift until client[:msgs].empty?
|
103
|
+
rd.each { |s| s == @serv ? acpt_sock(s) : read_sock(s) } if rd
|
104
|
+
wr.each { |s| write_sock s } if wr
|
111
105
|
end
|
112
106
|
|
113
107
|
# Register Client
|
@@ -146,24 +140,6 @@ module RxIO
|
|
146
140
|
@service_handler.on_join c if @service_handler.respond_to? :on_join
|
147
141
|
end
|
148
142
|
|
149
|
-
# Drop Client
|
150
|
-
# Unregisters a Client and closes the associated socket.
|
151
|
-
# Also, notifies the Handler Module if the _on_drop_ method is available.
|
152
|
-
# @param [Hash] c
|
153
|
-
def drop_client c
|
154
|
-
|
155
|
-
# Notify Service Handler
|
156
|
-
@service_handler.on_drop c if @service_handler.respond_to? :on_drop
|
157
|
-
|
158
|
-
# Drop Client
|
159
|
-
@cmap.delete c[:sock]
|
160
|
-
@socks.delete c[:sock]
|
161
|
-
@clients.delete c
|
162
|
-
|
163
|
-
# Kill Socket
|
164
|
-
c[:sock].close rescue nil
|
165
|
-
end
|
166
|
-
|
167
143
|
# Accept Socket
|
168
144
|
# Tries to accept any queued connection request in a non-blocking manner.
|
169
145
|
# Registers a new Client through _add_client_ around the newly-accepted socket if present.
|
@@ -177,45 +153,26 @@ module RxIO
|
|
177
153
|
add_client ns if ns
|
178
154
|
end
|
179
155
|
|
180
|
-
#
|
181
|
-
#
|
156
|
+
# Get Endpoint for Socket - Callback for IOBase
|
157
|
+
# Finds the Client associated with a given Socket
|
182
158
|
# @param [TCPSocket] s
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
c = @cmap[s]
|
187
|
-
|
188
|
-
# Check Client
|
189
|
-
return unless c
|
190
|
-
|
191
|
-
# Read Chunk from Socket
|
192
|
-
chunk = s.read_nonblock CHUNK_SIZE rescue nil
|
193
|
-
|
194
|
-
# Drop Client & Abort on Error
|
195
|
-
return drop_client c unless chunk
|
196
|
-
|
197
|
-
# Process Input
|
198
|
-
process_input c, chunk
|
159
|
+
# @return [Hash] The Endpoint associated with Socket _s_, or nil
|
160
|
+
def get_endpoint_for_sock s
|
161
|
+
@cmap[s]
|
199
162
|
end
|
200
163
|
|
201
|
-
#
|
202
|
-
#
|
203
|
-
# @param [
|
204
|
-
def
|
205
|
-
|
206
|
-
# Acquire Client
|
207
|
-
c = @cmap[s]
|
208
|
-
|
209
|
-
# Check Client
|
210
|
-
return unless c
|
164
|
+
# On Drop - Callback for IOBase
|
165
|
+
# Unregisters a Client and closes the associated socket.
|
166
|
+
# @param [Hash] c
|
167
|
+
def on_drop c
|
211
168
|
|
212
|
-
#
|
213
|
-
c[:
|
169
|
+
# Drop Client
|
170
|
+
@cmap.delete c[:sock]
|
171
|
+
@socks.delete c[:sock]
|
172
|
+
@clients.delete c
|
214
173
|
|
215
|
-
|
216
|
-
|
217
|
-
c[:obuf].slice! 0, size if size > 0
|
218
|
-
end
|
174
|
+
# Kill Socket
|
175
|
+
c[:sock].close rescue nil
|
219
176
|
end
|
220
177
|
end
|
221
178
|
end
|
data/lib/rxio/version.rb
CHANGED
data/lib/rxio.rb
CHANGED
data/rxio.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rxio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eresse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: runify
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Provides an implementation of the Reactor Pattern for Ruby Sockets.
|
56
70
|
email:
|
57
71
|
- eresse@eresse.net
|
@@ -65,7 +79,9 @@ files:
|
|
65
79
|
- README.md
|
66
80
|
- Rakefile
|
67
81
|
- lib/rxio.rb
|
82
|
+
- lib/rxio/client.rb
|
68
83
|
- lib/rxio/handler_base.rb
|
84
|
+
- lib/rxio/io_base.rb
|
69
85
|
- lib/rxio/io_filters.rb
|
70
86
|
- lib/rxio/io_filters/bin_delim.rb
|
71
87
|
- lib/rxio/io_filters/msg_size.rb
|