revent 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/revent/CallEvent.as +25 -0
- data/lib/revent/Client.as +95 -0
- data/lib/revent/amf3/LICENSE +22 -0
- data/lib/revent/amf3/README +6 -0
- data/lib/revent/amf3/amf3.rb +11 -0
- data/lib/revent/amf3/app/configuration.rb +79 -0
- data/lib/revent/amf3/app/fault_object.rb +13 -0
- data/lib/revent/amf3/exception/rubyamf_exception.rb +95 -0
- data/lib/revent/amf3/io/amf_deserializer.rb +315 -0
- data/lib/revent/amf3/io/amf_serializer.rb +184 -0
- data/lib/revent/amf3/io/read_write.rb +308 -0
- data/lib/revent/amf3/util/string.rb +33 -0
- data/lib/revent/amf3/util/vo_helper.rb +121 -0
- data/lib/revent/as_r.rb +178 -0
- data/lib/revent/r_r.rb +176 -0
- data/test/as_r/Document.as +63 -0
- data/test/as_r/client.fla +0 -0
- data/test/as_r/client.swf +0 -0
- data/test/as_r/server.rb +48 -0
- data/{sample → test/r_r}/client.rb +0 -0
- data/{sample → test/r_r}/server.rb +0 -0
- metadata +31 -6
- data/lib/revent.rb +0 -166
@@ -0,0 +1,25 @@
|
|
1
|
+
package revent {
|
2
|
+
import flash.events.Event;
|
3
|
+
|
4
|
+
public class CallEvent extends Event {
|
5
|
+
// The connection has opened, communication is ready.
|
6
|
+
public static const CONNECT:String = "CONNECT";
|
7
|
+
// The connection has closed, do not do any communication.
|
8
|
+
public static const CLOSE:String = "CLOSE";
|
9
|
+
public static const IO_ERROR:String = "IO_ERROR";
|
10
|
+
public static const SECURITY_ERROR:String = "SECURITY_ERROR";
|
11
|
+
|
12
|
+
public static const CALL:String = "CALL";
|
13
|
+
public static const RESULT:String = "RESULT";
|
14
|
+
public static const ERROR:String = "ERROR";
|
15
|
+
|
16
|
+
public var cmd:Object;
|
17
|
+
public var value:Object;
|
18
|
+
|
19
|
+
public function CallEvent(type:String, cmd:Object, value:Object):void {
|
20
|
+
this.cmd = cmd;
|
21
|
+
this.value = value;
|
22
|
+
super(type);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
package revent {
|
2
|
+
import flash.events.*;
|
3
|
+
import flash.net.Socket;
|
4
|
+
import flash.net.ObjectEncoding;
|
5
|
+
|
6
|
+
public class Client extends EventDispatcher {
|
7
|
+
private static const TYPE_CALL:int = 0;
|
8
|
+
private static const TYPE_RESULT:int = 1;
|
9
|
+
private static const TYPE_ERROR:int = 2;
|
10
|
+
|
11
|
+
private var _socket:Socket;
|
12
|
+
|
13
|
+
public function Client():void {
|
14
|
+
_socket = new Socket();
|
15
|
+
_socket.objectEncoding = ObjectEncoding.AMF3;
|
16
|
+
|
17
|
+
_socket.addEventListener(Event.CONNECT, onConnect);
|
18
|
+
_socket.addEventListener(Event.CLOSE, onClose);
|
19
|
+
_socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
|
20
|
+
_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
21
|
+
_socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
|
22
|
+
}
|
23
|
+
|
24
|
+
public function connect(host:String, port:int):void {
|
25
|
+
_socket.connect(host, port);
|
26
|
+
}
|
27
|
+
|
28
|
+
public function close():void {
|
29
|
+
_socket.close();
|
30
|
+
}
|
31
|
+
|
32
|
+
public function get connected():Boolean {
|
33
|
+
return _socket.connected;
|
34
|
+
}
|
35
|
+
|
36
|
+
public function call(cmd:Object, value:Object):void {
|
37
|
+
_socket.writeObject([TYPE_CALL, cmd, value]);
|
38
|
+
_socket.flush();
|
39
|
+
}
|
40
|
+
|
41
|
+
public function result(cmd:Object, value:Object):void {
|
42
|
+
_socket.writeObject([TYPE_RESULT, cmd, value]);
|
43
|
+
_socket.flush();
|
44
|
+
}
|
45
|
+
|
46
|
+
public function error(cmd:Object, value:Object):void {
|
47
|
+
_socket.writeObject([TYPE_ERROR, cmd, value]);
|
48
|
+
_socket.flush();
|
49
|
+
}
|
50
|
+
|
51
|
+
// ---------------------------------------------------------------------------
|
52
|
+
|
53
|
+
private function onConnect(event:Event):void {
|
54
|
+
dispatchEvent(new CallEvent(CallEvent.CONNECT, null, null));
|
55
|
+
}
|
56
|
+
|
57
|
+
private function onClose(event:Event):void {
|
58
|
+
dispatchEvent(new CallEvent(CallEvent.CLOSE, null, null));
|
59
|
+
}
|
60
|
+
|
61
|
+
private function onIOError(event:IOErrorEvent):void {
|
62
|
+
dispatchEvent(new CallEvent(CallEvent.IO_ERROR, null, null));
|
63
|
+
}
|
64
|
+
|
65
|
+
private function onSecurityError(event:SecurityErrorEvent):void {
|
66
|
+
dispatchEvent(new CallEvent(CallEvent.SECURITY_ERROR, null, null));
|
67
|
+
}
|
68
|
+
|
69
|
+
private function onData(event:ProgressEvent):void {
|
70
|
+
try {
|
71
|
+
while (true) {
|
72
|
+
var o:Array = _socket.readObject();
|
73
|
+
var type:int = o[0];
|
74
|
+
var cmd:Object = o[1];
|
75
|
+
var value:Object = o[2];
|
76
|
+
|
77
|
+
var e:CallEvent;
|
78
|
+
switch (type) {
|
79
|
+
case TYPE_CALL:
|
80
|
+
e = new CallEvent(CallEvent.CALL, cmd, value);
|
81
|
+
break;
|
82
|
+
case TYPE_RESULT:
|
83
|
+
e = new CallEvent(CallEvent.RESULT, cmd, value);
|
84
|
+
break;
|
85
|
+
case TYPE_ERROR:
|
86
|
+
e = new CallEvent(CallEvent.ERROR, cmd, value);
|
87
|
+
break;
|
88
|
+
}
|
89
|
+
dispatchEvent(e);
|
90
|
+
}
|
91
|
+
} catch (e:Error) {
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2007 Aaron Smith (aaron@rubyamf.org)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
There is one exception to the above MIT license. WebORB may not use this code
|
11
|
+
base in any of their releases of WebORB for RoR.
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# utils must be first
|
4
|
+
require "#{dir}/util/string"
|
5
|
+
require "#{dir}/util/vo_helper"
|
6
|
+
|
7
|
+
require "#{dir}/app/fault_object"
|
8
|
+
|
9
|
+
require 'kconv' # For string.toutf8
|
10
|
+
require "#{dir}/io/amf_deserializer"
|
11
|
+
require "#{dir}/io/amf_serializer"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#This stores supporting configuration classes used in the config file to register class mappings and parameter mappings etc.
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/../exception/rubyamf_exception"
|
4
|
+
|
5
|
+
module RubyAMF
|
6
|
+
module Configuration
|
7
|
+
#ClassMappings configuration support class
|
8
|
+
class ClassMappings
|
9
|
+
|
10
|
+
# these NEED to be outside the class << self to work
|
11
|
+
@ignore_fields = ['created_at','created_on','updated_at','updated_on']
|
12
|
+
@translate_case = false
|
13
|
+
@class_mappings_by_ruby_class = {}
|
14
|
+
@class_mappings_by_actionscript_class = {}
|
15
|
+
@scoped_class_mappings_by_ruby_class = {}
|
16
|
+
@attribute_names = {}
|
17
|
+
@hash_key_access = :symbol
|
18
|
+
@translate_case = false
|
19
|
+
@force_active_record_ids = true
|
20
|
+
@assume_types = false
|
21
|
+
@use_ruby_date_time = false
|
22
|
+
@use_array_collection = false
|
23
|
+
@check_for_associations = true
|
24
|
+
|
25
|
+
# Aryk: I cleaned up how the class variables are called here. It doesnt matter if you use class variables or instance variables on the class level. Check out this simple tutorial
|
26
|
+
# - http://sporkmonger.com/2007/2/19/instance-variables-class-variables-and-inheritance-in-ruby
|
27
|
+
|
28
|
+
class << self
|
29
|
+
include RubyAMF::Exceptions
|
30
|
+
|
31
|
+
attr_accessor :ignore_fields, :use_array_collection, :default_mapping_scope, :force_active_record_ids, :attribute_names,
|
32
|
+
:use_ruby_date_time, :current_mapping_scope, :check_for_associations, :translate_case, :assume_types, :hash_key_access #the rails parameter mapping type
|
33
|
+
|
34
|
+
def register(mapping) #register a value object map
|
35
|
+
#build out ignore field logic
|
36
|
+
hashed_ignores = {}
|
37
|
+
ClassMappings.ignore_fields.to_a.each{|k| hashed_ignores[k] = true} # strings and nils will be put into an array with to_a
|
38
|
+
mapping[:ignore_fields].to_a.each{|k| hashed_ignores[k] = true}
|
39
|
+
mapping[:ignore_fields] = hashed_ignores # overwrite the original ignore fields
|
40
|
+
|
41
|
+
# if they specify custom attributes, ensure that AR ids are being passed as well if they opt for it.
|
42
|
+
if force_active_record_ids && mapping[:attributes] && mapping[:type]=="active_record" && !mapping[:attributes].include?("id")
|
43
|
+
mapping[:attributes] << "id"
|
44
|
+
end
|
45
|
+
|
46
|
+
# created caching hashes for mapping
|
47
|
+
@class_mappings_by_ruby_class[mapping[:ruby]] = mapping # for quick referencing purposes
|
48
|
+
@class_mappings_by_actionscript_class[mapping[:actionscript]] = mapping # for quick referencing purposes
|
49
|
+
@scoped_class_mappings_by_ruby_class[mapping[:ruby]] = {} # used later for caching based on scope (will get cached after the first run)
|
50
|
+
# for deserialization - looking up in a hash is faster than looking up in an array.
|
51
|
+
begin
|
52
|
+
if mapping[:type] == "active_record"
|
53
|
+
@attribute_names[mapping[:ruby]] = (mapping[:ruby].constantize.new.attribute_names + ["id"]).inject({}){|hash, attr| hash[attr]=true ; hash} # include the id attribute
|
54
|
+
end
|
55
|
+
rescue ActiveRecord::StatementInvalid => e
|
56
|
+
# This error occurs during migrations, since the AR constructed above will check its columns, but the table won't exist yet.
|
57
|
+
# We'll ignore the error if we're migrating.
|
58
|
+
raise unless ARGV.include?("migrate") or ARGV.include?("db:migrate")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_vo_mapping_for_ruby_class(ruby_class)
|
63
|
+
return unless scoped_class_mapping = @scoped_class_mappings_by_ruby_class[ruby_class] # just in case they didnt specify a ClassMapping for this Ruby Class
|
64
|
+
scoped_class_mapping[@current_mapping_scope] ||= (if vo_mapping = @class_mappings_by_ruby_class[ruby_class]
|
65
|
+
vo_mapping = vo_mapping.dup # need to duplicate it or else we will overwrite the keys from the original mappings
|
66
|
+
vo_mapping[:attributes] = vo_mapping[:attributes][@current_mapping_scope]||[] if vo_mapping[:attributes].is_a?(Hash) # don't include any of these attributes if there is no scope
|
67
|
+
vo_mapping[:associations] = vo_mapping[:associations][@current_mapping_scope]||[] if vo_mapping[:associations].is_a?(Hash) # don't include any of these attributes
|
68
|
+
vo_mapping
|
69
|
+
end
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_vo_mapping_for_actionscript_class(actionscript_class)
|
74
|
+
@class_mappings_by_actionscript_class[actionscript_class]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#This is a helper to return FaultObjects. Often times there are sitiuations with database logic that requires an "error state"
|
2
|
+
#to be set in Flash / Flex, but returning false isn't the best because it still get's mapped to the onResult handler, even returning a
|
3
|
+
#generic object with some specific keys set, such as ({error:'someError', code:3}). That is still a pain because it gets mapped to
|
4
|
+
#the onResult function still. So return one of these objects to RubyAMF and it will auto generate a faultObject to return to flash
|
5
|
+
#so that it maps correctly to the onFault handler.
|
6
|
+
class FaultObject < Hash
|
7
|
+
def initialize(message = '')
|
8
|
+
self['faultCode'] = 1
|
9
|
+
self['code'] = 1
|
10
|
+
self['message'] = message
|
11
|
+
self['faultString'] = message
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module RubyAMF
|
2
|
+
module Exceptions
|
3
|
+
|
4
|
+
#Encompasses all rubyamf specific exceptions that occur
|
5
|
+
class RUBYAMFException < Exception
|
6
|
+
|
7
|
+
#when version is not 0 or 3
|
8
|
+
@VERSION_ERROR = 'RUBYAMF_AMF_VERSION_ERROR'
|
9
|
+
|
10
|
+
#when translating the target_uri of a body, there isn't a .(period) to map the service / method name
|
11
|
+
@SERVICE_TRANSLATION_ERROR = 'RUBYAMF_SERVICE_TRANSLATION_ERROR'
|
12
|
+
|
13
|
+
#when an authentication error occurs
|
14
|
+
@AUTHENTICATION_ERROR = 'RUBYAMF_ATUHENTICATION_ERROR'
|
15
|
+
|
16
|
+
#when a method is called, but the method is either private or doesn't exist
|
17
|
+
@METHOD_ACCESS_ERROR = 'RUBYAMF_METHOD_ACCESS_ERROR'
|
18
|
+
|
19
|
+
#when a mehod is undefined
|
20
|
+
@METHOD_UNDEFINED_METHOD_ERROR = 'RUBYAMF_UNDECLARED_METHOD_ERROR'
|
21
|
+
|
22
|
+
#when there is an error with session implementation
|
23
|
+
@SESSION_ERROR = 'RUBYAMF_SESSION_ERROR'
|
24
|
+
|
25
|
+
#when a general user error has occured
|
26
|
+
@USER_ERROR = 'RUBYAMF_USER_ERROR'
|
27
|
+
|
28
|
+
#when parsing AMF3, an undefined object reference
|
29
|
+
@UNDEFINED_OBJECT_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_OBJECT_REFERENCE_ERROR'
|
30
|
+
|
31
|
+
#when parsing AMF3, an undefined class definition
|
32
|
+
@UNDEFINED_DEFINITION_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_DEFINIITON_REFERENCE_ERROR'
|
33
|
+
|
34
|
+
#when parsing amf3, an undefined string reference
|
35
|
+
@UNDEFINED_STRING_REFERENCE_ERROR = 'RUBYAMF_UNDEFINED_STRING_REFERENCE_ERROR'
|
36
|
+
|
37
|
+
#unsupported AMF0 type
|
38
|
+
@UNSUPPORTED_AMF0_TYPE = 'UNSUPPORTED_AMF0_TYPE'
|
39
|
+
|
40
|
+
#when the Rails ActionController Filter chain haults
|
41
|
+
@FILTER_CHAIN_HAULTED = 'RAILS_ACTION_CONTROLLER_FILTER_CHAIN_HAULTED'
|
42
|
+
|
43
|
+
#when active record errors
|
44
|
+
@ACTIVE_RECORD_ERRORS = 'ACTIVE_RECORD_ERRORS'
|
45
|
+
|
46
|
+
#whan amf data is incomplete or incorrect
|
47
|
+
@AMF_ERROR = 'AMF_ERROR'
|
48
|
+
|
49
|
+
#vo errors
|
50
|
+
@VO_ERROR = 'VO_ERROR'
|
51
|
+
|
52
|
+
#when a parameter mapping error occurs
|
53
|
+
@PARAMETER_MAPPING_ERROR = "PARAMETER_MAPPING_ERROR"
|
54
|
+
|
55
|
+
attr_accessor :message
|
56
|
+
attr_accessor :etype
|
57
|
+
attr_accessor :ebacktrace
|
58
|
+
|
59
|
+
#static accessors
|
60
|
+
class << self
|
61
|
+
attr_accessor :VERSION_ERROR
|
62
|
+
attr_accessor :SERVICE_TRANSLATION_ERROR
|
63
|
+
attr_accessor :AUTHENTICATION_ERROR
|
64
|
+
attr_accessor :METHOD_ACCESS_ERROR
|
65
|
+
attr_accessor :METHOD_UNDEFINED_METHOD_ERROR
|
66
|
+
attr_accessor :SESSION_ERROR
|
67
|
+
attr_accessor :USER_ERROR
|
68
|
+
attr_accessor :UNDEFINED_OBJECT_REFERENCE_ERROR
|
69
|
+
attr_accessor :UNDEFINED_DEFINITION_REFERENCE_ERROR
|
70
|
+
attr_accessor :UNDEFINED_STRING_REFERENCE_ERROR
|
71
|
+
attr_accessor :UNSUPPORTED_TYPE
|
72
|
+
attr_accessor :ADAPTER_ERROR
|
73
|
+
attr_accessor :INTERNAL_ERROR
|
74
|
+
attr_accessor :UNSUPPORTED_AMF0_TYPE
|
75
|
+
attr_accessor :FILTER_CHAIN_HAULTED
|
76
|
+
attr_accessor :ACTIVE_RECORD_ERRORS
|
77
|
+
attr_accessor :VO_ERROR
|
78
|
+
attr_accessor :AMF_ERROR
|
79
|
+
attr_accessor :PARAMETER_MAPPING_ERROR
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(type,msg)
|
83
|
+
super(msg)
|
84
|
+
@message = msg
|
85
|
+
@etype = type
|
86
|
+
end
|
87
|
+
|
88
|
+
# stringify the message
|
89
|
+
def to_s
|
90
|
+
@msg
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
module RubyAMF
|
2
|
+
module IO
|
3
|
+
class AMFDeserializer
|
4
|
+
|
5
|
+
require "#{File.dirname(__FILE__)}/read_write"
|
6
|
+
|
7
|
+
include RubyAMF::Configuration
|
8
|
+
include RubyAMF::Exceptions
|
9
|
+
include RubyAMF::IO::BinaryReader
|
10
|
+
include RubyAMF::IO::Constants
|
11
|
+
include RubyAMF::VoHelper
|
12
|
+
|
13
|
+
attr_accessor :stream
|
14
|
+
attr_accessor :stream_position
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
reset
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@stream = ''
|
22
|
+
@stream_position = 0
|
23
|
+
reset_referencables
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset_referencables
|
27
|
+
@stored_strings = []
|
28
|
+
@stored_objects = []
|
29
|
+
@stored_defs = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_amf3
|
33
|
+
type = read_word8
|
34
|
+
case type
|
35
|
+
when AMF3_UNDEFINED
|
36
|
+
nil
|
37
|
+
when AMF3_NULL
|
38
|
+
nil
|
39
|
+
when AMF3_FALSE
|
40
|
+
false
|
41
|
+
when AMF3_TRUE
|
42
|
+
true
|
43
|
+
when AMF3_INTEGER
|
44
|
+
read_amf3_integer
|
45
|
+
when AMF3_NUMBER
|
46
|
+
read_amf3_number
|
47
|
+
when AMF3_STRING
|
48
|
+
read_amf3_string
|
49
|
+
when AMF3_XML
|
50
|
+
read_amf3_xml_string
|
51
|
+
when AMF3_DATE
|
52
|
+
read_amf3_date
|
53
|
+
when AMF3_ARRAY
|
54
|
+
read_amf3_array
|
55
|
+
when AMF3_OBJECT
|
56
|
+
read_amf3_object
|
57
|
+
when AMF3_XML_STRING
|
58
|
+
read_amf3_xml
|
59
|
+
when AMF3_BYTE_ARRAY
|
60
|
+
read_amf3_byte_array
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_amf3_integer
|
65
|
+
n = 0
|
66
|
+
b = read_word8||0
|
67
|
+
result = 0
|
68
|
+
|
69
|
+
while ((b & 0x80) != 0 && n < 3)
|
70
|
+
result = result << 7
|
71
|
+
result = result | (b & 0x7f)
|
72
|
+
b = read_word8||0
|
73
|
+
n = n + 1
|
74
|
+
end
|
75
|
+
|
76
|
+
if (n < 3)
|
77
|
+
result = result << 7
|
78
|
+
result = result | b
|
79
|
+
else
|
80
|
+
#Use all 8 bits from the 4th byte
|
81
|
+
result = result << 8
|
82
|
+
result = result | b
|
83
|
+
|
84
|
+
#Check if the integer should be negative
|
85
|
+
if (result > AMF3_INTEGER_MAX)
|
86
|
+
result -= (1 << 29)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return result
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_amf3_string
|
93
|
+
type = read_amf3_integer
|
94
|
+
isReference = (type & 0x01) == 0
|
95
|
+
if isReference
|
96
|
+
reference = type >> 1
|
97
|
+
if reference < @stored_strings.length
|
98
|
+
if @stored_strings[reference] == nil
|
99
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant string at index #{reference}, please tell aaron@rubyamf.org"))
|
100
|
+
end
|
101
|
+
return @stored_strings[reference]
|
102
|
+
else
|
103
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_STRING_REFERENCE_ERROR, "Reference to non existant string at index #{reference}, please tell aaron@rubyamf.org") )
|
104
|
+
end
|
105
|
+
else
|
106
|
+
|
107
|
+
length = type >> 1
|
108
|
+
|
109
|
+
#Note that we have to read the string into a byte buffer and then
|
110
|
+
#convert to a UTF-8 string, because for standard readUTF() it
|
111
|
+
#reads an unsigned short to get the string length.
|
112
|
+
#A string isn't stored as a reference if it is the empty string
|
113
|
+
#thanks Karl von Randow for this
|
114
|
+
if length > 0
|
115
|
+
str = String.new(readn(length)) #specifically cast as string, as we're reading verbatim from the stream
|
116
|
+
str.toutf8 #convert to utf8
|
117
|
+
@stored_strings << str
|
118
|
+
end
|
119
|
+
return str
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def read_amf3_xml
|
124
|
+
type = read_amf3_integer
|
125
|
+
length = type >> 1
|
126
|
+
readn(length)
|
127
|
+
end
|
128
|
+
|
129
|
+
def read_amf3_date
|
130
|
+
type = read_amf3_integer
|
131
|
+
isReference = (type & 0x01) == 0
|
132
|
+
if isReference
|
133
|
+
reference = type >> 1
|
134
|
+
if reference < @stored_objects.length
|
135
|
+
if @stored_objects[reference] == nil
|
136
|
+
|
137
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant date at index #{reference}, please tell aaron@rubyamf.org"))
|
138
|
+
end
|
139
|
+
return @stored_objects[reference]
|
140
|
+
else
|
141
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Undefined date object reference when deserialing AMF3: #{reference}") )
|
142
|
+
end
|
143
|
+
else
|
144
|
+
seconds = read_double.to_f/1000
|
145
|
+
time = if (seconds < 0) || ClassMappings.use_ruby_date_time # we can't use Time if its a negative second value
|
146
|
+
DateTime.strptime(seconds.to_s, "%s")
|
147
|
+
else
|
148
|
+
Time.at(seconds)
|
149
|
+
end
|
150
|
+
@stored_objects << time
|
151
|
+
time
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def read_amf3_array
|
156
|
+
type = read_amf3_integer
|
157
|
+
isReference = (type & 0x01) == 0
|
158
|
+
|
159
|
+
if isReference
|
160
|
+
reference = type >> 1
|
161
|
+
if reference < @stored_objects.length
|
162
|
+
if @stored_objects[reference] == nil
|
163
|
+
raise(RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant array at index #{reference}, please tell aaron@rubyamf.org"))
|
164
|
+
end
|
165
|
+
return @stored_objects[reference]
|
166
|
+
else
|
167
|
+
raise Exception.new("Reference to non-existent array at index #{reference}, please tell aaron@rubyamf.org")
|
168
|
+
end
|
169
|
+
else
|
170
|
+
length = type >> 1
|
171
|
+
propertyName = read_amf3_string
|
172
|
+
if propertyName != nil
|
173
|
+
array = {}
|
174
|
+
@stored_objects << array
|
175
|
+
begin
|
176
|
+
while(propertyName.length)
|
177
|
+
value = read_amf3
|
178
|
+
array[propertyName] = value
|
179
|
+
propertyName = read_amf3_string
|
180
|
+
end
|
181
|
+
rescue Exception => e #end of object exception, because propertyName.length will be non existent
|
182
|
+
end
|
183
|
+
0.upto(length - 1) do |i|
|
184
|
+
array["" + i.to_s] = read_amf3
|
185
|
+
end
|
186
|
+
else
|
187
|
+
array = []
|
188
|
+
@stored_objects << array
|
189
|
+
0.upto(length - 1) do
|
190
|
+
array << read_amf3
|
191
|
+
end
|
192
|
+
end
|
193
|
+
array
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def read_amf3_object
|
198
|
+
type = read_amf3_integer
|
199
|
+
isReference = (type & 0x01) == 0
|
200
|
+
|
201
|
+
if isReference
|
202
|
+
reference = type >> 1
|
203
|
+
if reference < @stored_objects.length
|
204
|
+
if @stored_objects[reference] == nil
|
205
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant object at index #{reference}, please tell aaron@rubyamf.org."))
|
206
|
+
end
|
207
|
+
return @stored_objects[reference]
|
208
|
+
else
|
209
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant object #{reference}"))
|
210
|
+
end
|
211
|
+
else
|
212
|
+
|
213
|
+
class_type = type >> 1
|
214
|
+
class_is_reference = (class_type & 0x01) == 0
|
215
|
+
|
216
|
+
if class_is_reference
|
217
|
+
class_reference = class_type >> 1
|
218
|
+
if class_reference < @stored_defs.length
|
219
|
+
class_definition = @stored_defs[class_reference]
|
220
|
+
else
|
221
|
+
raise RUBYAMFException.new(RUBYAMFException.UNDEFINED_DEFINITION_REFERENCE_ERROR, "Reference to non existant class definition #{class_reference}")
|
222
|
+
end
|
223
|
+
else
|
224
|
+
actionscript_class_name = read_amf3_string
|
225
|
+
externalizable = (class_type & 0x02) != 0
|
226
|
+
dynamic = (class_type & 0x04) != 0
|
227
|
+
attribute_count = class_type >> 3
|
228
|
+
|
229
|
+
class_attributes = []
|
230
|
+
attribute_count.times{class_attributes << read_amf3_string} # Read class members
|
231
|
+
|
232
|
+
class_definition = {"as_class_name" => actionscript_class_name, "members" => class_attributes, "externalizable" => externalizable, "dynamic" => dynamic}
|
233
|
+
@stored_defs << class_definition
|
234
|
+
end
|
235
|
+
action_class_name = class_definition['as_class_name'] #get the className according to type
|
236
|
+
|
237
|
+
# check to see if its the first main amf object or a flex message obj, because then we need a _explicitType field type and skip some things
|
238
|
+
skip_mapping = if action_class_name && action_class_name.include?("flex.messaging")
|
239
|
+
obj = VoHash.new # initialize an empty VoHash value holder
|
240
|
+
obj._explicitType = action_class_name
|
241
|
+
true
|
242
|
+
else # otherwise just use a normal hash
|
243
|
+
obj = {}
|
244
|
+
false
|
245
|
+
end
|
246
|
+
|
247
|
+
obj_position = @stored_objects.size # need to replace the object later for referencing (MUST be before inserting the object into stored_objs)
|
248
|
+
@stored_objects << obj
|
249
|
+
|
250
|
+
if class_definition['externalizable']
|
251
|
+
if ['flex.messaging.io.ObjectProxy','flex.messaging.io.ArrayCollection'].include?(action_class_name)
|
252
|
+
obj = read_amf3
|
253
|
+
else
|
254
|
+
raise( RUBYAMFException.new(RUBYAMFException.USER_ERROR, "Unable to read externalizable data type #{type}"))
|
255
|
+
end
|
256
|
+
else
|
257
|
+
translate_case = !skip_mapping&&ClassMappings.translate_case # remove the need for a method call / also, don't want to convert on main remoting object
|
258
|
+
class_definition['members'].each do |key|
|
259
|
+
value = read_amf3
|
260
|
+
#if (value)&& value != 'NaN'# have to read key to move the reader ahead in the stream
|
261
|
+
key.to_snake! if translate_case
|
262
|
+
obj[key] = value
|
263
|
+
#end
|
264
|
+
end
|
265
|
+
|
266
|
+
if class_definition['dynamic']
|
267
|
+
while (key = read_amf3_string) && key.length != 0 do # read next key
|
268
|
+
value = read_amf3
|
269
|
+
#if (value) && value != 'NaN'
|
270
|
+
key.to_snake! if translate_case
|
271
|
+
obj[key] = value
|
272
|
+
#end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
obj = VoUtil.get_vo_for_incoming(obj,action_class_name) unless skip_mapping
|
276
|
+
end
|
277
|
+
@stored_objects[obj_position] = obj # put the new object into the same position as the original object since it was worked on
|
278
|
+
obj
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def read_amf3_byte_array # according to the charles amf3 deserializer, they store byte array
|
283
|
+
type = read_amf3_integer
|
284
|
+
isReference = (type & 0x01) == 0
|
285
|
+
if isReference
|
286
|
+
reference = type >> 1
|
287
|
+
if reference < @stored_objects.length
|
288
|
+
if @stored_objects[reference] == nil
|
289
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant byteArray at index #{reference}, please tell aaron@rubyamf.org"))
|
290
|
+
end
|
291
|
+
return @stored_objects[reference]
|
292
|
+
else
|
293
|
+
raise( RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "Reference to non existant byteArray #{reference}"))
|
294
|
+
end
|
295
|
+
else
|
296
|
+
length = type >> 1
|
297
|
+
begin # first assume its gzipped and rescue an exception if its not
|
298
|
+
inflated_stream = Zlib::Inflate.inflate( self.stream[self.stream_position,length] )
|
299
|
+
arr = inflated_stream.unpack('c'*inflated_stream.length)
|
300
|
+
rescue Exception => e
|
301
|
+
arr = self.stream[self.stream_position,length].unpack('c'*length)
|
302
|
+
end
|
303
|
+
self.stream_position += length
|
304
|
+
@stored_objects << arr
|
305
|
+
arr
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def read_amf3_number
|
310
|
+
res = read_double
|
311
|
+
res.is_a?(Float)&&res.nan? ? nil : res # check for NaN and convert them to nil
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|