msgpack-rpc-over-http 0.0.7-java
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +16 -0
- data/LICENSE +22 -0
- data/README.md +97 -0
- data/Rakefile +8 -0
- data/lib/msgpack-rpc-over-http.rb +16 -0
- data/lib/msgpack/rpc_over_http/client.rb +116 -0
- data/lib/msgpack/rpc_over_http/error.rb +44 -0
- data/lib/msgpack/rpc_over_http/server.rb +25 -0
- data/lib/msgpack/rpc_over_http/server/dispatcher.rb +24 -0
- data/lib/msgpack/rpc_over_http/server/request_unpacker.rb +69 -0
- data/lib/msgpack/rpc_over_http/server/response_packer.rb +41 -0
- data/lib/msgpack/rpc_over_http/server/streamer.rb +67 -0
- data/lib/msgpack/rpc_over_http/version.rb +5 -0
- data/msgpack-rpc-over-http.gemspec +29 -0
- data/test/helper.rb +56 -0
- data/test/mock_server.ru +11 -0
- data/test/run-test.rb +18 -0
- data/test/test_client.rb +113 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1f30e1f9225beb285b0c4475ee31319dd7073b9b
|
4
|
+
data.tar.gz: 8782314fdb6acc4a80b10cd8fb143fa1c58b89da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 574b663a7231466937fe0987213a76a26b19269177bcf87166ac8657f2673e0e42e2d5253e11f58bee395a775838fc197ac0c25a13ca94e41b9736a932465a7d
|
7
|
+
data.tar.gz: 2ffd0b7e1afaacd05ccc7f66deba45268cb04d5c9fa03181e4217a1f2bd27faf40401a1f905e2d79a9ea767ffc0e3d5492997297116b9051496de45b88bb6bc6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in msgpack-rpc-over-http.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
platforms :ruby do
|
7
|
+
group :test do
|
8
|
+
gem 'thin'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
platforms :jruby do
|
13
|
+
group :test do
|
14
|
+
gem 'mizuno'
|
15
|
+
end
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Narihiro Nakamura
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# MessagePack-RPC over HTTP (Ruby)
|
2
|
+
|
3
|
+
This library provides [MessagePack-RPC](https://github.com/msgpack/msgpack-rpc) via HTTP as XML-RPC.
|
4
|
+
The original MessagePack-RPC Server in Ruby is not good in some cases.
|
5
|
+
It doesn't scale. It's incompatible with Thread. There is no decent termination processing...
|
6
|
+
|
7
|
+
We alreadly have various high perfomance HTTP servers.
|
8
|
+
We can use these in MessagePack-RPC over HTTP.
|
9
|
+
|
10
|
+
**CAUTION**
|
11
|
+
|
12
|
+
There is no compatibility with other implementation of normal MessagePack-RPC (not over HTTP).
|
13
|
+
So a normal RPC client can not connect a HTTP server.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
**Server**
|
18
|
+
|
19
|
+
confir.ru:
|
20
|
+
```ruby
|
21
|
+
require 'msgpack-rpc-over-http'
|
22
|
+
class MyHandler
|
23
|
+
def add(x,y) return x+y end
|
24
|
+
end
|
25
|
+
|
26
|
+
run MessagePack::RPCOverHTTP::Server.app(MyHandler.new)
|
27
|
+
```
|
28
|
+
|
29
|
+
rackup:
|
30
|
+
```zsh
|
31
|
+
% rackup config.ru -s thin
|
32
|
+
>> Thin web server (v1.5.0 codename Knife)
|
33
|
+
>> Maximum connections set to 1024
|
34
|
+
>> Listening on 0.0.0.0:9292, CTRL+C to stop
|
35
|
+
```
|
36
|
+
|
37
|
+
**Client**
|
38
|
+
|
39
|
+
client.rb:
|
40
|
+
```ruby
|
41
|
+
require 'msgpack-rpc-over-http'
|
42
|
+
c = MessagePack::RPCOverHTTP::Client.new("http://0.0.0.0:9292/")
|
43
|
+
result = c.call(:add, 1, 2) #=> 3
|
44
|
+
```
|
45
|
+
|
46
|
+
## Extended futures
|
47
|
+
|
48
|
+
Support streaming response via Chunked Transfer-Encoding.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# server side
|
52
|
+
class Handler
|
53
|
+
include MessagePack::RPCOverHTTP::Server::Streamer
|
54
|
+
def log
|
55
|
+
return stream do
|
56
|
+
File.open('/var/log/syslog') do |f|
|
57
|
+
while line = f.gets.chomp
|
58
|
+
# write a chunked data
|
59
|
+
chunk(line)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# client
|
67
|
+
client = MessagePack::RPCOverHTTP::Client.new("http://0.0.0.0:80/")
|
68
|
+
client.stream do |line|
|
69
|
+
p line # => "Nov 3 ..."
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## Installation
|
74
|
+
|
75
|
+
Add this line to your application's Gemfile:
|
76
|
+
|
77
|
+
gem 'msgpack-rpc-over-http'
|
78
|
+
|
79
|
+
And then execute:
|
80
|
+
|
81
|
+
$ bundle
|
82
|
+
|
83
|
+
Or install it yourself as:
|
84
|
+
|
85
|
+
$ gem install msgpack-rpc-over-http
|
86
|
+
|
87
|
+
## Usage
|
88
|
+
|
89
|
+
TODO: Write usage instructions here
|
90
|
+
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
1. Fork it
|
94
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
95
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
96
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
97
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require_relative "msgpack/rpc_over_http/version"
|
3
|
+
require_relative "msgpack/rpc_over_http/error"
|
4
|
+
require_relative "msgpack/rpc_over_http/server"
|
5
|
+
require_relative "msgpack/rpc_over_http/client"
|
6
|
+
|
7
|
+
module MessagePack
|
8
|
+
module RPCOverHTTP
|
9
|
+
REQUEST = 0 # [0, msgid, method, param]
|
10
|
+
RESPONSE = 1 # [1, msgid, error, result]
|
11
|
+
NOTIFY = 2 # [2, method, param]
|
12
|
+
|
13
|
+
NO_METHOD_ERROR = 0x01;
|
14
|
+
ARGUMENT_ERROR = 0x02;
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# require 'celluloid'
|
2
|
+
require 'httpclient'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module MessagePack
|
6
|
+
module RPCOverHTTP
|
7
|
+
|
8
|
+
# Cliet for MessagePack-RPC over HTTP.
|
9
|
+
class Client
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
HEADER = {"Content-Type" => 'application/x-msgpack'}
|
13
|
+
|
14
|
+
def initialize(url, options={})
|
15
|
+
@url = url
|
16
|
+
@client = HTTPClient.new
|
17
|
+
@reqtable = {}
|
18
|
+
@seqid = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def_delegators(:@client,
|
22
|
+
:connect_timeout, :send_timeout, :receive_timeout,
|
23
|
+
:connect_timeout=, :send_timeout=, :receive_timeout=,
|
24
|
+
:debug_dev=)
|
25
|
+
|
26
|
+
# call-seq:
|
27
|
+
# call(symbol, *args) -> result of remote method
|
28
|
+
#
|
29
|
+
# Calls remote method.
|
30
|
+
# This method is same as call_async(method, *args).value
|
31
|
+
def call(method, *args)
|
32
|
+
return send_request(method, args)
|
33
|
+
end
|
34
|
+
|
35
|
+
# call-seq:
|
36
|
+
# call_async(symbol, *args) -> Celluloid::Future
|
37
|
+
#
|
38
|
+
# Calls remote method asynchronously.
|
39
|
+
# This method is non-blocking and returns Future.
|
40
|
+
def call_async(method, *args)
|
41
|
+
require 'celluloid'
|
42
|
+
return Celluloid::Future.new{ send_request(method, args) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# call-seq:
|
46
|
+
# callback(symbol, *args) {|res, err| } -> Celluloid::Future
|
47
|
+
#
|
48
|
+
# Calls remote method asynchronously.
|
49
|
+
# The callback method is called with Future when the result is reached.
|
50
|
+
# `err' is assigned a instance of RemoteError or child if res is nil.
|
51
|
+
def callback(method, *args, &block)
|
52
|
+
require 'celluloid'
|
53
|
+
return Celluloid::Future.new do
|
54
|
+
begin
|
55
|
+
block.call(send_request(method, args))
|
56
|
+
rescue RemoteError => ex
|
57
|
+
block.call(nil, ex)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# call-seq:
|
63
|
+
# stream(symbol, *args) {|chunk| }
|
64
|
+
#
|
65
|
+
# Calls remote method with streaming.
|
66
|
+
# Remote method have to return a chunked response.
|
67
|
+
def stream(method, *args, &block)
|
68
|
+
data = create_request_body(method, args)
|
69
|
+
@client.post_content(@url, :body => data, :header => HEADER) do |chunk|
|
70
|
+
begin
|
71
|
+
block.call(get_result(chunk))
|
72
|
+
rescue RemoteError => ex
|
73
|
+
block.call(nil, ex)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# call-seq:
|
79
|
+
# stream_async(symbol, *args) {|chunk| } -> Celluloid::Future
|
80
|
+
#
|
81
|
+
# Calls remote method asynchronously with streaming.
|
82
|
+
def stream_async(method, *args, &block)
|
83
|
+
require 'celluloid'
|
84
|
+
return Celluloid::Future.new do
|
85
|
+
stream(method, *args, &block)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def send_request(method, param)
|
91
|
+
data = create_request_body(method, param)
|
92
|
+
body = @client.post_content(@url, :body => data, :header => HEADER)
|
93
|
+
return get_result(body)
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_request_body(method, param)
|
97
|
+
method = method.to_s
|
98
|
+
msgid = @seqid
|
99
|
+
@seqid += 1
|
100
|
+
@seqid = 0 if @seqid >= (1 << 31)
|
101
|
+
data = [REQUEST, msgid, method, param].to_msgpack
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_result(body)
|
105
|
+
type, msgid, err, res = MessagePack.unpack(body)
|
106
|
+
raise "Unknown message type #{type}" if type != RESPONSE
|
107
|
+
|
108
|
+
if err.nil?
|
109
|
+
return res
|
110
|
+
else
|
111
|
+
raise RemoteError.create(err, res)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module MessagePack
|
2
|
+
|
3
|
+
##
|
4
|
+
## MessagePack-RPCOverHTTP Exception
|
5
|
+
##
|
6
|
+
#
|
7
|
+
# RemoteError
|
8
|
+
# |
|
9
|
+
# +-- RuntimeError
|
10
|
+
# |
|
11
|
+
# +-- (user-defined errors)
|
12
|
+
#
|
13
|
+
module RPCOverHTTP
|
14
|
+
class RemoteError < StandardError
|
15
|
+
def initialize(code, *data)
|
16
|
+
@code = code.to_s
|
17
|
+
@data = data
|
18
|
+
super(@data.shift || @code)
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :code
|
22
|
+
attr_reader :data
|
23
|
+
|
24
|
+
def self.create(code, data)
|
25
|
+
error_class = constantize(code)
|
26
|
+
if error_class < RemoteError
|
27
|
+
error_class.new(code, *data)
|
28
|
+
else
|
29
|
+
self.new(code, *data)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def self.constantize(name)
|
35
|
+
return name.split("::").inject(Object) do |memo, i|
|
36
|
+
memo.const_get(i)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class RuntimeError < RemoteError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/builder'
|
3
|
+
require_relative 'server/dispatcher'
|
4
|
+
require_relative 'server/request_unpacker'
|
5
|
+
require_relative 'server/response_packer'
|
6
|
+
require_relative 'server/streamer'
|
7
|
+
|
8
|
+
module MessagePack
|
9
|
+
module RPCOverHTTP
|
10
|
+
class Server
|
11
|
+
|
12
|
+
# Retruns the application for MessagePack-RPC.
|
13
|
+
# It's create with Rack::Builder
|
14
|
+
def self.app(handler)
|
15
|
+
return Rack::Builder.app do
|
16
|
+
use Rack::Chunked
|
17
|
+
use RequestUnpacker
|
18
|
+
use ResponsePacker
|
19
|
+
use Dispatcher
|
20
|
+
run handler
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module MessagePack
|
2
|
+
module RPCOverHTTP
|
3
|
+
class Server
|
4
|
+
|
5
|
+
# Dispatcher of user-defined handler.
|
6
|
+
class Dispatcher
|
7
|
+
def initialize(handler, accept=handler.public_methods)
|
8
|
+
@handler = handler
|
9
|
+
@accept = accept
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
method = env['msgpack-rpc.method']
|
14
|
+
params = env['msgpack-rpc.params']
|
15
|
+
unless @accept.include?(method)
|
16
|
+
raise NoMethodError, "method `#{method}' is not accepted"
|
17
|
+
end
|
18
|
+
|
19
|
+
return @handler.__send__(method, *params)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module MessagePack
|
2
|
+
module RPCOverHTTP
|
3
|
+
class Server
|
4
|
+
|
5
|
+
# Rack Middleware that unpacks a serialized string in a HTTP
|
6
|
+
# request.
|
7
|
+
class RequestUnpacker
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
req = Rack::Request.new(env)
|
14
|
+
if (error = check_request(req))
|
15
|
+
return error
|
16
|
+
end
|
17
|
+
|
18
|
+
unpack(env, req.body.read)
|
19
|
+
return @app.call(env)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def check_request(req)
|
24
|
+
if req.request_method != "POST"
|
25
|
+
return [
|
26
|
+
405, # Method Not Allowed
|
27
|
+
{'Content-Type' => 'text/plain'},
|
28
|
+
["Only POST is allowed"]
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
if req.media_type != "application/x-msgpack"
|
33
|
+
return [
|
34
|
+
400, # Bad Request
|
35
|
+
{'Content-Type' => 'text/plain'},
|
36
|
+
["Only text/plain is allowed #{req.content_type}"]
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
if req.content_length.to_i <= 0
|
41
|
+
return [
|
42
|
+
411, # Length Required
|
43
|
+
{'Content-Type' => 'text/plain'},
|
44
|
+
["Missing Content-Length"]
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def unpack(env, body)
|
52
|
+
msg = MessagePack.unpack(body)
|
53
|
+
env['msgpack-rpc.type'] = msg[0]
|
54
|
+
case msg[0]
|
55
|
+
when REQUEST
|
56
|
+
env['msgpack-rpc.msgid'] = msg[1]
|
57
|
+
env['msgpack-rpc.method'] = msg[2].to_sym
|
58
|
+
env['msgpack-rpc.params'] = msg[3]
|
59
|
+
when NOTIFY
|
60
|
+
env['msgpack-rpc.method'] = msg[1].to_sym
|
61
|
+
env['msgpack-rpc.params'] = msg[2]
|
62
|
+
else
|
63
|
+
raise "unknown message type #{msg[0]}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MessagePack
|
2
|
+
module RPCOverHTTP
|
3
|
+
class Server
|
4
|
+
|
5
|
+
# Rack Middleware that packs a result of a handler method and
|
6
|
+
# create a HTTP Responce.
|
7
|
+
class ResponsePacker
|
8
|
+
def initialize(dispatcher)
|
9
|
+
@dispatcher = dispatcher
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
res = Rack::Response.new
|
14
|
+
res["Content-type"] = "text/plain"
|
15
|
+
msgid = env['msgpack-rpc.msgid']
|
16
|
+
|
17
|
+
body = @dispatcher.call(env)
|
18
|
+
if body.is_a?(Streamer::Body)
|
19
|
+
body.msgid = msgid
|
20
|
+
res.body = body
|
21
|
+
return [res.status.to_i, res.header, body]
|
22
|
+
else
|
23
|
+
res.write self.class.pack(msgid, nil, body)
|
24
|
+
return res.finish
|
25
|
+
end
|
26
|
+
rescue RPCOverHTTP::RemoteError => ex
|
27
|
+
res.write self.class.pack(msgid, ex.class.name, ex.message)
|
28
|
+
return res.finish
|
29
|
+
rescue ::RuntimeError => ex
|
30
|
+
res.write(self.class.pack(
|
31
|
+
msgid, RPCOverHTTP::RuntimeError.name, ex.message))
|
32
|
+
return res.finish
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.pack(msgid, error, result)
|
36
|
+
return MessagePack.pack([RESPONSE, msgid, error, result])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fiber'
|
2
|
+
|
3
|
+
module MessagePack
|
4
|
+
module RPCOverHTTP
|
5
|
+
class Server
|
6
|
+
|
7
|
+
# Support streaming
|
8
|
+
module Streamer
|
9
|
+
class Body
|
10
|
+
attr_accessor :msgid
|
11
|
+
|
12
|
+
def initialize(&proc)
|
13
|
+
@proc = proc
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
@proc.call(msgid, block)
|
18
|
+
return self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# call-seq:
|
23
|
+
# stream(&writer) -> streamable body
|
24
|
+
#
|
25
|
+
# Returns a Body object that responds to each.
|
26
|
+
# Chunks of body are created with calling chunk() in given block.
|
27
|
+
#
|
28
|
+
# def passwd
|
29
|
+
# return stream do
|
30
|
+
# File.open('/etc/passwd') do |file|
|
31
|
+
# while line = file.gets
|
32
|
+
# chunk(file.gets)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
def stream(&writer)
|
38
|
+
fi = Fiber.new do
|
39
|
+
writer.call
|
40
|
+
end
|
41
|
+
|
42
|
+
Body.new do |msgid, sender|
|
43
|
+
begin
|
44
|
+
while true
|
45
|
+
chunk = fi.resume
|
46
|
+
break unless fi.alive?
|
47
|
+
sender.call(ResponsePacker.pack(msgid, nil, chunk))
|
48
|
+
end
|
49
|
+
rescue RemoteError => ex
|
50
|
+
sender.call(ResponsePacker.pack(msgid, ex.class.name, nil))
|
51
|
+
rescue ::RuntimeError => ex
|
52
|
+
sender.call(ResponsePacker.pack(msgid, RuntimeError.name, nil))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# call-seq:
|
58
|
+
# chunk(obj)
|
59
|
+
#
|
60
|
+
# Send a object as chunked data in block of stream().
|
61
|
+
def chunk(obj)
|
62
|
+
Fiber.yield(obj)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/msgpack/rpc_over_http/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Narihiro Nakamura"]
|
6
|
+
gem.email = ["authornari@gmail.com"]
|
7
|
+
gem.description = %q{This library provides MessagePack-RPC via HTTP}
|
8
|
+
gem.summary = %q{This library provides MessagePack-RPC via HTTP}
|
9
|
+
gem.homepage = "https://github.com/authorNari/msgpack-rpc-over-http"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "msgpack-rpc-over-http"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = MessagePack::RPCOverHTTP::VERSION
|
17
|
+
|
18
|
+
if /java/ =~ RUBY_PLATFORM
|
19
|
+
gem.platform = "java"
|
20
|
+
end
|
21
|
+
|
22
|
+
gem.add_runtime_dependency "rack"
|
23
|
+
gem.add_runtime_dependency "msgpack", "~> 0.5.11"
|
24
|
+
gem.add_runtime_dependency "celluloid", "~> 0.16.0"
|
25
|
+
gem.add_runtime_dependency "httpclient"
|
26
|
+
|
27
|
+
gem.add_development_dependency "rake"
|
28
|
+
gem.add_development_dependency "test-unit", "~> 3.0"
|
29
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
class MockHandler
|
5
|
+
include MessagePack::RPCOverHTTP::Server::Streamer
|
6
|
+
|
7
|
+
class Error < MessagePack::RPCOverHTTP::RemoteError
|
8
|
+
end
|
9
|
+
|
10
|
+
def test(*args)
|
11
|
+
return args
|
12
|
+
end
|
13
|
+
|
14
|
+
def stream_normal(*args)
|
15
|
+
return stream { args.each{|arg| chunk(arg) } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def stream_error(*args)
|
19
|
+
return stream do
|
20
|
+
args.each{|arg| chunk(arg) }
|
21
|
+
raise "Error"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def error
|
26
|
+
raise "Something Error"
|
27
|
+
end
|
28
|
+
|
29
|
+
def user_defined_error
|
30
|
+
raise Error, "Something Error"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def jruby?
|
35
|
+
if /java/ =~ RUBY_PLATFORM
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def sleep_until_http_server_is_started(host, port)
|
42
|
+
timeout(30) do
|
43
|
+
while stopped_test_app_server?(host, port)
|
44
|
+
sleep 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stopped_test_app_server?(host, port)
|
50
|
+
begin
|
51
|
+
Net::HTTP.get(host, '/', port)
|
52
|
+
return false
|
53
|
+
rescue => ex
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
end
|
data/test/mock_server.ru
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#\ -s thin
|
2
|
+
|
3
|
+
### NOTE: this file is not used with jruby. FIX test/test_client.rb #setup if this file is fixed.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.expand_path("./../lib", File.dirname(__FILE__)))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'msgpack-rpc-over-http'
|
9
|
+
require 'helper'
|
10
|
+
|
11
|
+
run MessagePack::RPCOverHTTP::Server.app(MockHandler.new)
|
data/test/run-test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
base_dir = File.expand_path(File.dirname(__FILE__))
|
3
|
+
top_dir = File.expand_path("..", base_dir)
|
4
|
+
$LOAD_PATH.unshift(File.join(top_dir, "lib"))
|
5
|
+
$LOAD_PATH.unshift(File.join(top_dir, "test"))
|
6
|
+
|
7
|
+
require "bundler"
|
8
|
+
Bundler.require(:default, :test)
|
9
|
+
require "test/unit"
|
10
|
+
|
11
|
+
require "msgpack-rpc-over-http"
|
12
|
+
require "helper"
|
13
|
+
|
14
|
+
test_file = "./test/test_*.rb"
|
15
|
+
Dir.glob(test_file) do |file|
|
16
|
+
require file
|
17
|
+
end
|
18
|
+
|
data/test/test_client.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module MessagePack::RPCOverHTTP
|
4
|
+
class TestClient < Test::Unit::TestCase
|
5
|
+
def self.unused_port
|
6
|
+
s = TCPServer.open(0)
|
7
|
+
port = s.addr[1]
|
8
|
+
s.close
|
9
|
+
port
|
10
|
+
end
|
11
|
+
|
12
|
+
@@server_port = unused_port
|
13
|
+
@@is_stopped_test_app_server = true
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@client = Client.new("http://0.0.0.0:#{@@server_port}/")
|
17
|
+
if @@is_stopped_test_app_server
|
18
|
+
if /java/ =~ RUBY_PLATFORM
|
19
|
+
mizuno = nil
|
20
|
+
thread = Thread.new do
|
21
|
+
Thread.current.abort_on_exception = true
|
22
|
+
require 'mizuno/server'
|
23
|
+
app = Rack::Builder.new {
|
24
|
+
# MockHandler is in helper.rb
|
25
|
+
run MessagePack::RPCOverHTTP::Server.app(MockHandler.new)
|
26
|
+
}
|
27
|
+
mizuno = Mizuno::Server.new
|
28
|
+
mizuno.run(app, embedded: true, threads: 1, port: @@server_port, host: '0.0.0.0')
|
29
|
+
end
|
30
|
+
at_exit do
|
31
|
+
mizuno.stop
|
32
|
+
thread.kill
|
33
|
+
thread.join
|
34
|
+
end
|
35
|
+
else
|
36
|
+
pid = fork {
|
37
|
+
Rack::Server.start(config: "test/mock_server.ru", Port: @@server_port)
|
38
|
+
exit 0
|
39
|
+
}
|
40
|
+
at_exit do
|
41
|
+
Process.kill(:INT, pid)
|
42
|
+
Process.waitpid(pid)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
sleep_until_http_server_is_started("0.0.0.0", @@server_port)
|
46
|
+
@@is_stopped_test_app_server = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_call
|
51
|
+
assert_equal ["a", "b"], @client.call(:test, "a", "b")
|
52
|
+
assert_raise(MessagePack::RPCOverHTTP::RuntimeError) do
|
53
|
+
@client.call(:error)
|
54
|
+
end
|
55
|
+
assert_raise(MockHandler::Error) do
|
56
|
+
@client.call(:user_defined_error)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_call_async
|
61
|
+
pend "msgpack-rpc-over-http does not support Client#async for jruby" if jruby?
|
62
|
+
|
63
|
+
future = @client.call_async(:test, "a", "b")
|
64
|
+
assert_equal ["a", "b"], future.value
|
65
|
+
assert_raise(MessagePack::RPCOverHTTP::RuntimeError) do
|
66
|
+
future = @client.call(:error)
|
67
|
+
future.value
|
68
|
+
end
|
69
|
+
|
70
|
+
# multi-call
|
71
|
+
(1..20).map{|i| [@client.call_async(:test, i), i] }.each do |f, i|
|
72
|
+
assert_equal [i], f.value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_callback
|
77
|
+
future = @client.callback(:test, "a", "b") do |res|
|
78
|
+
assert_equal ["a", "b"], res
|
79
|
+
end
|
80
|
+
future.value
|
81
|
+
future = @client.callback(:error) do |res, err|
|
82
|
+
assert_kind_of MessagePack::RPCOverHTTP::RuntimeError, err
|
83
|
+
end
|
84
|
+
future.value
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_stream
|
88
|
+
pend "msgpack-rpc-over-http does not support Client#stream for jruby" if jruby?
|
89
|
+
expect = ["a", "b"]
|
90
|
+
@client.stream(:stream_normal, "a", "b") do |res|
|
91
|
+
assert_equal expect.shift, res
|
92
|
+
end
|
93
|
+
|
94
|
+
expect = ["a", "b"]
|
95
|
+
@client.stream(:stream_error) do |res, err|
|
96
|
+
if res
|
97
|
+
assert_equal expect.shift, res
|
98
|
+
else
|
99
|
+
assert_kind_of MessagePack::RPCOverHTTP::RuntimeError, err
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_stream_async
|
105
|
+
pend "msgpack-rpc-over-http does not support Client#stream_async for jruby" if jruby?
|
106
|
+
expect = ["a", "b"]
|
107
|
+
future = @client.stream_async(:stream_normal, "a", "b") do |res|
|
108
|
+
assert_equal expect.shift, res
|
109
|
+
end
|
110
|
+
future.value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: msgpack-rpc-over-http
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
|
+
platform: java
|
6
|
+
authors:
|
7
|
+
- Narihiro Nakamura
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - '>='
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
name: rack
|
20
|
+
prerelease: false
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.5.11
|
33
|
+
name: msgpack
|
34
|
+
prerelease: false
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.11
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.16.0
|
47
|
+
name: celluloid
|
48
|
+
prerelease: false
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.16.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
name: httpclient
|
62
|
+
prerelease: false
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
name: rake
|
76
|
+
prerelease: false
|
77
|
+
type: :development
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.0'
|
89
|
+
name: test-unit
|
90
|
+
prerelease: false
|
91
|
+
type: :development
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: This library provides MessagePack-RPC via HTTP
|
98
|
+
email:
|
99
|
+
- authornari@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/msgpack-rpc-over-http.rb
|
110
|
+
- lib/msgpack/rpc_over_http/client.rb
|
111
|
+
- lib/msgpack/rpc_over_http/error.rb
|
112
|
+
- lib/msgpack/rpc_over_http/server.rb
|
113
|
+
- lib/msgpack/rpc_over_http/server/dispatcher.rb
|
114
|
+
- lib/msgpack/rpc_over_http/server/request_unpacker.rb
|
115
|
+
- lib/msgpack/rpc_over_http/server/response_packer.rb
|
116
|
+
- lib/msgpack/rpc_over_http/server/streamer.rb
|
117
|
+
- lib/msgpack/rpc_over_http/version.rb
|
118
|
+
- msgpack-rpc-over-http.gemspec
|
119
|
+
- test/helper.rb
|
120
|
+
- test/mock_server.ru
|
121
|
+
- test/run-test.rb
|
122
|
+
- test/test_client.rb
|
123
|
+
homepage: https://github.com/authorNari/msgpack-rpc-over-http
|
124
|
+
licenses: []
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.4.5
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: This library provides MessagePack-RPC via HTTP
|
146
|
+
test_files:
|
147
|
+
- test/helper.rb
|
148
|
+
- test/mock_server.ru
|
149
|
+
- test/run-test.rb
|
150
|
+
- test/test_client.rb
|