jrpc 1.0.1 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89173d093947f7660038fd40edf8df685f0eba58
4
- data.tar.gz: 730eb903478be492a2708c4d104c016b120f17ec
3
+ metadata.gz: fa57d4ebd661941d43cc81ab71df478fc7e2cd90
4
+ data.tar.gz: 6675b878174f71c7fc96a7d4a7d5c485cdb99670
5
5
  SHA512:
6
- metadata.gz: c22cdaf992b9ed4ac0553c8d18fa5ac34de673cb2e7da20a0476734dbf50465e82a7951b7ed4424e7f62330db517c98aba25da2a999a916902c9174d2f5d56d2
7
- data.tar.gz: 39b0a463f4a6353a1daa9f8d81e65e7482907af7a6a8e0a34ee4cc20c8e6b3c1fd1eb7a50e799a5125acf5252701d5892c5d2086f78dfd5dadd00ed6944af87f
6
+ metadata.gz: d95b4e15d5611b9b0b032f599c5d0b675887a2cf834bc6796808dcce43c599cbcbd80380b6f9be6783a55f65b8190d7aaa0cb32f31889384d24c41307c2cf7e5
7
+ data.tar.gz: 9ecb04ea701ef5bd048d777e61c440cd519387247c42e02f39a05092ebbbc40520c918b4c36afb576e277f8dd013c65a34274747b8eef484002f146ef25efa6a
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  spec.add_dependency 'netstring', '~> 0'
21
- spec.add_dependency 'net_tcp_client', '~> 2.0'
22
21
  spec.add_dependency 'oj', '~> 2.0'
23
22
 
24
23
  spec.add_development_dependency 'bundler', '~> 1.10'
@@ -1,6 +1,8 @@
1
1
  require 'jrpc/version'
2
2
  require 'jrpc/utils'
3
3
  require 'jrpc/base_client'
4
+ require 'jrpc/transport/socket_base'
5
+ require 'jrpc/transport/socket_tcp'
4
6
  require 'jrpc/tcp_client'
5
7
  require 'jrpc/error/error'
6
8
  require 'jrpc/error/connection_error'
@@ -9,6 +9,13 @@ module JRPC
9
9
  ID_CHARACTERS = (('a'..'z').to_a + ('0'..'9').to_a + ('A'..'Z').to_a).freeze
10
10
  REQUEST_TYPES = [:request, :notification].freeze
11
11
 
12
+ def self.connect(uri, options)
13
+ client = new(uri, options)
14
+ yield(client)
15
+ ensure
16
+ client.close if client
17
+ end
18
+
12
19
  def initialize(uri, options)
13
20
  @uri = uri
14
21
  @options = options
@@ -1,4 +1,3 @@
1
- require 'net/tcp_client'
2
1
  require 'netstring'
3
2
  require 'logger'
4
3
  require 'benchmark'
@@ -18,22 +17,34 @@ module JRPC
18
17
  timeout = @options.fetch(:timeout, 5)
19
18
  connect_timeout = @options.fetch(:connect_timeout, timeout)
20
19
  read_timeout = @options.fetch(:read_timeout, timeout)
21
- write_timeout = @options.fetch(:write_timeout, nil) # default 60
22
- connect_retry_count = @options.fetch(:connect_retry_count, nil) # default 10
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
23
 
24
- @transport = Net::TCPClient.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
- buffered: false # recommended for RPC
30
- rescue ::SocketError
31
- raise ConnectionError, "Can't connect to #{@uri}"
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::SocketTcp::Error
32
+ raise ConnectionError, "Can't connect to #{@uri}"
33
+ end
32
34
  end
33
35
 
34
36
  private
35
37
 
36
- def send_command(request, options={})
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
37
48
  read_timeout = options.fetch(:read_timeout)
38
49
  write_timeout = options.fetch(:write_timeout)
39
50
  response = nil
@@ -49,15 +60,20 @@ module JRPC
49
60
  "(#{'%.2f' % (t * 1000)}ms) Response message: #{Utils.truncate(response, MAX_LOGGED_MESSAGE_LENGTH)}"
50
61
  end
51
62
  response
63
+ ensure
64
+ @transport.close if @close_after_sent
52
65
  end
53
66
 
54
- def send_notification(request, options={})
67
+ def send_notification(request, options = {})
68
+ ensure_connected
55
69
  write_timeout = options.fetch(:write_timeout)
56
70
  logger.debug { "Request address: #{uri}" }
57
71
  logger.debug { "Request message: #{Utils.truncate(request, MAX_LOGGED_MESSAGE_LENGTH)}" }
58
72
  logger.debug { "Request write_timeout: #{write_timeout}" }
59
73
  send_request(request, write_timeout)
60
74
  logger.debug { 'No response required' }
75
+ ensure
76
+ @transport.close if @close_after_sent
61
77
  end
62
78
 
63
79
  def create_message(method, params)
@@ -74,8 +90,7 @@ module JRPC
74
90
  def receive_response(timeout)
75
91
  timeout ||= @transport.read_timeout
76
92
  length = get_msg_length(timeout)
77
- response = ''
78
- @transport.read(length+1, response, timeout)
93
+ response = @transport.read(length + 1, timeout)
79
94
  raise ClientError.new('invalid response. missed comma as terminator') if response[-1] != ','
80
95
  response.chomp(',')
81
96
  rescue ::SocketError
@@ -85,7 +100,7 @@ module JRPC
85
100
  def get_msg_length(timeout)
86
101
  length = ''
87
102
  while true do
88
- character = @transport.read(1, nil, timeout)
103
+ character = @transport.read(1, timeout)
89
104
  break if character == ':'
90
105
  length += character
91
106
  end
@@ -0,0 +1,82 @@
1
+ module JRPC
2
+ module Transport
3
+ class SocketBase
4
+
5
+ class Error < StandardError
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
+ attr_reader :options, :read_timeout, :write_timeout
27
+
28
+ def self.connect(options)
29
+ connection = new(options)
30
+ yield(connection)
31
+ ensure
32
+ connection.close if connection
33
+ end
34
+
35
+ def initialize(options)
36
+ @server = options.fetch(:server)
37
+ @read_timeout = options.fetch(:read_timeout, nil)
38
+ @write_timeout = options.fetch(:write_timeout, nil)
39
+ @connect_timeout = options.fetch(:connect_timeout, nil)
40
+ @connect_retry_count = options.fetch(:connect_retry_count, 0)
41
+ @options = options
42
+ end
43
+
44
+ def connect
45
+ retries = @connect_retry_count
46
+
47
+ while retries >= 0
48
+ begin
49
+ connect_socket
50
+ break
51
+ rescue Error => e
52
+ retries -= 1
53
+ raise e if retries < 0
54
+ end
55
+ end
56
+ end
57
+
58
+ def read(_length, _timeout = @read_timeout)
59
+ raise NotImplementedError
60
+ end
61
+
62
+ def write(_data, _timeout = @write_timeout)
63
+ raise NotImplementedError
64
+ end
65
+
66
+ def close
67
+ raise NotImplementedError
68
+ end
69
+
70
+ def closed?
71
+ raise NotImplementedError
72
+ end
73
+
74
+ private
75
+
76
+ def connect_socket
77
+ raise NotImplementedError
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,78 @@
1
+ module JRPC
2
+ module Transport
3
+ class SocketTcp < SocketBase
4
+
5
+ attr_reader :socket
6
+
7
+ def initialize(options)
8
+ super
9
+ @socket = build_socket
10
+ end
11
+
12
+ def read(length, timeout = @read_timeout)
13
+ received = ''
14
+ length_to_read = length
15
+ while length_to_read > 0
16
+ io_read, = IO.select([@socket], [], [], timeout)
17
+ raise ReadTimeoutError unless io_read
18
+ chunk = io_read[0].read_nonblock(length_to_read)
19
+ received += chunk
20
+ length_to_read -= chunk.bytesize
21
+ end
22
+ received
23
+ end
24
+
25
+ def write(data, timeout = @write_timeout)
26
+ length_written = 0
27
+ data_to_write = data
28
+ while data_to_write.bytesize > 0
29
+ _, io_write, = IO.select([], [@socket], [], timeout)
30
+ raise WriteTimeoutError unless io_write
31
+ chunk_length = io_write[0].write_nonblock(data_to_write)
32
+ length_written += chunk_length
33
+ data_to_write = data.byteslice(length_written, data.length)
34
+ end
35
+ length_written
36
+ end
37
+
38
+ def close
39
+ @socket.close
40
+ end
41
+
42
+ def closed?
43
+ @socket.closed?
44
+ end
45
+
46
+ private
47
+
48
+ def set_timeout_to(socket, type, value)
49
+ secs = Integer(value)
50
+ u_secs = Integer((value - secs) * 1_000_000)
51
+ opt_val = [secs, u_secs].pack('l_2')
52
+ socket.setsockopt Socket::SOL_SOCKET, type, opt_val
53
+ end
54
+
55
+ def build_socket
56
+ host = @server.split(':').first
57
+ addr = Socket.getaddrinfo(host, nil)
58
+ sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
59
+ set_timeout_to(sock, Socket::SO_RCVTIMEO, @connect_timeout) if @connect_timeout
60
+ sock
61
+ end
62
+
63
+ def connect_socket
64
+ host, port = @server.split(':')
65
+ addr = Socket.getaddrinfo(host, nil)
66
+ full_addr = Socket.pack_sockaddr_in(port, addr[0][3])
67
+ @socket.connect(full_addr)
68
+ rescue Errno::EISCONN => _
69
+ # already connected
70
+ rescue Errno::ETIMEDOUT => _
71
+ raise ConnectionTimeoutError
72
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT => e
73
+ raise ConnectionFailedError, "#{e.class} #{e.message}"
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -1,3 +1,3 @@
1
1
  module JRPC
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jrpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-11 00:00:00.000000000 Z
11
+ date: 2018-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: netstring
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: net_tcp_client
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '2.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '2.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: oj
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -123,6 +109,8 @@ files:
123
109
  - lib/jrpc/error/server_error.rb
124
110
  - lib/jrpc/error/unknown_error.rb
125
111
  - lib/jrpc/tcp_client.rb
112
+ - lib/jrpc/transport/socket_base.rb
113
+ - lib/jrpc/transport/socket_tcp.rb
126
114
  - lib/jrpc/utils.rb
127
115
  - lib/jrpc/version.rb
128
116
  homepage: https://github.com/senid231/jrpc