RocketAMF 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/README.rdoc +36 -0
  2. data/Rakefile +54 -0
  3. data/lib/rocketamf.rb +12 -0
  4. data/lib/rocketamf/class_mapping.rb +211 -0
  5. data/lib/rocketamf/common.rb +35 -0
  6. data/lib/rocketamf/constants.rb +47 -0
  7. data/lib/rocketamf/pure.rb +30 -0
  8. data/lib/rocketamf/pure/deserializer.rb +361 -0
  9. data/lib/rocketamf/pure/io_helpers.rb +94 -0
  10. data/lib/rocketamf/pure/remoting.rb +89 -0
  11. data/lib/rocketamf/pure/serializer.rb +327 -0
  12. data/lib/rocketamf/remoting.rb +133 -0
  13. data/lib/rocketamf/values/array_collection.rb +9 -0
  14. data/lib/rocketamf/values/messages.rb +133 -0
  15. data/lib/rocketamf/values/typed_hash.rb +13 -0
  16. data/lib/rocketamf/version.rb +9 -0
  17. data/spec/amf/class_mapping_set_spec.rb +34 -0
  18. data/spec/amf/class_mapping_spec.rb +120 -0
  19. data/spec/amf/deserializer_spec.rb +311 -0
  20. data/spec/amf/request_spec.rb +25 -0
  21. data/spec/amf/response_spec.rb +68 -0
  22. data/spec/amf/serializer_spec.rb +328 -0
  23. data/spec/amf/values/array_collection_spec.rb +6 -0
  24. data/spec/amf/values/messages_spec.rb +31 -0
  25. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  26. data/spec/fixtures/objects/amf0-date.bin +0 -0
  27. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  28. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  29. data/spec/fixtures/objects/amf0-null.bin +1 -0
  30. data/spec/fixtures/objects/amf0-number.bin +0 -0
  31. data/spec/fixtures/objects/amf0-object.bin +0 -0
  32. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  33. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  34. data/spec/fixtures/objects/amf0-string.bin +0 -0
  35. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  36. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  37. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  38. data/spec/fixtures/objects/amf3-0.bin +0 -0
  39. data/spec/fixtures/objects/amf3-arrayRef.bin +1 -0
  40. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  41. data/spec/fixtures/objects/amf3-date.bin +0 -0
  42. data/spec/fixtures/objects/amf3-datesRef.bin +0 -0
  43. data/spec/fixtures/objects/amf3-dynObject.bin +2 -0
  44. data/spec/fixtures/objects/amf3-emptyArray.bin +1 -0
  45. data/spec/fixtures/objects/amf3-emptyArrayRef.bin +1 -0
  46. data/spec/fixtures/objects/amf3-emptyStringRef.bin +1 -0
  47. data/spec/fixtures/objects/amf3-false.bin +1 -0
  48. data/spec/fixtures/objects/amf3-graphMember.bin +0 -0
  49. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  50. data/spec/fixtures/objects/amf3-largeMax.bin +0 -0
  51. data/spec/fixtures/objects/amf3-largeMin.bin +0 -0
  52. data/spec/fixtures/objects/amf3-max.bin +1 -0
  53. data/spec/fixtures/objects/amf3-min.bin +0 -0
  54. data/spec/fixtures/objects/amf3-mixedArray.bin +11 -0
  55. data/spec/fixtures/objects/amf3-null.bin +1 -0
  56. data/spec/fixtures/objects/amf3-objRef.bin +0 -0
  57. data/spec/fixtures/objects/amf3-primArray.bin +1 -0
  58. data/spec/fixtures/objects/amf3-string.bin +1 -0
  59. data/spec/fixtures/objects/amf3-stringRef.bin +0 -0
  60. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  61. data/spec/fixtures/objects/amf3-true.bin +1 -0
  62. data/spec/fixtures/objects/amf3-typedObject.bin +2 -0
  63. data/spec/fixtures/request/acknowledge-response.bin +0 -0
  64. data/spec/fixtures/request/amf0-error-response.bin +0 -0
  65. data/spec/fixtures/request/commandMessage.bin +0 -0
  66. data/spec/fixtures/request/remotingMessage.bin +0 -0
  67. data/spec/fixtures/request/simple-response.bin +0 -0
  68. data/spec/spec.opts +1 -0
  69. data/spec/spec_helper.rb +32 -0
  70. metadata +133 -0
@@ -0,0 +1,94 @@
1
+ module RocketAMF
2
+ module Pure
3
+ module ReadIOHelpers #:nodoc:
4
+ def read_int8 source
5
+ source.read(1).unpack('c').first
6
+ end
7
+
8
+ def read_word8 source
9
+ source.read(1).unpack('C').first
10
+ end
11
+
12
+ def read_double source
13
+ source.read(8).unpack('G').first
14
+ end
15
+
16
+ def read_word16_network source
17
+ source.read(2).unpack('n').first
18
+ end
19
+
20
+ def read_int16_network source
21
+ str = source.read(2)
22
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
23
+ str.unpack('s').first
24
+ end
25
+
26
+ def read_word32_network source
27
+ source.read(4).unpack('N').first
28
+ end
29
+
30
+ def byte_order
31
+ if [0x12345678].pack("L") == "\x12\x34\x56\x78"
32
+ :BigEndian
33
+ else
34
+ :LittleEndian
35
+ end
36
+ end
37
+
38
+ def byte_order_little?
39
+ (byte_order == :LittleEndian) ? true : false;
40
+ end
41
+ end
42
+
43
+ module WriteIOHelpers #:nodoc:
44
+ def pack_integer(integer)
45
+ integer = integer & 0x1fffffff
46
+ if(integer < 0x80)
47
+ [integer].pack('c')
48
+ elsif(integer < 0x4000)
49
+ [integer >> 7 & 0x7f | 0x80].pack('c')+
50
+ [integer & 0x7f].pack('c')
51
+ elsif(integer < 0x200000)
52
+ [integer >> 14 & 0x7f | 0x80].pack('c') +
53
+ [integer >> 7 & 0x7f | 0x80].pack('c') +
54
+ [integer & 0x7f].pack('c')
55
+ else
56
+ [integer >> 22 & 0x7f | 0x80].pack('c')+
57
+ [integer >> 15 & 0x7f | 0x80].pack('c')+
58
+ [integer >> 8 & 0x7f | 0x80].pack('c')+
59
+ [integer & 0xff].pack('c')
60
+ end
61
+ end
62
+
63
+ def pack_double(double)
64
+ [double].pack('G')
65
+ end
66
+
67
+ def pack_int8(val)
68
+ [val].pack('c')
69
+ end
70
+
71
+ def pack_int16_network(val)
72
+ [val].pack('n')
73
+ end
74
+
75
+ def pack_word32_network(val)
76
+ str = [val].pack('L')
77
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
78
+ str
79
+ end
80
+
81
+ def byte_order
82
+ if [0x12345678].pack("L") == "\x12\x34\x56\x78"
83
+ :BigEndian
84
+ else
85
+ :LittleEndian
86
+ end
87
+ end
88
+
89
+ def byte_order_little?
90
+ (byte_order == :LittleEndian) ? true : false;
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,89 @@
1
+ require 'rocketamf/pure/io_helpers'
2
+
3
+ module RocketAMF
4
+ module Pure
5
+ # Request deserialization module - provides a method that can be included into
6
+ # RocketAMF::Request for deserializing the given stream.
7
+ module Request
8
+ def populate_from_stream stream
9
+ stream = StringIO.new(stream) unless StringIO === stream
10
+
11
+ # Initialize
12
+ @amf_version = 0
13
+ @headers = []
14
+ @messages = []
15
+
16
+ # Read AMF version
17
+ @amf_version = read_word16_network stream
18
+
19
+ # Read in headers
20
+ header_count = read_word16_network stream
21
+ 0.upto(header_count-1) do
22
+ name = stream.read(read_word16_network(stream))
23
+ must_understand = read_int8(stream) != 0
24
+ length = read_word32_network stream
25
+ data = RocketAMF.deserialize stream
26
+ @headers << RocketAMF::Header.new(name, must_understand, data)
27
+ end
28
+
29
+ # Read in messages
30
+ message_count = read_word16_network stream
31
+ 0.upto(message_count-1) do
32
+ target_uri = stream.read(read_word16_network(stream))
33
+ response_uri = stream.read(read_word16_network(stream))
34
+ length = read_word32_network stream
35
+ data = RocketAMF.deserialize stream
36
+ if data.is_a?(Array) && data.length == 1 && data[0].is_a?(::RocketAMF::Values::AbstractMessage)
37
+ data = data[0]
38
+ end
39
+ @messages << RocketAMF::Message.new(target_uri, response_uri, data)
40
+ end
41
+
42
+ self
43
+ end
44
+
45
+ private
46
+ include RocketAMF::Pure::ReadIOHelpers
47
+ end
48
+
49
+ # Response serialization module - provides a method that can be included into
50
+ # RocketAMF::Response for deserializing the given stream.
51
+ module Response
52
+ def serialize
53
+ stream = ""
54
+
55
+ # Write version
56
+ stream << pack_int16_network(@amf_version)
57
+
58
+ # Write headers
59
+ stream << pack_int16_network(@headers.length) # Header count
60
+ @headers.each do |h|
61
+ stream << pack_int16_network(h.name.length)
62
+ stream << h.name
63
+ stream << pack_int8(h.must_understand ? 1 : 0)
64
+ stream << pack_word32_network(-1)
65
+ stream << RocketAMF.serialize(h.data, 0)
66
+ end
67
+
68
+ # Write messages
69
+ stream << pack_int16_network(@messages.length) # Message count
70
+ @messages.each do |m|
71
+ stream << pack_int16_network(m.target_uri.length)
72
+ stream << m.target_uri
73
+
74
+ stream << pack_int16_network(m.response_uri.length)
75
+ stream << m.response_uri
76
+
77
+ stream << pack_word32_network(-1)
78
+ stream << AMF0_AMF3_MARKER if @amf_version == 3
79
+ stream << RocketAMF.serialize(m.data, @amf_version)
80
+ end
81
+
82
+ stream
83
+ end
84
+
85
+ private
86
+ include RocketAMF::Pure::WriteIOHelpers
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,327 @@
1
+ require 'rocketamf/pure/io_helpers'
2
+
3
+ module RocketAMF
4
+ module Pure
5
+ # AMF0 implementation of serializer
6
+ class Serializer
7
+ def initialize
8
+ @ref_cache = SerializerCache.new :object
9
+ end
10
+
11
+ def version
12
+ 0
13
+ end
14
+
15
+ def serialize obj, stream = ""
16
+ if obj.respond_to?(:to_amf)
17
+ stream << obj.to_amf(self)
18
+ elsif @ref_cache[obj] != nil
19
+ write_reference @ref_cache[obj], stream
20
+ elsif obj.is_a?(NilClass)
21
+ write_null stream
22
+ elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
23
+ write_boolean obj, stream
24
+ elsif obj.is_a?(Float) || obj.is_a?(Integer)
25
+ write_number obj, stream
26
+ elsif obj.is_a?(Symbol) || obj.is_a?(String)
27
+ write_string obj.to_s, stream
28
+ elsif obj.is_a?(Time)
29
+ write_date obj, stream
30
+ elsif obj.is_a?(Array)
31
+ write_array obj, stream
32
+ elsif obj.is_a?(Hash)
33
+ write_hash obj, stream
34
+ elsif obj.is_a?(Object)
35
+ write_object obj, stream
36
+ end
37
+ stream
38
+ end
39
+
40
+ def write_null stream
41
+ stream << AMF0_NULL_MARKER
42
+ end
43
+
44
+ def write_boolean bool, stream
45
+ stream << AMF0_BOOLEAN_MARKER
46
+ stream << pack_int8(bool ? 1 : 0)
47
+ end
48
+
49
+ def write_number num, stream
50
+ stream << AMF0_NUMBER_MARKER
51
+ stream << pack_double(num)
52
+ end
53
+
54
+ def write_string str, stream
55
+ len = str.length
56
+ if len > 2**16-1
57
+ stream << AMF0_LONG_STRING_MARKER
58
+ stream << pack_word32_network(len)
59
+ else
60
+ stream << AMF0_STRING_MARKER
61
+ stream << pack_int16_network(len)
62
+ end
63
+ stream << str
64
+ end
65
+
66
+ def write_date date, stream
67
+ stream << AMF0_DATE_MARKER
68
+
69
+ date.utc unless date.utc?
70
+ seconds = (date.to_f * 1000).to_i
71
+ stream << pack_double(seconds)
72
+
73
+ stream << pack_int16_network(0)
74
+ end
75
+
76
+ def write_reference index, stream
77
+ stream << AMF0_REFERENCE_MARKER
78
+ stream << pack_int16_network(index)
79
+ end
80
+
81
+ def write_array array, stream
82
+ @ref_cache.add_obj array
83
+ stream << AMF0_STRICT_ARRAY_MARKER
84
+ stream << pack_word32_network(array.length)
85
+ array.each do |elem|
86
+ serialize elem, stream
87
+ end
88
+ end
89
+
90
+ def write_hash hash, stream
91
+ @ref_cache.add_obj hash
92
+ stream << AMF0_HASH_MARKER
93
+ stream << pack_word32_network(hash.length)
94
+ write_prop_list hash, stream
95
+ end
96
+
97
+ def write_object obj, stream
98
+ @ref_cache.add_obj obj
99
+
100
+ # Is it a typed object?
101
+ class_name = ClassMapper.get_as_class_name obj
102
+ if class_name
103
+ stream << AMF0_TYPED_OBJECT_MARKER
104
+ stream << pack_int16_network(class_name.length)
105
+ stream << class_name
106
+ else
107
+ stream << AMF0_OBJECT_MARKER
108
+ end
109
+
110
+ write_prop_list obj, stream
111
+ end
112
+
113
+ private
114
+ include RocketAMF::Pure::WriteIOHelpers
115
+ def write_prop_list obj, stream
116
+ # Write prop list
117
+ props = ClassMapper.props_for_serialization obj
118
+ props.sort.each do |key, value| # Sort keys before writing
119
+ stream << pack_int16_network(key.length)
120
+ stream << key
121
+ serialize value, stream
122
+ end
123
+
124
+ # Write end
125
+ stream << pack_int16_network(0)
126
+ stream << AMF0_OBJECT_END_MARKER
127
+ end
128
+ end
129
+
130
+ # AMF3 implementation of serializer
131
+ class AMF3Serializer
132
+ attr_reader :string_cache
133
+
134
+ def initialize
135
+ @string_cache = SerializerCache.new :string
136
+ @object_cache = SerializerCache.new :object
137
+ end
138
+
139
+ def version
140
+ 3
141
+ end
142
+
143
+ def serialize obj, stream = ""
144
+ if obj.respond_to?(:to_amf)
145
+ stream << obj.to_amf(self)
146
+ elsif obj.is_a?(NilClass)
147
+ write_null stream
148
+ elsif obj.is_a?(TrueClass)
149
+ write_true stream
150
+ elsif obj.is_a?(FalseClass)
151
+ write_false stream
152
+ elsif obj.is_a?(Float)
153
+ write_float obj, stream
154
+ elsif obj.is_a?(Integer)
155
+ write_integer obj, stream
156
+ elsif obj.is_a?(Symbol) || obj.is_a?(String)
157
+ write_string obj.to_s, stream
158
+ elsif obj.is_a?(Time)
159
+ write_date obj, stream
160
+ elsif obj.is_a?(Array)
161
+ write_array obj, stream
162
+ elsif obj.is_a?(Hash) || obj.is_a?(Object)
163
+ write_object obj, stream
164
+ end
165
+ stream
166
+ end
167
+
168
+ def write_reference index, stream
169
+ header = index << 1 # shift value left to leave a low bit of 0
170
+ stream << pack_integer(header)
171
+ end
172
+
173
+ def write_null stream
174
+ stream << AMF3_NULL_MARKER
175
+ end
176
+
177
+ def write_true stream
178
+ stream << AMF3_TRUE_MARKER
179
+ end
180
+
181
+ def write_false stream
182
+ stream << AMF3_FALSE_MARKER
183
+ end
184
+
185
+ def write_integer int, stream
186
+ if int < MIN_INTEGER || int > MAX_INTEGER # Check valid range for 29 bits
187
+ write_float int.to_f, stream
188
+ else
189
+ stream << AMF3_INTEGER_MARKER
190
+ stream << pack_integer(int)
191
+ end
192
+ end
193
+
194
+ def write_float float, stream
195
+ stream << AMF3_DOUBLE_MARKER
196
+ stream << pack_double(float)
197
+ end
198
+
199
+ def write_string str, stream
200
+ stream << AMF3_STRING_MARKER
201
+ write_utf8_vr str, stream
202
+ end
203
+
204
+ def write_date date, stream
205
+ stream << AMF3_DATE_MARKER
206
+ if @object_cache[date] != nil
207
+ write_reference @object_cache[date], stream
208
+ else
209
+ # Cache date
210
+ @object_cache.add_obj date
211
+
212
+ # Build AMF string
213
+ date.utc unless date.utc?
214
+ seconds = (date.to_f * 1000).to_i
215
+ stream << pack_integer(AMF3_NULL_MARKER)
216
+ stream << pack_double(seconds)
217
+ end
218
+ end
219
+
220
+ def write_array array, stream
221
+ stream << AMF3_ARRAY_MARKER
222
+ if @object_cache[array] != nil
223
+ write_reference @object_cache[array], stream
224
+ else
225
+ # Cache array
226
+ @object_cache.add_obj array
227
+
228
+ # Build AMF string
229
+ header = array.length << 1 # make room for a low bit of 1
230
+ header = header | 1 # set the low bit to 1
231
+ stream << pack_integer(header)
232
+ stream << AMF3_CLOSE_DYNAMIC_ARRAY
233
+ array.each do |elem|
234
+ serialize elem, stream
235
+ end
236
+ end
237
+ end
238
+
239
+ def write_object obj, stream
240
+ stream << AMF3_OBJECT_MARKER
241
+ if @object_cache[obj] != nil
242
+ write_reference @object_cache[obj], stream
243
+ else
244
+ # Cache object
245
+ @object_cache.add_obj obj
246
+
247
+ # Always serialize things as dynamic objects
248
+ stream << AMF3_DYNAMIC_OBJECT
249
+
250
+ # Write class name/anonymous
251
+ class_name = ClassMapper.get_as_class_name obj
252
+ if class_name
253
+ write_utf8_vr class_name, stream
254
+ else
255
+ stream << AMF3_ANONYMOUS_OBJECT
256
+ end
257
+
258
+ # Write out properties
259
+ props = ClassMapper.props_for_serialization obj
260
+ props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
261
+ write_utf8_vr key.to_s, stream
262
+ serialize val, stream
263
+ end
264
+
265
+ # Write close
266
+ stream << AMF3_CLOSE_DYNAMIC_OBJECT
267
+ end
268
+ end
269
+
270
+ private
271
+ include RocketAMF::Pure::WriteIOHelpers
272
+
273
+ def write_utf8_vr str, stream
274
+ if str == ''
275
+ stream << AMF3_EMPTY_STRING
276
+ elsif @string_cache[str] != nil
277
+ write_reference @string_cache[str], stream
278
+ else
279
+ # Cache string
280
+ @string_cache.add_obj str
281
+
282
+ # Build AMF string
283
+ header = str.length << 1 # make room for a low bit of 1
284
+ header = header | 1 # set the low bit to 1
285
+ stream << pack_integer(header)
286
+ stream << str
287
+ end
288
+ end
289
+ end
290
+
291
+ class SerializerCache #:nodoc:
292
+ def self.new type
293
+ if type == :string
294
+ StringCache.new
295
+ elsif type == :object
296
+ ObjectCache.new
297
+ end
298
+ end
299
+
300
+ class StringCache < Hash #:nodoc:
301
+ def initialize
302
+ @cache_index = 0
303
+ end
304
+
305
+ def add_obj str
306
+ self[str] = @cache_index
307
+ @cache_index += 1
308
+ end
309
+ end
310
+
311
+ class ObjectCache < Hash #:nodoc:
312
+ def initialize
313
+ @cache_index = 0
314
+ end
315
+
316
+ def [] obj
317
+ super(obj.object_id)
318
+ end
319
+
320
+ def add_obj obj
321
+ self[obj.object_id] = @cache_index
322
+ @cache_index += 1
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end