amfora 0.0.1

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.
@@ -0,0 +1,94 @@
1
+ module AMF
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,119 @@
1
+ require 'amf/pure/io_helpers'
2
+
3
+ module AMF
4
+ module Pure
5
+ # AMF request object wrapper, it is responsible for deserializing AMF requests
6
+ class Request
7
+ attr_reader :amf_version, :headers, :message
8
+
9
+ def initialize
10
+ @amf_version = 3
11
+ @headers = []
12
+ end
13
+
14
+ def populate_from_stream(stream)
15
+ stream = StringIO.new(stream) unless StringIO === stream
16
+
17
+ # Read AMF version
18
+ @amf_version = read_word16_network(stream)
19
+
20
+ # Read in headers
21
+ header_count = read_word16_network(stream)
22
+ 0.upto(header_count-1) do
23
+ name = stream.read(read_word16_network(stream))
24
+ must_understand = read_int8(stream) != 0
25
+ length = read_word32_network(stream)
26
+ data = AMF.deserialize(stream)
27
+ @headers << Header.new(name, must_understand, data)
28
+ end
29
+
30
+ # Read in first message
31
+ message_count = read_word16_network(stream)
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 = AMF.deserialize(stream)
36
+ if data.is_a?(Array) && data.length == 1 && data[0].is_a?(::AMF::Messages::AbstractMessage)
37
+ data = data[0]
38
+ end
39
+ @message = Message.new(target_uri, response_uri, data)
40
+
41
+ self
42
+ end
43
+
44
+ private
45
+ include AMF::Pure::ReadIOHelpers
46
+ end
47
+
48
+ # AMF response object wrapper, it is responsible for serializing the AMF response
49
+ class Response
50
+ attr_accessor :amf_version, :headers, :message
51
+
52
+ def initialize
53
+ @amf_version = 0
54
+ @headers = []
55
+ end
56
+
57
+ def serialize
58
+ stream = ""
59
+
60
+ # Write version
61
+ stream << pack_int16_network(@amf_version)
62
+
63
+ # Write headers
64
+ stream << pack_int16_network(@headers.length) # Header count
65
+ @headers.each do |h|
66
+ stream << pack_int16_network(h.name.length)
67
+ stream << h.name
68
+ stream << pack_int8(h.must_understand ? 1 : 0)
69
+ stream << pack_word32_network(-1)
70
+ stream << AMF.serialize(h.data, 0)
71
+ end
72
+
73
+ # Write messages
74
+ stream << pack_int16_network(1) # we only send one message
75
+ stream << pack_int16_network(@message.target_uri.length)
76
+ stream << @message.target_uri
77
+
78
+ stream << pack_int16_network(@message.response_uri.length)
79
+ stream << @message.response_uri
80
+
81
+ stream << pack_word32_network(-1)
82
+ stream << AMF0_AMF3_MARKER if @amf_version == 3
83
+ # stream << AMF.serialize(m.data, @amf_version)
84
+ stream << @message.data
85
+
86
+ stream
87
+ end
88
+
89
+ def to_s
90
+ serialize
91
+ end
92
+
93
+ private
94
+ include AMF::Pure::WriteIOHelpers
95
+ end
96
+
97
+ # AMF::Request or AMF::Response header
98
+ class Header
99
+ attr_accessor :name, :must_understand, :data
100
+
101
+ def initialize name, must_understand, data
102
+ @name = name
103
+ @must_understand = must_understand
104
+ @data = data
105
+ end
106
+ end
107
+
108
+ # AMF::Request or AMF::Response message
109
+ class Message
110
+ attr_accessor :target_uri, :response_uri, :data
111
+
112
+ def initialize target_uri, response_uri, data
113
+ @target_uri = target_uri
114
+ @response_uri = response_uri
115
+ @data = data
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,230 @@
1
+ require 'amf/pure/io_helpers'
2
+
3
+ module AMF
4
+ module Pure
5
+ class AMF0Serializer
6
+ def version
7
+ 0
8
+ end
9
+
10
+ def serialize(obj, &block)
11
+
12
+ end
13
+ end
14
+
15
+ # AMF3 implementation of serializer
16
+ class AMF3Serializer
17
+ attr_accessor :stream
18
+ attr_reader :string_cache
19
+
20
+ def initialize(params = {})
21
+ @stream = ""
22
+ @string_cache = SerializerCache.new :string
23
+ @object_cache = SerializerCache.new :object
24
+ @serializable_names = params[:serializable_names]
25
+ @opts = params[:options]
26
+ end
27
+
28
+ def version
29
+ 3
30
+ end
31
+
32
+ def serialize(obj, &block)
33
+ if obj.is_a?(NilClass)
34
+ write_null
35
+ elsif obj.is_a?(TrueClass)
36
+ write_true
37
+ elsif obj.is_a?(FalseClass)
38
+ write_false
39
+ elsif obj.is_a?(Float)
40
+ write_float(obj)
41
+ elsif obj.is_a?(Integer)
42
+ write_integer(obj)
43
+ elsif obj.is_a?(Symbol) || obj.is_a?(String)
44
+ write_string(obj.to_s)
45
+ elsif obj.is_a?(Time)
46
+ write_date(obj)
47
+ elsif obj.is_a?(Array)
48
+ write_array(obj, &block)
49
+ elsif obj.is_a?(Hash) || obj.is_a?(Object)
50
+ write_object(obj, &block)
51
+ end
52
+ @stream
53
+ end
54
+
55
+ def write_reference index
56
+ header = index << 1 # shift value left to leave a low bit of 0
57
+ @stream << pack_integer(header)
58
+ end
59
+
60
+ def write_null
61
+ @stream << AMF3_NULL_MARKER
62
+ end
63
+
64
+ def write_true
65
+ @stream << AMF3_TRUE_MARKER
66
+ end
67
+
68
+ def write_false
69
+ @stream << AMF3_FALSE_MARKER
70
+ end
71
+
72
+ def write_integer(int)
73
+ if int < MIN_INTEGER || int > MAX_INTEGER # Check valid range for 29 bits
74
+ write_float(int.to_f)
75
+ else
76
+ @stream << AMF3_INTEGER_MARKER
77
+ @stream << pack_integer(int)
78
+ end
79
+ end
80
+
81
+ def write_float(float)
82
+ @stream << AMF3_DOUBLE_MARKER
83
+ @stream << pack_double(float)
84
+ end
85
+
86
+ def write_string(str)
87
+ @stream << AMF3_STRING_MARKER
88
+ write_utf8_vr(str)
89
+ end
90
+
91
+ def write_date(date)
92
+ @stream << AMF3_DATE_MARKER
93
+ if @object_cache[date] != nil
94
+ write_reference(@object_cache[date])
95
+ else
96
+ # Cache date
97
+ @object_cache.add_obj(date)
98
+
99
+ # Build AMF string
100
+ date.utc unless date.utc?
101
+ seconds = (date.to_f * 1000).to_i
102
+ @stream << pack_integer(AMF3_NULL_MARKER)
103
+ @stream << pack_double(seconds)
104
+ end
105
+ end
106
+
107
+ def write_array(array)
108
+ @stream << AMF3_ARRAY_MARKER
109
+ if @object_cache[array] != nil
110
+ write_reference(@object_cache[array])
111
+ else
112
+ # Cache array
113
+ @object_cache.add_obj(array)
114
+
115
+ # Build AMF string
116
+ header = array.length << 1 # make room for a low bit of 1
117
+ header = header | 1 # set the low bit to 1
118
+ @stream << pack_integer(header)
119
+ @stream << AMF3_CLOSE_DYNAMIC_ARRAY
120
+ array.each do |elem|
121
+ if elem.respond_to?(:to_amf)
122
+ @stream << elem.to_amf(@opts)
123
+ else
124
+ serialize(elem)
125
+ end
126
+ end
127
+
128
+ block.call(self) if block_given?
129
+ end
130
+ end
131
+
132
+ def write_object(obj, &block)
133
+ @stream << AMF3_OBJECT_MARKER
134
+ if @object_cache[obj] != nil
135
+ write_reference(@object_cache[obj])
136
+ else
137
+ # Cache object
138
+ @object_cache.add_obj(obj)
139
+
140
+ # Always serialize things as dynamic objects
141
+ @stream << AMF3_DYNAMIC_OBJECT
142
+
143
+ if obj.is_a?(Hash)
144
+ @stream << AMF3_ANONYMOUS_OBJECT
145
+
146
+ # Stringify keys to make it easier later on and allow sorting
147
+ obj.each do |k,v|
148
+ write_utf8_vr(k.to_s)
149
+ serialize(obj[k])
150
+ end
151
+ else
152
+ # Write class name/anonymous
153
+ class_name = AMF::ClassMapper.get_as_class_name(obj)
154
+ if class_name
155
+ write_utf8_vr(class_name)
156
+ else
157
+ @stream << AMF3_ANONYMOUS_OBJECT
158
+ end
159
+
160
+ @serializable_names.sort.each do |name|
161
+ write_utf8_vr(name.to_s.camelize(:lower))
162
+ serialize(obj.send(name))
163
+ end
164
+ end
165
+
166
+ block.call(self) if block_given?
167
+
168
+ # Write close
169
+ @stream << AMF3_CLOSE_DYNAMIC_OBJECT
170
+ end
171
+ end
172
+
173
+ def write_utf8_vr(str)
174
+ if str == ''
175
+ @stream << AMF3_EMPTY_STRING
176
+ elsif @string_cache[str] != nil
177
+ write_reference(@string_cache[str])
178
+ else
179
+ # Cache string
180
+ @string_cache.add_obj(str)
181
+
182
+ # Build AMF string
183
+ header = str.length << 1 # make room for a low bit of 1
184
+ header = header | 1 # set the low bit to 1
185
+ @stream << pack_integer(header)
186
+ @stream << str
187
+ end
188
+ end
189
+
190
+ private
191
+ include AMF::Pure::WriteIOHelpers
192
+ end
193
+
194
+ class SerializerCache #:nodoc:all:
195
+ def self.new(type)
196
+ if type == :string
197
+ StringCache.new
198
+ elsif type == :object
199
+ ObjectCache.new
200
+ end
201
+ end
202
+
203
+ class StringCache < Hash
204
+ def initialize
205
+ @cache_index = 0
206
+ end
207
+
208
+ def add_obj(str)
209
+ self[str] = @cache_index
210
+ @cache_index += 1
211
+ end
212
+ end
213
+
214
+ class ObjectCache < Hash
215
+ def initialize
216
+ @cache_index = 0
217
+ end
218
+
219
+ def [](obj)
220
+ super(obj.object_id)
221
+ end
222
+
223
+ def add_obj(obj)
224
+ self[obj.object_id] = @cache_index
225
+ @cache_index += 1
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,9 @@
1
+ module AMF
2
+ # AMF version
3
+ VERSION = '0.0.1'
4
+ VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
7
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
8
+ VARIANT_BINARY = false
9
+ end