hessian2 1.0.3 → 1.1.0

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.rdoc CHANGED
@@ -1,8 +1,6 @@
1
1
  = hessian2
2
2
 
3
- hessian客户端。在Christer Sandberghessian基础上,修正了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.
3
+ implement hessian 1.0.2 specification. refactor Christer Sandberg's hessian, ruby 1.9.3 required.
6
4
 
7
5
  == Using
8
6
 
@@ -14,9 +12,10 @@ hessian client. based on Christer Sandberg's hessian, fixed UTF-8 pack, allow de
14
12
 
15
13
  client = Hessian2::HessianClient.new(url)
16
14
 
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
-
15
+ # call remote function: public Person getPersonByUid(Long uid);
16
+ uid = Hessian2::TypeWrapper.new('L', 59)
17
+ puts client.getPersonByUid(uid)
18
+
20
19
  == Authors
21
20
 
22
21
  * {Takafan}[http://hululuu.com]
data/hessian2.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.email = ["takafan@163.com"]
10
10
  s.homepage = "http://github.com/takafan/hessian2"
11
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.}
12
+ s.description = %q{implement hessian 1.0.2 specification. refactor Christer Sandberg's hessian, ruby 1.9.3 required.}
13
13
 
14
14
  s.rubyforge_project = "hessian2"
15
15
 
@@ -0,0 +1,38 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'hessian2/hessian_writer'
5
+ require 'hessian2/hessian_parser'
6
+
7
+ module Hessian2
8
+ class HessianClient
9
+ attr_accessor :user, :password
10
+ attr_reader :scheme, :host, :port, :path, :proxy
11
+
12
+ include HessianWriter
13
+ include HessianParser
14
+
15
+ def initialize(url, proxy = {})
16
+ uri = URI.parse(url)
17
+ @scheme, @host, @port, @path = uri.scheme, uri.host, uri.port, uri.path
18
+ raise "Unsupported Hessian protocol: #{@scheme}" unless %w(http https).include? @scheme
19
+ @proxy = proxy
20
+ end
21
+
22
+ def method_missing(id, *args)
23
+ return invoke(id.id2name, args)
24
+ end
25
+
26
+ private
27
+ def invoke(method, args)
28
+ req = Net::HTTP::Post.new(@path, { 'Content-Type' => 'application/binary' })
29
+ req.basic_auth @user, @password if @user
30
+ conn = Net::HTTP.new(@host, @port, *@proxy.values_at(:host, :port, :user, :password))
31
+ conn.use_ssl = true and conn.verify_mode = OpenSSL::SSL::VERIFY_NONE if @scheme == 'https'
32
+ conn.start do |http|
33
+ parse http.request(req, write_call(method, args)).body
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Hessian2
2
+ class HessianException < RuntimeError; end
3
+ end
@@ -0,0 +1,75 @@
1
+ module Hessian2
2
+ module HessianParser
3
+
4
+ def parse(data, refs = [], chunks = [])
5
+ t = data.slice!(0)
6
+ case t
7
+ when 'r' # reply
8
+ data.slice!(0, 2)
9
+ parse(data)
10
+ when 'f' # fault
11
+ parse(data)
12
+ code = parse(data)
13
+ parse(data)
14
+ message = parse(data)
15
+ raise HessianException.new, "#{code}: #{message}"
16
+ when 'N' # null
17
+ nil
18
+ when 'T' # true
19
+ true
20
+ when 'F' # false
21
+ false
22
+ when 'I' # int
23
+ data.slice!(0, 4).unpack('l>')[0]
24
+ when 'L' # long
25
+ data.slice!(0, 8).unpack('q>')[0]
26
+ when 'D' # double
27
+ data.slice!(0, 8).unpack('G')[0]
28
+ when 'd' # date
29
+ val = data.slice!(0, 8).unpack('Q>')[0]
30
+ Time.at(val / 1000, val % 1000 * 1000)
31
+ when 'S', 's', 'X', 'x' # string, xml
32
+ len = data.slice!(0, 2).unpack('n')[0]
33
+
34
+ chunk = data.unpack("U#{len}")
35
+ chunks << chunk
36
+ data.slice!(0, chunk.pack('U*').bytesize)
37
+
38
+ if 'sx'.include?(t)
39
+ parse(data, refs, chunks)
40
+ else
41
+ chunks.flatten.pack('U*')
42
+ end
43
+ when 'B', 'b' # binary
44
+ len = data.slice!(0, 2).unpack('n')[0]
45
+
46
+ chunk = data.slice!(0, len)
47
+ chunks << chunk
48
+
49
+ if t == 'b'
50
+ parse(data, refs, chunks)
51
+ else
52
+ chunks.flatten
53
+ end
54
+ when 'V' # list
55
+ data.slice!(0, 3 + data.unpack('an')[1]) if data[0] == 't'
56
+ data.slice!(0, 5) if data[0] == 'l'
57
+ refs << (list = [])
58
+ list << parse(data, refs) while data[0] != 'z'
59
+ data.slice!(0)
60
+ list
61
+ when 'M' # map
62
+ data.slice!(0, 3 + data.unpack('an')[1]) if data[0] == 't'
63
+ refs << (map = {})
64
+ map[parse(data, refs)] = parse(data, refs) while data[0] != 'z'
65
+ data.slice!(0)
66
+ map
67
+ when 'R' # ref
68
+ refs[data.slice!(0, 4).unpack('N')[0]]
69
+ else
70
+ raise HessianException.new, "Invalid type: '#{t}'"
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,130 @@
1
+ module Hessian2
2
+ module HessianWriter
3
+ CHUNK_SIZE = 32768
4
+
5
+ def write_call(method, args)
6
+ refs = {}
7
+ out = [ 'c', '0', '1', 'm', method.length ].pack('ahhan') << method
8
+ args.each { |arg| out << write(arg, refs) }
9
+ out << 'z'
10
+ end
11
+
12
+ private
13
+ def write(val, refs = {}, chunks = [], type = nil)
14
+ case val
15
+ when TypeWrapper
16
+ obj, hessian_type = val.object, val.hessian_type
17
+ case hessian_type
18
+ when 'L', 'Long', 'long' # declare as long
19
+ [ 'L', obj ].pack('aq>') # long
20
+ when 'X', 'x' # declare as xml
21
+ if obj.size > CHUNK_SIZE
22
+ chunk = obj.slice!(0, CHUNK_SIZE)
23
+ if chunk.ascii_only?
24
+ chunks << [ 's', CHUNK_SIZE ].pack('an') << chunk
25
+ else
26
+ chunks << [ 's', CHUNK_SIZE, chunk.unpack('U*') ].flatten.pack('anU*')
27
+ end
28
+ write(TypeWrapper.new('X', obj), refs, chunks)
29
+ else
30
+ if obj.bytesize == obj.size
31
+ chunks << [ 'X', obj.size ].pack('an') << obj
32
+ else
33
+ chunks << [ 'X', obj.size, obj.unpack('U*') ].flatten.pack('anU*')
34
+ end
35
+ chunks.join # xml
36
+ end
37
+ when 'B', 'b' # declare as binary
38
+ [ 'B', obj.size ].pack('an') << obj
39
+ if obj.size > CHUNK_SIZE
40
+ chunk = obj.slice!(0, CHUNK_SIZE)
41
+ chunks << [ 'b', CHUNK_SIZE ].pack('an') << chunk
42
+ write(TypeWrapper.new('B', obj), refs, chunks)
43
+ else
44
+ chunks << [ 'B', obj.size ].pack('an') << obj
45
+ chunks.join # binary
46
+ end
47
+ else # type for list, map
48
+ write(obj, refs, chunks, hessian_type)
49
+ end
50
+ when NilClass
51
+ 'N' # null
52
+ when TrueClass
53
+ 'T' # true
54
+ when FalseClass
55
+ 'F' # false
56
+ when Fixnum
57
+ [ 'I', val ].pack('al>') # int
58
+ when Bignum
59
+ [ 'L', val ].pack('aq>') # long
60
+ when Float
61
+ [ 'D', val ].pack('aG') # double
62
+ when Time
63
+ [ 'd', val.to_f * 1000 ].pack('aQ>') # date
64
+ when String
65
+ if val.size > CHUNK_SIZE
66
+ chunk = val.slice!(0, CHUNK_SIZE)
67
+ if chunk.ascii_only?
68
+ chunks << [ 's', CHUNK_SIZE ].pack('an') << chunk
69
+ else
70
+ # unpack-pack if chunk incompatible with ASCII-8BIT
71
+ chunks << [ 's', CHUNK_SIZE, chunk.unpack('U*') ].flatten.pack('anU*')
72
+ end
73
+ write(val, refs, chunks)
74
+ else
75
+ if val.bytesize == val.size
76
+ chunks << [ 'S', val.size ].pack('an') << val
77
+ else
78
+ chunks << [ 'S', val.size, val.unpack('U*') ].flatten.pack('anU*')
79
+ end
80
+ chunks.join # string
81
+ end
82
+ when Symbol
83
+ str = val.to_s
84
+ [ 'S', str.size ].pack('an') << str # string
85
+ when Array
86
+ id = refs[val.object_id]
87
+ return [ 'R', id ].pack('aN') if id
88
+
89
+ refs[val.object_id] = refs.size
90
+
91
+ str = 'V'
92
+ str << 't' << [ type.size, type ].pack('na*') if type
93
+ str << 'l' << [ val.size ].pack('N')
94
+ val.each{ |v| str << write(v, refs) }
95
+ str << 'z' # list
96
+ when Hash
97
+ id = refs[val.object_id]
98
+ return [ 'R', id ].pack('aN') if id
99
+
100
+ refs[val.object_id] = refs.size
101
+
102
+ str = 'M'
103
+ str << 't' << [ type.size, type ].pack('na*') if type
104
+ val.each do |k, v|
105
+ str << write(k, refs)
106
+ str << write(v, refs)
107
+ end
108
+ str << 'z' # map
109
+ else # covert val to hash
110
+ hash = {}.tap do |h|
111
+ val.instance_variables.each {|var| h[var.to_s.delete("@")] = val.instance_variable_get(var) }
112
+ end
113
+
114
+ unless type
115
+ # map ruby module to java package
116
+ arr = val.class.to_s.split('::')
117
+ if arr.size > 1
118
+ klass = arr.pop
119
+ type = arr.map{|m| m.downcase}.join('.') << ".#{klass}"
120
+ else
121
+ type = arr.first
122
+ end
123
+ end
124
+
125
+ write(hash, refs, chunks, type)
126
+ end
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,8 @@
1
+ module Hessian2
2
+ class TypeWrapper
3
+ attr_accessor :hessian_type, :object
4
+ def initialize(hessian_type, object)
5
+ @hessian_type, @object = hessian_type, object
6
+ end
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Hessian2
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/hessian2.rb CHANGED
@@ -1,231 +1,6 @@
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('U*').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
1
+ # http://hessian.caucho.com/doc/hessian-1.0-spec.xtp
231
2
 
3
+ require "hessian2/version"
4
+ require 'hessian2/hessian_client'
5
+ require 'hessian2/type_wrapper'
6
+ require 'hessian2/hessian_exception'
@@ -4,9 +4,8 @@ require 'hessian2'
4
4
  require 'test/unit'
5
5
 
6
6
  class HessianParserTest < Test::Unit::TestCase
7
- def parse res
8
- Hessian2::HessianClient::HessianParser.new.parse_response res
9
- end
7
+
8
+ include Hessian2::HessianParser
10
9
 
11
10
  def test_integer
12
11
  assert_equal 4711, parse("r\001\000I\000\000\022gz")
@@ -53,5 +52,5 @@ class HessianParserTest < Test::Unit::TestCase
53
52
  assert_equal map, parse([ "r\001\000Mt\000\000S\000\anumbersVt\000\a[double",
54
53
  "l\000\000\000\003D?\361\231\231\231\231\231\232D?\363333333D?",
55
54
  "\364\314\314\314\314\314\315zS\000\006sillenI\000\000\000 zz" ].join)
56
- end
55
+ end
57
56
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hessian2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-12 00:00:00.000000000 Z
12
+ date: 2012-12-08 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: hessian client. based on Christer Sandberg's hessian, fixed UTF-8 pack,
15
- allow declare long type, ruby1.9 tested.
14
+ description: implement hessian 1.0.2 specification. refactor Christer Sandberg's hessian,
15
+ ruby 1.9.3 required.
16
16
  email:
17
17
  - takafan@163.com
18
18
  executables: []
@@ -25,8 +25,12 @@ files:
25
25
  - Rakefile
26
26
  - hessian2.gemspec
27
27
  - lib/hessian2.rb
28
+ - lib/hessian2/hessian_client.rb
29
+ - lib/hessian2/hessian_exception.rb
30
+ - lib/hessian2/hessian_parser.rb
31
+ - lib/hessian2/hessian_writer.rb
32
+ - lib/hessian2/type_wrapper.rb
28
33
  - lib/hessian2/version.rb
29
- - test/servlet_invoker.rb
30
34
  - test/test_hessian_parser.rb
31
35
  homepage: http://github.com/takafan/hessian2
32
36
  licenses: []
@@ -1,17 +0,0 @@
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