zmqjsonrpc 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 275b2e7bd02313197bd72c9791bfb20774a8f0c7
4
+ data.tar.gz: 32c9c8d5bba102217c2f19472575e8d893821de2
5
+ SHA512:
6
+ metadata.gz: 3d636755631636c90c151348753be54440f0b2ff212a1a712f111514bcf2a1a615c48e81a366ad7d08a9a5c4c3ffc496e038bda0a7903cbd03215c81650af5bf
7
+ data.tar.gz: b6c77e276528cf85b0040aa7e71ef0fceb3105bd5caadf8511b573aad9e90923c4905df61cc56d1b72858d411f84a6f37649756e71676d0f17904405215b3c3d
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ .vagrant
3
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simplejsonrpc.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,23 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simplejsonrpc (0.1)
5
+ ffi-rzmq (~> 2.0.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ffi (1.9.6)
11
+ ffi-rzmq (2.0.4)
12
+ ffi-rzmq-core (>= 1.0.1)
13
+ ffi-rzmq-core (1.0.3)
14
+ ffi (~> 1.9)
15
+ rake (10.4.2)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ bundler (~> 1.3)
22
+ rake
23
+ simplejsonrpc!
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # zmqjsonrpc
2
+
3
+ This gem implements a very simple [JSON RPC 2.0](http://www.jsonrpc.org/specification) client and server which uses zeroMQ for transport.
4
+ Let's not talk to much, let's see some code:
5
+
6
+ ```ruby
7
+ require 'rubygems'
8
+ require 'zmqjsonrpc'
9
+
10
+ # client request to a running server
11
+ client = ZmqJsonRpc::Client.new("tcp://127.0.0.1:49200")
12
+ client.some_method(1, "b", [1,{a:1}])
13
+ ```
14
+
15
+ ```ruby
16
+ require 'rubygems'
17
+ require 'zmqjsonrpc'
18
+
19
+ class Proxy
20
+ def some_method(a,b,c)
21
+ # do you thing
22
+ return ["xyz", 77]
23
+ end
24
+ end
25
+
26
+ # blocking server
27
+ proxy = Proxy.new()
28
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200")
29
+ server.server_loop
30
+
31
+ # -or- dispatch a thread
32
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200")
33
+ thread = Thread.new {
34
+ server.server_loop
35
+ }
36
+ # and cancel the thread if you want to shut the server down
37
+ thread.exit
38
+ ```
39
+
40
+ ## Resources
41
+
42
+ * The [JSON RPC 2.0 Spec](http://www.jsonrpc.org/specification)
43
+ * The used [ZeroMQ gem](https://github.com/chuckremes/ffi-rzmq) and [good examples](http://github.com/andrewvc/learn-ruby-zeromq)
44
+ * [Gem making](http://guides.rubygems.org/make-your-own-gem/)
45
+
46
+ ## Stuff left to do
47
+
48
+ * Add support for by-name parameters (see [the spec](http://www.jsonrpc.org/specification#parameter_structures))
49
+ * Add individual exception classes in the client.
50
+ * Send different error codes if something goes wrong in the server.
51
+ * Keep the client connection alive instead of re-establishing every time.
52
+ * Add more tests.
53
+
54
+ ## Licence
55
+
56
+ This code is released under the terms of MIT License.
57
+
58
+ ## Contribute
59
+
60
+ Please do so! Just send a messahe or send a pull request.
61
+ Especially, adding webrick for transport would be nice.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
data/Vagrantfile ADDED
@@ -0,0 +1,31 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ provision = <<SCRIPT
5
+ # Update system
6
+ sudo apt-get -y update
7
+ sudo apt-get -y upgrade
8
+ sudo apt-get -y install build-essential git
9
+
10
+ # ruby
11
+ sudo apt-get -y install ruby2.0 ruby2.0-dev bundler rake
12
+ sudo ln -sf /usr/bin/ruby2.0 /usr/bin/ruby # make ruby 2 default
13
+ sudo ln -sf /usr/bin/gem2.0 /usr/bin/gem
14
+ sudo ln -sf /usr/bin/irb2.0 /usr/bin/irb
15
+
16
+ # install tools for development
17
+ sudo apt-get -y install vim man
18
+
19
+ # install dependencies
20
+ sudo apt-get install libzmq3 libzmq3-dev
21
+ SCRIPT
22
+
23
+ Vagrant.configure("2") do |config|
24
+ config.vm.box = "ubuntu/trusty64"
25
+ config.vm.hostname = "simplejsonrpc"
26
+ config.vm.provision :shell, inline: provision
27
+
28
+ config.vm.provider "virtualbox" do |vb|
29
+ vb.name = "simplejsonrpc"
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ require 'json'
2
+ require 'securerandom'
3
+
4
+ module ZmqJsonRpc
5
+ class ClientError < RuntimeError
6
+ end
7
+
8
+ # You shall instanciate this class with a connect string which follows the zeroMQ conventions: http://api.zeromq.org/2-1:zmq-connect
9
+ # After connecting you can call any method on this class and it will be sent to the server.
10
+ # If something goes wrong ZmqJsonRpc::ClientError is thrown.
11
+ # timeout is measured in milliseconds.
12
+ #
13
+ # NOTE
14
+ # The client does not keep the connection alive. For each request, a new connection is esablished and torn down after the response.
15
+ # This could become a performance issue.
16
+ class Client
17
+ def initialize(connect="tcp://127.0.0.1:49200", timeout=10000)
18
+ @connect = connect
19
+ @timeout = timeout
20
+ end
21
+
22
+ def send_rpc(method, params=[])
23
+ begin
24
+ # connect socket
25
+ @context = ZMQ::Context.new(1)
26
+ @socket = @context.socket(ZMQ::REQ)
27
+ @socket.connect(@connect)
28
+ @socket.setsockopt(ZMQ::SNDTIMEO, @timeout)
29
+ @socket.setsockopt(ZMQ::RCVTIMEO, @timeout)
30
+
31
+ # build and send request
32
+ req_id = SecureRandom.uuid
33
+ request = {
34
+ id: req_id,
35
+ jsonrpc: "2.0",
36
+ method: method.to_s,
37
+ params: params
38
+ }
39
+ rc = @socket.send_string(request.to_json) # this will always succeed, even if the server is not reachable.
40
+
41
+ # interpret response
42
+ response = ''
43
+ rc = @socket.recv_string(response)
44
+ raise "Could talk to the server (server unreachable? time out?)" if rc < 0
45
+ resjson = JSON.parse(response)
46
+ # check response
47
+ raise "Response's id did not match the sent id" if resjson["id"] != req_id
48
+ raise "Response's version number is not supported (#{resjson["jsonrpc"]})" if resjson["jsonrpc"].strip != "2.0"
49
+ raise "Server returned error (#{resjson["error"]["code"] || "?"}): #{resjson["error"]["message"]}\n#{resjson["error"]["data"]}" if resjson["error"]
50
+
51
+ return resjson["result"]
52
+ rescue => e
53
+ raise ClientError, e.message
54
+ ensure
55
+ @socket.close rescue ''
56
+ end
57
+ end
58
+
59
+ def method_missing(meth, *args, &block)
60
+ self.send_rpc(meth, args)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,87 @@
1
+ require 'ffi-rzmq'
2
+ require 'json'
3
+
4
+ module ZmqJsonRpc
5
+ # This class implements the server:
6
+ #
7
+ # USE
8
+ # class Proxy
9
+ # def some_method(a,b)
10
+ # return [a,b,{"XXXX"=> 1}]
11
+ # end
12
+ # end
13
+ # server = ZmqJsonRpc::Server.new(Proxy.new())
14
+ # server.server_loop
15
+ #
16
+ #
17
+ # If you want a non blocking server do
18
+ # server = ZmqJsonRpc::Server.new(Proxy.new())
19
+ # thread = Thread.new {
20
+ # server.server_loop
21
+ # }
22
+ # # later either do
23
+ # thread.join # waiting for the server to finish -- which is never!
24
+ # # or
25
+ # thread.exit # or end the thread
26
+ class Server
27
+ # For errors the spec says:
28
+ # The error codes from and including -32768 to -32000 are reserved for pre-defined errors. Any code within this range, but not defined explicitly below is reserved for future use. The error codes are nearly the same as those suggested for XML-RPC at the following url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
29
+ #
30
+ # For details on the zmq gem, please see https://github.com/chuckremes/ffi-rzmq (best to look in the code)
31
+ def initialize(proxy, connect="tcp://*:49200", logger=nil)
32
+ @connect = connect
33
+ @proxy = proxy
34
+ @logger = logger
35
+ end
36
+
37
+ def handle_request(request)
38
+ begin
39
+ req_id = nil
40
+ rpc = JSON.parse(request)
41
+ raise "Received unsupprted jsonrpc version (#{rpc['jsonrpc']})" if rpc["jsonrpc"].strip != "2.0"
42
+ rid = rpc["id"]
43
+ method = rpc["method"]
44
+ params = rpc["params"]
45
+
46
+ @logger.info "Received JSON RPC request: #{method}(#{params.collect {|p| p.inspect}.join(", ")})" unless @logger.nil?
47
+ result = @proxy.send(method.to_sym, *params)
48
+ response = {
49
+ id: rid,
50
+ jsonrpc: "2.0",
51
+ result: result
52
+ }
53
+ return response.to_json
54
+ rescue => e
55
+ # If there is more time to spare, we could implement the actual error codes here.
56
+ @logger.warn "Returning error for RPC request: #{e.message})" unless @logger.nil?
57
+ response = {
58
+ id: rid,
59
+ jsonrpc: "2.0",
60
+ error: {
61
+ code: 32603,
62
+ message: e.message,
63
+ data: e.backtrace.inspect
64
+ }
65
+ }
66
+ return response.to_json
67
+ end
68
+ end
69
+
70
+ def server_loop
71
+ @context = ZMQ::Context.new(1)
72
+ @socket = @context.socket(ZMQ::REP)
73
+ @socket.bind(@connect)
74
+ begin
75
+ loop do
76
+ request = ''
77
+ rc = @socket.recv_string(request)
78
+ response = handle_request(request)
79
+ @socket.send_string(response)
80
+ end
81
+ ensure
82
+ @socket.close
83
+ # @context.terminate
84
+ end
85
+ end
86
+ end
87
+ end
data/lib/zmqjsonrpc.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require "simple_json_rpc/client"
3
+ require "simple_json_rpc/server"
4
+
5
+ module ZmqJsonRpc
6
+ end
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require "logger"
3
+ require "stringio"
4
+ require_relative '../lib/zmqjsonrpc'
5
+
6
+ class TestClientServer < Test::Unit::TestCase
7
+ def setup
8
+ @buffer = StringIO.new
9
+ @logger = Logger.new(@buffer)
10
+ @logger.level = Logger::WARN
11
+ end
12
+
13
+ def assert_empty_log
14
+ @buffer.seek(0)
15
+ assert_equal @buffer.read, "", "Server log included warnings."
16
+ end
17
+
18
+ def test_client_with_server
19
+ proxy_class = Class.new() do
20
+ def initialize(test_case)
21
+ @test_case = test_case
22
+ @method_was_called = false
23
+ end
24
+ def method_was_called?
25
+ return @method_was_called
26
+ end
27
+
28
+ def some_method(a,b,c)
29
+ @test_case.assert_equal a, 1
30
+ @test_case.assert_equal b, "b"
31
+ @test_case.assert_equal c, [1,{"a" => 1}]
32
+ @method_was_called = true
33
+ return "abc"
34
+ end
35
+ end
36
+
37
+ proxy = proxy_class.new(self)
38
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200", @logger)
39
+ thread = Thread.new {
40
+ server.server_loop
41
+ }
42
+ client = ZmqJsonRpc::Client.new("tcp://127.0.0.1:49200")
43
+ assert_equal client.some_method(1, "b", [1,{a:1}]), "abc"
44
+ assert proxy.method_was_called?, "Server method was not called"
45
+
46
+ assert_raise ZmqJsonRpc::ClientError do
47
+ client.fishy_method()
48
+ end
49
+
50
+ thread.exit
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ # more info here: http://guides.rubygems.org/specification-reference/
2
+
3
+ # coding: utf-8
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "zmqjsonrpc"
9
+ spec.version = "0.1"
10
+ spec.authors = ["Tom Rothe"]
11
+ spec.email = ["tom@bisdn.de"]
12
+ spec.description = 'Simple JSON RPC 2.0 client and server via zmq.'
13
+ spec.summary = 'ZeroMQ JSON RPC client and server.'
14
+ spec.homepage = "https://github.com/bisdn/zmqjsonrpc"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.test_files = Dir.glob('test/*.rb')
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency("ffi-rzmq", "~> 2.0.4")
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zmqjsonrpc
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Tom Rothe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi-rzmq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple JSON RPC 2.0 client and server via zmq.
56
+ email:
57
+ - tom@bisdn.de
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - README.md
66
+ - Rakefile
67
+ - Vagrantfile
68
+ - lib/simple_json_rpc/client.rb
69
+ - lib/simple_json_rpc/server.rb
70
+ - lib/zmqjsonrpc.rb
71
+ - test/test_client_server.rb
72
+ - zmqjsonrpc.gemspec
73
+ homepage: https://github.com/bisdn/zmqjsonrpc
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.0.14
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: ZeroMQ JSON RPC client and server.
97
+ test_files:
98
+ - test/test_client_server.rb