revent 0.1 → 0.2
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/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,184 @@
|
|
1
|
+
module RubyAMF
|
2
|
+
module IO
|
3
|
+
class AMFSerializer
|
4
|
+
|
5
|
+
require "#{File.dirname(__FILE__)}/read_write"
|
6
|
+
|
7
|
+
include RubyAMF::Configuration
|
8
|
+
include RubyAMF::IO::BinaryWriter
|
9
|
+
include RubyAMF::IO::Constants
|
10
|
+
include RubyAMF::VoHelper
|
11
|
+
|
12
|
+
attr_accessor :stream
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
reset
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
@stream = ''
|
20
|
+
reset_referencables
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_referencables
|
24
|
+
@stored_strings = {} # hash is way faster than array
|
25
|
+
@stored_strings[""] = true # add this in automatically
|
26
|
+
@write_amf3_integer_results = {} # cache the integers
|
27
|
+
@current_strings_index = 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def write_amf3(value)
|
31
|
+
if !value
|
32
|
+
@stream << "\001" # represents an amf3 null
|
33
|
+
|
34
|
+
elsif (value.is_a?(TrueClass) || value.is_a?(FalseClass))
|
35
|
+
value ? (@stream << "\003") : (@stream << "\002") # represents an amf3 true and false
|
36
|
+
|
37
|
+
elsif value.is_a?(Numeric)
|
38
|
+
if value.is_a?(Integer) # Aryk: This was also incorrect before because you has Bignum check AFTER the Integer check, which means the Bignum's were getting picked up by Integers
|
39
|
+
if value.is_a?(Bignum)
|
40
|
+
@stream << "\005" # represents an amf3 complex number
|
41
|
+
write_double(value)
|
42
|
+
else
|
43
|
+
write_amf3_number(value)
|
44
|
+
end
|
45
|
+
elsif(value.is_a?(Float))
|
46
|
+
@stream << "\005" # represents an amf3 complex number
|
47
|
+
write_double(value)
|
48
|
+
elsif value.is_a?(BigDecimal) # Aryk: BigDecimal does not relate to Float, so keep it as a seperate check.
|
49
|
+
# TODO: Aryk: Not quite sure why you do value.to_s.to_f? can't you just do value.to_f?
|
50
|
+
value = value.to_s('F').to_f #this is turning a string into a Ruby Float, but because there are no further operations on it it is safe
|
51
|
+
@stream << "\005" # represents an amf3 complex number
|
52
|
+
write_double(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
elsif(value.is_a?(String))
|
56
|
+
@stream << "\006" # represents an amf3 string
|
57
|
+
write_amf3_string(value)
|
58
|
+
|
59
|
+
elsif(value.is_a?(Array))
|
60
|
+
write_amf3_array(value)
|
61
|
+
|
62
|
+
elsif(value.is_a?(Hash))
|
63
|
+
write_amf3_object(value)
|
64
|
+
|
65
|
+
elsif (value.is_a?(Time)||value.is_a?(Date))
|
66
|
+
@stream << "\b" # represents an amf3 date
|
67
|
+
write_amf3_date(value)
|
68
|
+
|
69
|
+
# I know we can combine this with the last condition, but don't ; the Rexml and Beautiful Soup test is expensive, and for large record sets with many AR its better to be able to skip the next step
|
70
|
+
elsif value.is_a?(ActiveRecord::Base) # Aryk: this way, we can bypass the "['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s) " operation
|
71
|
+
write_amf3_object(VoUtil.get_vo_hash_for_outgoing(value))
|
72
|
+
|
73
|
+
elsif ['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s)
|
74
|
+
write_byte(AMF3_XML)
|
75
|
+
write_amf3_xml(value)
|
76
|
+
|
77
|
+
elsif value.is_a?(Object)
|
78
|
+
write_amf3_object(VoUtil.get_vo_hash_for_outgoing(value) )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_amf3_integer(int)
|
83
|
+
@stream << (@write_amf3_integer_results[int] ||= (
|
84
|
+
int = int & 0x1fffffff
|
85
|
+
if(int < 0x80)
|
86
|
+
[int].pack('c')
|
87
|
+
elsif(int < 0x4000)
|
88
|
+
[int >> 7 & 0x7f | 0x80].pack('c')+
|
89
|
+
[int & 0x7f].pack('c')
|
90
|
+
elsif(int < 0x200000)
|
91
|
+
[int >> 14 & 0x7f | 0x80].pack('c')+
|
92
|
+
[int >> 7 & 0x7f | 0x80].pack('c')+
|
93
|
+
[int & 0x7f].pack('c')
|
94
|
+
else
|
95
|
+
[int >> 22 & 0x7f | 0x80].pack('c')+
|
96
|
+
[int >> 15 & 0x7f | 0x80].pack('c')+
|
97
|
+
[int >> 8 & 0x7f | 0x80].pack('c')+
|
98
|
+
[int & 0xff].pack('c')
|
99
|
+
end
|
100
|
+
))
|
101
|
+
end
|
102
|
+
|
103
|
+
def write_amf3_number(number)
|
104
|
+
if(number >= AMF3_INTEGER_MIN && number <= AMF3_INTEGER_MAX) #check valid range for 29bits
|
105
|
+
@stream << "\004" # represents an amf3 integer
|
106
|
+
write_amf3_integer(number)
|
107
|
+
else #overflow condition otherwise
|
108
|
+
@stream << "\005" # represents an amf3 complex number
|
109
|
+
write_double(number)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def write_amf3_string(string)
|
114
|
+
if index = @stored_strings[string]
|
115
|
+
if string == "" # store this initially so it gets caught by the stored_strings check
|
116
|
+
@stream << "\001" # represents an amf3 empty string
|
117
|
+
else
|
118
|
+
reference = index << 1
|
119
|
+
write_amf3_integer(reference)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
@stored_strings[string] = @current_strings_index
|
123
|
+
@current_strings_index += 1 # increment the index
|
124
|
+
reference = string.length
|
125
|
+
reference = reference << 1
|
126
|
+
reference = reference | 1
|
127
|
+
write_amf3_integer(reference)
|
128
|
+
writen(string)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def write_amf3_object(hash)
|
133
|
+
not_vo_hash = !hash.is_a?(VoHash) # is this not a vohash - then doesnt have an _explicitType parameter
|
134
|
+
@stream << "\n\v" # represents an amf3 object and dynamic object
|
135
|
+
not_vo_hash || !hash._explicitType ? (@stream << "\001") : write_amf3_string(hash._explicitType)
|
136
|
+
hash.each do |attr, value| # Aryk: no need to remove any "_explicitType" or "rmember" key since they werent added as keys
|
137
|
+
if not_vo_hash # then that means that the attr might not be symbols and it hasn't gone through camelizing if thats needed
|
138
|
+
attr = attr.to_s.dup # need this just in case its frozen
|
139
|
+
attr.to_camel! if ClassMappings.translate_case
|
140
|
+
end
|
141
|
+
write_amf3_string(attr)
|
142
|
+
value ? write_amf3(value) : (@stream << "\001") # represents an amf3 null
|
143
|
+
end
|
144
|
+
@stream << "\001" # represents an amf3 empty string #close open object
|
145
|
+
end
|
146
|
+
|
147
|
+
def write_amf3_array(array)
|
148
|
+
num_objects = array.length * 2 + 1
|
149
|
+
if ClassMappings.use_array_collection
|
150
|
+
@stream << "\n\a" # AMF3_OBJECT and AMF3_XML
|
151
|
+
write_amf3_string("flex.messaging.io.ArrayCollection")
|
152
|
+
end
|
153
|
+
@stream << "\t" # represents an amf3 array
|
154
|
+
write_amf3_integer(num_objects)
|
155
|
+
@stream << "\001" # represents an amf3 empty string #write empty for string keyed elements here, as it's never allowed from ruby
|
156
|
+
array.each{|v| write_amf3(v) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def write_amf3_date(datetime) # Aryk: Dates will almost never be the same, so turn off the storing_objects
|
160
|
+
write_amf3_integer(1)
|
161
|
+
seconds = if datetime.is_a?(Time)
|
162
|
+
datetime.utc unless datetime.utc?
|
163
|
+
datetime.to_f
|
164
|
+
elsif datetime.is_a?(Date) # this also handles the case for DateTime
|
165
|
+
datetime.strftime("%s").to_i
|
166
|
+
# datetime = Time.gm( datetime.year, datetime.month, datetime.day )
|
167
|
+
# datetime = Time.gm( datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec )
|
168
|
+
end
|
169
|
+
write_double( (seconds*1000).to_i ) # used to be total_milliseconds = datetime.to_i * 1000 + ( datetime.usec/1000 )
|
170
|
+
end
|
171
|
+
|
172
|
+
def write_amf3_xml(value)
|
173
|
+
xml = value.to_s
|
174
|
+
a = xml.strip
|
175
|
+
if(a != nil)
|
176
|
+
b = a.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
|
177
|
+
else
|
178
|
+
b = xml.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
|
179
|
+
end
|
180
|
+
write_amf3_string(b)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
begin
|
2
|
+
module RubyAMF
|
3
|
+
module IO
|
4
|
+
module Constants
|
5
|
+
AMF3_TYPE = 0x11
|
6
|
+
AMF3_UNDEFINED = 0x00
|
7
|
+
AMF3_NULL = 0x01
|
8
|
+
AMF3_FALSE = 0x02
|
9
|
+
AMF3_TRUE = 0x03
|
10
|
+
AMF3_INTEGER = 0x04
|
11
|
+
AMF3_NUMBER = 0x05
|
12
|
+
AMF3_STRING = 0x06
|
13
|
+
AMF3_XML = 0x07
|
14
|
+
AMF3_DATE = 0x08
|
15
|
+
AMF3_ARRAY = 0x09
|
16
|
+
AMF3_OBJECT = 0x0A
|
17
|
+
AMF3_XML_STRING = 0x0B
|
18
|
+
AMF3_BYTE_ARRAY = 0x0C
|
19
|
+
AMF3_INTEGER_MAX = 268435455
|
20
|
+
AMF3_INTEGER_MIN = -268435456
|
21
|
+
end
|
22
|
+
module BinaryReader
|
23
|
+
|
24
|
+
Native = :Native
|
25
|
+
Big = BigEndian = Network = :BigEndian
|
26
|
+
Little = LittleEndian = :LittleEndian
|
27
|
+
|
28
|
+
#examines the locale byte order on the running machine
|
29
|
+
def byte_order
|
30
|
+
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
31
|
+
:BigEndian
|
32
|
+
else
|
33
|
+
:LittleEndian
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def byte_order_little?
|
38
|
+
(byte_order == :LittleEndian) ? true : false;
|
39
|
+
end
|
40
|
+
|
41
|
+
def byte_order_big?
|
42
|
+
(byte_order == :BigEndian) ? true : false;
|
43
|
+
end
|
44
|
+
alias :byte_order_network? :byte_order_big?
|
45
|
+
|
46
|
+
#read N length from stream starting at position
|
47
|
+
def readn(length)
|
48
|
+
self.stream_position ||= 0
|
49
|
+
str = self.stream[self.stream_position, length]
|
50
|
+
self.stream_position += length
|
51
|
+
str
|
52
|
+
end
|
53
|
+
|
54
|
+
#reada a boolean
|
55
|
+
def read_boolean
|
56
|
+
d = self.stream[self.stream_position,1].unpack('c').first
|
57
|
+
self.stream_position += 1
|
58
|
+
(d == 1) ? true : false;
|
59
|
+
end
|
60
|
+
|
61
|
+
#8bits no byte order
|
62
|
+
def read_int8
|
63
|
+
d = self.stream[self.stream_position,1].unpack('c').first
|
64
|
+
self.stream_position += 1
|
65
|
+
d
|
66
|
+
end
|
67
|
+
alias :read_byte :read_int8
|
68
|
+
|
69
|
+
# Aryk: TODO: This needs to be written more cleanly. Using rescue and then regex checks on top of that slows things down
|
70
|
+
def read_word8
|
71
|
+
begin
|
72
|
+
d = self.stream[self.stream_position,1].unpack('C').first
|
73
|
+
self.stream_position += 1
|
74
|
+
d
|
75
|
+
rescue Exception => e
|
76
|
+
#this handles an exception condition when Rails'
|
77
|
+
#ActionPack strips off the last "\000" of the AMF stream
|
78
|
+
self.stream_position += 1
|
79
|
+
return 0
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#16 bits Unsigned
|
84
|
+
def read_word16_native
|
85
|
+
d = self.stream[self.stream_position,2].unpack('S').first
|
86
|
+
self.stream_position += 2
|
87
|
+
d
|
88
|
+
end
|
89
|
+
|
90
|
+
def read_word16_little
|
91
|
+
d = self.stream[self.stream_position,2].unpack('v').first
|
92
|
+
self.stream_position += 2
|
93
|
+
d
|
94
|
+
end
|
95
|
+
|
96
|
+
def read_word16_network
|
97
|
+
d = self.stream[self.stream_position,2].unpack('n').first
|
98
|
+
self.stream_position += 2
|
99
|
+
d
|
100
|
+
end
|
101
|
+
|
102
|
+
#16 bits Signed
|
103
|
+
def read_int16_native
|
104
|
+
str = self.readn(2).unpack('s').first
|
105
|
+
end
|
106
|
+
|
107
|
+
def read_int16_little
|
108
|
+
str = self.readn(2)
|
109
|
+
str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
|
110
|
+
str.unpack('s').first
|
111
|
+
end
|
112
|
+
|
113
|
+
def read_int16_network
|
114
|
+
str = self.readn(2)
|
115
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
116
|
+
str.unpack('s').first
|
117
|
+
end
|
118
|
+
|
119
|
+
#32 bits unsigned
|
120
|
+
def read_word32_native
|
121
|
+
d = self.stream[self.stream_position,4].unpack('L').first
|
122
|
+
self.stream_position += 4
|
123
|
+
d
|
124
|
+
end
|
125
|
+
|
126
|
+
def read_word32_little
|
127
|
+
d = self.stream[self.stream_position,4].unpack('V').first
|
128
|
+
self.stream_position += 4
|
129
|
+
d
|
130
|
+
end
|
131
|
+
|
132
|
+
def read_word32_network
|
133
|
+
d = self.stream[self.stream_position,4].unpack('N').first
|
134
|
+
self.stream_position += 4
|
135
|
+
d
|
136
|
+
end
|
137
|
+
|
138
|
+
#32 bits signed
|
139
|
+
def read_int32_native
|
140
|
+
d = self.stream[self.stream_position,4].unpack('l').first
|
141
|
+
self.stream_position += 4
|
142
|
+
d
|
143
|
+
end
|
144
|
+
|
145
|
+
def read_int32_little
|
146
|
+
str = readn(4)
|
147
|
+
str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
|
148
|
+
str.unpack('l').first
|
149
|
+
end
|
150
|
+
|
151
|
+
def read_int32_network
|
152
|
+
str = readn(4)
|
153
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
154
|
+
str.unpack('l').first
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
#UTF string
|
159
|
+
def read_utf
|
160
|
+
length = self.read_word16_network
|
161
|
+
readn(length)
|
162
|
+
end
|
163
|
+
|
164
|
+
def read_int32_network
|
165
|
+
str = self.readn(4)
|
166
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
167
|
+
str.unpack('l').first
|
168
|
+
end
|
169
|
+
|
170
|
+
def read_double
|
171
|
+
d = self.stream[self.stream_position,8].unpack('G').first
|
172
|
+
self.stream_position += 8
|
173
|
+
d
|
174
|
+
end
|
175
|
+
|
176
|
+
def read_long_utf(length)
|
177
|
+
length = read_word32_network #get the length of the string (1st 4 bytes)
|
178
|
+
self.readn(length) #read length number of bytes
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
module BinaryWriter
|
184
|
+
|
185
|
+
#examines the locale byte order on the running machine
|
186
|
+
def byte_order
|
187
|
+
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
188
|
+
:BigEndian
|
189
|
+
else
|
190
|
+
:LittleEndian
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def byte_order_little?
|
195
|
+
(byte_order == :LittleEndian) ? true : false;
|
196
|
+
end
|
197
|
+
|
198
|
+
def byte_order_big?
|
199
|
+
(byte_order == :BigEndian) ? true : false;
|
200
|
+
end
|
201
|
+
alias :byte_order_network? :byte_order_big?
|
202
|
+
|
203
|
+
def writen(val)
|
204
|
+
@stream << val
|
205
|
+
end
|
206
|
+
|
207
|
+
#8 bit no byteorder
|
208
|
+
def write_word8(val)
|
209
|
+
self.stream << [val].pack('C')
|
210
|
+
end
|
211
|
+
|
212
|
+
def write_int8(val)
|
213
|
+
self.stream << [val].pack('c')
|
214
|
+
end
|
215
|
+
|
216
|
+
#16 bit unsigned
|
217
|
+
def write_word16_native(val)
|
218
|
+
self.stream << [val].pack('S')
|
219
|
+
end
|
220
|
+
|
221
|
+
def write_word16_little(val)
|
222
|
+
str = [val].pack('S')
|
223
|
+
str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
|
224
|
+
self.stream << str
|
225
|
+
end
|
226
|
+
|
227
|
+
def write_word16_network(val)
|
228
|
+
str = [val].pack('S')
|
229
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
230
|
+
self.stream << str
|
231
|
+
end
|
232
|
+
|
233
|
+
#16 bits signed
|
234
|
+
def write_int16_native(val)
|
235
|
+
self.stream << [val].pack('s')
|
236
|
+
end
|
237
|
+
|
238
|
+
def write_int16_little(val)
|
239
|
+
self.stream << [val].pack('v')
|
240
|
+
end
|
241
|
+
|
242
|
+
def write_int16_network(val)
|
243
|
+
self.stream << [val].pack('n')
|
244
|
+
end
|
245
|
+
|
246
|
+
#32 bit unsigned
|
247
|
+
def write_word32_native(val)
|
248
|
+
self.stream << [val].pack('L')
|
249
|
+
end
|
250
|
+
|
251
|
+
def write_word32_little(val)
|
252
|
+
str = [val].pack('L')
|
253
|
+
str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
|
254
|
+
self.stream << str
|
255
|
+
end
|
256
|
+
|
257
|
+
def write_word32_network(val)
|
258
|
+
str = [val].pack('L')
|
259
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
260
|
+
self.stream << str
|
261
|
+
end
|
262
|
+
|
263
|
+
#32 signed
|
264
|
+
def write_int32_native(val)
|
265
|
+
self.stream << [val].pack('l')
|
266
|
+
end
|
267
|
+
|
268
|
+
def write_int32_little(val)
|
269
|
+
self.stream << [val].pack('V')
|
270
|
+
end
|
271
|
+
|
272
|
+
def write_int32_network(val)
|
273
|
+
self.stream << [val].pack('N')
|
274
|
+
end
|
275
|
+
|
276
|
+
# write utility methods
|
277
|
+
def write_byte(val)
|
278
|
+
#self.write_int8(val)
|
279
|
+
@stream << [val].pack('c')
|
280
|
+
end
|
281
|
+
|
282
|
+
def write_boolean(val)
|
283
|
+
if val then self.write_byte(1) else self.write_byte(0) end
|
284
|
+
end
|
285
|
+
|
286
|
+
def write_utf(str)
|
287
|
+
self.write_int16_network(str.length)
|
288
|
+
self.stream << str
|
289
|
+
end
|
290
|
+
|
291
|
+
def write_long_utf(str)
|
292
|
+
self.write_int32_network(str.length)
|
293
|
+
self.stream << str
|
294
|
+
end
|
295
|
+
|
296
|
+
def write_double(val)
|
297
|
+
self.stream << ( @floats_cache[val] ||=
|
298
|
+
[val].pack('G')
|
299
|
+
)
|
300
|
+
#puts "WRITE DOUBLE"
|
301
|
+
#puts @floats_cache
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
rescue Exception => e
|
307
|
+
raise RUBYAMFException.new(RUBYAMFException.AMF_ERROR, "The AMF data is incorrect or incomplete.")
|
308
|
+
end
|