simplerpc 0.2.4 → 0.3.0c

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc9653218ecb394370d5c9a4a9ddc57c496b7124
4
- data.tar.gz: f6c2cbc647bc3573dfb471c2c032e032245369fb
3
+ metadata.gz: 727d91ba2fea5bec49b44ad808dd37eef9d6f800
4
+ data.tar.gz: 70950f3bf04baa1f281926e748554dee0623edb7
5
5
  SHA512:
6
- metadata.gz: 071d0a5ce8fc194496c4253dc8ec2d4323e00ea2b66af41e8368639b84fab5545eabe76f3bbe36ec338b3d3a589f31bc874cb130e1f661e7ae61e19865b7ccfd
7
- data.tar.gz: 27c9bdff6148aa3849a6d3707aef01351373c4f76b900c33194ab9a447c609479facb45d2e988ba589fc840248622676dd5aba487b1ff67d6e61f9343721abe2
6
+ metadata.gz: e3894eeff293db5d898c4ae87d7d83f840ee9e18e0356cb77af5ce9d6e17bcf70aa5b2d672d80d05e30e071d97b32fe66f7bb7c8ed40db3584de247e9e4fe29a
7
+ data.tar.gz: 3ba4862e358f939cd462506492dfeaf27a5589a49bba8c0af3f7ecaccc494015d2f72a474dcf8dbb8e620c9847526ee4344fe152d95357ce0c56a72f49527839
@@ -1,7 +1,4 @@
1
1
 
2
- require 'simplerpc/server'
3
- require 'simplerpc/client'
4
-
5
2
  # SimpleRPC is a very simple RPC library for ruby, designed to be as fast as possible whilst still
6
3
  # retaining a simple API.
7
4
  #
@@ -14,7 +11,8 @@ require 'simplerpc/client'
14
11
  # This module simply contains version information,
15
12
  # and including it includes all other project files
16
13
  module SimpleRPC
17
-
18
- VERSION = '0.2.3'
19
-
14
+ require_relative 'simplerpc/exceptions'
15
+ require_relative 'simplerpc/server'
16
+ require_relative 'simplerpc/client'
17
+ require_relative 'simplerpc/version'
20
18
  end
@@ -1,5 +1,6 @@
1
1
  require 'socket'
2
2
  require 'simplerpc/socket_protocol'
3
+ require 'simplerpc/exceptions'
3
4
 
4
5
  # rubocop:disable LineLength
5
6
 
@@ -11,30 +12,6 @@ module SimpleRPC
11
12
  class AuthenticationError < StandardError
12
13
  end
13
14
 
14
- # Thrown when the server raises an exception.
15
- #
16
- # The message is set to the server's exception class.
17
- class RemoteException < Exception
18
-
19
- attr_reader :remote_exception
20
-
21
- def initialize(exception)
22
- super(exception)
23
- @remote_exception = exception
24
- end
25
-
26
- # Return the backtrace from the original (remote)
27
- # exception
28
- def backtrace
29
- @remote_exception.backtrace
30
- end
31
-
32
- # Return a string representing the remote exception
33
- def to_s
34
- @remote_exception.to_s
35
- end
36
- end
37
-
38
15
  # The superclass of a proxy object
39
16
  class RemoteObject < BasicObject
40
17
  end
@@ -97,7 +74,8 @@ module SimpleRPC
97
74
  #
98
75
  # == Exceptions
99
76
  #
100
- # Remote exceptions fired by the server during a call are wrapped in RemoteException.
77
+ # Remote exceptions fired by the server during a call are returned as RemoteExceptions,
78
+ # and have the message and backtrace set as if you are on the remote server.
101
79
  #
102
80
  # Network errors are exposed directly. The server will not close a pipe during
103
81
  # an operation, so if using connect-on-demand you should only observe
@@ -160,8 +138,8 @@ module SimpleRPC
160
138
  #
161
139
  class Client
162
140
 
163
- attr_reader :hostname, :port, :threaded
164
- attr_accessor :serialiser, :timeout, :fast_auth
141
+ attr_reader :hostname, :port, :threaded, :timeout
142
+ attr_accessor :serialiser, :fast_auth
165
143
  attr_writer :password, :secret
166
144
 
167
145
  # Create a new client for the network.
@@ -187,7 +165,8 @@ module SimpleRPC
187
165
  @hostname = opts[:hostname] || '127.0.0.1'
188
166
  @port = opts[:port]
189
167
  raise 'Port required' unless @port
190
- @timeout = opts[:timeout]
168
+ timeout = opts[:timeout]
169
+
191
170
 
192
171
  # Support multiple connections at once?
193
172
  @threaded = !(opts[:threaded] == false)
@@ -216,6 +195,20 @@ module SimpleRPC
216
195
  end
217
196
  end
218
197
 
198
+
199
+ # Set the timeout on all socket operations,
200
+ # including connection
201
+ def timeout=(timeout)
202
+ @timeout = timeout
203
+ @socket_timeout = nil
204
+
205
+ if @timeout.to_f > 0
206
+ secs = @timeout.floor
207
+ usecs = (@timeout - secs).floor * 1_000_000
208
+ @socket_timeout = [secs, usecs].pack("l_2")
209
+ end
210
+ end
211
+
219
212
  # Connect to the remote server and return two things:
220
213
  #
221
214
  # * A proxy object for communicating with the server
@@ -296,6 +289,9 @@ module SimpleRPC
296
289
  end
297
290
  end
298
291
  end
292
+ rescue EOFError, Errno::ECONNRESET, Errno::ETIMEDOUT,
293
+ Errno::ECONNREFUSED, Errno::ECONNABORTED, Errno::EPIPE => e
294
+ raise ConnectionError.new(e)
299
295
  end
300
296
 
301
297
  # Close all persistent connections to the server.
@@ -344,23 +340,28 @@ module SimpleRPC
344
340
  _get_socket() do |s, persist|
345
341
 
346
342
  # send method name and arity
347
- SocketProtocol::Stream.send(s, [m, args, block_given?, persist], @serialiser, @timeout)
343
+ SocketProtocol::Stream.send(s, [m, args, block_given?, persist], @serialiser)
348
344
 
349
345
  # Call with args
350
- success, result = SocketProtocol::Stream.recv(s, @serialiser, @timeout)
346
+ success, result = SocketProtocol::Stream.recv(s, @serialiser)
351
347
 
352
348
  # Check if we should yield
353
349
  while success == SocketProtocol::REQUEST_YIELD do
354
350
  block_result = yield(*result)
355
- SocketProtocol::Stream.send(s, block_result, @serialiser, @timeout)
356
- success, result = SocketProtocol::Stream.recv(s, @serialiser, @timeout)
351
+ SocketProtocol::Stream.send(s, block_result, @serialiser)
352
+ success, result = SocketProtocol::Stream.recv(s, @serialiser)
357
353
  end
358
354
 
359
355
  end
360
356
 
361
357
  # If it didn't succeed, treat the payload as an exception
362
- raise RemoteException.new(result) unless success == SocketProtocol::REQUEST_SUCCESS
358
+ raise result unless success == SocketProtocol::REQUEST_SUCCESS
363
359
  return result
360
+ rescue EOFError, Errno::ECONNRESET, Errno::ETIMEDOUT,
361
+ Errno::ECONNREFUSED, Errno::ECONNABORTED, Errno::EPIPE => e
362
+ raise ConnectionError.new(e)
363
+ rescue StandardError => e
364
+ raise FormatError.new(e)
364
365
  end
365
366
 
366
367
  # Returns a proxy object that is all but indistinguishable
@@ -403,21 +404,32 @@ module SimpleRPC
403
404
  # Connect to the server and return a socket
404
405
  def _connect
405
406
  # Connect to the host
406
- s = Socket.tcp(@hostname, @port, nil, nil, connect_timeout: @timeout)
407
+ # s = Socket.tcp(@hostname, @port, nil, nil, connect_timeout: @timeout)
408
+ s = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
407
409
 
408
410
  # Disable Nagle's algorithm
409
411
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
410
412
 
413
+ # Set timeout directly on socket
414
+ if @socket_timeout
415
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, @socket_timeout)
416
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, @socket_timeout)
417
+ end
418
+
419
+
420
+ s.connect( Socket.pack_sockaddr_in( @port, @hostname.to_s ) )
421
+
422
+
411
423
  # if auth is required
412
424
  if @password && @secret
413
- salt = SocketProtocol::Simple.recv(s, @timeout)
425
+ salt = SocketProtocol::Simple.recv(s)
414
426
  challenge = Encryption.encrypt(@password, @secret, salt)
415
427
 
416
- SocketProtocol::Simple.send(s, challenge, @timeout)
428
+ SocketProtocol::Simple.send(s, challenge)
417
429
 
418
430
  # Check return if not @fast_auth
419
431
  unless @fast_auth
420
- unless SocketProtocol::Simple.recv(s, @timeout) == SocketProtocol::AUTH_SUCCESS
432
+ unless SocketProtocol::Simple.recv(s) == SocketProtocol::AUTH_SUCCESS
421
433
  s.close
422
434
  raise AuthenticationError, 'Authentication failed'
423
435
  end
@@ -501,4 +513,10 @@ module SimpleRPC
501
513
 
502
514
  end
503
515
 
516
+
517
+ class FastClient
518
+
519
+ end
520
+
521
+
504
522
  end
@@ -0,0 +1,36 @@
1
+
2
+
3
+
4
+
5
+ module SimpleRPC
6
+
7
+ # Superclass of all RPC-related exceptions
8
+ class RPCError < Exception
9
+
10
+ attr_reader :cause
11
+
12
+ def initialize(exception)
13
+ super("#{exception.class}: #{exception}")
14
+ set_backtrace(exception.backtrace)
15
+ end
16
+
17
+ end
18
+
19
+
20
+ # Called when the serialiser fails to deserialise something
21
+ class FormatError < RPCError
22
+ end
23
+
24
+ # Called when the connection fails
25
+ class ConnectionError < RPCError
26
+ end
27
+
28
+ # Thrown when the server raises an exception.
29
+ #
30
+ # The message is set to the server's exception class.
31
+ class RemoteException < RPCError
32
+ end
33
+
34
+ end
35
+
36
+
@@ -1,6 +1,7 @@
1
1
 
2
2
  require 'socket' # Get sockets from stdlib
3
3
  require 'simplerpc/socket_protocol'
4
+ require 'simplerpc/exceptions'
4
5
 
5
6
  # rubocop:disable LineLength
6
7
 
@@ -76,8 +77,8 @@ module SimpleRPC
76
77
  #
77
78
  class Server
78
79
 
79
- attr_reader :hostname, :port, :obj, :threaded
80
- attr_accessor :verbose_errors, :serialiser, :timeout, :fast_auth
80
+ attr_reader :hostname, :port, :obj, :threaded, :timeout
81
+ attr_accessor :verbose_errors, :serialiser, :fast_auth
81
82
  attr_writer :password, :secret
82
83
 
83
84
  # Create a new server for a given proxy object.
@@ -121,7 +122,7 @@ module SimpleRPC
121
122
  @close_in, @close_out = UNIXSocket.pair
122
123
 
123
124
  # Connect/receive timeouts
124
- @timeout = opts[:timeout]
125
+ timeout = opts[:timeout]
125
126
 
126
127
  # Auth
127
128
  if opts[:password] && opts[:secret]
@@ -143,6 +144,21 @@ module SimpleRPC
143
144
  @ml = Mutex.new
144
145
  end
145
146
 
147
+
148
+ # Set the timeout on all socket operations,
149
+ # including connection
150
+ def timeout=(timeout)
151
+ @timeout = timeout
152
+ @socket_timeout = nil
153
+
154
+ if @timeout.to_f > 0
155
+ secs = @timeout.floor
156
+ usecs = (@timeout - secs).floor * 1_000_000
157
+ @socket_timeout = [secs, usecs].pack("l_2")
158
+ end
159
+ end
160
+
161
+
146
162
  # Start listening forever.
147
163
  #
148
164
  # Use threads and .close to stop the server.
@@ -160,6 +176,13 @@ module SimpleRPC
160
176
 
161
177
  # Accept in an interruptable manner
162
178
  if (c = interruptable_accept(s))
179
+
180
+ # Set timeout directly on socket
181
+ if @socket_timeout
182
+ c.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, @socket_timeout)
183
+ c.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, @socket_timeout)
184
+ end
185
+
163
186
  # Threaded
164
187
  if @threaded
165
188
 
@@ -196,6 +219,7 @@ module SimpleRPC
196
219
 
197
220
  # Close socket
198
221
  @close = false if @close # say we've closed
222
+ ensure
199
223
  @ml.unlock
200
224
  end
201
225
 
@@ -204,7 +228,7 @@ module SimpleRPC
204
228
  # Returns 0 if :threaded is set to false.
205
229
  def active_client_threads
206
230
  # If threaded return a count from the clients list
207
- return @mc.synchronize { @clients.length } if @threaded
231
+ return @clients.length if @threaded
208
232
 
209
233
  # Else return 0 if not threaded
210
234
  return 0
@@ -212,13 +236,25 @@ module SimpleRPC
212
236
 
213
237
  # Close the server object nicely,
214
238
  # waiting on threads if necessary
215
- def close
239
+ def close(timeout = false)
240
+ # Return immediately if the server isn't listening
241
+ return unless @ml.locked?
242
+
216
243
  # Ask the loop to close
217
244
  @close_in.putc 'x' # Tell select to close
218
245
 
246
+
219
247
  # Wait for loop to end
220
- @ml.lock
221
- @ml.unlock
248
+ elapsed_time = 0
249
+ while @ml.locked? do
250
+ sleep(0.05)
251
+ elapsed_time += 0.05
252
+
253
+ # If a timeout is given, try killing threads at this point
254
+ if timeout && elapsed_time > timeout
255
+ @clients.each {|id, thread| thread.kill() }
256
+ end
257
+ end
222
258
  end
223
259
 
224
260
  private
@@ -232,8 +268,6 @@ module SimpleRPC
232
268
  def interruptable_accept(s)
233
269
  c = IO.select([s, @close_out], nil, nil)
234
270
 
235
- # puts "--> #{c}"
236
-
237
271
  return nil unless c
238
272
  if c[0][0] == @close_out
239
273
  # @close is set, so consume from socket
@@ -244,6 +278,7 @@ module SimpleRPC
244
278
  return nil
245
279
  end
246
280
  return s.accept if !@close && c
281
+ return nil
247
282
  rescue IOError
248
283
  # cover 'closed stream' errors
249
284
  return nil
@@ -266,17 +301,17 @@ module SimpleRPC
266
301
  rescue NotImplementedError
267
302
  salt = Random.new.bytes(@salt_size)
268
303
  end
269
- SocketProtocol::Simple.send(c, salt, @timeout)
304
+ SocketProtocol::Simple.send(c, salt)
270
305
 
271
306
  # Receive encrypted challenge
272
- raw = SocketProtocol::Simple.recv(c, @timeout)
307
+ raw = SocketProtocol::Simple.recv(c)
273
308
 
274
309
  # D/c if failed
275
310
  unless Encryption.decrypt(raw, @secret, salt) == @password
276
- SocketProtocol::Simple.send(c, SocketProtocol::AUTH_FAIL, @timeout) unless @fast_auth
311
+ SocketProtocol::Simple.send(c, SocketProtocol::AUTH_FAIL) unless @fast_auth
277
312
  return
278
313
  end
279
- SocketProtocol::Simple.send(c, SocketProtocol::AUTH_SUCCESS, @timeout) unless @fast_auth
314
+ SocketProtocol::Simple.send(c, SocketProtocol::AUTH_SUCCESS) unless @fast_auth
280
315
  rescue
281
316
  # Auth failure is silent for the server
282
317
  return
@@ -288,7 +323,7 @@ module SimpleRPC
288
323
  while !@close && persist do
289
324
 
290
325
  # Note, when clients d/c this throws EOFError
291
- m, args, remote_block_given, persist = SocketProtocol::Stream.recv(c, @serialiser, @timeout)
326
+ m, args, remote_block_given, persist = SocketProtocol::Stream.recv(c, @serialiser)
292
327
  # puts "Method: #{m}, args: #{args}, block?: #{remote_block_given}, persist: #{persist}"
293
328
 
294
329
  if m && args
@@ -303,8 +338,8 @@ module SimpleRPC
303
338
  if remote_block_given
304
339
  # Proxy with a block that sends back to the client
305
340
  result = @obj.send(m, *args) do |*yield_args|
306
- SocketProtocol::Stream.send(c, [SocketProtocol::REQUEST_YIELD, yield_args], @serialiser, @timeout)
307
- SocketProtocol::Stream.recv(c, @serialiser, @timeout)
341
+ SocketProtocol::Stream.send(c, [SocketProtocol::REQUEST_YIELD, yield_args], @serialiser)
342
+ SocketProtocol::Stream.recv(c, @serialiser)
308
343
  end
309
344
 
310
345
  else
@@ -313,13 +348,15 @@ module SimpleRPC
313
348
  end
314
349
 
315
350
  rescue StandardError => se
316
- result = se
351
+ # Ensure the passed exception has no class hierarchy from
352
+ # this object space (which would not work on the client)
353
+ result = RemoteException.new(se)
317
354
  success = SocketProtocol::REQUEST_FAIL
318
355
  end
319
356
 
320
357
  # Send over the result
321
358
  # puts "[s] sending result..."
322
- SocketProtocol::Stream.send(c, [success, result], @serialiser, @timeout)
359
+ SocketProtocol::Stream.send(c, [success, result], @serialiser)
323
360
  else
324
361
  persist = false
325
362
  end
@@ -38,14 +38,12 @@ module SimpleRPC
38
38
  module Stream
39
39
 
40
40
  # Send using a serialiser writing through the socket
41
- def self.send(s, obj, serialiser, timeout = nil)
42
- raise Errno::ETIMEDOUT unless IO.select([], [s], [], timeout)
41
+ def self.send(s, obj, serialiser)
43
42
  return serialiser.dump(obj, s)
44
43
  end
45
44
 
46
45
  # Recieve using a serialiser reading from the socket
47
- def self.recv(s, serialiser, timeout = nil)
48
- raise Errno::ETIMEDOUT unless IO.select([s], [], [], timeout)
46
+ def self.recv(s, serialiser)
49
47
  return serialiser.load(s)
50
48
  end
51
49
 
@@ -59,40 +57,38 @@ module SimpleRPC
59
57
  module Simple
60
58
 
61
59
  # Send a buffer
62
- def self.send(s, buf, timeout = nil)
60
+ def self.send(s, buf)
63
61
  # Dump into buffer
64
62
  buflen = buf.length
65
63
 
66
64
  # Send buffer length
67
- raise Errno::ETIMEDOUT unless IO.select([], [s], [], timeout)
68
65
  s.puts(buflen)
69
66
 
70
67
  # Send buffer
71
68
  sent = 0
72
- while sent < buflen && (x = IO.select([], [s], [], timeout)) do
69
+ while sent < buflen do
73
70
  sent += s.write(buf[sent..-1])
74
71
  end
75
- raise Errno::ETIMEDOUT unless x
72
+ # raise Errno::ETIMEDOUT unless x
76
73
 
77
74
  end
78
75
 
79
76
  # Receive a buffer
80
- def self.recv(s, timeout = nil)
81
- raise Errno::ETIMEDOUT unless IO.select([s], [], [], timeout)
77
+ def self.recv(s)
82
78
  buflen = s.gets.to_s.chomp.to_i
83
79
 
84
80
  return nil if buflen <= 0
85
81
 
86
82
  buf = ''
87
83
  recieved = 0
88
- while recieved < buflen && (x = IO.select([s], [], [], timeout)) do
84
+ while recieved < buflen do
89
85
  str = s.read(buflen - recieved)
90
86
  buf += str
91
87
  recieved += str.length
92
88
  end
93
- raise Errno::ETIMEDOUT unless x
94
89
 
95
90
  return buf
91
+
96
92
  end
97
93
 
98
94
  end
@@ -0,0 +1,5 @@
1
+ module SimpleRPC
2
+
3
+ VERSION = '0.3.0c'
4
+
5
+ end
metadata CHANGED
@@ -1,48 +1,50 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplerpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0c
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Wattam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-20 00:00:00.000000000 Z
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A very simple and fast RPC library
14
- email: stephenwattam@gmail.com
14
+ email: steve@stephenwattam.com
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
+ - "./lib/simplerpc.rb"
20
+ - LICENSE
21
+ - lib/simplerpc/client.rb
19
22
  - lib/simplerpc/encryption.rb
23
+ - lib/simplerpc/exceptions.rb
20
24
  - lib/simplerpc/server.rb
21
25
  - lib/simplerpc/socket_protocol.rb
22
- - lib/simplerpc/client.rb
23
- - ./lib/simplerpc.rb
24
- - LICENSE
26
+ - lib/simplerpc/version.rb
25
27
  homepage: http://stephenwattam.com/projects/simplerpc
26
28
  licenses:
27
29
  - Beerware
28
30
  metadata: {}
29
- post_install_message: Thanks for installing SimpleRPC!
31
+ post_install_message:
30
32
  rdoc_options: []
31
33
  require_paths:
32
34
  - lib
33
35
  required_ruby_version: !ruby/object:Gem::Requirement
34
36
  requirements:
35
- - - '>='
37
+ - - ">="
36
38
  - !ruby/object:Gem::Version
37
39
  version: '2.0'
38
40
  required_rubygems_version: !ruby/object:Gem::Requirement
39
41
  requirements:
40
- - - '>='
42
+ - - ">"
41
43
  - !ruby/object:Gem::Version
42
- version: '0'
44
+ version: 1.3.1
43
45
  requirements: []
44
46
  rubyforge_project:
45
- rubygems_version: 2.0.14
47
+ rubygems_version: 2.5.1
46
48
  signing_key:
47
49
  specification_version: 4
48
50
  summary: Simple RPC library