class RocketAMF::Pure::Deserializer

Pure ruby deserializer for AMF3 requests

Attributes

source[R]

Properties

Public Class Methods

new(class_mapper) click to toggle source

Pass in the class mapper instance to use when deserializing. This enables better caching behavior in the class mapper and allows one to change mappings between deserialization attempts.

   # File lib/rocketamf/pure/deserializer.rb
25 def initialize(class_mapper)
26   @class_mapper = class_mapper
27 end

Public Instance Methods

deserialize(source) click to toggle source

Deserialize the source using AMF3. Source should either be a string or StringIO object. If you pass a StringIO object, it will have its position updated to the end of the deserialized data. raise AMFError if error appeared in deserialize, source is nil return hash {requests: [], incomplete_request: String}

   # File lib/rocketamf/pure/deserializer.rb
36 def deserialize(source)
37   raise AMFError, 'no source to deserialize' if source.nil?
38 
39   @source = source.is_a?(StringIO) ? source : StringIO.new(source)
40 
41   requests = []
42 
43   incomplete_request = nil
44 
45   until @source.eof?
46     begin
47       @string_cache = []
48       @object_cache = []
49       @trait_cache  = []
50 
51       @position_request_read = @source.pos
52 
53       requests << amf3_deserialize
54     rescue AMFErrorIncomplete => e
55       @source.pos = @position_request_read
56 
57       incomplete_request = @source.read
58 
59       break
60     end
61   end
62 
63   {
64       requests:           requests,
65       incomplete_request: incomplete_request
66   }
67 end
read_object() click to toggle source

Reads an object from the deserializer stream and returns it.

   # File lib/rocketamf/pure/deserializer.rb
71 def read_object
72   amf3_deserialize
73 end

Private Instance Methods

amf3_deserialize() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
 76 def amf3_deserialize
 77   type = read_int8(@source)
 78   case type
 79     when AMF3_UNDEFINED_MARKER
 80       nil
 81     when AMF3_NULL_MARKER
 82       nil
 83     when AMF3_FALSE_MARKER
 84       false
 85     when AMF3_TRUE_MARKER
 86       true
 87     when AMF3_INTEGER_MARKER
 88       amf3_read_integer
 89     when AMF3_DOUBLE_MARKER
 90       amf3_read_number
 91     when AMF3_STRING_MARKER
 92       amf3_read_string
 93     when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER
 94       amf3_read_xml
 95     when AMF3_DATE_MARKER
 96       amf3_read_date
 97     when AMF3_ARRAY_MARKER
 98       amf3_read_array
 99     when AMF3_OBJECT_MARKER
100       amf3_read_object
101     when AMF3_BYTE_ARRAY_MARKER
102       amf3_read_byte_array
103     when AMF3_VECTOR_INT_MARKER, AMF3_VECTOR_UINT_MARKER, AMF3_VECTOR_DOUBLE_MARKER, AMF3_VECTOR_OBJECT_MARKER
104       amf3_read_vector(type)
105     when AMF3_DICT_MARKER
106       amf3_read_dict
107     else
108       raise AMFError, "Invalid type: #{type}"
109   end
110 end
amf3_read_array() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
256 def amf3_read_array
257   result = nil
258 
259   type = amf3_read_integer
260 
261   result = get_as_reference_object(type)
262 
263   if result.nil?
264     length        = type >> 1
265     property_name = amf3_read_string
266     result        = property_name.length > 0 ? {} : []
267     @object_cache << result
268 
269     while property_name.length > 0
270       value                 = amf3_deserialize
271       result[property_name] = value
272       property_name         = amf3_read_string
273     end
274 
275     0.upto(length - 1) { |i| result[i] = amf3_deserialize }
276   end
277 
278   result
279 end
amf3_read_byte_array() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
234 def amf3_read_byte_array
235   result = nil
236 
237   type = amf3_read_integer
238 
239   result = get_as_reference_object(type)
240 
241   if result.nil?
242     length = type >> 1
243 
244     if length > (@source.size - @source.pos)
245       raise AMFErrorIncomplete.new
246     end
247 
248     result = StringIO.new(@source.read(length))
249     @object_cache << result
250   end
251 
252   result
253 end
amf3_read_date() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
353 def amf3_read_date
354   result = nil
355 
356   type = amf3_read_integer
357 
358   result = get_as_reference_object(type)
359 
360   if result.nil?
361     seconds = read_double(@source).to_f/1000
362     result  = Time.at(seconds)
363     @object_cache << result
364   end
365 
366   result
367 end
amf3_read_dict() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
370 def amf3_read_dict
371   result = nil
372 
373   type = amf3_read_integer
374 
375   result = get_as_reference_object(type)
376 
377   if result.nil?
378     result = {}
379     @object_cache << result
380     length    = type >> 1
381     weak_keys = read_int8(@source) # Ignore: Not supported in ruby
382 
383     0.upto(length - 1) do |i|
384       result[amf3_deserialize] = amf3_deserialize
385     end
386 
387   end
388 
389   result
390 end
amf3_read_integer() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
137 def amf3_read_integer
138   result = 0
139 
140   n = 0
141   b = read_word8(@source) || 0
142 
143   while (b & 0x80) != 0 && n < 3
144     result = result << 7
145     result = result | (b & 0x7f)
146     b      = read_word8(@source) || 0
147     n      = n + 1
148   end
149 
150   if n < 3
151     result = result << 7
152     result = result | b
153   else
154     #Use all 8 bits from the 4th byte
155     result = result << 8
156     result = result | b
157 
158     #Check if the integer should be negative
159     if result > MAX_INTEGER
160       result -= (1 << 29)
161     end
162   end
163 
164   result
165 end
amf3_read_number() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
168 def amf3_read_number
169   result = read_double(@source)
170 
171   #check for NaN and convert them to nil
172   if result.is_a?(Float) && result.nan?
173     result = nil
174   end
175 
176   result
177 end
amf3_read_object() click to toggle source

externalizable - an instance of a Class that implements flash.utils.IExternalizable and completely controls the serialization of its members (no property names are included in the trait information) dynamic - c an instance of a Class definition with the dynamic trait declared; public variable members can be added and removed from instances dynamically at runtime

    # File lib/rocketamf/pure/deserializer.rb
284 def amf3_read_object
285   result = nil
286 
287   type = amf3_read_integer
288 
289   result = get_as_reference_object(type)
290 
291   if result.nil?
292     class_type         = type >> 1
293     class_is_reference = (class_type & 0x01) == 0
294 
295     if class_is_reference
296       reference = class_type >> 1
297       traits    = @trait_cache[reference]
298     else
299       externalizable  = (class_type & 0x02) != 0
300       dynamic         = (class_type & 0x04) != 0
301       attribute_count = class_type >> 3
302       class_name      = amf3_read_string
303 
304       class_attributes = []
305       attribute_count.times { class_attributes << amf3_read_string } # Read class members
306 
307       traits =
308           {
309               class_name:     class_name,
310               members:        class_attributes,
311               externalizable: externalizable,
312               dynamic:        dynamic
313           }
314       @trait_cache << traits
315     end
316 
317     # Optimization for deserializing ArrayCollection
318     if traits[:class_name] == 'flex.messaging.io.ArrayCollection'
319       result = amf3_deserialize # Adds ArrayCollection array to object cache
320       @object_cache << result # Add again for ArrayCollection source array
321       return result
322     end
323 
324     result = @class_mapper.get_ruby_obj(traits[:class_name])
325     @object_cache << result
326 
327     if traits[:externalizable]
328       result.read_external(self)
329     else
330       properties = {}
331 
332       traits[:members].each do |key|
333         value           = amf3_deserialize
334         properties[key] = value
335       end
336 
337       if traits[:dynamic]
338         while (key = amf3_read_string) && key.length != 0 do # read next key
339           value           = amf3_deserialize
340           properties[key] = value
341         end
342       end
343 
344       @class_mapper.populate_ruby_obj(result, properties)
345     end
346 
347   end
348 
349   result
350 end
amf3_read_string() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
180 def amf3_read_string
181   result = nil
182 
183   type = amf3_read_integer
184 
185   result = get_as_reference_string(type)
186 
187   if result.nil?
188     length = type >> 1
189     result = ''
190 
191     if length > 0
192 
193       if length > (@source.size - @source.pos)
194         raise AMFErrorIncomplete.new
195       end
196 
197       result = @source.read(length)
198       result.force_encoding('UTF-8') if result.respond_to?(:force_encoding)
199       @string_cache << result
200     end
201   end
202 
203   result
204 end
amf3_read_vector(vector_type) click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
393 def amf3_read_vector(vector_type)
394   result = nil
395 
396   type = amf3_read_integer
397 
398   result = get_as_reference_object(type)
399 
400   if result.nil?
401 
402     result = []
403     @object_cache << result
404 
405     length       = type >> 1
406     fixed_vector = read_int8(@source) # Ignore
407 
408     case vector_type
409       when AMF3_VECTOR_INT_MARKER
410         0.upto(length - 1) do |i|
411           int = read_word32_network(@source)
412           int = int - 2**32 if int > MAX_INTEGER
413           result << int
414         end
415       when AMF3_VECTOR_UINT_MARKER
416         0.upto(length - 1) do |i|
417           result << read_word32_network(@source)
418         end
419       when AMF3_VECTOR_DOUBLE_MARKER
420         0.upto(length - 1) do |i|
421           result << amf3_read_number
422         end
423       when AMF3_VECTOR_OBJECT_MARKER
424         vector_class = amf3_read_string # Ignore
425         0.upto(length - 1) do |i|
426           result << amf3_deserialize
427         end
428       else
429         #do nothing
430     end
431   end #if
432 
433   result
434 end
amf3_read_xml() click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
207 def amf3_read_xml
208   result = nil
209 
210   type = amf3_read_integer
211 
212   result = get_as_reference_object(type)
213 
214   if result.nil?
215     length = type >> 1
216 
217     result = ''
218 
219     if length > 0
220       if length > (@source.size - @source.pos)
221         raise AMFErrorIncomplete.new
222       end
223 
224       result = @source.read(length)
225       result.force_encoding('UTF-8') if result.respond_to?(:force_encoding)
226       @object_cache << result
227     end
228   end
229 
230   result
231 end
get_as_reference_object(type) click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
113 def get_as_reference_object(type)
114   result = nil
115 
116   if (type & 0x01) == 0 #is reference?
117     reference = type >> 1
118     result    = @object_cache[reference]
119   end
120 
121   result
122 end
get_as_reference_string(type) click to toggle source
    # File lib/rocketamf/pure/deserializer.rb
125 def get_as_reference_string(type)
126   result = nil
127 
128   if (type & 0x01) == 0 #is reference?
129     reference = type >> 1
130     result    = @string_cache[reference]
131   end
132 
133   result
134 end