json-rpc 0.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.
- data/README.md +69 -0
- data/example/async_app.ru +28 -0
- data/example/rack_app.ru +20 -0
- data/lib/json-rpc.rb +127 -0
- data/test/test_rpc_module.rb +59 -0
- metadata +68 -0
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
Json Rpc
|
2
|
+
========
|
3
|
+
|
4
|
+
Implementation of [JSON RPC 2.0](http://groups.google.com/group/json-rpc/web/json-rpc-2-0) protocol.
|
5
|
+
It allows you to create easily json rpc server.
|
6
|
+
|
7
|
+
Usage
|
8
|
+
-----
|
9
|
+
|
10
|
+
Simple Rack example:
|
11
|
+
|
12
|
+
~~~~~~ {ruby}
|
13
|
+
require 'json-rpc'
|
14
|
+
|
15
|
+
class SyncApp
|
16
|
+
include JsonRpc
|
17
|
+
def call env
|
18
|
+
result = dispatch(env) { |e|
|
19
|
+
logger.info "#{e}"
|
20
|
+
}
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def rpc_sum a, b
|
25
|
+
a + b
|
26
|
+
end
|
27
|
+
end
|
28
|
+
run SyncApp.new
|
29
|
+
~~~~~~
|
30
|
+
|
31
|
+
Asynchronous Event Machine example:
|
32
|
+
|
33
|
+
~~~~~~ {ruby}
|
34
|
+
require 'json-rpc'
|
35
|
+
|
36
|
+
class AsyncApp
|
37
|
+
include JsonRpc
|
38
|
+
AsyncResponse = [-1, {}, []].freeze
|
39
|
+
def call env
|
40
|
+
result = dispatch(env)
|
41
|
+
env['async.callback'].call result
|
42
|
+
AsyncResponse
|
43
|
+
end
|
44
|
+
|
45
|
+
def rpc_sum a, b
|
46
|
+
result = Rpc::AsyncResult.new
|
47
|
+
EventMachine::next_tick do
|
48
|
+
result.reply a + b
|
49
|
+
result.succeed
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
~~~~~~
|
55
|
+
|
56
|
+
### License
|
57
|
+
Copyright 2011 [Helios Technologies Ltd.](http://www.heliostech.hk)
|
58
|
+
|
59
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
60
|
+
you may not use this file except in compliance with the License.
|
61
|
+
You may obtain a copy of the License at
|
62
|
+
|
63
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
64
|
+
|
65
|
+
Unless required by applicable law or agreed to in writing, software
|
66
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
67
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
68
|
+
See the License for the specific language governing permissions and
|
69
|
+
limitations under the License.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- RUBY -*-
|
2
|
+
$: << File::join(File::dirname(__FILE__), "..", "lib")
|
3
|
+
require 'json-rpc'
|
4
|
+
|
5
|
+
class AsyncApp
|
6
|
+
include JsonRpc
|
7
|
+
|
8
|
+
AsyncResponse = [-1, {}, []].freeze
|
9
|
+
|
10
|
+
def call env
|
11
|
+
result = dispatch(env) { |e|
|
12
|
+
puts "#{e} backtrace: #{e.backtrace.join "\n"}"
|
13
|
+
}
|
14
|
+
env['async.callback'].call result
|
15
|
+
AsyncResponse
|
16
|
+
end
|
17
|
+
|
18
|
+
def rpc_sum a, b
|
19
|
+
result = Rpc::AsyncResult.new
|
20
|
+
EventMachine::next_tick do
|
21
|
+
result.reply a + b
|
22
|
+
result.succeed
|
23
|
+
end
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
run AsyncApp.new
|
data/example/rack_app.ru
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- RUBY -*-
|
2
|
+
$: << File::join(File::dirname(__FILE__), "..", "lib")
|
3
|
+
require 'json-rpc'
|
4
|
+
|
5
|
+
class SyncApp
|
6
|
+
include JsonRpc
|
7
|
+
|
8
|
+
def call env
|
9
|
+
result = dispatch(env) { |e|
|
10
|
+
logger.info "#{e} backtrace: #{e.backtrace.join "\n"}"
|
11
|
+
}
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def rpc_sum a, b
|
16
|
+
a + b
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
run SyncApp.new
|
data/lib/json-rpc.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module JsonRpc
|
6
|
+
|
7
|
+
# Call the correct method for each query
|
8
|
+
# The method should be prefixed by rpc_
|
9
|
+
# If the method doesn't exists, an error will be return in JSON
|
10
|
+
# More details in http://groups.google.com/group/json-rpc/web/json-rpc-2-0
|
11
|
+
def rpc_call env, &ecb
|
12
|
+
begin
|
13
|
+
request = Rpc::parse env
|
14
|
+
status = Rpc::validate request
|
15
|
+
result = Rpc::route request, self
|
16
|
+
|
17
|
+
rescue Rpc::Error => e
|
18
|
+
status = e.status
|
19
|
+
result = e.result
|
20
|
+
ecb.call(e) if ecb
|
21
|
+
end
|
22
|
+
|
23
|
+
[status, {'Content-Type' => Rpc::ContentType}, result]
|
24
|
+
end
|
25
|
+
|
26
|
+
module Rpc
|
27
|
+
Version = "2.0".freeze
|
28
|
+
Prefix = "rpc_".freeze
|
29
|
+
ContentType = "application/json".freeze
|
30
|
+
|
31
|
+
ErrorProtocol = {
|
32
|
+
:parse_error => [500, -32700, "Parse error"],
|
33
|
+
:invalid_request => [400, -32600, "Invalid Request"],
|
34
|
+
:method_not_found => [404, -32601, "Method not found"],
|
35
|
+
:invalid_params => [500, -32602, "Invalid params"],
|
36
|
+
:internal_error => [500, -32603, "Internal error"],
|
37
|
+
}
|
38
|
+
|
39
|
+
class Error < RuntimeError
|
40
|
+
attr_reader :status, :code, :msg
|
41
|
+
attr_accessor :id
|
42
|
+
def initialize status, code, msg
|
43
|
+
@status, @code, @msg = status, code, msg
|
44
|
+
end
|
45
|
+
def result
|
46
|
+
res = {"jsonrpc" => "2.0", "id" => id,
|
47
|
+
"error" => {"code" => code, "message" => msg}
|
48
|
+
}
|
49
|
+
res.delete_if { |k, v| v == nil}
|
50
|
+
res.to_json
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.error index, id = nil
|
55
|
+
id = nil unless id.is_a? Fixnum
|
56
|
+
ex = Rpc::Error.new *ErrorProtocol[index]
|
57
|
+
ex.id = id
|
58
|
+
ex
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.validate request
|
62
|
+
return 200 if request["jsonrpc"] == Version and
|
63
|
+
request["method"].kind_of?(String) and
|
64
|
+
request["method"] != ""
|
65
|
+
raise error :invalid_request, request["id"]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.parse env
|
69
|
+
begin
|
70
|
+
case env["REQUEST_METHOD"]
|
71
|
+
when "POST"
|
72
|
+
JSON.parse(env["rack.input"].read)
|
73
|
+
when "GET"
|
74
|
+
req = Rack::Request.new(env)
|
75
|
+
obj = req.params
|
76
|
+
obj["id"] = obj["id"] ? obj["id"].to_i : nil
|
77
|
+
obj["params"] = JSON::parse(obj["params"])
|
78
|
+
obj
|
79
|
+
else
|
80
|
+
raise error :invalid_request
|
81
|
+
end
|
82
|
+
|
83
|
+
rescue JSON::ParserError, Exception
|
84
|
+
raise error :parse_error
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.route request, ctrl
|
89
|
+
method, params = Prefix + request["method"], request["params"]
|
90
|
+
|
91
|
+
unless ctrl.respond_to? method
|
92
|
+
raise error :method_not_found, request["id"]
|
93
|
+
end
|
94
|
+
|
95
|
+
result = ctrl.send(method, *params)
|
96
|
+
if result.is_a? AsyncResult
|
97
|
+
result.id = request["id"]
|
98
|
+
return result
|
99
|
+
end
|
100
|
+
forge_response result, request["id"]
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.forge_response result, id = nil
|
104
|
+
return nil if id == nil
|
105
|
+
{"jsonrpc" => "2.0", "id" => id, "result" => result}.to_json
|
106
|
+
end
|
107
|
+
|
108
|
+
# The class RpcDeferrable is useful helps you to build a Json Rpc server
|
109
|
+
# into an asynchronous way
|
110
|
+
class AsyncResult
|
111
|
+
include EventMachine::Deferrable
|
112
|
+
|
113
|
+
attr_reader :response
|
114
|
+
attr_accessor :id
|
115
|
+
|
116
|
+
def reply obj
|
117
|
+
@callback.call(Rpc::forge_response(obj, @id))
|
118
|
+
end
|
119
|
+
|
120
|
+
#FIXME thin specific
|
121
|
+
def each &blk
|
122
|
+
@callback = blk
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'json-rpc'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
class All < Test::Unit::TestCase
|
7
|
+
include JsonRpc
|
8
|
+
|
9
|
+
ReqSubtract = {"jsonrpc"=>"2.0", "method"=>"subtract", "params"=>[42, 23], "id"=>1}
|
10
|
+
ReqSubtractString = JSON::dump(ReqSubtract)
|
11
|
+
|
12
|
+
def test_post_parse
|
13
|
+
opts = {
|
14
|
+
:input => ReqSubtractString,
|
15
|
+
:method => 'POST'
|
16
|
+
}
|
17
|
+
rs = Rpc::parse Rack::MockRequest.env_for("", opts)
|
18
|
+
assert_equal ReqSubtract, rs
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_get_parse
|
22
|
+
opts = {:method => 'GET'}
|
23
|
+
rs = Rpc::parse Rack::MockRequest.env_for("/?method=subtract¶ms=[42,23]&id=1&jsonrpc=2.0")
|
24
|
+
assert_equal ReqSubtract, rs
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_invalid_json_post_parse
|
28
|
+
req_string = '{"jsonrpc": "2.0", "method": "subtract", "params": [42, 2'
|
29
|
+
|
30
|
+
# POST REQUEST
|
31
|
+
opts = {:input => req_string,
|
32
|
+
:method => 'POST'
|
33
|
+
}
|
34
|
+
assert_raise(Rpc::Error) {
|
35
|
+
rs = Rpc::parse Rack::MockRequest.env_for("", opts)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_validate
|
40
|
+
req_hash = { "jsonrpc" => "2.0", "method" => "s", "params" => 1, "id" => 22 }
|
41
|
+
assert_nothing_raised {
|
42
|
+
Rpc::validate req_hash
|
43
|
+
}
|
44
|
+
req_hash = { "method" => "s", "params" => 1, "id" => 22 }
|
45
|
+
assert_raise(Rpc::Error) {
|
46
|
+
Rpc::validate req_hash
|
47
|
+
}
|
48
|
+
req_hash = { "jsonrpc" => "2.0", "method" => "s", "params" => 1, "id" => "22" }
|
49
|
+
assert_raise(Rpc::Error) {
|
50
|
+
Rpc::validate req_hash
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_response_forging
|
55
|
+
assert_equal({"jsonrpc" => "2.0", "result" => 12, "id" => 7},
|
56
|
+
JSON.parse(Rpc::forge_response(12, 7)))
|
57
|
+
assert_equal(nil, Rpc::forge_response(12))
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json-rpc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Helios Technologies Ltd.
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-15 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Implementation of JSON RPC 2.0 protocol. It allows you to create easily a json rpc server in pure Rack, in Rails, or asynchronous using Thin and EventMachine.
|
22
|
+
email: contact@heliostech.hk
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/json-rpc.rb
|
31
|
+
- test/test_rpc_module.rb
|
32
|
+
- example/async_app.ru
|
33
|
+
- example/rack_app.ru
|
34
|
+
- README.md
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: https://github.com/helios-technologies/json-rpc
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project: "[none]"
|
63
|
+
rubygems_version: 1.3.7
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: JSON RPC 2.0 library for rack applications
|
67
|
+
test_files: []
|
68
|
+
|