rack-amf 0.0.2 → 0.0.3
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/README.rdoc +2 -2
- data/Rakefile +2 -2
- data/lib/amf/constants.rb +9 -8
- data/lib/amf/pure/deserializer.rb +18 -10
- data/lib/amf/pure/remoting.rb +3 -3
- data/lib/amf/pure/serializer.rb +142 -30
- data/lib/amf/version.rb +1 -1
- data/lib/rack/amf.rb +61 -10
- data/lib/rack/amf/environment.rb +34 -0
- data/lib/rack/amf/middleware.rb +35 -0
- data/lib/rack/amf/middleware/pass_through.rb +20 -0
- data/lib/rack/amf/middleware/rails.rb +39 -0
- data/lib/rack/amf/middleware/service_manager.rb +38 -0
- data/lib/rack/amf/rails/rack_amf_controller.rb +23 -0
- data/lib/rack/amf/request.rb +6 -0
- data/lib/rack/amf/response.rb +44 -20
- data/spec/amf/class_mapping_spec.rb +11 -11
- data/spec/amf/deserializer_spec.rb +12 -0
- data/spec/amf/remoting_spec.rb +2 -0
- data/spec/amf/serializer_spec.rb +76 -0
- data/spec/amf/values/messages_spec.rb +5 -1
- data/spec/spec_helper.rb +9 -0
- metadata +8 -4
- data/lib/rack/amf/application.rb +0 -32
- data/lib/rack/amf/service_manager.rb +0 -35
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
== DESCRIPTION:
|
2
2
|
|
3
|
-
|
3
|
+
A full featured AMF gateway implemented as a Rack middleware. Includes fully compliant AMF0/AMF3 serializers and deserializers and a Rack middleware that includes a service handler and the ability to easily handle requests yourself if you don't want to use the service handler.
|
4
4
|
|
5
5
|
== INSTALL:
|
6
6
|
|
@@ -19,7 +19,7 @@ config.ru:
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
Rack::AMF::
|
22
|
+
Rack::AMF::Environment.register_service 'TestService', TestService.new
|
23
23
|
|
24
24
|
run lambda {|env| [200, { 'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"] ] }
|
25
25
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,7 @@ end
|
|
23
23
|
|
24
24
|
spec = Gem::Specification.new do |s|
|
25
25
|
s.name = 'rack-amf'
|
26
|
-
s.version = '0.0.
|
26
|
+
s.version = '0.0.3'
|
27
27
|
s.summary = 'AMF serializer/deserializer and AMF gateway packaged as a rack middleware'
|
28
28
|
|
29
29
|
s.files = FileList['README.rdoc', 'Rakefile', 'lib/**/*.rb', 'spec/**/*.rb']
|
@@ -51,4 +51,4 @@ task :gemspec do
|
|
51
51
|
File.open("#{spec.name}.gemspec", 'w') do |f|
|
52
52
|
f.write spec.to_ruby
|
53
53
|
end
|
54
|
-
end
|
54
|
+
end
|
data/lib/amf/constants.rb
CHANGED
@@ -34,13 +34,14 @@ module AMF
|
|
34
34
|
AMF3_XML_MARKER = 0x0B #"\v"
|
35
35
|
AMF3_BYTE_ARRAY_MARKER = 0x0C #"\f"
|
36
36
|
|
37
|
-
# Other Markers
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
# Other AMF3 Markers
|
38
|
+
AMF3_EMPTY_STRING = 0x01
|
39
|
+
AMF3_ANONYMOUS_OBJECT = 0x01
|
40
|
+
AMF3_DYNAMIC_OBJECT = 0x0B
|
41
|
+
AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
|
42
|
+
AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
# Other Constants
|
45
|
+
MAX_INTEGER = 268435455
|
46
|
+
MIN_INTEGER = -268435456
|
46
47
|
end
|
@@ -64,15 +64,15 @@ module AMF
|
|
64
64
|
source.read(len)
|
65
65
|
end
|
66
66
|
|
67
|
-
def read_object source
|
67
|
+
def read_object source, add_to_ref_cache=true
|
68
68
|
obj = {}
|
69
|
+
@ref_cache << obj if add_to_ref_cache
|
69
70
|
while true
|
70
71
|
key = read_string source
|
71
72
|
type = read_int8 source
|
72
73
|
break if type == AMF0_OBJECT_END_MARKER
|
73
74
|
obj[key.to_sym] = deserialize(source, type)
|
74
75
|
end
|
75
|
-
@ref_cache << obj
|
76
76
|
obj
|
77
77
|
end
|
78
78
|
|
@@ -93,6 +93,8 @@ module AMF
|
|
93
93
|
if key.to_i.to_s == key
|
94
94
|
# Array
|
95
95
|
obj = []
|
96
|
+
@ref_cache << obj
|
97
|
+
|
96
98
|
obj[key.to_i] = deserialize(source, type)
|
97
99
|
while true
|
98
100
|
key = read_string source
|
@@ -102,7 +104,10 @@ module AMF
|
|
102
104
|
end
|
103
105
|
else
|
104
106
|
# Hash
|
105
|
-
obj = {
|
107
|
+
obj = {}
|
108
|
+
@ref_cache << obj
|
109
|
+
|
110
|
+
obj[key.to_sym] = deserialize(source, type)
|
106
111
|
while true
|
107
112
|
key = read_string source
|
108
113
|
type = read_int8 source
|
@@ -110,17 +115,17 @@ module AMF
|
|
110
115
|
obj[key.to_sym] = deserialize(source, type)
|
111
116
|
end
|
112
117
|
end
|
113
|
-
@ref_cache << obj
|
114
118
|
obj
|
115
119
|
end
|
116
120
|
|
117
121
|
def read_array source
|
118
122
|
len = read_word32_network(source)
|
119
123
|
array = []
|
124
|
+
@ref_cache << array
|
125
|
+
|
120
126
|
0.upto(len - 1) do
|
121
127
|
array << deserialize(source)
|
122
128
|
end
|
123
|
-
@ref_cache << array
|
124
129
|
array
|
125
130
|
end
|
126
131
|
|
@@ -132,14 +137,17 @@ module AMF
|
|
132
137
|
end
|
133
138
|
|
134
139
|
def read_typed_object source
|
140
|
+
# Create object to add to ref cache
|
135
141
|
class_name = read_string source
|
136
|
-
props = read_object source
|
137
|
-
@ref_cache.pop
|
138
|
-
|
139
142
|
obj = ClassMapper.get_ruby_obj class_name
|
140
|
-
ClassMapper.populate_ruby_obj obj, props, {}
|
141
143
|
@ref_cache << obj
|
142
|
-
|
144
|
+
|
145
|
+
# Read object props
|
146
|
+
props = read_object source, false
|
147
|
+
|
148
|
+
# Populate object
|
149
|
+
ClassMapper.populate_ruby_obj obj, props, {}
|
150
|
+
return obj
|
143
151
|
end
|
144
152
|
end
|
145
153
|
|
data/lib/amf/pure/remoting.rb
CHANGED
@@ -53,7 +53,7 @@ module AMF
|
|
53
53
|
attr_accessor :amf_version, :headers, :messages
|
54
54
|
|
55
55
|
def initialize
|
56
|
-
@amf_version =
|
56
|
+
@amf_version = 0
|
57
57
|
@headers = []
|
58
58
|
@messages = []
|
59
59
|
end
|
@@ -95,7 +95,7 @@ module AMF
|
|
95
95
|
include AMF::Pure::WriteIOHelpers
|
96
96
|
end
|
97
97
|
|
98
|
-
# AMF::Request or AMF::Response header
|
98
|
+
# AMF::Pure::Request or AMF::Pure::Response header
|
99
99
|
class Header
|
100
100
|
attr_accessor :name, :must_understand, :data
|
101
101
|
|
@@ -106,7 +106,7 @@ module AMF
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
# AMF::Request or AMF::Response message
|
109
|
+
# AMF::Pure::Request or AMF::Pure::Response message
|
110
110
|
class Message
|
111
111
|
attr_accessor :target_uri, :response_uri, :data
|
112
112
|
|
data/lib/amf/pure/serializer.rb
CHANGED
@@ -5,7 +5,7 @@ module AMF
|
|
5
5
|
# AMF0 implementation of serializer
|
6
6
|
class Serializer
|
7
7
|
def initialize
|
8
|
-
@ref_cache = SerializerCache.new
|
8
|
+
@ref_cache = SerializerCache.new :object
|
9
9
|
end
|
10
10
|
|
11
11
|
def version
|
@@ -13,9 +13,117 @@ module AMF
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def serialize obj, stream = ""
|
16
|
-
if
|
17
|
-
|
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 AMF::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
|
18
122
|
end
|
123
|
+
|
124
|
+
# Write end
|
125
|
+
stream << pack_int16_network(0)
|
126
|
+
stream << AMF0_OBJECT_END_MARKER
|
19
127
|
end
|
20
128
|
end
|
21
129
|
|
@@ -24,8 +132,8 @@ module AMF
|
|
24
132
|
attr_reader :string_cache
|
25
133
|
|
26
134
|
def initialize
|
27
|
-
@string_cache = SerializerCache.new
|
28
|
-
@object_cache = SerializerCache.new
|
135
|
+
@string_cache = SerializerCache.new :string
|
136
|
+
@object_cache = SerializerCache.new :object
|
29
137
|
end
|
30
138
|
|
31
139
|
def version
|
@@ -121,7 +229,7 @@ module AMF
|
|
121
229
|
header = array.length << 1 # make room for a low bit of 1
|
122
230
|
header = header | 1 # set the low bit to 1
|
123
231
|
stream << pack_integer(header)
|
124
|
-
stream <<
|
232
|
+
stream << AMF3_CLOSE_DYNAMIC_ARRAY
|
125
233
|
array.each do |elem|
|
126
234
|
serialize elem, stream
|
127
235
|
end
|
@@ -137,14 +245,14 @@ module AMF
|
|
137
245
|
@object_cache.add_obj obj
|
138
246
|
|
139
247
|
# Always serialize things as dynamic objects
|
140
|
-
stream <<
|
248
|
+
stream << AMF3_DYNAMIC_OBJECT
|
141
249
|
|
142
250
|
# Write class name/anonymous
|
143
251
|
class_name = ClassMapper.get_as_class_name obj
|
144
252
|
if class_name
|
145
253
|
write_utf8_vr class_name, stream
|
146
254
|
else
|
147
|
-
stream <<
|
255
|
+
stream << AMF3_ANONYMOUS_OBJECT
|
148
256
|
end
|
149
257
|
|
150
258
|
# Write out properties
|
@@ -155,7 +263,7 @@ module AMF
|
|
155
263
|
end
|
156
264
|
|
157
265
|
# Write close
|
158
|
-
stream <<
|
266
|
+
stream << AMF3_CLOSE_DYNAMIC_OBJECT
|
159
267
|
end
|
160
268
|
end
|
161
269
|
|
@@ -164,7 +272,7 @@ module AMF
|
|
164
272
|
|
165
273
|
def write_utf8_vr str, stream
|
166
274
|
if str == ''
|
167
|
-
stream <<
|
275
|
+
stream << AMF3_EMPTY_STRING
|
168
276
|
elsif @string_cache[str] != nil
|
169
277
|
write_reference @string_cache[str], stream
|
170
278
|
else
|
@@ -181,33 +289,37 @@ module AMF
|
|
181
289
|
end
|
182
290
|
|
183
291
|
class SerializerCache #:nodoc:
|
184
|
-
def
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
@store[object_key(obj)]
|
292
|
+
def self.new type
|
293
|
+
if type == :string
|
294
|
+
StringCache.new
|
295
|
+
elsif type == :object
|
296
|
+
ObjectCache.new
|
297
|
+
end
|
191
298
|
end
|
192
299
|
|
193
|
-
|
194
|
-
|
195
|
-
|
300
|
+
class StringCache < Hash #:nodoc:
|
301
|
+
def initialize
|
302
|
+
@cache_index = 0
|
303
|
+
end
|
196
304
|
|
197
|
-
|
198
|
-
|
199
|
-
if @store[key].nil?
|
200
|
-
@store[key] = @cache_index
|
305
|
+
def add_obj str
|
306
|
+
self[str] = @cache_index
|
201
307
|
@cache_index += 1
|
202
308
|
end
|
203
309
|
end
|
204
310
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
211
323
|
end
|
212
324
|
end
|
213
325
|
end
|
data/lib/amf/version.rb
CHANGED
data/lib/rack/amf.rb
CHANGED
@@ -1,17 +1,68 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'amf'
|
3
|
-
|
4
|
-
require 'rack/amf/application'
|
5
|
-
require 'rack/amf/service_manager'
|
6
|
-
require 'rack/amf/request'
|
7
|
-
require 'rack/amf/response'
|
3
|
+
require 'rack/amf/environment'
|
8
4
|
|
9
5
|
module Rack::AMF
|
10
|
-
|
6
|
+
def self.new app, options={} #:nodoc:
|
7
|
+
# Set default mode
|
8
|
+
options[:mode] = :service_manager if !options[:mode]
|
9
|
+
|
10
|
+
# Which version of the middleware?
|
11
|
+
if options[:mode] == :pass_through
|
12
|
+
require 'rack/amf/middleware/pass_through'
|
13
|
+
Middleware::PassThrough.new(app, options)
|
14
|
+
elsif options[:mode] == :service_manager
|
15
|
+
require 'rack/amf/middleware/service_manager'
|
16
|
+
Middleware::ServiceManager.new(app, options)
|
17
|
+
else
|
18
|
+
raise "Invalide mode: #{options[:mode]}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
=begin
|
23
|
+
require 'rack'
|
24
|
+
require 'amf'
|
25
|
+
require 'rack/amf/environment'
|
26
|
+
|
27
|
+
# Rack::AMF middleware
|
28
|
+
module Rack::AMF; end;
|
29
|
+
|
30
|
+
# Bootstrap based on environment
|
31
|
+
if defined?(Rails)
|
32
|
+
# Load in needed files
|
33
|
+
require 'rack/amf/middleware/rails'
|
34
|
+
|
35
|
+
#--
|
36
|
+
# Then we'll modify new to return the middleware to use in rails
|
37
|
+
module Rack::AMF
|
38
|
+
def self.new app, options={} #:nodoc:
|
39
|
+
Middleware::Rails.new(app, options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Set default configs
|
44
|
+
Rack::AMF::Environment.url = '/amf'
|
45
|
+
Rack::AMF::Environment.mode = :rails
|
11
46
|
|
12
|
-
|
47
|
+
# Install rails mods
|
48
|
+
Rack::AMF::Middleware::Rails.install_environment
|
49
|
+
else
|
50
|
+
module Rack::AMF
|
51
|
+
def self.new app, options={} #:nodoc:
|
52
|
+
# Set default mode
|
53
|
+
options[:mode] = :service_manager if !options[:mode]
|
13
54
|
|
14
|
-
|
15
|
-
|
55
|
+
# Which version of the middleware?
|
56
|
+
if options[:mode] == :pass_through
|
57
|
+
require 'rack/amf/middleware/pass_through'
|
58
|
+
Middleware::PassThrough.new(app, options)
|
59
|
+
elsif options[:mode] == :service_manager
|
60
|
+
require 'rack/amf/middleware/service_manager'
|
61
|
+
Middleware::ServiceManager.new(app, options)
|
62
|
+
else
|
63
|
+
raise "Invalide mode: #{options[:mode]}"
|
64
|
+
end
|
65
|
+
end
|
16
66
|
end
|
17
|
-
end
|
67
|
+
end
|
68
|
+
=end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rack::AMF
|
2
|
+
module Environment
|
3
|
+
class << self
|
4
|
+
attr_accessor :url, :mode, :debug, :services
|
5
|
+
debug = false # Set to off by default
|
6
|
+
|
7
|
+
# Used to register a service for use with the ServiceManager middleware.
|
8
|
+
# To register a service, simply pass in the root path for the service and
|
9
|
+
# an object that can receive service calls.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# Rack::AMF::Environment.register_service 'SpecialService', SpecialService.new
|
14
|
+
# Rack::AMF::Environment.register_service 'org.rack-amf.AMFService', AMFService.new
|
15
|
+
def register_service path, service
|
16
|
+
@services ||= {}
|
17
|
+
@services[path] = service
|
18
|
+
end
|
19
|
+
|
20
|
+
# Populates the environment from the given options hash, which was passed
|
21
|
+
# in through rack
|
22
|
+
def populate options={} #:nodoc:
|
23
|
+
url = options[:url] if options.key?(:url)
|
24
|
+
debug = options[:debug] if options.key?(:debug)
|
25
|
+
mode = options[:mode] if options.key?(:mode)
|
26
|
+
end
|
27
|
+
|
28
|
+
def log data #:nodoc:
|
29
|
+
return if !debug
|
30
|
+
puts data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rack/amf/request'
|
2
|
+
require 'rack/amf/response'
|
3
|
+
|
4
|
+
module Rack::AMF
|
5
|
+
# Provide some helper items that can be included in the various middleware
|
6
|
+
# being offered.
|
7
|
+
module Middleware #:nodoc:
|
8
|
+
APPLICATION_AMF = 'application/x-amf'.freeze
|
9
|
+
|
10
|
+
# Standard middleware call method. Calls "handle" with the environment after
|
11
|
+
# creating the request and response objects, and handles serializing the
|
12
|
+
# response after the middleware is done.
|
13
|
+
def call env #:nodoc:
|
14
|
+
return @app.call(env) unless should_handle?(env)
|
15
|
+
|
16
|
+
# Wrap request and response
|
17
|
+
env['rack-amf.request'] = Request.new(env)
|
18
|
+
env['rack-amf.response'] = Response.new(env['rack-amf.request'])
|
19
|
+
|
20
|
+
# Call handle on "inheriting" class
|
21
|
+
handle env
|
22
|
+
|
23
|
+
# Calculate length and return response
|
24
|
+
response = env['rack-amf.response'].to_s
|
25
|
+
[200, {"Content-Type" => APPLICATION_AMF, 'Content-Length' => response.length.to_s}, [response]]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Check if we should handle it based on the environment
|
29
|
+
def should_handle? env #:nodoc:
|
30
|
+
return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
|
31
|
+
return false if Rack::AMF::Environment.url && env['PATH_INFO'] != Rack::AMF::Environment.url
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rack/amf/middleware'
|
2
|
+
|
3
|
+
module Rack::AMF::Middleware #:nodoc:
|
4
|
+
# Middleware which simply passes AMF requests through. Sets env['rack-amf.request']
|
5
|
+
# to the Rack::AMF::Request object and env['rack-amf.response'] to the
|
6
|
+
# Rack::AMF::Response object. Simply modify the response as necessary and it
|
7
|
+
# will be automatically serialized and sent.
|
8
|
+
class PassThrough
|
9
|
+
include Rack::AMF::Middleware
|
10
|
+
|
11
|
+
def initialize app, options={}
|
12
|
+
@app = app
|
13
|
+
Rack::AMF::Environment.populate options
|
14
|
+
end
|
15
|
+
|
16
|
+
def handle
|
17
|
+
@app.call env
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rack/amf/middleware'
|
2
|
+
|
3
|
+
module Rack::AMF::Middleware #:nodoc:
|
4
|
+
class Rails
|
5
|
+
include Rack::AMF::Middleware
|
6
|
+
|
7
|
+
def initialize app, options={}
|
8
|
+
@app = app
|
9
|
+
|
10
|
+
options.delete(:url) # Too late to modify the URL
|
11
|
+
Rack::AMF::Environment.populate options
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle env
|
15
|
+
@app.call env
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.install_environment
|
19
|
+
return if @installed
|
20
|
+
@installed = true
|
21
|
+
|
22
|
+
# Load in files
|
23
|
+
extras_dir = File.dirname(__FILE__)+'/../rails'
|
24
|
+
Dir["#{extras_dir}/*.rb"].each {|f| require f}
|
25
|
+
|
26
|
+
# Install route
|
27
|
+
ActionController::Routing::RouteSet.class_eval do
|
28
|
+
next if self.instance_methods.include? 'draw_with_rackamf'
|
29
|
+
def draw_with_rackamf
|
30
|
+
draw_without_rackamf do |map|
|
31
|
+
map.rack_amf Rack::AMF::Environment.url, :controller => 'rack_amf', :action => 'handle'
|
32
|
+
yield map
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias_method_chain :draw, :rackamf
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rack/amf/middleware'
|
2
|
+
|
3
|
+
module Rack::AMF::Middleware #:nodoc:
|
4
|
+
# Internal AMF handler, it uses the ServiceManager to handle request service
|
5
|
+
# mapping.
|
6
|
+
class ServiceManager
|
7
|
+
include Rack::AMF::Middleware
|
8
|
+
|
9
|
+
def initialize app, options={}
|
10
|
+
@app = app
|
11
|
+
Rack::AMF::Environment.populate options
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle env
|
15
|
+
env['rack-amf.response'].each_method_call do |method, args|
|
16
|
+
handle_method method, args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def handle_method method, args
|
22
|
+
path = method.split('.')
|
23
|
+
method_name = path.pop
|
24
|
+
path = path.join('.')
|
25
|
+
|
26
|
+
s = Rack::AMF::Environment.services
|
27
|
+
if s[path]
|
28
|
+
if s[path].respond_to?(method_name)
|
29
|
+
s[path].send(method_name, *args)
|
30
|
+
else
|
31
|
+
raise "Service #{path} does not respond to #{method_name}"
|
32
|
+
end
|
33
|
+
else
|
34
|
+
raise "Service #{path} does not exist"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class RackAmfController < ApplicationController
|
2
|
+
def handle
|
3
|
+
if request.env['rack-amf.request']
|
4
|
+
RAILS_DEFAULT_LOGGER.info "[rack-amf] Handle request"
|
5
|
+
request.env['rack-amf.response'].each_method_call do |method, args|
|
6
|
+
handle_call method, args
|
7
|
+
end
|
8
|
+
else
|
9
|
+
render :text => '<html><body>Rack::AMF Gateway</body></html>'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def handle_call method, args
|
15
|
+
path = method.split('.')
|
16
|
+
raise "Invalid method '#{method}': Methods must look like this 'ServiceController.method'" if path.length != 2
|
17
|
+
|
18
|
+
action = path.pop
|
19
|
+
controller = path.pop
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/rack/amf/request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Rack::AMF
|
2
|
+
# Rack specific wrapper around AMF::Request
|
2
3
|
class Request
|
3
4
|
attr_reader :raw_request
|
4
5
|
|
@@ -7,6 +8,11 @@ module Rack::AMF
|
|
7
8
|
@raw_request = ::AMF::Request.new.populate_from_stream(env['rack.input'].read)
|
8
9
|
end
|
9
10
|
|
11
|
+
# Returns the request AMF version
|
12
|
+
def version
|
13
|
+
raw_request.amf_version
|
14
|
+
end
|
15
|
+
|
10
16
|
# Returns all messages in the request
|
11
17
|
def messages
|
12
18
|
@raw_request.messages
|
data/lib/rack/amf/response.rb
CHANGED
@@ -1,53 +1,77 @@
|
|
1
1
|
module Rack::AMF
|
2
|
+
# Rack specific wrapper around AMF::Response
|
2
3
|
class Response
|
3
4
|
attr_reader :raw_response
|
4
5
|
|
6
|
+
V = ::AMF::Values
|
7
|
+
|
5
8
|
def initialize request
|
6
9
|
@request = request
|
7
10
|
@raw_response = ::AMF::Response.new
|
11
|
+
@raw_response.amf_version = @request.version == 3 ? 3 : 0 # Can't just copy version because FMS sends version as 1
|
8
12
|
end
|
9
13
|
|
10
14
|
# Builds response, iterating over each method call and using the return value
|
11
15
|
# as the method call's return value
|
16
|
+
#--
|
17
|
+
# Iterate over all the sent messages. If they're somthing we can handle, like
|
18
|
+
# a command message, then simply add the response message ourselves. If it's
|
19
|
+
# a method call, then call the block with the method and args, catching errors
|
20
|
+
# for handling. Then create the appropriate response message using the return
|
21
|
+
# value of the block as the return value for the method call.
|
12
22
|
def each_method_call &block
|
13
|
-
|
14
|
-
target_uri = m.response_uri
|
23
|
+
raise 'Response already constructed' if @constructed
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
@request.messages.each do |m|
|
26
|
+
# What's the request body?
|
27
|
+
case m.data
|
28
|
+
when V::CommandMessage
|
29
|
+
# Pings should be responded to with an AcknowledgeMessage built using the ping
|
30
|
+
# Everything else is unsupported
|
31
|
+
command_msg = m.data
|
32
|
+
if command_msg.operation == V::CommandMessage::CLIENT_PING_OPERATION
|
33
|
+
response_value = V::AcknowledgeMessage.new(command_msg)
|
20
34
|
else
|
21
|
-
|
35
|
+
response_value = V::ErrorMessage.new(Exception.new("CommandMessage #{command_msg.operation} not implemented"), command_msg)
|
22
36
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
37
|
+
when V::RemotingMessage
|
38
|
+
# Using RemoteObject style message calls
|
39
|
+
remoting_msg = m.data
|
40
|
+
acknowledge_msg = V::AcknowledgeMessage.new(remoting_msg)
|
41
|
+
body = dispatch_call :method => remoting_msg.source+'.'+remoting_msg.operation, :args => remoting_msg.body, :source => remoting_msg, :block => block
|
42
|
+
|
43
|
+
# Response should be the bare ErrorMessage if there was an error
|
44
|
+
if body.is_a?(V::ErrorMessage)
|
45
|
+
response_value = body
|
28
46
|
else
|
29
|
-
|
30
|
-
|
47
|
+
acknowledge_msg.body = body
|
48
|
+
response_value = acknowledge_msg
|
31
49
|
end
|
32
50
|
else
|
33
|
-
|
51
|
+
# Standard response message
|
52
|
+
response_value = dispatch_call :method => m.target_uri, :args => m.data, :source => m, :block => block
|
34
53
|
end
|
35
54
|
|
36
|
-
target_uri
|
37
|
-
|
55
|
+
target_uri = m.response_uri
|
56
|
+
target_uri += response_value.is_a?(V::ErrorMessage) ? '/onStatus' : '/onResult'
|
57
|
+
@raw_response.messages << ::AMF::Message.new(target_uri, '', response_value)
|
38
58
|
end
|
59
|
+
|
60
|
+
@constructed = true
|
39
61
|
end
|
40
62
|
|
63
|
+
# Return the serialized response as a string
|
41
64
|
def to_s
|
42
65
|
raw_response.serialize
|
43
66
|
end
|
44
67
|
|
45
68
|
private
|
46
|
-
def dispatch_call
|
69
|
+
def dispatch_call p
|
47
70
|
begin
|
48
|
-
|
71
|
+
p[:block].call(p[:method], p[:args])
|
49
72
|
rescue Exception => e
|
50
|
-
|
73
|
+
# Create ErrorMessage object using the source message as the base
|
74
|
+
V::ErrorMessage.new(p[:source], e)
|
51
75
|
end
|
52
76
|
end
|
53
77
|
end
|
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
|
|
2
2
|
|
3
3
|
describe AMF::ClassMapping do
|
4
4
|
before(:all) do
|
5
|
-
class
|
5
|
+
class ClassMappingTest
|
6
6
|
attr_accessor :prop_a
|
7
7
|
attr_accessor :prop_b
|
8
8
|
attr_accessor :prop_c
|
@@ -12,25 +12,25 @@ describe AMF::ClassMapping do
|
|
12
12
|
before :each do
|
13
13
|
@mapper = AMF::ClassMapping.new
|
14
14
|
@mapper.define do |m|
|
15
|
-
m.map :as => 'ASClass', :ruby => '
|
15
|
+
m.map :as => 'ASClass', :ruby => 'ClassMappingTest'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should return AS class name for ruby objects" do
|
20
|
-
@mapper.get_as_class_name(
|
21
|
-
@mapper.get_as_class_name('
|
20
|
+
@mapper.get_as_class_name(ClassMappingTest.new).should == 'ASClass'
|
21
|
+
@mapper.get_as_class_name('ClassMappingTest').should == 'ASClass'
|
22
22
|
end
|
23
23
|
|
24
24
|
it "should allow config modification" do
|
25
25
|
@mapper.define do |m|
|
26
|
-
m.map :as => 'SecondClass', :ruby => '
|
26
|
+
m.map :as => 'SecondClass', :ruby => 'ClassMappingTest'
|
27
27
|
end
|
28
|
-
@mapper.get_as_class_name(
|
28
|
+
@mapper.get_as_class_name(ClassMappingTest.new).should == 'SecondClass'
|
29
29
|
end
|
30
30
|
|
31
31
|
describe "ruby object generator" do
|
32
32
|
it "should instantiate a ruby class" do
|
33
|
-
@mapper.get_ruby_obj('ASClass').should be_a(
|
33
|
+
@mapper.get_ruby_obj('ASClass').should be_a(ClassMappingTest)
|
34
34
|
end
|
35
35
|
|
36
36
|
it "should properly instantiate namespaced classes" do
|
@@ -48,7 +48,7 @@ describe AMF::ClassMapping do
|
|
48
48
|
|
49
49
|
describe "ruby object populator" do
|
50
50
|
it "should populate a ruby class" do
|
51
|
-
obj = @mapper.populate_ruby_obj
|
51
|
+
obj = @mapper.populate_ruby_obj ClassMappingTest.new, {:prop_a => 'Data'}
|
52
52
|
obj.prop_a.should == 'Data'
|
53
53
|
end
|
54
54
|
|
@@ -84,7 +84,7 @@ describe AMF::ClassMapping do
|
|
84
84
|
end
|
85
85
|
|
86
86
|
it "should extract object properties" do
|
87
|
-
obj =
|
87
|
+
obj = ClassMappingTest.new
|
88
88
|
obj.prop_a = 'Test A'
|
89
89
|
obj.prop_b = 'Test B'
|
90
90
|
|
@@ -93,9 +93,9 @@ describe AMF::ClassMapping do
|
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should extract inherited object properties" do
|
96
|
-
class
|
96
|
+
class ClassMappingTest2 < ClassMappingTest
|
97
97
|
end
|
98
|
-
obj =
|
98
|
+
obj = ClassMappingTest2.new
|
99
99
|
obj.prop_a = 'Test A'
|
100
100
|
obj.prop_b = 'Test B'
|
101
101
|
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
3
|
describe "when deserializing" do
|
4
|
+
before :each do
|
5
|
+
AMF::ClassMapper.reset
|
6
|
+
end
|
7
|
+
|
4
8
|
describe "AMF0" do
|
5
9
|
it "should deserialize numbers" do
|
6
10
|
input = object_fixture('amf0-number.bin')
|
@@ -58,12 +62,20 @@ describe "when deserializing" do
|
|
58
62
|
output.should == ['a', 'b', 'c', 'd']
|
59
63
|
end
|
60
64
|
|
65
|
+
it "should serialize strict arrays" do
|
66
|
+
input = object_fixture('amf0-strict-array.bin')
|
67
|
+
output = AMF.deserialize(input, 0)
|
68
|
+
output.should == ['a', 'b', 'c', 'd']
|
69
|
+
end
|
70
|
+
|
61
71
|
it "should deserialize dates" do
|
62
72
|
input = object_fixture('amf0-date.bin')
|
63
73
|
output = AMF.deserialize(input, 0)
|
64
74
|
output.should == Time.utc(2003, 2, 13, 5)
|
65
75
|
end
|
66
76
|
|
77
|
+
it "should deserialize XML"
|
78
|
+
|
67
79
|
it "should deserialize an unmapped object as a dynamic anonymous object" do
|
68
80
|
input = object_fixture("amf0-typed-object.bin")
|
69
81
|
output = AMF.deserialize(input, 0)
|
data/spec/amf/remoting_spec.rb
CHANGED
@@ -29,6 +29,7 @@ end
|
|
29
29
|
describe "when handling responses" do
|
30
30
|
it "should serialize a simple call" do
|
31
31
|
resp = AMF::Response.new
|
32
|
+
resp.amf_version = 3
|
32
33
|
resp.messages << AMF::Message.new('/1/onResult', '', 'hello')
|
33
34
|
|
34
35
|
expected = request_fixture('simple-response.bin')
|
@@ -41,6 +42,7 @@ describe "when handling responses" do
|
|
41
42
|
ak.messageId = "7B0ACE15-8D57-6AE5-B9D4-99C2D32C8246"
|
42
43
|
ak.timestamp = 0
|
43
44
|
resp = AMF::Response.new
|
45
|
+
resp.amf_version = 3
|
44
46
|
resp.messages << AMF::Message.new('/1/onResult', '', ak)
|
45
47
|
|
46
48
|
expected = request_fixture('acknowledge-response.bin')
|
data/spec/amf/serializer_spec.rb
CHANGED
@@ -3,6 +3,82 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
|
|
3
3
|
require 'rexml/document'
|
4
4
|
|
5
5
|
describe "when serializing" do
|
6
|
+
before :each do
|
7
|
+
AMF::ClassMapper.reset
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "AMF0" do
|
11
|
+
it "should serialize nils" do
|
12
|
+
output = AMF.serialize(nil, 0)
|
13
|
+
output.should == object_fixture('amf0-null.bin')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should serialize booleans" do
|
17
|
+
output = AMF.serialize(true, 0)
|
18
|
+
output.should === object_fixture('amf0-boolean.bin')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should serialize numbers" do
|
22
|
+
output = AMF.serialize(3.5, 0)
|
23
|
+
output.should == object_fixture('amf0-number.bin')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should serialize strings" do
|
27
|
+
output = AMF.serialize("this is a テスト", 0)
|
28
|
+
output.should == object_fixture('amf0-string.bin')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should serialize arrays" do
|
32
|
+
output = AMF.serialize(['a', 'b', 'c', 'd'], 0)
|
33
|
+
output.should == object_fixture('amf0-strict-array.bin')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should serialize references" do
|
37
|
+
class OtherClass
|
38
|
+
attr_accessor :foo, :bar
|
39
|
+
end
|
40
|
+
obj = OtherClass.new
|
41
|
+
obj.foo = "baz"
|
42
|
+
obj.bar = 3.14
|
43
|
+
|
44
|
+
output = AMF.serialize({'0' => obj, '1' => obj}, 0)
|
45
|
+
output.should == object_fixture('amf0-ref-test.bin')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should serialize dates" do
|
49
|
+
output = AMF.serialize(Time.utc(2003, 2, 13, 5), 0)
|
50
|
+
output.should == object_fixture('amf0-date.bin')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should serialize hashes" do
|
54
|
+
output = AMF.serialize({:a => 'b', :c => 'd'}, 0)
|
55
|
+
output.should == object_fixture('amf0-hash.bin')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should serialize unmapped objects" do
|
59
|
+
class RubyClass
|
60
|
+
attr_accessor :foo, :baz
|
61
|
+
end
|
62
|
+
obj = RubyClass.new
|
63
|
+
obj.foo = "bar"
|
64
|
+
|
65
|
+
output = AMF.serialize(obj, 0)
|
66
|
+
output.should == object_fixture('amf0-untyped-object.bin')
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should serialize mapped objects" do
|
70
|
+
class RubyClass
|
71
|
+
attr_accessor :foo, :baz
|
72
|
+
end
|
73
|
+
obj = RubyClass.new
|
74
|
+
obj.foo = "bar"
|
75
|
+
AMF::ClassMapper.define {|m| m.map :as => 'org.rackAMF.ASClass', :ruby => 'RubyClass'}
|
76
|
+
|
77
|
+
output = AMF.serialize(obj, 0)
|
78
|
+
output.should == object_fixture('amf0-typed-object.bin')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
6
82
|
describe "AMF3" do
|
7
83
|
describe "simple messages" do
|
8
84
|
it "should serialize a null" do
|
@@ -17,7 +17,11 @@ describe AMF::Values::ErrorMessage do
|
|
17
17
|
@message = AMF::Values::ErrorMessage.new(nil, @e)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "should serialize as a hash in AMF0"
|
20
|
+
it "should serialize as a hash in AMF0" do
|
21
|
+
response = AMF::Response.new
|
22
|
+
response.messages << AMF::Message.new('1/onStatus', '', @message)
|
23
|
+
response.serialize.should == request_fixture('amf0-error-response.bin')
|
24
|
+
end
|
21
25
|
|
22
26
|
it "should extract exception properties correctly" do
|
23
27
|
@message.faultCode.should == 'Exception'
|
data/spec/spec_helper.rb
CHANGED
@@ -21,4 +21,13 @@ end
|
|
21
21
|
def create_rack_request(binary_path)
|
22
22
|
env = {'rack.input' => StringIO.new(request_fixture(binary_path))}
|
23
23
|
Rack::AMF::Request.new(env)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Add reset support to ClassMapping
|
27
|
+
module AMF
|
28
|
+
class ClassMapping
|
29
|
+
def reset
|
30
|
+
@mappings = nil
|
31
|
+
end
|
32
|
+
end
|
24
33
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-amf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Hillerson
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-10-
|
13
|
+
date: 2009-10-19 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -38,10 +38,14 @@ files:
|
|
38
38
|
- lib/amf/values/typed_hash.rb
|
39
39
|
- lib/amf/version.rb
|
40
40
|
- lib/amf.rb
|
41
|
-
- lib/rack/amf/
|
41
|
+
- lib/rack/amf/environment.rb
|
42
|
+
- lib/rack/amf/middleware/pass_through.rb
|
43
|
+
- lib/rack/amf/middleware/rails.rb
|
44
|
+
- lib/rack/amf/middleware/service_manager.rb
|
45
|
+
- lib/rack/amf/middleware.rb
|
46
|
+
- lib/rack/amf/rails/rack_amf_controller.rb
|
42
47
|
- lib/rack/amf/request.rb
|
43
48
|
- lib/rack/amf/response.rb
|
44
|
-
- lib/rack/amf/service_manager.rb
|
45
49
|
- lib/rack/amf.rb
|
46
50
|
- spec/amf/class_mapping_set_spec.rb
|
47
51
|
- spec/amf/class_mapping_spec.rb
|
data/lib/rack/amf/application.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'rack/amf/request'
|
2
|
-
require 'rack/amf/response'
|
3
|
-
|
4
|
-
module Rack::AMF
|
5
|
-
class Application
|
6
|
-
def initialize app, mode
|
7
|
-
@app = app
|
8
|
-
@mode = mode
|
9
|
-
end
|
10
|
-
|
11
|
-
def call env
|
12
|
-
if env['CONTENT_TYPE'] != APPLICATION_AMF
|
13
|
-
return [200, {"Content-Type" => "text/plain"}, ["Hello From Rack::AMF"]]
|
14
|
-
end
|
15
|
-
|
16
|
-
# Wrap request and response
|
17
|
-
env['amf.request'] = Request.new(env)
|
18
|
-
env['amf.response'] = Response.new(env['amf.request'])
|
19
|
-
|
20
|
-
# Handle request
|
21
|
-
if @mode == :pass_through
|
22
|
-
@app.call env
|
23
|
-
elsif @mode == :internal
|
24
|
-
# Have the service manager handle it
|
25
|
-
Services.handle(env)
|
26
|
-
end
|
27
|
-
|
28
|
-
response = env['amf.response'].to_s
|
29
|
-
[200, {"Content-Type" => APPLICATION_AMF, 'Content-Length' => response.length.to_s}, [response]]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Rack::AMF
|
2
|
-
class ServiceManager
|
3
|
-
def initialize
|
4
|
-
@services = {}
|
5
|
-
end
|
6
|
-
|
7
|
-
def register path, service
|
8
|
-
@services ||= {}
|
9
|
-
@services[path] = service
|
10
|
-
end
|
11
|
-
|
12
|
-
def handle env
|
13
|
-
env['amf.response'].each_method_call do |method, args|
|
14
|
-
handle_method method, args
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
def handle_method method, args
|
20
|
-
path = method.split('.')
|
21
|
-
method_name = path.pop
|
22
|
-
path = path.join('.')
|
23
|
-
|
24
|
-
if @services[path]
|
25
|
-
if @services[path].respond_to?(method_name)
|
26
|
-
@services[path].send(method_name, *args)
|
27
|
-
else
|
28
|
-
raise "Service #{path} does not respond to #{method_name}"
|
29
|
-
end
|
30
|
-
else
|
31
|
-
raise "Service #{path} does not exist"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|