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 +5 -6
- data/hessian2.gemspec +1 -1
- data/lib/hessian2/hessian_client.rb +38 -0
- data/lib/hessian2/hessian_exception.rb +3 -0
- data/lib/hessian2/hessian_parser.rb +75 -0
- data/lib/hessian2/hessian_writer.rb +130 -0
- data/lib/hessian2/type_wrapper.rb +8 -0
- data/lib/hessian2/version.rb +1 -1
- data/lib/hessian2.rb +5 -230
- data/test/test_hessian_parser.rb +3 -4
- metadata +9 -5
- data/test/servlet_invoker.rb +0 -17
    
        data/README.rdoc
    CHANGED
    
    | @@ -1,8 +1,6 @@ | |
| 1 1 | 
             
            = hessian2
         | 
| 2 2 |  | 
| 3 | 
            -
            hessian | 
| 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:  | 
| 18 | 
            -
               | 
| 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  | 
| 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,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
         | 
    
        data/lib/hessian2/version.rb
    CHANGED
    
    
    
        data/lib/hessian2.rb
    CHANGED
    
    | @@ -1,231 +1,6 @@ | |
| 1 | 
            -
             | 
| 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'
         | 
    
        data/test/test_hessian_parser.rb
    CHANGED
    
    | @@ -4,9 +4,8 @@ require 'hessian2' | |
| 4 4 | 
             
            require 'test/unit'
         | 
| 5 5 |  | 
| 6 6 | 
             
            class HessianParserTest < Test::Unit::TestCase
         | 
| 7 | 
            -
               | 
| 8 | 
            -
             | 
| 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 | 
            -
             | 
| 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 | 
| 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- | 
| 12 | 
            +
            date: 2012-12-08 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies: []
         | 
| 14 | 
            -
            description: hessian  | 
| 15 | 
            -
               | 
| 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: []
         | 
    
        data/test/servlet_invoker.rb
    DELETED
    
    | @@ -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
         |