hprose 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|