rack-amf 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|