hprose 1.4.1
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 +29 -0
- data/examples/client.rb +31 -0
- data/examples/server.rb +48 -0
- data/lib/hprose/client.rb +156 -0
- data/lib/hprose/common.rb +41 -0
- data/lib/hprose/httpclient.rb +141 -0
- data/lib/hprose/httpservice.rb +76 -0
- data/lib/hprose/io.rb +929 -0
- data/lib/hprose/service.rb +347 -0
- data/lib/hprose.rb +50 -0
- data/lib/hproseclient.rb +28 -0
- data/lib/hprosecommon.rb +30 -0
- data/lib/hproseio.rb +36 -0
- data/lib/hproseserver.rb +28 -0
- metadata +61 -0
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Hprose for Ruby
|
2
|
+
|
3
|
+
*Hprose* is a High Performance Remote Object Service Engine.
|
4
|
+
|
5
|
+
It is a modern, lightweight, cross-language, cross-platform, object-oriented, high performance, remote dynamic communication middleware. It is not only easy to use, but powerful. You just need a little time to learn, then you can use it to easily construct cross language cross platform distributed application system.
|
6
|
+
|
7
|
+
*Hprose* supports many programming languages, for example:
|
8
|
+
|
9
|
+
* AAuto Quicker
|
10
|
+
* ActionScript
|
11
|
+
* ASP
|
12
|
+
* C++
|
13
|
+
* Dart
|
14
|
+
* Delphi/Free Pascal
|
15
|
+
* dotNET(C#, Visual Basic...)
|
16
|
+
* Golang
|
17
|
+
* Java
|
18
|
+
* JavaScript
|
19
|
+
* Node.js
|
20
|
+
* Objective-C
|
21
|
+
* Perl
|
22
|
+
* PHP
|
23
|
+
* Python
|
24
|
+
* Ruby
|
25
|
+
* ...
|
26
|
+
|
27
|
+
Through *Hprose*, You can conveniently and efficiently intercommunicate between those programming languages.
|
28
|
+
|
29
|
+
This project is the implementation of Hprose for Ruby.
|
data/examples/client.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hprose'
|
3
|
+
|
4
|
+
client = HproseHttpClient.new('http://localhost:3000/')
|
5
|
+
def error(name, e)
|
6
|
+
puts name
|
7
|
+
puts e
|
8
|
+
end
|
9
|
+
client.onerror = :error
|
10
|
+
|
11
|
+
math = client.use_service(nil, :math)
|
12
|
+
|
13
|
+
client.hello('World') { |result|
|
14
|
+
puts result
|
15
|
+
}
|
16
|
+
|
17
|
+
math.add(1, 2) { |result|
|
18
|
+
puts result
|
19
|
+
}.join
|
20
|
+
|
21
|
+
math.sub(1, 2) { |result|
|
22
|
+
puts result
|
23
|
+
}.join
|
24
|
+
|
25
|
+
puts client.sum(1,3,4,5,6,7)
|
26
|
+
user = client.getUser()
|
27
|
+
puts user.name
|
28
|
+
puts user.age
|
29
|
+
|
30
|
+
puts client.hi('hprose')
|
31
|
+
puts client.push([user, user, user])
|
data/examples/server.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
require 'hprose'
|
4
|
+
|
5
|
+
def hello(name)
|
6
|
+
return 'hello ' << name << '!'
|
7
|
+
end
|
8
|
+
|
9
|
+
class User
|
10
|
+
def initialize()
|
11
|
+
@name = "Tom"
|
12
|
+
@age = 28
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def getUser()
|
17
|
+
return User.new()
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class MyService
|
22
|
+
def add(a, b)
|
23
|
+
return a + b
|
24
|
+
end
|
25
|
+
def sub(a, b)
|
26
|
+
return a - b
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def mf(name, args)
|
31
|
+
return name << " => " << HproseFormatter.serialize(args)
|
32
|
+
end
|
33
|
+
|
34
|
+
HproseClassManager.register(User, "User")
|
35
|
+
|
36
|
+
app = HproseHttpService.new()
|
37
|
+
|
38
|
+
app.add(:hello)
|
39
|
+
app.add(:sum) { |*num|
|
40
|
+
result = 0
|
41
|
+
num.each { |item| result += item }
|
42
|
+
result
|
43
|
+
}
|
44
|
+
app.add(:getUser)
|
45
|
+
app.add_missing_function(:mf)
|
46
|
+
app.add(MyService.new, :math)
|
47
|
+
app.debug = true
|
48
|
+
Rack::Handler::WEBrick.run(app, {:Port => 3000})
|
@@ -0,0 +1,156 @@
|
|
1
|
+
############################################################
|
2
|
+
# #
|
3
|
+
# hprose #
|
4
|
+
# #
|
5
|
+
# Official WebSite: http://www.hprose.com/ #
|
6
|
+
# http://www.hprose.net/ #
|
7
|
+
# http://www.hprose.org/ #
|
8
|
+
# #
|
9
|
+
############################################################
|
10
|
+
|
11
|
+
############################################################
|
12
|
+
# #
|
13
|
+
# hprose/client.rb #
|
14
|
+
# #
|
15
|
+
# hprose client for ruby #
|
16
|
+
# #
|
17
|
+
# LastModified: Mar 8, 2014 #
|
18
|
+
# Author: Ma Bingyao <andot@hprose.com> #
|
19
|
+
# #
|
20
|
+
############################################################
|
21
|
+
|
22
|
+
require "hprose/common"
|
23
|
+
require "hprose/io"
|
24
|
+
|
25
|
+
module Hprose
|
26
|
+
class Client
|
27
|
+
include Tags
|
28
|
+
include ResultMode
|
29
|
+
public
|
30
|
+
def initialize(uri = nil)
|
31
|
+
@onerror = nil
|
32
|
+
@filter = Filter.new
|
33
|
+
@simple = false
|
34
|
+
self.uri = uri
|
35
|
+
end
|
36
|
+
def uri=(uri)
|
37
|
+
@uri = URI.parse(uri) unless uri.nil?
|
38
|
+
end
|
39
|
+
attr_reader :uri
|
40
|
+
attr_accessor :filter, :simple
|
41
|
+
def onerror(&block)
|
42
|
+
@onerror = block if block_given?
|
43
|
+
@onerror
|
44
|
+
end
|
45
|
+
def onerror=(error_handler)
|
46
|
+
error_handler = error_handler.to_sym if error_handler.is_a?(String)
|
47
|
+
if error_handler.is_a?(Symbol) then
|
48
|
+
error_handler = Object.method(error_handler)
|
49
|
+
end
|
50
|
+
@onerror = error_handler
|
51
|
+
end
|
52
|
+
def use_service(uri = nil, namespace = nil)
|
53
|
+
self.uri = uri
|
54
|
+
Proxy.new(self, namespace)
|
55
|
+
end
|
56
|
+
def [](namespace)
|
57
|
+
Proxy.new(self, namespace)
|
58
|
+
end
|
59
|
+
def invoke(methodname, args = [], byref = false, resultMode = Normal, simple = nil, &block)
|
60
|
+
simple = @simple if simple.nil?
|
61
|
+
if block_given? then
|
62
|
+
Thread.start do
|
63
|
+
begin
|
64
|
+
result = do_invoke(methodname, args, byref, resultMode, simple)
|
65
|
+
case block.arity
|
66
|
+
when 0 then yield
|
67
|
+
when 1 then yield result
|
68
|
+
when 2 then yield result, args
|
69
|
+
end
|
70
|
+
rescue ::Exception => e
|
71
|
+
@onerror.call(methodname, e) if (@onerror.is_a?(Proc) or
|
72
|
+
@onerror.is_a?(Method) or
|
73
|
+
@onerror.respond_to?(:call))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
else
|
77
|
+
return do_invoke(methodname, args, byref, resultMode, simple)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
protected
|
81
|
+
def send_and_receive(data)
|
82
|
+
raise NotImplementedError.new("#{self.class.name}#send_and_receive is an abstract method")
|
83
|
+
end
|
84
|
+
private
|
85
|
+
def do_output(methodname, args, byref, simple)
|
86
|
+
stream = StringIO.new
|
87
|
+
writer = Writer.new(stream, simple)
|
88
|
+
stream.putc(TagCall)
|
89
|
+
writer.write_string(methodname.to_s)
|
90
|
+
if (args.size > 0 or byref) then
|
91
|
+
writer.reset
|
92
|
+
writer.write_list(args)
|
93
|
+
writer.write_boolean(true) if byref
|
94
|
+
end
|
95
|
+
stream.putc(TagEnd)
|
96
|
+
data = @filter.output_filter(stream.string)
|
97
|
+
stream.close
|
98
|
+
return data
|
99
|
+
end
|
100
|
+
def do_input(data, args, resultMode)
|
101
|
+
data = @filter.input_filter(data)
|
102
|
+
raise Exception.exception("Wrong Response: \r\n#{data}") if data[data.size - 1].ord != TagEnd
|
103
|
+
return data if resultMode == RawWithEndTag
|
104
|
+
return data.chop! if resultMode == Raw
|
105
|
+
stream = StringIO.new(data, 'rb')
|
106
|
+
reader = Reader.new(stream)
|
107
|
+
result = nil
|
108
|
+
while (tag = stream.getbyte) != TagEnd do
|
109
|
+
case tag
|
110
|
+
when TagResult then
|
111
|
+
if resultMode == Normal then
|
112
|
+
reader.reset
|
113
|
+
result = reader.unserialize
|
114
|
+
else
|
115
|
+
s = reader.read_raw
|
116
|
+
result = s.string
|
117
|
+
s.close
|
118
|
+
end
|
119
|
+
when TagArgument then
|
120
|
+
reader.reset
|
121
|
+
a = reader.read_list
|
122
|
+
args.each_index { |i| args[i] = a[i] }
|
123
|
+
when TagError then
|
124
|
+
reader.reset
|
125
|
+
result = Exception.exception(reader.read_string())
|
126
|
+
else
|
127
|
+
raise Exception.exception("Wrong Response: \r\n#{data}")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
return result
|
131
|
+
end
|
132
|
+
def do_invoke(methodname, args, byref, resultMode, simple)
|
133
|
+
data = do_output(methodname, args, byref, simple)
|
134
|
+
result = do_input(send_and_receive(data), args, resultMode)
|
135
|
+
raise result if result.is_a?(Exception)
|
136
|
+
return result
|
137
|
+
end
|
138
|
+
def method_missing(methodname, *args, &block)
|
139
|
+
self.invoke(methodname, args, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
class Proxy
|
143
|
+
def initialize(client, namespace = nil)
|
144
|
+
@client = client
|
145
|
+
@namespace = namespace
|
146
|
+
end
|
147
|
+
def [](namespace)
|
148
|
+
Proxy.new(@client, @namespace.to_s + '_' + namespace.to_s)
|
149
|
+
end
|
150
|
+
def method_missing(methodname, *args, &block)
|
151
|
+
methodname = @namespace.to_s + '_' + methodname.to_s unless @namespace.nil?
|
152
|
+
@client.invoke(methodname, args, &block)
|
153
|
+
end
|
154
|
+
end # class Proxy
|
155
|
+
end # class Client
|
156
|
+
end # module Hprose
|
@@ -0,0 +1,41 @@
|
|
1
|
+
############################################################
|
2
|
+
# #
|
3
|
+
# hprose #
|
4
|
+
# #
|
5
|
+
# Official WebSite: http://www.hprose.com/ #
|
6
|
+
# http://www.hprose.net/ #
|
7
|
+
# http://www.hprose.org/ #
|
8
|
+
# #
|
9
|
+
############################################################
|
10
|
+
|
11
|
+
############################################################
|
12
|
+
# #
|
13
|
+
# hprose/common.rb #
|
14
|
+
# #
|
15
|
+
# hprose common library for ruby #
|
16
|
+
# #
|
17
|
+
# LastModified: Dec 2, 2012 #
|
18
|
+
# Author: Ma Bingyao <andot@hprose.com> #
|
19
|
+
# #
|
20
|
+
############################################################
|
21
|
+
|
22
|
+
module Hprose
|
23
|
+
class Exception < Exception; end
|
24
|
+
|
25
|
+
module ResultMode
|
26
|
+
Normal = 0
|
27
|
+
Serialized = 1
|
28
|
+
Raw = 2
|
29
|
+
RawWithEndTag = 3
|
30
|
+
end
|
31
|
+
|
32
|
+
class Filter
|
33
|
+
def input_filter(data)
|
34
|
+
return data
|
35
|
+
end
|
36
|
+
def output_filter(data)
|
37
|
+
return data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end # module Hprose
|
@@ -0,0 +1,141 @@
|
|
1
|
+
############################################################
|
2
|
+
# #
|
3
|
+
# hprose #
|
4
|
+
# #
|
5
|
+
# Official WebSite: http://www.hprose.com/ #
|
6
|
+
# http://www.hprose.net/ #
|
7
|
+
# http://www.hprose.org/ #
|
8
|
+
# #
|
9
|
+
############################################################
|
10
|
+
|
11
|
+
############################################################
|
12
|
+
# #
|
13
|
+
# hprose/httpclient.rb #
|
14
|
+
# #
|
15
|
+
# hprose http client for ruby #
|
16
|
+
# #
|
17
|
+
# LastModified: Mar 8, 2014 #
|
18
|
+
# Author: Ma Bingyao <andot@hprose.com> #
|
19
|
+
# #
|
20
|
+
############################################################
|
21
|
+
|
22
|
+
require "hprose/client"
|
23
|
+
require "net/http"
|
24
|
+
require "net/https"
|
25
|
+
require "uri"
|
26
|
+
|
27
|
+
module Hprose
|
28
|
+
class HttpClient < Client
|
29
|
+
include Tags
|
30
|
+
@@cookie_manager = {}
|
31
|
+
@@cookie_manager_mutex = Mutex.new
|
32
|
+
public
|
33
|
+
def initialize(uri = nil)
|
34
|
+
super
|
35
|
+
Net::HTTP.version_1_2
|
36
|
+
@http = Net::HTTP
|
37
|
+
@header = {}
|
38
|
+
@timeout = 30
|
39
|
+
@keepalive = false
|
40
|
+
@keepalive_timeout = 300
|
41
|
+
end
|
42
|
+
attr_reader :header
|
43
|
+
attr_accessor :timeout, :keepalive, :keepalive_timeout
|
44
|
+
def proxy=(proxy)
|
45
|
+
@http = case proxy
|
46
|
+
when Net::HTTP then
|
47
|
+
proxy
|
48
|
+
when String then
|
49
|
+
uri = URI.parse(proxy)
|
50
|
+
Net::HTTP::Proxy(uri.host, uri.port, uri.user, uri.password)
|
51
|
+
else
|
52
|
+
proxy.superclass == Net::HTTP ? proxy : Net::HTTP
|
53
|
+
end
|
54
|
+
end
|
55
|
+
protected
|
56
|
+
def send_and_receive(data)
|
57
|
+
httpclient = @http.new(@uri.host, @uri.port)
|
58
|
+
httpclient.open_timeout = @timeout
|
59
|
+
httpclient.read_timeout = @timeout
|
60
|
+
httpclient.use_ssl = (@uri.scheme == 'https')
|
61
|
+
#httpclient.set_debug_output $stderr
|
62
|
+
httpclient.start
|
63
|
+
headers = {'Content-Type' => 'application/hprose',
|
64
|
+
'Connection' => 'close'}
|
65
|
+
if @keepalive then
|
66
|
+
headers['Connection'] = 'keep-alive'
|
67
|
+
headers['Keep-Alive'] = @keepalive_timeout.to_s
|
68
|
+
end
|
69
|
+
headers['Authorization'] = 'Basic ' << ["#{@uri.user}:#{@uri.password}"].pack('m').delete!("\n") unless @uri.user.nil? or @uri.password.nil?
|
70
|
+
@header.each { |name, value|
|
71
|
+
headers[name] = value
|
72
|
+
}
|
73
|
+
headers['Content-Length'] = data.size.to_s
|
74
|
+
headers['Cookie'] = _get_cookie(@uri.host.downcase, @uri.path, @uri.scheme == 'https')
|
75
|
+
reqpath = @uri.path
|
76
|
+
reqpath << '?' << @uri.query unless @uri.query.nil?
|
77
|
+
response = httpclient.request_post(reqpath, data, headers)
|
78
|
+
case response
|
79
|
+
when Net::HTTPSuccess then
|
80
|
+
cookielist = []
|
81
|
+
cookielist.concat(response['set-cookie'].split(',')) if response.key?('set-cookie')
|
82
|
+
cookielist.concat(response['set-cookie2'].split(',')) if response.key?('set-cookie2')
|
83
|
+
_set_cookie(cookielist, @uri.host.downcase)
|
84
|
+
return response.body
|
85
|
+
else
|
86
|
+
raise Exception.exception(response.message)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
private
|
90
|
+
def _set_cookie(cookielist, host)
|
91
|
+
@@cookie_manager_mutex.synchronize do
|
92
|
+
cookielist.each do |cookies|
|
93
|
+
unless cookies == '' then
|
94
|
+
cookies = cookies.strip.split(';')
|
95
|
+
cookie = {}
|
96
|
+
value = cookies[0].strip.split('=', 2)
|
97
|
+
cookie['name'] = value[0]
|
98
|
+
cookie['value'] = value.size == 2 ? value[1] : ''
|
99
|
+
1.upto(cookies.size - 1) do |i|
|
100
|
+
value = cookies[i].strip.split('=', 2)
|
101
|
+
cookie[value[0].upcase] = value.size == 2 ? value[1] : ''
|
102
|
+
end
|
103
|
+
# Tomcat can return SetCookie2 with path wrapped in "
|
104
|
+
if cookie.has_key?('PATH') then
|
105
|
+
cookie['PATH'][0] = '' if cookie['PATH'][0] == ?"
|
106
|
+
cookie['PATH'].chop! if cookie['PATH'][-1] == ?"
|
107
|
+
else
|
108
|
+
cookie['PATH'] = '/'
|
109
|
+
end
|
110
|
+
cookie['EXPIRES'] = Time.parse(cookie['EXPIRES']) if cookie.has_key?('EXPIRES')
|
111
|
+
cookie['DOMAIN'] = cookie.has_key?('DOMAIN') ? cookie['COMAIN'].downcase : host
|
112
|
+
cookie['SECURE'] = cookie.has_key?('SECURE')
|
113
|
+
@@cookie_manager[cookie['DOMAIN']] = {} unless @@cookie_manager.has_key?(cookie['DOMAIN'])
|
114
|
+
@@cookie_manager[cookie['DOMAIN']][cookie['name']] = cookie
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
def _get_cookie(host, path, secure)
|
120
|
+
cookies = []
|
121
|
+
@@cookie_manager_mutex.synchronize do
|
122
|
+
@@cookie_manager.each do |domain, cookielist|
|
123
|
+
if host =~ Regexp.new(Regexp.escape(domain) + '$') then
|
124
|
+
names = []
|
125
|
+
cookielist.each do |name, cookie|
|
126
|
+
if cookie.has_key?('EXPIRES') and Time.now <=> cookie['EXPIRES'] > 0 then
|
127
|
+
names << name
|
128
|
+
elsif path =~ Regexp.new('^' + Regexp.escape(cookie['PATH'])) then
|
129
|
+
if ((secure and cookie['SECURE']) or not cookie['SECURE']) and cookie['value'] != '' then
|
130
|
+
cookies << (cookie['name'] + '=' + cookie['value'])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
names.each { |name| @@cookie_manager[domain].delete(name) }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
return cookies.join('; ')
|
139
|
+
end
|
140
|
+
end # class HttpClient
|
141
|
+
end # module Hprose
|
@@ -0,0 +1,76 @@
|
|
1
|
+
############################################################
|
2
|
+
# #
|
3
|
+
# hprose #
|
4
|
+
# #
|
5
|
+
# Official WebSite: http://www.hprose.com/ #
|
6
|
+
# http://www.hprose.net/ #
|
7
|
+
# http://www.hprose.org/ #
|
8
|
+
# #
|
9
|
+
############################################################
|
10
|
+
|
11
|
+
############################################################
|
12
|
+
# #
|
13
|
+
# hprose/httpservice.rb #
|
14
|
+
# #
|
15
|
+
# hprose http service for ruby #
|
16
|
+
# #
|
17
|
+
# LastModified: Mar 8, 2014 #
|
18
|
+
# Author: Ma Bingyao <andot@hprose.com> #
|
19
|
+
# #
|
20
|
+
############################################################
|
21
|
+
|
22
|
+
require "hprose/io"
|
23
|
+
require "hprose/service"
|
24
|
+
|
25
|
+
module Hprose
|
26
|
+
class HttpService < Service
|
27
|
+
attr_accessor :crossdomain
|
28
|
+
attr_accessor :p3p
|
29
|
+
attr_accessor :get
|
30
|
+
attr_accessor :on_send_header
|
31
|
+
def initialize
|
32
|
+
super
|
33
|
+
@crossdomain = false
|
34
|
+
@p3p = false
|
35
|
+
@get = true
|
36
|
+
@on_send_header = nil
|
37
|
+
end
|
38
|
+
def call(context)
|
39
|
+
header = {'Content-Type' => 'text/plain'}
|
40
|
+
header['P3P'] = 'CP="CAO DSP COR CUR ADM DEV TAI PSA PSD ' +
|
41
|
+
'IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi ' +
|
42
|
+
'PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT ' +
|
43
|
+
'STA POL HEA PRE GOV"' if @p3p
|
44
|
+
if @crossdomain then
|
45
|
+
origin = context["HTTP_ORIGIN"]
|
46
|
+
if (origin and origin != "null") then
|
47
|
+
header['Access-Control-Allow-Origin'] = origin
|
48
|
+
header['Access-Control-Allow-Credentials'] = 'true'
|
49
|
+
else
|
50
|
+
header['Access-Control-Allow-Origin'] = '*'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
begin
|
54
|
+
statuscode = 200
|
55
|
+
@on_send_header.call(header) unless @on_send_header.nil?
|
56
|
+
if (context['REQUEST_METHOD'] == 'GET') and @get then
|
57
|
+
body = do_function_list
|
58
|
+
elsif (context['REQUEST_METHOD'] == 'POST') then
|
59
|
+
body = handle(context['rack.input'].read, context)
|
60
|
+
else
|
61
|
+
statuscode = 403
|
62
|
+
body = 'Forbidden'
|
63
|
+
end
|
64
|
+
rescue ::Exception => e
|
65
|
+
body = do_error(e)
|
66
|
+
end
|
67
|
+
header['Content-Length'] = body.size.to_s
|
68
|
+
return [statuscode, header, [body]]
|
69
|
+
end
|
70
|
+
protected
|
71
|
+
def fix_args(args, arity, context)
|
72
|
+
session = context['rack.session'] ? context['rack.session'] : {}
|
73
|
+
((arity > 0) and (args.length + 1 == arity)) ? args + [session] : args
|
74
|
+
end
|
75
|
+
end # class HttpService
|
76
|
+
end # module Hprose
|