jrpc 1.1.7 → 2.0.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +55 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +228 -0
  6. data/CHANGELOG.md +84 -0
  7. data/Gemfile +17 -0
  8. data/README.md +163 -13
  9. data/Rakefile +3 -1
  10. data/bin/console +15 -0
  11. data/bin/jrpc +111 -0
  12. data/bin/jrpc-shell +109 -0
  13. data/bin/setup +8 -0
  14. data/jrpc.gemspec +9 -8
  15. data/lib/jrpc/errors.rb +65 -0
  16. data/lib/jrpc/id_generator.rb +22 -0
  17. data/lib/jrpc/message.rb +78 -0
  18. data/lib/jrpc/shared_client/outbound_queue.rb +71 -0
  19. data/lib/jrpc/shared_client/registry.rb +46 -0
  20. data/lib/jrpc/shared_client/ticket.rb +84 -0
  21. data/lib/jrpc/shared_client/transport_loop.rb +290 -0
  22. data/lib/jrpc/shared_client.rb +194 -0
  23. data/lib/jrpc/simple_client.rb +89 -0
  24. data/lib/jrpc/transport/base.rb +60 -0
  25. data/lib/jrpc/transport/tcp.rb +243 -0
  26. data/lib/jrpc/transport.rb +12 -0
  27. data/lib/jrpc/version.rb +3 -1
  28. data/lib/jrpc.rb +15 -16
  29. metadata +35 -76
  30. data/.travis.yml +0 -4
  31. data/lib/jrpc/base_client.rb +0 -123
  32. data/lib/jrpc/error/client_error.rb +0 -5
  33. data/lib/jrpc/error/connection_error.rb +0 -11
  34. data/lib/jrpc/error/error.rb +0 -5
  35. data/lib/jrpc/error/internal_error.rb +0 -9
  36. data/lib/jrpc/error/internal_server_error.rb +0 -5
  37. data/lib/jrpc/error/invalid_params.rb +0 -9
  38. data/lib/jrpc/error/invalid_request.rb +0 -9
  39. data/lib/jrpc/error/method_not_found.rb +0 -9
  40. data/lib/jrpc/error/parse_error.rb +0 -9
  41. data/lib/jrpc/error/server_error.rb +0 -11
  42. data/lib/jrpc/error/unknown_error.rb +0 -5
  43. data/lib/jrpc/tcp_client.rb +0 -112
  44. data/lib/jrpc/transport/socket_base.rb +0 -88
  45. data/lib/jrpc/transport/socket_tcp.rb +0 -98
  46. data/lib/jrpc/utils.rb +0 -9
@@ -1,11 +0,0 @@
1
- module JRPC
2
- class ServerError < Error
3
- attr_reader :code
4
-
5
- def initialize(message, code)
6
- @code = code
7
- super(message)
8
- end
9
-
10
- end
11
- end
@@ -1,5 +0,0 @@
1
- module JRPC
2
- class UnknownError < ServerError
3
-
4
- end
5
- end
@@ -1,112 +0,0 @@
1
- require 'netstring'
2
- require 'logger'
3
- require 'benchmark'
4
- module JRPC
5
- class TcpClient < BaseClient
6
- attr_reader :namespace, :transport
7
- attr_accessor :logger
8
- def_delegators :@transport, :close, :closed?, :connect
9
-
10
- MAX_LOGGED_MESSAGE_LENGTH = 255
11
-
12
- def initialize(uri, options = {})
13
- super
14
- @logger = @options.delete(:logger) || Logger.new($null)
15
- @namespace = @options.delete(:namespace).to_s
16
-
17
- timeout = @options.fetch(:timeout, 5)
18
- connect_timeout = @options.fetch(:connect_timeout, timeout)
19
- read_timeout = @options.fetch(:read_timeout, timeout)
20
- write_timeout = @options.fetch(:write_timeout, 60) # default 60
21
- connect_retry_count = @options.fetch(:connect_retry_count, 10) # default 10
22
- @close_after_sent = @options.fetch(:close_after_sent, false)
23
-
24
- @transport = JRPC::Transport::SocketTcp.new server: @uri,
25
- connect_retry_count: connect_retry_count,
26
- connect_timeout: connect_timeout,
27
- read_timeout: read_timeout,
28
- write_timeout: write_timeout
29
- begin
30
- @transport.connect
31
- rescue JRPC::Transport::SocketTcp::Error
32
- raise ConnectionError, "Can't connect to #{@uri}"
33
- end
34
- end
35
-
36
- private
37
-
38
- def ensure_connected
39
- if @transport.closed?
40
- logger.debug { 'Connecting transport...' }
41
- @transport.connect
42
- logger.debug { 'Connected.' }
43
- end
44
- end
45
-
46
- def send_command(request, options = {})
47
- ensure_connected
48
- read_timeout = options.fetch(:read_timeout)
49
- write_timeout = options.fetch(:write_timeout)
50
- response = nil
51
- t = Benchmark.realtime do
52
- logger.debug { "Request address: #{uri}" }
53
- logger.debug { "Request message: #{Utils.truncate(request, MAX_LOGGED_MESSAGE_LENGTH)}" }
54
- logger.debug { "Request read_timeout: #{read_timeout}" }
55
- logger.debug { "Request write_timeout: #{write_timeout}" }
56
- send_request(request, write_timeout)
57
- response = receive_response(read_timeout)
58
- end
59
- logger.debug do
60
- "(#{'%.2f' % (t * 1000)}ms) Response message: #{Utils.truncate(response, MAX_LOGGED_MESSAGE_LENGTH)}"
61
- end
62
- response
63
- ensure
64
- @transport.close if @close_after_sent
65
- end
66
-
67
- def send_notification(request, options = {})
68
- ensure_connected
69
- write_timeout = options.fetch(:write_timeout)
70
- logger.debug { "Request address: #{uri}" }
71
- logger.debug { "Request message: #{Utils.truncate(request, MAX_LOGGED_MESSAGE_LENGTH)}" }
72
- logger.debug { "Request write_timeout: #{write_timeout}" }
73
- send_request(request, write_timeout)
74
- logger.debug { 'No response required' }
75
- ensure
76
- @transport.close if @close_after_sent
77
- end
78
-
79
- def create_message(method, params)
80
- super("#{namespace}#{method}", params)
81
- end
82
-
83
- def send_request(request, timeout)
84
- timeout ||= @transport.write_timeout
85
- @transport.write Netstring.dump(request.to_s), timeout
86
- rescue ::SocketError
87
- raise ConnectionError, "Can't send request to #{uri}"
88
- end
89
-
90
- def receive_response(timeout)
91
- timeout ||= @transport.read_timeout
92
- length = get_msg_length(timeout)
93
- response = @transport.read(length + 1, timeout)
94
- raise ClientError.new('invalid response. missed comma as terminator') if response[-1] != ','
95
- response.chomp(',')
96
- rescue ::SocketError
97
- raise ConnectionError, "Can't receive response from #{uri}"
98
- end
99
-
100
- def get_msg_length(timeout)
101
- length = ''
102
- while true do
103
- character = @transport.read(1, timeout)
104
- break if character == ':'
105
- length += character
106
- end
107
-
108
- Integer(length)
109
- end
110
-
111
- end
112
- end
@@ -1,88 +0,0 @@
1
- module JRPC
2
- module Transport
3
- class SocketBase
4
-
5
- class Error < ::JRPC::Error
6
- end
7
-
8
- class TimeoutError < Error
9
- def initialize
10
- super(self.class.to_s.split('::').last)
11
- end
12
- end
13
-
14
- class ReadTimeoutError < TimeoutError
15
- end
16
-
17
- class WriteTimeoutError < TimeoutError
18
- end
19
-
20
- class ConnectionTimeoutError < TimeoutError
21
- end
22
-
23
- class ConnectionFailedError < Error
24
- end
25
-
26
- class WriteFailedError < Error
27
- end
28
-
29
- class ReadFailedError < Error
30
- end
31
-
32
- attr_reader :options, :read_timeout, :write_timeout
33
-
34
- def self.connect(options)
35
- connection = new(options)
36
- yield(connection)
37
- ensure
38
- connection.close if connection
39
- end
40
-
41
- def initialize(options)
42
- @server = options.fetch(:server)
43
- @read_timeout = options.fetch(:read_timeout, nil)
44
- @write_timeout = options.fetch(:write_timeout, nil)
45
- @connect_timeout = options.fetch(:connect_timeout, nil)
46
- @connect_retry_count = options.fetch(:connect_retry_count, 0)
47
- @options = options
48
- end
49
-
50
- def connect
51
- retries = @connect_retry_count
52
-
53
- while retries >= 0
54
- begin
55
- connect_socket
56
- break
57
- rescue Error => e
58
- retries -= 1
59
- raise e if retries < 0
60
- end
61
- end
62
- end
63
-
64
- def read(_length, _timeout = @read_timeout)
65
- raise NotImplementedError
66
- end
67
-
68
- def write(_data, _timeout = @write_timeout)
69
- raise NotImplementedError
70
- end
71
-
72
- def close
73
- raise NotImplementedError
74
- end
75
-
76
- def closed?
77
- raise NotImplementedError
78
- end
79
-
80
- private
81
-
82
- def connect_socket
83
- raise NotImplementedError
84
- end
85
-
86
- end
87
- end
88
- end
@@ -1,98 +0,0 @@
1
- module JRPC
2
- module Transport
3
- class SocketTcp < SocketBase
4
-
5
- def read(length, timeout = @read_timeout)
6
- received = ''
7
- length_to_read = length
8
- while length_to_read > 0
9
- io_read, = IO.select([socket], [], [], timeout)
10
- raise ReadTimeoutError unless io_read
11
- chunk = io_read[0].read_nonblock(length_to_read)
12
- received += chunk
13
- length_to_read -= chunk.bytesize
14
- end
15
- received
16
- rescue Errno::EPIPE, EOFError => e
17
- # EPIPE, in this case, means that the data connection was unexpectedly terminated.
18
- clear_socket!
19
- raise ReadFailedError, "#{e.class} #{e.message}"
20
- end
21
-
22
- def write(data, timeout = @write_timeout)
23
- length_written = 0
24
- data_to_write = data
25
- while data_to_write.bytesize > 0
26
- _, io_write, = IO.select([], [socket], [], timeout)
27
- raise WriteTimeoutError unless io_write
28
- chunk_length = io_write[0].write_nonblock(data_to_write)
29
- length_written += chunk_length
30
- data_to_write = data.byteslice(length_written, data.length)
31
- end
32
- length_written
33
- rescue Errno::EPIPE => e
34
- # EPIPE, in this case, means that the data connection was unexpectedly terminated.
35
- clear_socket!
36
- raise WriteFailedError, "#{e.class} #{e.message}"
37
- end
38
-
39
- def close
40
- return if @socket.nil?
41
- socket.close
42
- end
43
-
44
- def closed?
45
- @socket.nil? || socket.closed?
46
- end
47
-
48
- def socket
49
- @socket ||= build_socket
50
- end
51
-
52
- private
53
-
54
- def clear_socket!
55
- return if @socket.nil?
56
- @socket.close unless @socket.closed?
57
- @socket = nil
58
- end
59
-
60
- def set_timeout_to(socket, type, value)
61
- secs = Integer(value)
62
- u_secs = Integer((value - secs) * 1_000_000)
63
- opt_val = [secs, u_secs].pack('l_2')
64
- socket.setsockopt Socket::SOL_SOCKET, type, opt_val
65
- end
66
-
67
- def build_socket
68
- host = @server.split(':').first
69
- addr = Socket.getaddrinfo(host, nil)
70
- Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
71
- end
72
-
73
- def connect_socket
74
- host, port = @server.split(':')
75
- addr = Socket.getaddrinfo(host, nil)
76
- full_addr = Socket.pack_sockaddr_in(port, addr[0][3])
77
-
78
- begin
79
- socket.connect_nonblock(full_addr)
80
- rescue IO::WaitWritable => _
81
- if IO.select(nil, [socket], nil, @connect_timeout)
82
- socket.connect_nonblock(full_addr)
83
- else
84
- clear_socket!
85
- raise ConnectionFailedError, "Can't connect during #{@connect_timeout}"
86
- end
87
- end
88
-
89
- rescue Errno::EISCONN => _
90
- # already connected
91
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::EPIPE => e
92
- clear_socket!
93
- raise ConnectionFailedError, "#{e.class} #{e.message}"
94
- end
95
-
96
- end
97
- end
98
- end
data/lib/jrpc/utils.rb DELETED
@@ -1,9 +0,0 @@
1
- module JRPC
2
- class Utils
3
-
4
- def self.truncate(string, length, ommiter = '...')
5
- "#{string[0..length]}#{ommiter if string.length > length}"
6
- end
7
-
8
- end
9
- end