jrpc 1.1.7 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67534bff4d06dfed8a49ddf71bb56356d65f9c119731b220e6490394a187f720
4
- data.tar.gz: fd53da1804ddc60f54d549811b7d866541601dc4b3f58dc15de2a112c10039cf
3
+ metadata.gz: e116c8eef34ce5bd8a4ced0e2e693ad37df69895e67c3d28d68537c60711d012
4
+ data.tar.gz: 8a60df19ba2676a28c03925d0be33b8cda720f9da147e4854a87cba9f25dd251
5
5
  SHA512:
6
- metadata.gz: c99a86b3b5a59ff431d5356604d4cfcd1803212a8e3be22819de4bbab9d826d985a47bec9fed72cc82f8adfa80f208b1823ab85566325fe3f7965db52d631911
7
- data.tar.gz: d565e6cf0cd0afa20b935d1ca6e554d2aa6142dd46bfbefd1468f4910175f326656a6015fae7a160ef5ad8da41574e0c8c9d000ea8d624e5e989e5f8683ca262
6
+ metadata.gz: 10a940570df20f1246c61c6df6d2c950e9acf5e16e397727c6693822f121bfeff14409529b732e385f2f8806ed9c26d32ddcd9c545406ab54c3b26b5e9ee6db2
7
+ data.tar.gz: e79fc4f45058b4aa8570928866d97fb9e473b805fbc2c730879d0d6478824e91c9a37728900fde9e827a58753978b3b9468dd03d145d6c2cd09310bafceb49e8
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .rake_tasks~
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ ### Unreleased
4
+
5
+ ### 1.1.8
6
+ * handling FIN signal for TCP socket [didww/jrpc#19](https://github.com/didww/jrpc/pull/19)
7
+ * add gem executables [didww/jrpc#19](https://github.com/didww/jrpc/pull/19)
8
+
9
+ ### 1.1.7
10
+ * connect ot socket in nonblock mode
11
+
12
+ ### 1.1.6
13
+ * update oj version to ~> 3.0
14
+
15
+ ### 1.1.5
16
+ * update oj version to ~> 2.0
17
+
18
+ ### 1.1.4
19
+ * handle EOF on read
20
+ * fix jrpc error require
21
+ * use JRPC::Error as base class for JRPC::Transport::SocketBase::Error
22
+
23
+ ### 1.1.3
24
+ * close socket when clearing socket if it's not closed
25
+
26
+ ### 1.1.2
27
+ * reset socket when broken pipe error appears
28
+
29
+ ### 1.1.1
30
+ * fix rescuing error in TcpClient initializer
31
+
32
+ ### 1.1.0
33
+ * use own socket wrapper
34
+
35
+ ### 1.0.1
36
+ * Net::TCPClient#read method process data with buffer variable
37
+
38
+ ### 1.0.0
39
+ * stable release
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'jrpc'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'optparse'
5
+ require 'jrpc'
6
+
7
+ Options = Struct.new(
8
+ :host,
9
+ :port,
10
+ :type,
11
+ :method,
12
+ :params,
13
+ :id,
14
+ :debug,
15
+ :namespace,
16
+ :timeout
17
+ )
18
+
19
+ class Parser
20
+ def self.parse(argv)
21
+ args = Options.new
22
+ args.host = '127.0.0.1'
23
+ args.port = 7080
24
+ args.type = 'request'
25
+ args.timeout = 5
26
+
27
+ opt_parser = OptionParser.new do |opts|
28
+ opts.banner = 'Usage: jrpc [options] method [params, ...]'
29
+
30
+ opts.on('--host=HOST', 'host (default 127.0.0.1)') do |host|
31
+ args.host = host
32
+ end
33
+
34
+ opts.on('-p=PORT', '--port=PORT', 'port (default 7080)') do |port|
35
+ args.port = port
36
+ end
37
+
38
+ opts.on('-r', '--request', 'Sets type to request (default true)') do
39
+ args.type = 'request'
40
+ end
41
+
42
+ opts.on('-n', '--notification', 'Sets type to is notification (default false)') do
43
+ args.type = 'notification'
44
+ end
45
+
46
+ opts.on('--namespace=NAMESPACE', 'Sets method namespace') do |namespace|
47
+ args.namespace = namespace
48
+ end
49
+
50
+ opts.on('--id=ID', 'Request ID (will be generated randomly by default)') do |id|
51
+ args.id = id
52
+ end
53
+
54
+ opts.on('--timeout=TIMEOUT', 'timeout for socket') do |timeout|
55
+ args.timeout = timeout
56
+ end
57
+
58
+ opts.on('-d', '--debug', 'Debug output') do
59
+ args.debug = true
60
+ end
61
+
62
+ opts.on('-h', '--help', 'Prints this help and exit') do
63
+ puts opts
64
+ exit
65
+ end
66
+
67
+ opts.on('-v', '--version', 'Prints version and exit') do
68
+ puts "JRPC version: #{JRPC::VERSION}"
69
+ exit
70
+ end
71
+ end
72
+
73
+ opt_parser.parse!(argv)
74
+ args.method = argv.first
75
+ args.params = argv[1..-1]
76
+ # puts "PARSED:\n#{args.inspect}\n#{argv.inspect}"
77
+ return args
78
+ end
79
+ end
80
+
81
+ options = Parser.parse(ARGV.dup)
82
+
83
+ logger = Logger.new($stdout)
84
+ logger.level = options.debug ? Logger::DEBUG : Logger::INFO
85
+ addr = "#{options.host}:#{options.port}"
86
+ logger.debug { "Connecting to #{addr} ..." }
87
+ client = JRPC::TcpClient.new(addr, namespace: options.namespace, timeout: options.timeout, logger: logger)
88
+
89
+ logger.debug { "Sending #{options.type} #{options.method} #{options.params} ..." }
90
+ response = client.perform_request(options.method, params: options.params, type: options.type.to_sym)
91
+
92
+ if options.type == 'request'
93
+ logger.debug { "Request was sent. Response: #{response.inspect}" }
94
+ puts JSON.pretty_generate(response)
95
+ else
96
+ logger.debug 'Notification was sent.'
97
+ end
98
+
99
+ client.close
100
+ logger.debug 'Exited'
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'readline'
5
+ require 'singleton'
6
+ require 'jrpc'
7
+
8
+ class Command
9
+ include Singleton
10
+
11
+ def self.call(command, args)
12
+ meth = "cmd_#{command}"
13
+ if instance.respond_to?(meth)
14
+ instance.public_send(meth, *args)
15
+ else
16
+ "ERROR: invalid command #{command.inspect}\n#{instance.help_usage}"
17
+ end
18
+ rescue ArgumentError => e
19
+ "ERROR: ArgumentError #{e.message}\n#{instance.help_usage}"
20
+ end
21
+
22
+ attr_accessor :logger, :client, :help_usage
23
+ instance.logger = Logger.new(STDOUT)
24
+ instance.logger.level = Logger::INFO
25
+ instance.help_usage = [
26
+ 'Usage:',
27
+ ' connect host port',
28
+ ' disconnect',
29
+ ' request method param1 param2',
30
+ ' request method {"param1": 1. "param2": 2}',
31
+ ' notification method param1 param2',
32
+ ' notification method {"param1": 1. "param2": 2}',
33
+ ' help',
34
+ ' version'
35
+ ].join("\n")
36
+
37
+ def cmd_help
38
+ help_usage
39
+ end
40
+
41
+ def cmd_version
42
+ "JRPC version: #{JRPC::VERSION}"
43
+ end
44
+
45
+ def cmd_connect(host, port)
46
+ client&.close
47
+ self.client = JRPC::TcpClient.new("#{host}:#{port}", namespace: '', timeout: 5, logger: logger)
48
+ 'Connected.'
49
+ rescue JRPC::Error => e
50
+ "ERROR: JRPC #{e.message}\n#{help_usage}"
51
+ end
52
+
53
+ def cmd_disconnect
54
+ return "ERROR: Not connected\n#{help_usage}" if client.nil?
55
+
56
+ client.close
57
+ self.client = nil
58
+ 'Disconnected'
59
+ end
60
+
61
+ def cmd_request(method, *params)
62
+ return "ERROR: Not connected\n#{help_usage}" if client.nil?
63
+
64
+ params = JSON.parse(params.first) if params.size == 1 && params[0] == '{'
65
+
66
+ response = client.perform_request(method, params: params)
67
+ JSON.pretty_generate(response)
68
+ rescue JRPC::Error => e
69
+ "ERROR: JRPC #{e.message}\n#{help_usage}"
70
+ end
71
+
72
+ def cmd_notification(method, *params)
73
+ return "ERROR: Not connected\n#{help_usage}" if client.nil?
74
+
75
+ params = JSON.parse(params.first) if params.size == 1 && params[0] == '{'
76
+
77
+ response = client.perform_request(method, params: params, type: :notification)
78
+ JSON.pretty_generate(response)
79
+ rescue JRPC::Error => e
80
+ "ERROR: JRPC #{e.message}\n#{help_usage}"
81
+ end
82
+ end
83
+
84
+ puts 'Welcome to JRPC shell'
85
+ while input = Readline.readline('> ', true)
86
+ if %w[exit close quit].include?(input)
87
+ break
88
+ elsif input == 'hist'
89
+ puts Readline::HISTORY.to_a
90
+ elsif input == ''
91
+ # Remove blank lines from history
92
+ Readline::HISTORY.pop
93
+ else
94
+ command, *args = input.split(' ')
95
+ puts Command.call(command, args)
96
+ end
97
+ end
98
+
99
+ puts 'Shell Exited'
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = 'JSON RPC client'
13
13
  spec.description = 'JSON RPC client over TCP'
14
- spec.homepage = 'https://github.com/senid231/jrpc'
14
+ spec.homepage = 'https://github.com/didww/jrpc'
15
15
  spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -20,7 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.add_dependency 'netstring', '~> 0'
21
21
  spec.add_dependency 'oj', '~> 3.0'
22
22
 
23
- spec.add_development_dependency 'bundler', '~> 1.10'
24
- spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.executables << 'jrpc'
24
+ spec.executables << 'jrpc-shell'
25
+
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake', '~> 13.0'
25
28
  spec.add_development_dependency 'rspec', '~> 3.0'
26
29
  end
@@ -1,3 +1,5 @@
1
+ require 'socket'
2
+ require 'json'
1
3
  require 'jrpc/version'
2
4
  require 'jrpc/error/error'
3
5
  require 'jrpc/utils'
@@ -8,6 +8,7 @@ module JRPC
8
8
  while length_to_read > 0
9
9
  io_read, = IO.select([socket], [], [], timeout)
10
10
  raise ReadTimeoutError unless io_read
11
+ check_fin_signal
11
12
  chunk = io_read[0].read_nonblock(length_to_read)
12
13
  received += chunk
13
14
  length_to_read -= chunk.bytesize
@@ -25,6 +26,7 @@ module JRPC
25
26
  while data_to_write.bytesize > 0
26
27
  _, io_write, = IO.select([], [socket], [], timeout)
27
28
  raise WriteTimeoutError unless io_write
29
+ check_fin_signal
28
30
  chunk_length = io_write[0].write_nonblock(data_to_write)
29
31
  length_written += chunk_length
30
32
  data_to_write = data.byteslice(length_written, data.length)
@@ -41,8 +43,27 @@ module JRPC
41
43
  socket.close
42
44
  end
43
45
 
46
+ # Socket implementation allows client to send data to server after FIN,
47
+ # but server will never receive this data.
48
+ # So we consider socket closed when it have FIN event
49
+ # and close it correctly from client side.
44
50
  def closed?
45
- @socket.nil? || socket.closed?
51
+ return true if @socket.nil? || socket.closed?
52
+
53
+ if fin_signal?
54
+ close
55
+ return true
56
+ end
57
+
58
+ false
59
+ end
60
+
61
+ # Socket implementation allows client to send data to server after FIN,
62
+ # but server will never receive this data.
63
+ # We correctly close socket from client side when FIN event received.
64
+ # Should be checked before send data to socket or recv data from socket.
65
+ def check_fin_signal
66
+ close if socket && !socket.closed? && fin_signal?
46
67
  end
47
68
 
48
69
  def socket
@@ -51,6 +72,19 @@ module JRPC
51
72
 
52
73
  private
53
74
 
75
+ # when recv_nonblock(1) responds with empty string means that FIN event was received.
76
+ # in other cases it will return 1 byte string or raise EAGAINWaitReadable.
77
+ # MSG_PEEK means we do not move pointer when reading data.
78
+ # see https://apidock.com/ruby/BasicSocket/recv_nonblock
79
+ def fin_signal?
80
+ begin
81
+ resp = socket.recv_nonblock(1, Socket::MSG_PEEK)
82
+ rescue IO::EAGAINWaitReadable => _
83
+ resp = nil
84
+ end
85
+ resp == ''
86
+ end
87
+
54
88
  def clear_socket!
55
89
  return if @socket.nil?
56
90
  @socket.close unless @socket.closed?
@@ -1,3 +1,3 @@
1
1
  module JRPC
2
- VERSION = '1.1.7'
2
+ VERSION = '1.1.8'
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.1.7
4
+ version: 1.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-04 00:00:00.000000000 Z
11
+ date: 2020-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: netstring
@@ -42,30 +42,30 @@ dependencies:
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.10'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.10'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: '13.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: '13.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -83,17 +83,24 @@ dependencies:
83
83
  description: JSON RPC client over TCP
84
84
  email:
85
85
  - senid231@gmail.com
86
- executables: []
86
+ executables:
87
+ - jrpc
88
+ - jrpc-shell
87
89
  extensions: []
88
90
  extra_rdoc_files: []
89
91
  files:
90
92
  - ".gitignore"
91
93
  - ".rspec"
92
94
  - ".travis.yml"
95
+ - CHANGELOG.md
93
96
  - Gemfile
94
97
  - LICENSE.txt
95
98
  - README.md
96
99
  - Rakefile
100
+ - bin/console
101
+ - bin/jrpc
102
+ - bin/jrpc-shell
103
+ - bin/setup
97
104
  - jrpc.gemspec
98
105
  - lib/jrpc.rb
99
106
  - lib/jrpc/base_client.rb
@@ -113,7 +120,7 @@ files:
113
120
  - lib/jrpc/transport/socket_tcp.rb
114
121
  - lib/jrpc/utils.rb
115
122
  - lib/jrpc/version.rb
116
- homepage: https://github.com/senid231/jrpc
123
+ homepage: https://github.com/didww/jrpc
117
124
  licenses:
118
125
  - MIT
119
126
  metadata: {}
@@ -132,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
139
  - !ruby/object:Gem::Version
133
140
  version: '0'
134
141
  requirements: []
135
- rubygems_version: 3.1.2
142
+ rubygems_version: 3.0.8
136
143
  signing_key:
137
144
  specification_version: 4
138
145
  summary: JSON RPC client