hessian2 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.komodoproject
6
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hessian2.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ = hessian2
2
+
3
+ hessian客户端。在Christer Sandberg的hessian基础上,修正了pack UTF-8(中文等字符),指定Long型,更好地被ruby1.9支持。
4
+
5
+ hessian client. based on Christer Sandberg's hessian, fixed UTF-8 pack, allow declare long type, ruby1.9 tested.
6
+
7
+ == Using
8
+
9
+ gem install hessian2
10
+
11
+ require 'hessian2'
12
+
13
+ url = 'http://localhost:8080/person_service'
14
+
15
+ client = Hessian2::HessianClient.new(url)
16
+
17
+ #call remote function: Map<Long, Person> queryPassportUidMap(List<Long> uidList)
18
+ puts client.queryPassportUidMap([Hessian2::TypeWrapper.new('Long', 47), Hessian2::TypeWrapper.new('Long', 48)])
19
+
20
+ == Authors
21
+
22
+ * {Takafan}[http://hululuu.com]
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/hessian2.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hessian2/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hessian2"
7
+ s.version = Hessian2::VERSION
8
+ s.authors = ["takafan"]
9
+ s.email = ["takafan@163.com"]
10
+ s.homepage = "http://github.com/takafan/hessian2"
11
+ s.summary = %q{Hessian2}
12
+ s.description = %q{hessian client. based on Christer Sandberg's hessian, fixed UTF-8 pack, allow declare long type, ruby1.9 tested.}
13
+
14
+ s.rubyforge_project = "hessian2"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,3 @@
1
+ module Hessian2
2
+ VERSION = "1.0.2"
3
+ end
data/lib/hessian2.rb ADDED
@@ -0,0 +1,231 @@
1
+ require "hessian2/version"
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'net/https'
6
+
7
+ module Hessian2
8
+ class TypeWrapper
9
+ attr_accessor :hessian_type, :object
10
+ def initialize(hessian_type, object)
11
+ @hessian_type, @object = hessian_type, object
12
+ end
13
+ end
14
+
15
+ class Binary
16
+ attr :data
17
+ def initialize(data)
18
+ @data = data.to_s
19
+ end
20
+ end
21
+
22
+ class HessianException < RuntimeError
23
+ attr_reader :code
24
+ def initialize(code)
25
+ @code = code
26
+ end
27
+ end
28
+
29
+ class HessianClient
30
+ attr_accessor :user, :password
31
+ attr_reader :scheme, :host, :port, :path, :proxy
32
+ def initialize(url, proxy = {})
33
+ uri = URI.parse(url)
34
+ @scheme, @host, @port, @path = uri.scheme, uri.host, uri.port, uri.path
35
+ raise "Unsupported Hessian protocol: #@scheme" unless @scheme == 'http' || @scheme == 'https'
36
+ @proxy = proxy
37
+ end
38
+
39
+ def method_missing(id, *args)
40
+ return invoke(id.id2name, args)
41
+ end
42
+
43
+ private
44
+ def invoke(method, args)
45
+ call = HessianWriter.new.write_call method, args
46
+ header = { 'Content-Type' => 'application/binary' }
47
+ req = Net::HTTP::Post.new(@path, header)
48
+ req.basic_auth @user, @password if @user
49
+ conn = Net::HTTP.new(@host, @port, *@proxy.values_at(:host, :port, :user, :password))
50
+ conn.use_ssl = true and conn.verify_mode = OpenSSL::SSL::VERIFY_NONE if @scheme == 'https'
51
+ conn.start do |http|
52
+ res = http.request(req, call)
53
+ HessianParser.new.parse_response res.body
54
+ end
55
+ end
56
+
57
+ class HessianWriter
58
+ def write_call(method, args)
59
+ @refs = {}
60
+ out = [ 'c', '0', '1', 'm', method.length ].pack('ahhan') << method
61
+ args.each { |arg| out << write_object(arg) }
62
+ out << 'z'
63
+ end
64
+
65
+ private
66
+ def write_object(val, hessian_type = nil)
67
+ return 'N' if val.nil?
68
+ case val
69
+ when TypeWrapper
70
+ val.hessian_type == 'Long' ? "L%s" % to_long(val.object) : write_object(val.object, val.hessian_type)
71
+ when Struct
72
+ write_object(val.members.inject({}) { |map, m| map[m] = val[m]; map })
73
+ when Binary
74
+ [ 'B', val.data.length ].pack('an') << val.data
75
+ when String
76
+ [ 'S', val.length ].pack('an') << val.unpack('C*').pack('U*')
77
+ when
78
+ Integer
79
+ # Max and min values for integers in Java.
80
+ if val >= -0x80000000 && val <= 0x7fffffff
81
+ [ 'I', val ].pack('aN')
82
+ else
83
+ "L%s" % to_long(val)
84
+ end
85
+ when Float
86
+ [ 'D', val ].pack('aG')
87
+ when Time
88
+ "d%s" % to_long((val.to_f * 1000).to_i)
89
+ when TrueClass
90
+ 'T'
91
+ when FalseClass
92
+ 'F'
93
+ when Array
94
+ ref = write_ref val; return ref if ref
95
+ t = hessian_type_string(hessian_type, val)
96
+ str = 'Vt' << t << 'l' << [ val.length ].pack('N')
97
+ val.each { |v| str << write_object(v) }
98
+ str << 'z'
99
+ when Hash
100
+ ref = write_ref val; return ref if ref
101
+ str = 'Mt' << hessian_type_string(hessian_type, val)
102
+ val.each { |k, v| str << write_object(k); str << write_object(v) }
103
+ str << 'z'
104
+ else
105
+ raise "Not implemented for #{val.class}"
106
+ end
107
+ end
108
+
109
+ def hessian_type_string(hessian_type, object)
110
+ if hessian_type.nil? && object.respond_to?(:hessian_type)
111
+ hessian_type = object.hessian_type
112
+ end
113
+ hessian_type ? [ hessian_type.length, hessian_type ].pack('na*') : "\000\000"
114
+ end
115
+
116
+ def to_long(val)
117
+ val2 = val.to_s(2)
118
+ ['0' * (64 - val2.size) << val2].pack("B*")
119
+ end
120
+
121
+ def write_ref(val)
122
+ id = @refs[val.object_id]
123
+ if id
124
+ [ 'R', id ].pack('aN')
125
+ else
126
+ @refs[val.object_id] = @refs.length
127
+ nil
128
+ end
129
+ end
130
+ end
131
+
132
+ class HessianParser
133
+ def parse_response(res)
134
+ raise "Invalid response, expected 'r', received '#{res[0,1]}'" unless res[0,1] == 'r'
135
+ @chunks = []
136
+ @refs = []
137
+ @data = res[3..-1]
138
+ parse_object
139
+ end
140
+
141
+ private
142
+ def parse_object
143
+ t = @data.slice!(0, 1)
144
+
145
+ case t
146
+ when 'f'
147
+ raise_exception
148
+ when 's', 'S', 'x', 'X'
149
+
150
+ v = from_utf8(@data.slice!(0, 2).unpack('n')[0])
151
+ @data.slice!(0, v[1])
152
+ @chunks << v[0]
153
+
154
+ if 'sx'.include? t
155
+
156
+ parse_object
157
+ else
158
+ str = @chunks.join; @chunks.clear; str
159
+ end
160
+ when 'b', 'B'
161
+ v = @data.slice!(0, @data.slice!(0, 2).unpack('n')[0])
162
+ @chunks << v
163
+ if t == 'b'
164
+ parse_object
165
+ else
166
+ bytes = @chunks.join; @chunks.clear; Binary.new bytes
167
+ end
168
+ when 'I'
169
+ @data.slice!(0, 4).unpack('N')[0]
170
+ when 'L'
171
+ parse_long
172
+ when 'd'
173
+ l = parse_long; Time.at(l / 1000, l % 1000 * 1000)
174
+ when 'D'
175
+ @data.slice!(0, 8).unpack('G')[0]
176
+ when 'T'
177
+ true
178
+ when 'F'
179
+ false
180
+ when 'N'
181
+ nil
182
+ when 'R'
183
+ @refs[@data.slice!(0, 4).unpack('N')[0]]
184
+ when 'V'
185
+ # Skip type + type length (2 bytes) if specified.
186
+ @data.slice!(0, 3 + @data.unpack('an')[1]) if @data[0,1] == 't'
187
+ # Skip the list length if specified.
188
+ @data.slice!(0, 5) if @data[0,1] == 'l'
189
+ @refs << (list = [])
190
+ list << parse_object while @data[0,1] != 'z'
191
+ # Get rid of the 'z'.
192
+ @data.slice!(0, 1)
193
+ list
194
+ when 'M'
195
+ # Skip type + type length (2 bytes) if specified.
196
+ @data.slice!(0, 3 + @data.unpack('an')[1]) if @data[0,1] == 't'
197
+ @refs << (map = {})
198
+ map[parse_object()] = parse_object while @data[0,1] != 'z'
199
+ # Get rid of the 'z'.
200
+ @data.slice!(0, 1)
201
+ map
202
+ else
203
+ puts @data
204
+ raise "Invalid type: '#{t}'"
205
+ end
206
+ end
207
+
208
+ def from_utf8(len = '*')
209
+ s = @data.unpack("U#{len}").pack('U*')
210
+ [ s, s.unpack('C*').pack('U*').length ]
211
+ end
212
+
213
+ def parse_long
214
+ val, o = 0, 56
215
+ @data.slice!(0, 8).each_byte { |b| val += (b & 0xff) << o; o -= 8 }
216
+ val
217
+ end
218
+
219
+ def raise_exception
220
+ # Skip code description.
221
+ parse_object
222
+ code = parse_object
223
+ # Skip message description
224
+ parse_object
225
+ msg = parse_object
226
+ raise HessianException.new(code), msg
227
+ end
228
+ end
229
+ end
230
+ end
231
+
@@ -0,0 +1,17 @@
1
+ require 'net/http'
2
+
3
+ HEADER = { 'Content-Type' => 'application/binary' }
4
+
5
+ call = %w(c 0 1 m).pack('ahha')
6
+ methods = %w(
7
+ getInt getLong getDouble getFalse getTrue getString getNull
8
+ getDate getIntArray getObjectArray getArrayInList getMap
9
+ )
10
+
11
+ methods.each do |m|
12
+ Net::HTTP.start('localhost', 8080) do |http|
13
+ res = http.send_request('POST', '/test',
14
+ call + [ m.length, m ].pack('na*') + 'z', HEADER)
15
+ p res.body
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ lib_path = File.expand_path('../../lib', __FILE__)
2
+ $:.unshift(lib_path)
3
+ require 'hessian2'
4
+ require 'test/unit'
5
+
6
+ class HessianParserTest < Test::Unit::TestCase
7
+ def parse res
8
+ Hessian2::HessianClient::HessianParser.new.parse_response res
9
+ end
10
+
11
+ def test_integer
12
+ assert_equal 4711, parse("r\001\000I\000\000\022gz")
13
+ end
14
+ def test_long
15
+ assert_equal 2, parse("r\001\000L\000\000\000\000\000\000\000\002z")
16
+ end
17
+ def test_double
18
+ assert_equal 3.4, parse("r\001\000D@\v333333z")
19
+ end
20
+ def test_false
21
+ assert_equal false, parse("r\001\000Fz")
22
+ end
23
+ def test_true
24
+ assert_equal true, parse("r\001\000Tz")
25
+ end
26
+ def test_string
27
+ assert_equal "string", parse("r\001\000S\000\006stringz")
28
+ end
29
+ def test_null
30
+ assert_equal nil, parse("r\001\000Nz")
31
+ end
32
+ def test_date
33
+ time = parse("r\001\000d\000\000\001\010\344\036\332\360z")
34
+ assert_instance_of Time, time
35
+ assert_equal '2006-01-19 19:23:13', time.getutc.strftime("%Y-%m-%d %H:%M:%S")
36
+ assert_equal 520000, time.usec
37
+ end
38
+ def test_integer_array
39
+ assert_equal [ 1, 2, 3 ], parse([ "r\001\000Vt\000\004[intl\000\000\000\003",
40
+ "I\000\000\000\001I\000\000\000\002I\000\000\000\003zz" ].join)
41
+ end
42
+ def test_array
43
+ assert_equal [ 'sillen', 32 ], parse([ "r\001\000Vt\000\a[objectl\000\000\000\002",
44
+ "S\000\006sillenI\000\000\000 zz" ].join)
45
+ end
46
+ def test_array_in_array
47
+ assert_equal [ 'A list', [ 9, 3 ] ], parse([ "r\001\000Vl\000\000\000\002S\000\006",
48
+ "A listVt\000\022[java.lang.Integerl\000\000\000\002I\000\000\000\t",
49
+ "I\000\000\000\003zzz" ].join)
50
+ end
51
+ def test_map
52
+ map = { 'sillen' => 32, 'numbers' => [ 1.1, 1.2, 1.3 ] }
53
+ assert_equal map, parse([ "r\001\000Mt\000\000S\000\anumbersVt\000\a[double",
54
+ "l\000\000\000\003D?\361\231\231\231\231\231\232D?\363333333D?",
55
+ "\364\314\314\314\314\314\315zS\000\006sillenI\000\000\000 zz" ].join)
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hessian2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - takafan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-24 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: hessian client. based on Christer Sandberg's hessian, fixed UTF-8 pack,
15
+ allow declare long type, ruby1.9 tested.
16
+ email:
17
+ - takafan@163.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - README.rdoc
25
+ - Rakefile
26
+ - hessian2.gemspec
27
+ - lib/hessian2.rb
28
+ - lib/hessian2/version.rb
29
+ - test/servlet_invoker.rb
30
+ - test/test_hessian_parser.rb
31
+ homepage: http://github.com/takafan/hessian2
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project: hessian2
51
+ rubygems_version: 1.8.11
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Hessian2
55
+ test_files:
56
+ - test/servlet_invoker.rb
57
+ - test/test_hessian_parser.rb