hessian2 1.0.2

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/.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