amfora 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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