class RocketAMF::Pure::Deserializer
Pure
ruby deserializer for AMF3 requests
Attributes
Properties
Public Class Methods
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 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
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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
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
# 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
# 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
# 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
# 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
# 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