hessian2 1.0.3 → 1.1.0

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