rack-amf 0.0.4 → 1.0.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 +18 -9
- data/Rakefile +23 -38
- data/lib/rack/amf/environment.rb +5 -8
- data/lib/rack/amf/middleware/pass_through.rb +5 -4
- data/lib/rack/amf/middleware/service_manager.rb +11 -3
- data/lib/rack/amf/middleware.rb +2 -1
- data/lib/rack/amf/request.rb +3 -16
- data/lib/rack/amf/response.rb +8 -67
- data/lib/rack/amf.rb +41 -1
- data/spec/{rack/service_manager_spec.rb → service_manager_spec.rb} +1 -2
- data/spec/spec.opts +2 -1
- data/spec/spec_helper.rb +1 -24
- metadata +19 -84
- data/lib/amf/class_mapping.rb +0 -211
- data/lib/amf/common.rb +0 -28
- data/lib/amf/constants.rb +0 -47
- data/lib/amf/pure/deserializer.rb +0 -361
- data/lib/amf/pure/io_helpers.rb +0 -94
- data/lib/amf/pure/remoting.rb +0 -120
- data/lib/amf/pure/serializer.rb +0 -327
- data/lib/amf/pure.rb +0 -14
- data/lib/amf/values/array_collection.rb +0 -9
- data/lib/amf/values/messages.rb +0 -133
- data/lib/amf/values/typed_hash.rb +0 -13
- data/lib/amf/version.rb +0 -9
- data/lib/amf.rb +0 -17
- data/spec/amf/class_mapping_set_spec.rb +0 -34
- data/spec/amf/class_mapping_spec.rb +0 -120
- data/spec/amf/deserializer_spec.rb +0 -311
- data/spec/amf/remoting_spec.rb +0 -51
- data/spec/amf/serializer_spec.rb +0 -328
- data/spec/amf/values/array_collection_spec.rb +0 -6
- data/spec/amf/values/messages_spec.rb +0 -31
- data/spec/fixtures/objects/amf0-boolean.bin +0 -1
- data/spec/fixtures/objects/amf0-date.bin +0 -0
- data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
- data/spec/fixtures/objects/amf0-hash.bin +0 -0
- data/spec/fixtures/objects/amf0-null.bin +0 -1
- data/spec/fixtures/objects/amf0-number.bin +0 -0
- data/spec/fixtures/objects/amf0-object.bin +0 -0
- data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
- data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
- data/spec/fixtures/objects/amf0-string.bin +0 -0
- data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
- data/spec/fixtures/objects/amf0-undefined.bin +0 -1
- data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
- data/spec/fixtures/objects/amf3-0.bin +0 -0
- data/spec/fixtures/objects/amf3-arrayRef.bin +0 -1
- data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
- data/spec/fixtures/objects/amf3-date.bin +0 -0
- data/spec/fixtures/objects/amf3-datesRef.bin +0 -0
- data/spec/fixtures/objects/amf3-dynObject.bin +0 -2
- data/spec/fixtures/objects/amf3-emptyArray.bin +0 -1
- data/spec/fixtures/objects/amf3-emptyArrayRef.bin +0 -1
- data/spec/fixtures/objects/amf3-emptyStringRef.bin +0 -1
- data/spec/fixtures/objects/amf3-false.bin +0 -1
- data/spec/fixtures/objects/amf3-graphMember.bin +0 -0
- data/spec/fixtures/objects/amf3-hash.bin +0 -2
- data/spec/fixtures/objects/amf3-largeMax.bin +0 -0
- data/spec/fixtures/objects/amf3-largeMin.bin +0 -0
- data/spec/fixtures/objects/amf3-max.bin +0 -1
- data/spec/fixtures/objects/amf3-min.bin +0 -0
- data/spec/fixtures/objects/amf3-mixedArray.bin +0 -11
- data/spec/fixtures/objects/amf3-null.bin +0 -1
- data/spec/fixtures/objects/amf3-objRef.bin +0 -0
- data/spec/fixtures/objects/amf3-primArray.bin +0 -1
- data/spec/fixtures/objects/amf3-string.bin +0 -1
- data/spec/fixtures/objects/amf3-stringRef.bin +0 -0
- data/spec/fixtures/objects/amf3-symbol.bin +0 -1
- data/spec/fixtures/objects/amf3-true.bin +0 -1
- data/spec/fixtures/objects/amf3-typedObject.bin +0 -2
- data/spec/fixtures/request/acknowledge-response.bin +0 -0
- data/spec/fixtures/request/amf0-error-response.bin +0 -0
- data/spec/fixtures/request/commandMessage.bin +0 -0
- data/spec/fixtures/request/remotingMessage.bin +0 -0
- data/spec/fixtures/request/simple-response.bin +0 -0
- data/spec/rack/request_spec.rb +0 -6
- data/spec/rack/response_spec.rb +0 -47
data/lib/amf/class_mapping.rb
DELETED
@@ -1,211 +0,0 @@
|
|
1
|
-
require 'amf/values/typed_hash'
|
2
|
-
require 'amf/values/array_collection'
|
3
|
-
require 'amf/values/messages'
|
4
|
-
|
5
|
-
module AMF
|
6
|
-
# == Class Mapping
|
7
|
-
#
|
8
|
-
# Handles class name mapping between actionscript and ruby and assists in
|
9
|
-
# serializing and deserializing data between them. Simply map an AS class to a
|
10
|
-
# ruby class and when the object is (de)serialized it will end up as the
|
11
|
-
# appropriate class.
|
12
|
-
#
|
13
|
-
# Example:
|
14
|
-
#
|
15
|
-
# AMF::ClassMapper.define do |m|
|
16
|
-
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
17
|
-
# m.map :as => 'vo.User', :ruby => 'User'
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# == Object Population/Serialization
|
21
|
-
#
|
22
|
-
# In addition to handling class name mapping, it also provides helper methods
|
23
|
-
# for populating ruby objects from AMF and extracting properties from ruby objects
|
24
|
-
# for serialization. Support for hash-like objects and objects using
|
25
|
-
# <tt>attr_accessor</tt> for properties is currently built in, but custom classes
|
26
|
-
# may need custom support. As such, it is possible to create a custom populator
|
27
|
-
# or serializer.
|
28
|
-
#
|
29
|
-
# Populators are processed in insert order and must respond to the <tt>can_handle?</tt>
|
30
|
-
# and <tt>populate</tt> methods.
|
31
|
-
#
|
32
|
-
# Example:
|
33
|
-
#
|
34
|
-
# class CustomPopulator
|
35
|
-
# def can_handle? obj
|
36
|
-
# true
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# def populate obj, props, dynamic_props
|
40
|
-
# obj.merge! props
|
41
|
-
# obj.merge!(dynamic_props) if dynamic_props
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
# AMF::ClassMapper.object_populators << CustomPopulator.new
|
45
|
-
#
|
46
|
-
# Serializers are also processed in insert order and must respond to the
|
47
|
-
# <tt>can_handle?</tt> and <tt>serialize</tt> methods.
|
48
|
-
#
|
49
|
-
# Example:
|
50
|
-
#
|
51
|
-
# class CustomSerializer
|
52
|
-
# def can_handle? obj
|
53
|
-
# true
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# def serialize obj
|
57
|
-
# {}
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
# AMF::ClassMapper.object_serializers << CustomSerializer.new
|
61
|
-
class ClassMapping
|
62
|
-
# Container for all mapped classes
|
63
|
-
class MappingSet
|
64
|
-
def initialize #:nodoc:
|
65
|
-
@as_mappings = {}
|
66
|
-
@ruby_mappings = {}
|
67
|
-
|
68
|
-
# Map defaults
|
69
|
-
map :as => 'flex.messaging.messages.AbstractMessage', :ruby => 'AMF::Values::AbstractMessage'
|
70
|
-
map :as => 'flex.messaging.messages.RemotingMessage', :ruby => 'AMF::Values::RemotingMessage'
|
71
|
-
map :as => 'flex.messaging.messages.AsyncMessage', :ruby => 'AMF::Values::AsyncMessage'
|
72
|
-
map :as => 'flex.messaging.messages.CommandMessage', :ruby => 'AMF::Values::CommandMessage'
|
73
|
-
map :as => 'flex.messaging.messages.AcknowledgeMessage', :ruby => 'AMF::Values::AcknowledgeMessage'
|
74
|
-
map :as => 'flex.messaging.messages.ErrorMessage', :ruby => 'AMF::Values::ErrorMessage'
|
75
|
-
map :as => 'flex.messaging.io.ArrayCollection', :ruby => 'AMF::Values::ArrayCollection'
|
76
|
-
end
|
77
|
-
|
78
|
-
# Map a given AS class to a ruby class.
|
79
|
-
#
|
80
|
-
# Use fully qualified names for both.
|
81
|
-
#
|
82
|
-
# Example:
|
83
|
-
#
|
84
|
-
# m.map :as 'com.example.Date', :ruby => 'Example::Date'
|
85
|
-
def map params
|
86
|
-
[:as, :ruby].each {|k| params[k] = params[k].to_s} # Convert params to strings
|
87
|
-
@as_mappings[params[:as]] = params[:ruby]
|
88
|
-
@ruby_mappings[params[:ruby]] = params[:as]
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns the AS class name for the given ruby class name, returing nil if
|
92
|
-
# not found
|
93
|
-
def get_as_class_name class_name #:nodoc:
|
94
|
-
@ruby_mappings[class_name.to_s]
|
95
|
-
end
|
96
|
-
|
97
|
-
# Returns the ruby class name for the given AS class name, returing nil if
|
98
|
-
# not found
|
99
|
-
def get_ruby_class_name class_name #:nodoc:
|
100
|
-
@as_mappings[class_name.to_s]
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# Array of custom object populators.
|
105
|
-
attr_reader :object_populators
|
106
|
-
|
107
|
-
# Array of custom object serializers.
|
108
|
-
attr_reader :object_serializers
|
109
|
-
|
110
|
-
def initialize #:nodoc:
|
111
|
-
@object_populators = []
|
112
|
-
@object_serializers = []
|
113
|
-
end
|
114
|
-
|
115
|
-
# Define class mappings in the block. Block is passed a MappingSet object as
|
116
|
-
# the first parameter.
|
117
|
-
#
|
118
|
-
# Example:
|
119
|
-
#
|
120
|
-
# AMF::ClassMapper.define do |m|
|
121
|
-
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
122
|
-
# end
|
123
|
-
def define #:yields: mapping_set
|
124
|
-
yield mappings
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the AS class name for the given ruby object. Will also take a string
|
128
|
-
# containing the ruby class name
|
129
|
-
def get_as_class_name obj
|
130
|
-
# Get class name
|
131
|
-
if obj.is_a?(String)
|
132
|
-
ruby_class_name = obj
|
133
|
-
elsif obj.is_a?(Values::TypedHash)
|
134
|
-
ruby_class_name = obj.type
|
135
|
-
else
|
136
|
-
ruby_class_name = obj.class.name
|
137
|
-
end
|
138
|
-
|
139
|
-
# Get mapped AS class name
|
140
|
-
mappings.get_as_class_name ruby_class_name
|
141
|
-
end
|
142
|
-
|
143
|
-
# Instantiates a ruby object using the mapping configuration based on the
|
144
|
-
# source AS class name. If there is no mapping defined, it returns a hash.
|
145
|
-
def get_ruby_obj as_class_name
|
146
|
-
ruby_class_name = mappings.get_ruby_class_name as_class_name
|
147
|
-
if ruby_class_name.nil?
|
148
|
-
# Populate a simple hash, since no mapping
|
149
|
-
return Values::TypedHash.new(as_class_name)
|
150
|
-
else
|
151
|
-
ruby_class = ruby_class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
|
152
|
-
return ruby_class.new
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Populates the ruby object using the given properties
|
157
|
-
def populate_ruby_obj obj, props, dynamic_props=nil
|
158
|
-
# Process custom populators
|
159
|
-
@object_populators.each do |p|
|
160
|
-
next unless p.can_handle?(obj)
|
161
|
-
p.populate obj, props, dynamic_props
|
162
|
-
return obj
|
163
|
-
end
|
164
|
-
|
165
|
-
# Fallback populator
|
166
|
-
props.merge! dynamic_props if dynamic_props
|
167
|
-
hash_like = obj.respond_to?("[]=")
|
168
|
-
props.each do |key, value|
|
169
|
-
if obj.respond_to?("#{key}=")
|
170
|
-
obj.send("#{key}=", value)
|
171
|
-
elsif hash_like
|
172
|
-
obj[key.to_sym] = value
|
173
|
-
end
|
174
|
-
end
|
175
|
-
obj
|
176
|
-
end
|
177
|
-
|
178
|
-
# Extracts all exportable properties from the given ruby object and returns
|
179
|
-
# them in a hash
|
180
|
-
def props_for_serialization ruby_obj
|
181
|
-
# Proccess custom serializers
|
182
|
-
@object_serializers.each do |s|
|
183
|
-
next unless s.can_handle?(ruby_obj)
|
184
|
-
return s.serialize(ruby_obj)
|
185
|
-
end
|
186
|
-
|
187
|
-
# Handle hashes
|
188
|
-
if ruby_obj.is_a?(Hash)
|
189
|
-
# Stringify keys to make it easier later on and allow sorting
|
190
|
-
h = {}
|
191
|
-
ruby_obj.each {|k,v| h[k.to_s] = v}
|
192
|
-
return h
|
193
|
-
end
|
194
|
-
|
195
|
-
# Fallback serializer
|
196
|
-
props = {}
|
197
|
-
@ignored_props ||= Object.new.public_methods
|
198
|
-
(ruby_obj.public_methods - @ignored_props).each do |method_name|
|
199
|
-
# Add them to the prop hash if they take no arguments
|
200
|
-
method_def = ruby_obj.method(method_name)
|
201
|
-
props[method_name] = ruby_obj.send(method_name) if method_def.arity == 0
|
202
|
-
end
|
203
|
-
props
|
204
|
-
end
|
205
|
-
|
206
|
-
private
|
207
|
-
def mappings
|
208
|
-
@mappings ||= MappingSet.new
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
data/lib/amf/common.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module AMF
|
2
|
-
class << self
|
3
|
-
# Deserialize the AMF string _source_ into a Ruby data structure and return it.
|
4
|
-
def deserialize source, amf_version = 0
|
5
|
-
if amf_version == 0
|
6
|
-
AMF::Deserializer.new.deserialize(source)
|
7
|
-
elsif amf_version == 3
|
8
|
-
AMF::AMF3Deserializer.new.deserialize(source)
|
9
|
-
else
|
10
|
-
raise AMFError, "unsupported version #{amf_version}"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
# Serialize the given Ruby data structure _obj_ into an AMF stream
|
15
|
-
def serialize obj, amf_version = 0
|
16
|
-
if amf_version == 0
|
17
|
-
AMF::Serializer.new.serialize(obj)
|
18
|
-
elsif amf_version == 3
|
19
|
-
AMF::AMF3Serializer.new.serialize(obj)
|
20
|
-
else
|
21
|
-
raise AMFError, "unsupported version #{amf_version}"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# The base exception for AMF errors.
|
27
|
-
class AMFError < StandardError; end
|
28
|
-
end
|
data/lib/amf/constants.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
module AMF
|
2
|
-
# AMF0 Type Markers
|
3
|
-
AMF0_NUMBER_MARKER = 0x00 #"\000"
|
4
|
-
AMF0_BOOLEAN_MARKER = 0x01 #"\001"
|
5
|
-
AMF0_STRING_MARKER = 0x02 #"\002"
|
6
|
-
AMF0_OBJECT_MARKER = 0x03 #"\003"
|
7
|
-
AMF0_MOVIE_CLIP_MARKER = 0x04 #"\004" # Unused
|
8
|
-
AMF0_NULL_MARKER = 0x05 #"\005"
|
9
|
-
AMF0_UNDEFINED_MARKER = 0x06 #"\006"
|
10
|
-
AMF0_REFERENCE_MARKER = 0x07 #"\a"
|
11
|
-
AMF0_HASH_MARKER = 0x08 #"\b"
|
12
|
-
AMF0_OBJECT_END_MARKER = 0x09 #"\t"
|
13
|
-
AMF0_STRICT_ARRAY_MARKER = 0x0A #"\n"
|
14
|
-
AMF0_DATE_MARKER = 0x0B #"\v"
|
15
|
-
AMF0_LONG_STRING_MARKER = 0x0C #"\f"
|
16
|
-
AMF0_UNSUPPORTED_MARKER = 0x0D #"\r"
|
17
|
-
AMF0_RECORDSET_MARKER = 0x0E #"\016" # Unused
|
18
|
-
AMF0_XML_MARKER = 0x0F #"\017"
|
19
|
-
AMF0_TYPED_OBJECT_MARKER = 0x10 #"\020"
|
20
|
-
AMF0_AMF3_MARKER = 0x11 #"\021"
|
21
|
-
|
22
|
-
# AMF3 Type Markers
|
23
|
-
AMF3_UNDEFINED_MARKER = 0x00 #"\000"
|
24
|
-
AMF3_NULL_MARKER = 0x01 #"\001"
|
25
|
-
AMF3_FALSE_MARKER = 0x02 #"\002"
|
26
|
-
AMF3_TRUE_MARKER = 0x03 #"\003"
|
27
|
-
AMF3_INTEGER_MARKER = 0x04 #"\004"
|
28
|
-
AMF3_DOUBLE_MARKER = 0x05 #"\005"
|
29
|
-
AMF3_STRING_MARKER = 0x06 #"\006"
|
30
|
-
AMF3_XML_DOC_MARKER = 0x07 #"\a"
|
31
|
-
AMF3_DATE_MARKER = 0x08 #"\b"
|
32
|
-
AMF3_ARRAY_MARKER = 0x09 #"\t"
|
33
|
-
AMF3_OBJECT_MARKER = 0x0A #"\n"
|
34
|
-
AMF3_XML_MARKER = 0x0B #"\v"
|
35
|
-
AMF3_BYTE_ARRAY_MARKER = 0x0C #"\f"
|
36
|
-
|
37
|
-
# Other AMF3 Markers
|
38
|
-
AMF3_EMPTY_STRING = 0x01
|
39
|
-
AMF3_ANONYMOUS_OBJECT = 0x01
|
40
|
-
AMF3_DYNAMIC_OBJECT = 0x0B
|
41
|
-
AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
|
42
|
-
AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
|
43
|
-
|
44
|
-
# Other Constants
|
45
|
-
MAX_INTEGER = 268435455
|
46
|
-
MIN_INTEGER = -268435456
|
47
|
-
end
|
@@ -1,361 +0,0 @@
|
|
1
|
-
require 'amf/pure/io_helpers'
|
2
|
-
|
3
|
-
module AMF
|
4
|
-
module Pure
|
5
|
-
# Pure ruby deserializer
|
6
|
-
#--
|
7
|
-
# AMF0 deserializer, it switches over to AMF3 when it sees the switch flag
|
8
|
-
class Deserializer
|
9
|
-
def initialize
|
10
|
-
@ref_cache = []
|
11
|
-
end
|
12
|
-
|
13
|
-
def deserialize(source, type=nil)
|
14
|
-
source = StringIO.new(source) unless StringIO === source
|
15
|
-
type = read_int8 source unless type
|
16
|
-
case type
|
17
|
-
when AMF0_NUMBER_MARKER
|
18
|
-
read_number source
|
19
|
-
when AMF0_BOOLEAN_MARKER
|
20
|
-
read_boolean source
|
21
|
-
when AMF0_STRING_MARKER
|
22
|
-
read_string source
|
23
|
-
when AMF0_OBJECT_MARKER
|
24
|
-
read_object source
|
25
|
-
when AMF0_NULL_MARKER
|
26
|
-
nil
|
27
|
-
when AMF0_UNDEFINED_MARKER
|
28
|
-
nil
|
29
|
-
when AMF0_REFERENCE_MARKER
|
30
|
-
read_reference source
|
31
|
-
when AMF0_HASH_MARKER
|
32
|
-
read_hash source
|
33
|
-
when AMF0_STRICT_ARRAY_MARKER
|
34
|
-
read_array source
|
35
|
-
when AMF0_DATE_MARKER
|
36
|
-
read_date source
|
37
|
-
when AMF0_LONG_STRING_MARKER
|
38
|
-
read_string source, true
|
39
|
-
when AMF0_UNSUPPORTED_MARKER
|
40
|
-
nil
|
41
|
-
when AMF0_XML_MARKER
|
42
|
-
#read_xml source
|
43
|
-
when AMF0_TYPED_OBJECT_MARKER
|
44
|
-
read_typed_object source
|
45
|
-
when AMF0_AMF3_MARKER
|
46
|
-
AMF3Deserializer.new.deserialize(source)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
include AMF::Pure::ReadIOHelpers
|
52
|
-
|
53
|
-
def read_number source
|
54
|
-
res = read_double source
|
55
|
-
res.is_a?(Float)&&res.nan? ? nil : res # check for NaN and convert them to nil
|
56
|
-
end
|
57
|
-
|
58
|
-
def read_boolean source
|
59
|
-
read_int8(source) != 0
|
60
|
-
end
|
61
|
-
|
62
|
-
def read_string source, long=false
|
63
|
-
len = long ? read_word32_network(source) : read_word16_network(source)
|
64
|
-
source.read(len)
|
65
|
-
end
|
66
|
-
|
67
|
-
def read_object source, add_to_ref_cache=true
|
68
|
-
obj = {}
|
69
|
-
@ref_cache << obj if add_to_ref_cache
|
70
|
-
while true
|
71
|
-
key = read_string source
|
72
|
-
type = read_int8 source
|
73
|
-
break if type == AMF0_OBJECT_END_MARKER
|
74
|
-
obj[key.to_sym] = deserialize(source, type)
|
75
|
-
end
|
76
|
-
obj
|
77
|
-
end
|
78
|
-
|
79
|
-
def read_reference source
|
80
|
-
index = read_word16_network(source)
|
81
|
-
@ref_cache[index]
|
82
|
-
end
|
83
|
-
|
84
|
-
def read_hash source
|
85
|
-
len = read_word32_network(source) # Read and ignore length
|
86
|
-
|
87
|
-
# Read first pair
|
88
|
-
key = read_string source
|
89
|
-
type = read_int8 source
|
90
|
-
return [] if type == AMF0_OBJECT_END_MARKER
|
91
|
-
|
92
|
-
# We need to figure out whether this is a real hash, or whether some stupid serializer gave up
|
93
|
-
if key.to_i.to_s == key
|
94
|
-
# Array
|
95
|
-
obj = []
|
96
|
-
@ref_cache << obj
|
97
|
-
|
98
|
-
obj[key.to_i] = deserialize(source, type)
|
99
|
-
while true
|
100
|
-
key = read_string source
|
101
|
-
type = read_int8 source
|
102
|
-
break if type == AMF0_OBJECT_END_MARKER
|
103
|
-
obj[key.to_i] = deserialize(source, type)
|
104
|
-
end
|
105
|
-
else
|
106
|
-
# Hash
|
107
|
-
obj = {}
|
108
|
-
@ref_cache << obj
|
109
|
-
|
110
|
-
obj[key.to_sym] = deserialize(source, type)
|
111
|
-
while true
|
112
|
-
key = read_string source
|
113
|
-
type = read_int8 source
|
114
|
-
break if type == AMF0_OBJECT_END_MARKER
|
115
|
-
obj[key.to_sym] = deserialize(source, type)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
obj
|
119
|
-
end
|
120
|
-
|
121
|
-
def read_array source
|
122
|
-
len = read_word32_network(source)
|
123
|
-
array = []
|
124
|
-
@ref_cache << array
|
125
|
-
|
126
|
-
0.upto(len - 1) do
|
127
|
-
array << deserialize(source)
|
128
|
-
end
|
129
|
-
array
|
130
|
-
end
|
131
|
-
|
132
|
-
def read_date source
|
133
|
-
seconds = read_double(source).to_f/1000
|
134
|
-
time = Time.at(seconds)
|
135
|
-
tz = read_word16_network(source) # Unused
|
136
|
-
time
|
137
|
-
end
|
138
|
-
|
139
|
-
def read_typed_object source
|
140
|
-
# Create object to add to ref cache
|
141
|
-
class_name = read_string source
|
142
|
-
obj = ClassMapper.get_ruby_obj class_name
|
143
|
-
@ref_cache << obj
|
144
|
-
|
145
|
-
# Read object props
|
146
|
-
props = read_object source, false
|
147
|
-
|
148
|
-
# Populate object
|
149
|
-
ClassMapper.populate_ruby_obj obj, props, {}
|
150
|
-
return obj
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# AMF3 implementation of deserializer, loaded automatically by the AMF0
|
155
|
-
# deserializer when needed
|
156
|
-
class AMF3Deserializer
|
157
|
-
def initialize
|
158
|
-
@string_cache = []
|
159
|
-
@object_cache = []
|
160
|
-
@trait_cache = []
|
161
|
-
end
|
162
|
-
|
163
|
-
def deserialize(source, type=nil)
|
164
|
-
source = StringIO.new(source) unless StringIO === source
|
165
|
-
type = read_int8 source unless type
|
166
|
-
case type
|
167
|
-
when AMF3_UNDEFINED_MARKER
|
168
|
-
nil
|
169
|
-
when AMF3_NULL_MARKER
|
170
|
-
nil
|
171
|
-
when AMF3_FALSE_MARKER
|
172
|
-
false
|
173
|
-
when AMF3_TRUE_MARKER
|
174
|
-
true
|
175
|
-
when AMF3_INTEGER_MARKER
|
176
|
-
read_integer source
|
177
|
-
when AMF3_DOUBLE_MARKER
|
178
|
-
read_number source
|
179
|
-
when AMF3_STRING_MARKER
|
180
|
-
read_string source
|
181
|
-
when AMF3_XML_DOC_MARKER
|
182
|
-
#read_xml_string
|
183
|
-
when AMF3_DATE_MARKER
|
184
|
-
read_date source
|
185
|
-
when AMF3_ARRAY_MARKER
|
186
|
-
read_array source
|
187
|
-
when AMF3_OBJECT_MARKER
|
188
|
-
read_object source
|
189
|
-
when AMF3_XML_MARKER
|
190
|
-
#read_amf3_xml
|
191
|
-
when AMF3_BYTE_ARRAY_MARKER
|
192
|
-
#read_amf3_byte_array
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
private
|
197
|
-
include AMF::Pure::ReadIOHelpers
|
198
|
-
|
199
|
-
def read_integer source
|
200
|
-
n = 0
|
201
|
-
b = read_word8(source) || 0
|
202
|
-
result = 0
|
203
|
-
|
204
|
-
while ((b & 0x80) != 0 && n < 3)
|
205
|
-
result = result << 7
|
206
|
-
result = result | (b & 0x7f)
|
207
|
-
b = read_word8(source) || 0
|
208
|
-
n = n + 1
|
209
|
-
end
|
210
|
-
|
211
|
-
if (n < 3)
|
212
|
-
result = result << 7
|
213
|
-
result = result | b
|
214
|
-
else
|
215
|
-
#Use all 8 bits from the 4th byte
|
216
|
-
result = result << 8
|
217
|
-
result = result | b
|
218
|
-
|
219
|
-
#Check if the integer should be negative
|
220
|
-
if (result > MAX_INTEGER)
|
221
|
-
result -= (1 << 29)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
result
|
225
|
-
end
|
226
|
-
|
227
|
-
def read_number source
|
228
|
-
res = read_double source
|
229
|
-
res.is_a?(Float)&&res.nan? ? nil : res # check for NaN and convert them to nil
|
230
|
-
end
|
231
|
-
|
232
|
-
def read_string source
|
233
|
-
type = read_integer source
|
234
|
-
isReference = (type & 0x01) == 0
|
235
|
-
|
236
|
-
if isReference
|
237
|
-
reference = type >> 1
|
238
|
-
return @string_cache[reference]
|
239
|
-
else
|
240
|
-
length = type >> 1
|
241
|
-
#HACK needed for ['',''] array of empty strings
|
242
|
-
#It may be better to take one more parameter that
|
243
|
-
#would specify whether or not they expect us to return
|
244
|
-
#a string
|
245
|
-
str = "" #if stringRequest
|
246
|
-
if length > 0
|
247
|
-
str = source.read(length)
|
248
|
-
@string_cache << str
|
249
|
-
end
|
250
|
-
return str
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
def read_array source
|
255
|
-
type = read_integer source
|
256
|
-
isReference = (type & 0x01) == 0
|
257
|
-
|
258
|
-
if isReference
|
259
|
-
reference = type >> 1
|
260
|
-
return @object_cache[reference]
|
261
|
-
else
|
262
|
-
length = type >> 1
|
263
|
-
propertyName = read_string source
|
264
|
-
if propertyName != ""
|
265
|
-
array = {}
|
266
|
-
@object_cache << array
|
267
|
-
begin
|
268
|
-
while(propertyName.length)
|
269
|
-
value = deserialize(source)
|
270
|
-
array[propertyName] = value
|
271
|
-
propertyName = read_string source
|
272
|
-
end
|
273
|
-
rescue Exception => e #end of object exception, because propertyName.length will be non existent
|
274
|
-
end
|
275
|
-
0.upto(length - 1) do |i|
|
276
|
-
array["" + i.to_s] = deserialize(source)
|
277
|
-
end
|
278
|
-
else
|
279
|
-
array = []
|
280
|
-
@object_cache << array
|
281
|
-
0.upto(length - 1) do
|
282
|
-
array << deserialize(source)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
array
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
def read_object source
|
290
|
-
type = read_integer source
|
291
|
-
isReference = (type & 0x01) == 0
|
292
|
-
|
293
|
-
if isReference
|
294
|
-
reference = type >> 1
|
295
|
-
return @object_cache[reference]
|
296
|
-
else
|
297
|
-
class_type = type >> 1
|
298
|
-
class_is_reference = (class_type & 0x01) == 0
|
299
|
-
|
300
|
-
if class_is_reference
|
301
|
-
reference = class_type >> 1
|
302
|
-
class_definition = @trait_cache[reference]
|
303
|
-
else
|
304
|
-
class_name = read_string source
|
305
|
-
externalizable = (class_type & 0x02) != 0
|
306
|
-
dynamic = (class_type & 0x04) != 0
|
307
|
-
attribute_count = class_type >> 3
|
308
|
-
|
309
|
-
class_attributes = []
|
310
|
-
attribute_count.times{class_attributes << read_string(source)} # Read class members
|
311
|
-
|
312
|
-
class_definition = {"class_name" => class_name,
|
313
|
-
"members" => class_attributes,
|
314
|
-
"externalizable" => externalizable,
|
315
|
-
"dynamic" => dynamic}
|
316
|
-
@trait_cache << class_definition
|
317
|
-
end
|
318
|
-
|
319
|
-
obj = ClassMapper.get_ruby_obj class_definition["class_name"]
|
320
|
-
@object_cache << obj
|
321
|
-
|
322
|
-
if class_definition['externalizable']
|
323
|
-
obj.externalized_data = deserialize(source)
|
324
|
-
else
|
325
|
-
props = {}
|
326
|
-
class_definition['members'].each do |key|
|
327
|
-
value = deserialize(source)
|
328
|
-
props[key.to_sym] = value
|
329
|
-
end
|
330
|
-
|
331
|
-
dynamic_props = nil
|
332
|
-
if class_definition['dynamic']
|
333
|
-
dynamic_props = {}
|
334
|
-
while (key = read_string source) && key.length != 0 do # read next key
|
335
|
-
value = deserialize(source)
|
336
|
-
dynamic_props[key.to_sym] = value
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
ClassMapper.populate_ruby_obj obj, props, dynamic_props
|
341
|
-
end
|
342
|
-
obj
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
def read_date source
|
347
|
-
type = read_integer source
|
348
|
-
isReference = (type & 0x01) == 0
|
349
|
-
if isReference
|
350
|
-
reference = type >> 1
|
351
|
-
return @object_cache[reference]
|
352
|
-
else
|
353
|
-
seconds = read_double(source).to_f/1000
|
354
|
-
time = Time.at(seconds)
|
355
|
-
@object_cache << time
|
356
|
-
time
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|