rene-adhearsion 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/CHANGELOG +73 -0
  2. data/EVENTS +11 -0
  3. data/LICENSE +456 -0
  4. data/Rakefile +130 -0
  5. data/adhearsion.gemspec +173 -0
  6. data/app_generators/ahn/USAGE +5 -0
  7. data/app_generators/ahn/ahn_generator.rb +96 -0
  8. data/app_generators/ahn/templates/.ahnrc +34 -0
  9. data/app_generators/ahn/templates/README +8 -0
  10. data/app_generators/ahn/templates/Rakefile +25 -0
  11. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  12. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  13. data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
  14. data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
  15. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
  16. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
  17. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +263 -0
  18. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
  19. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
  20. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  21. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  22. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
  23. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
  24. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
  25. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  26. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  27. data/app_generators/ahn/templates/config/startup.rb +83 -0
  28. data/app_generators/ahn/templates/dialplan.rb +3 -0
  29. data/app_generators/ahn/templates/events.rb +32 -0
  30. data/bin/ahn +28 -0
  31. data/bin/ahnctl +68 -0
  32. data/bin/jahn +42 -0
  33. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  34. data/lib/adhearsion.rb +45 -0
  35. data/lib/adhearsion/cli.rb +228 -0
  36. data/lib/adhearsion/component_manager.rb +272 -0
  37. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  38. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  39. data/lib/adhearsion/events_support.rb +84 -0
  40. data/lib/adhearsion/foundation/all.rb +15 -0
  41. data/lib/adhearsion/foundation/blank_slate.rb +3 -0
  42. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  43. data/lib/adhearsion/foundation/event_socket.rb +204 -0
  44. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  45. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  46. data/lib/adhearsion/foundation/numeric.rb +13 -0
  47. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  48. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  49. data/lib/adhearsion/foundation/string.rb +26 -0
  50. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  51. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  52. data/lib/adhearsion/host_definitions.rb +67 -0
  53. data/lib/adhearsion/initializer.rb +395 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +87 -0
  55. data/lib/adhearsion/initializer/configuration.rb +321 -0
  56. data/lib/adhearsion/initializer/database.rb +60 -0
  57. data/lib/adhearsion/initializer/drb.rb +31 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/ldap.rb +57 -0
  60. data/lib/adhearsion/initializer/rails.rb +41 -0
  61. data/lib/adhearsion/initializer/xmpp.rb +42 -0
  62. data/lib/adhearsion/logging.rb +92 -0
  63. data/lib/adhearsion/tasks.rb +16 -0
  64. data/lib/adhearsion/tasks/database.rb +5 -0
  65. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  66. data/lib/adhearsion/tasks/generating.rb +20 -0
  67. data/lib/adhearsion/tasks/lint.rb +4 -0
  68. data/lib/adhearsion/tasks/testing.rb +37 -0
  69. data/lib/adhearsion/version.rb +33 -0
  70. data/lib/adhearsion/voip/asterisk.rb +4 -0
  71. data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
  72. data/lib/adhearsion/voip/asterisk/commands.rb +1510 -0
  73. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  74. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  75. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  76. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  77. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  78. data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
  79. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1680 -0
  80. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +340 -0
  81. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  82. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  83. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  84. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  85. data/lib/adhearsion/voip/call.rb +497 -0
  86. data/lib/adhearsion/voip/call_routing.rb +64 -0
  87. data/lib/adhearsion/voip/commands.rb +9 -0
  88. data/lib/adhearsion/voip/constants.rb +39 -0
  89. data/lib/adhearsion/voip/conveniences.rb +18 -0
  90. data/lib/adhearsion/voip/dial_plan.rb +246 -0
  91. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  92. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  93. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  94. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
  96. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  97. data/lib/adhearsion/voip/dsl/numerical_string.rb +115 -0
  98. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  99. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  100. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  101. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  102. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  103. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  104. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  105. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  106. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  107. data/lib/adhearsion/xmpp/connection.rb +61 -0
  108. data/lib/theatre.rb +151 -0
  109. data/lib/theatre/README.markdown +64 -0
  110. data/lib/theatre/callback_definition_loader.rb +84 -0
  111. data/lib/theatre/guid.rb +23 -0
  112. data/lib/theatre/invocation.rb +121 -0
  113. data/lib/theatre/namespace_manager.rb +153 -0
  114. data/lib/theatre/version.rb +2 -0
  115. metadata +241 -0
@@ -0,0 +1,25 @@
1
+ # This file is for the "rake" tool which automates project-related tasks. If you need to automate things, you can create
2
+ # a new Rake task here. See http://rake.rubyforge.org for more info.
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'adhearsion'
7
+ require 'adhearsion/tasks'
8
+ rescue LoadError
9
+ STDERR.puts "\nCannot load Adhearsion! Not all Rake tasks will be loaded!\n\n"
10
+ end
11
+
12
+ desc "Writes a .gitignore file that ignores certain SCM annoyances such as log files"
13
+ task :gitignore do
14
+ ignore_file = "#{Dir.pwd}/.gitignore"
15
+ if File.exists? ignore_file
16
+ STDERR.puts "File #{ignore_file} already exists!"
17
+ else
18
+ File.open ignore_file, 'w' do |file|
19
+ # Add other files to the Array below
20
+ %w[ log ].each do |pattern|
21
+ file.puts pattern
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ methods_for :rpc do
2
+
3
+ # Simply create proxy methods for the high-level AMI methods
4
+
5
+ [:send_action, :introduce, :originate, :call_into_context, :call_and_exec, :ping].each do |method_name|
6
+ define_method(method_name) do |*args|
7
+ if VoIP::Asterisk.manager_interface
8
+ VoIP::Asterisk.manager_interface.send(method_name, *args)
9
+ else
10
+ ahn_log.ami_remote.error "AMI has not been enabled in startup.rb!"
11
+ end
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,7 @@
1
+ Type "ahn enable component COMPONENT_NAME_FROM_DISABLED_FOLDER
2
+
3
+ For example:
4
+
5
+ ~/Desktop $ ahn create myapp
6
+ ~/Desktop $ cd myapp
7
+ ~/Desktop/myapp $ ahn enable component stomp_gateway
@@ -0,0 +1,11 @@
1
+ Adhearsion RESTful RPC Component
2
+ ================================
3
+
4
+ This is a component for people want to integrate their telephony systems with non-Ruby systems. When enabled, this component
5
+ will start up a HTTP server within the Adhearsion process and accept POST requests to invoke Ruby methods shared in the
6
+ `methods_for(:rpc)` context.
7
+
8
+ Protocol Notes
9
+ --------------
10
+
11
+ When POSTing your data to.
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rest_client'
3
+ require 'json'
4
+
5
+ # You must have the "rest-client" and "json" gems installed for this file to work.
6
+
7
+ class RESTfulAdhearsion
8
+
9
+ DEFAULT_OPTIONS = {
10
+ # Note: :user and :password are non-existent by default
11
+ :host => "localhost",
12
+ :port => "5000",
13
+ :path_nesting => "/"
14
+ }
15
+
16
+ def initialize(options={})
17
+ @options = DEFAULT_OPTIONS.merge options
18
+
19
+ @path_nesting = @options.delete :path_nesting
20
+ @host = @options.delete :host
21
+ @port = @options.delete :port
22
+
23
+ @url_beginning = "http://#{@host}:#{@port}#{@path_nesting}"
24
+ end
25
+
26
+ def method_missing(method_name, *args)
27
+ JSON.parse RestClient::Resource.new(@url_beginning + method_name.to_s, @options).post(args.to_json)
28
+ end
29
+
30
+ end
31
+
32
+ Adhearsion = RESTfulAdhearsion.new :host => "localhost", :port => 5000, :user => "jicksta", :password => "roflcopterz"
33
+
34
+ # ### Sample component code. Try doing "ahn create component testing123" and pasting this code in.
35
+ #
36
+ # methods_for :rpc do
37
+ # def i_like_hashes(options={})
38
+ # options.has_key?(:foo)
39
+ # end
40
+ # def i_like_arrays(*args)
41
+ # args.reverse
42
+ # end
43
+ # end
44
+
45
+ # Note: everything returned will be wrapped in an Array
46
+
47
+ p Adhearsion.i_like_hashes(:foo => "bar")
48
+ p Adhearsion.i_like_arrays(1,2,3,4,5)
@@ -0,0 +1,91 @@
1
+ begin
2
+ require 'rack'
3
+ require 'json'
4
+ rescue LoadError
5
+ abort "ERROR: restful_rpc requires the 'rack' and 'json' gems"
6
+ end
7
+
8
+ # Don't you love regular expressions? Matches only 0-255 octets. Recognizes "*" as an octet wildcard.
9
+ VALID_IP_ADDRESS = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)$/
10
+
11
+ def ip_allowed?(ip)
12
+ raise ArgumentError, "#{ip.inspect} is not a valid IP address!" unless ip.kind_of?(String) && ip =~ VALID_IP_ADDRESS
13
+
14
+ octets = ip.split "."
15
+
16
+ case COMPONENTS.restful_rpc["access"]
17
+ when "everyone"
18
+ true
19
+ when "whitelist"
20
+ whitelist = COMPONENTS.restful_rpc["whitelist"]
21
+ !! whitelist.find do |pattern|
22
+ pattern_octets = pattern.split "."
23
+ # Traverse both arrays in parallel
24
+ octets.zip(pattern_octets).map do |octet, octet_pattern|
25
+ octet_pattern == "*" ? true : (octet == octet_pattern)
26
+ end == [true, true, true, true]
27
+ end
28
+ when "blacklist"
29
+ blacklist = COMPONENTS.restful_rpc["blacklist"]
30
+ ! blacklist.find do |pattern|
31
+ pattern_octets = pattern.split "."
32
+ # Traverse both arrays in parallel
33
+ octets.zip(pattern_octets).map do |octet, octet_pattern|
34
+ octet_pattern == "*" ? true : (octet == octet_pattern)
35
+ end == [true, true, true, true]
36
+ end
37
+ else
38
+ raise Adhearsion::Components::ConfigurationError, 'Unrecognized "access" configuration value!'
39
+ end
40
+ end
41
+
42
+ RESTFUL_API_HANDLER = lambda do |env|
43
+ json = env["rack.input"].read
44
+
45
+ # Return "Bad Request" HTTP error if the client forgot
46
+ return [400, {}, "You must POST a valid JSON object!"] if json.blank?
47
+
48
+ json = JSON.parse json
49
+
50
+ nesting = COMPONENTS.restful_rpc["path_nesting"]
51
+ path = env["PATH_INFO"]
52
+
53
+ return [404, {}, "This resource does not respond to #{path.inspect}"] unless path[0...nesting.size] == nesting
54
+
55
+ path = path[nesting.size..-1]
56
+
57
+ return [404, {"Content-Type" => "application/json"}, "You cannot nest method names!"] if path.include?("/")
58
+
59
+ rpc_object = Adhearsion::Components.component_manager.extend_object_with(Object.new, :rpc)
60
+
61
+ # TODO: set the content-type and other HTTP headers
62
+ response_object = rpc_object.send(path, *json)
63
+ [200, {"Content-Type" => "application/json"}, response_object.to_json]
64
+
65
+ end
66
+
67
+ initialization do
68
+ config = COMPONENTS.restful_rpc
69
+
70
+ api = RESTFUL_API_HANDLER
71
+
72
+ port = config["port"] || 5000
73
+ authentication = config["authentication"]
74
+ show_exceptions = config["show_exceptions"]
75
+ handler = Rack::Handler.const_get(config["handler"] || "Mongrel")
76
+
77
+ if authentication
78
+ api = Rack::Auth::Basic.new(api) do |username, password|
79
+ authentication[username] == password
80
+ end
81
+ api.realm = "Adhearsion API"
82
+ end
83
+
84
+ if show_exceptions
85
+ api = Rack::ShowStatus.new(Rack::ShowExceptions.new(api))
86
+ end
87
+
88
+ Thread.new do
89
+ handler.run api, :Port => port
90
+ end
91
+ end
@@ -0,0 +1,34 @@
1
+ # Use path_nesting to specify an arbitrarily nested. Could be used for additional security or in HTTP reverse proxy server.
2
+ path_nesting: /
3
+
4
+ port: 5000
5
+
6
+ # The "handler" option here can be any valid Rack::Handler constant name.
7
+ # Other options: WEBrick, EventedMongrel.
8
+ # If you don't know the differences between these, "Mongrel" is definitely a good choice.
9
+ handler: Mongrel
10
+
11
+ # In a production system, you should make this "false" since
12
+ show_exceptions: true
13
+
14
+ # The "authentication" config option can either be "false" or key/value pairs representing allowed usernames and passwords.
15
+
16
+ #authentication: false
17
+ authentication:
18
+ jicksta: roflcopterz
19
+ foo: bar6213671682
20
+
21
+ access: everyone # When allowing "everyone" access, no IPs are blocked.
22
+ #access: whitelist # When using a whitelist, the "whitelist" data below will be used.
23
+ #access: blacklist # When using a blacklist, the "blacklist" data below will be used.
24
+
25
+ # This is a list of IPs which are exclusively allowed to call this web service.
26
+ # Note: whitelists are far more secure than blacklists.
27
+ whitelist:
28
+ - 127.0.0.1
29
+ - 192.168.*.*
30
+
31
+ # This is a list of the IPs which are explicitly NOT allowed to call this web service. This will only be used if "access" is
32
+ # set to "blacklist" above.
33
+ blacklist:
34
+ - 100.200.100.200
@@ -0,0 +1,263 @@
1
+ unless defined? Adhearsion
2
+ if File.exists? File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
3
+ # If you wish to freeze a copy of Adhearsion to this app, simply place a copy of Adhearsion
4
+ # into a folder named "adhearsion" within this app's main directory.
5
+ require File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
6
+ elsif File.exists? File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
7
+ # This file may be ran from the within the Adhearsion framework code (before a project has been generated)
8
+ require File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
9
+ else
10
+ require 'rubygems'
11
+ gem 'adhearsion', '>= 0.7.999'
12
+ require 'adhearsion'
13
+ end
14
+ end
15
+
16
+ require 'adhearsion/component_manager/spec_framework'
17
+
18
+ RESTFUL_RPC = ComponentTester.new("restful_rpc", File.dirname(__FILE__) + "/../..")
19
+
20
+ ##### This is here for a reference
21
+ #{"CONTENT_LENGTH" => "12",
22
+ # "CONTENT_TYPE" => "application/x-www-form-urlencoded",
23
+ # "GATEWAY_INTERFACE" => "CGI/1.1",
24
+ # "HTTP_ACCEPT" => "application/xml",
25
+ # "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
26
+ # "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
27
+ # "HTTP_HOST" => "localhost:5000",
28
+ # "HTTP_VERSION" => "HTTP/1.1",
29
+ # "PATH_INFO" => "/rofl",
30
+ # "QUERY_STRING" => "",
31
+ # "rack.errors" => StringIO.new(""),
32
+ # "rack.input" => StringIO.new('["o","hai!"]'),
33
+ # "rack.multiprocess" => false,
34
+ # "rack.multithread" => true,
35
+ # "rack.run_once" => false,
36
+ # "rack.url_scheme" => "http",
37
+ # "rack.version" => [0, 1],
38
+ # "REMOTE_ADDR" => "::1",
39
+ # "REMOTE_HOST" => "localhost",
40
+ # "REMOTE_USER" => "jicksta",
41
+ # "REQUEST_METHOD" => "POST"
42
+ # "REQUEST_PATH" => "/",
43
+ # "REQUEST_URI" => "http://localhost:5000/rofl",
44
+ # "SCRIPT_NAME" => "",
45
+ # "SERVER_NAME" => "localhost",
46
+ # "SERVER_PORT" => "5000",
47
+ # "SERVER_PROTOCOL" => "HTTP/1.1",
48
+ # "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)"}
49
+
50
+
51
+
52
+ describe "The VALID_IP_ADDRESS regular expression" do
53
+
54
+ it "should match only valid IP addresses" do
55
+ valid_ip_addresses = ["192.168.1.98", "10.0.1.200", "255.255.255.0", "123.*.4.*"]
56
+ invalid_ip_addresses = ["10.0.1.1 foo", "bar 255.255.255.0", "0*0*0*0", "1234"]
57
+
58
+ valid_ip_addresses. each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should =~ ip }
59
+ invalid_ip_addresses.each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should_not =~ ip }
60
+ end
61
+ end
62
+
63
+ describe "The initialization block" do
64
+
65
+ it "should create a new Thread" do
66
+ mock_component_config_with :restful_rpc => {}
67
+ mock(Thread).new { nil }
68
+ RESTFUL_RPC.initialize!
69
+ end
70
+
71
+ it "should run the Rack adapter specified in the configuration" do
72
+ mock(Thread).new.yields
73
+ mock_component_config_with :restful_rpc => {"adapter" => "Mongrel"}
74
+ mock(Rack::Handler::Mongrel).run is_a(Proc), :Port => 5000
75
+ RESTFUL_RPC.initialize!
76
+ end
77
+
78
+ it "should wrap the RESTFUL_API_HANDLER in an Rack::Auth::Basic object if authentication is enabled" do
79
+ mock(Thread).new.yields
80
+ mock_component_config_with :restful_rpc => {"authentication" => {"foo" => "bar"}}
81
+
82
+ proper_authenticator = lambda do |obj|
83
+ request = OpenStruct.new :credentials => ["foo", "bar"]
84
+ obj.is_a?(Rack::Auth::Basic) && obj.send(:valid?, request)
85
+ end
86
+
87
+ mock(Rack::Handler::Mongrel).run(satisfy(&proper_authenticator), :Port => 5000)
88
+ RESTFUL_RPC.initialize!
89
+ end
90
+
91
+ it 'should wrap the RESTFUL_API_HANDLER in ShowStatus and ShowExceptions objects when show_exceptions is enabled' do
92
+ mock(Thread).new.yields
93
+ mock_component_config_with :restful_rpc => {"show_exceptions" => true}
94
+
95
+ mock.proxy(Rack::ShowExceptions).new(is_a(Proc))
96
+ mock.proxy(Rack::ShowStatus).new is_a(Rack::ShowExceptions)
97
+
98
+ mock(Rack::Handler::Mongrel).run is_a(Rack::ShowStatus), :Port => 5000
99
+ RESTFUL_RPC.initialize!
100
+ end
101
+
102
+ end
103
+
104
+ describe 'Private helper methods' do
105
+
106
+ describe "the RESTFUL_API_HANDLER lambda" do
107
+
108
+ it "should return a 200 for requests which execute a method that has been defined in the methods_for(:rpc) context" do
109
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
110
+
111
+ mock(Adhearsion::Components).component_manager { component_manager }
112
+ component_manager.load_code <<-RUBY
113
+ methods_for(:rpc) do
114
+ def testing_123456(one,two)
115
+ [two.reverse, one.reverse]
116
+ end
117
+ end
118
+ RUBY
119
+
120
+ input = StringIO.new %w[jay phillips].to_json
121
+
122
+ mock_component_config_with :restful_rpc => {"path_nesting" => "/"}
123
+
124
+ env = {"PATH_INFO" => "/testing_123456", "rack.input" => input}
125
+
126
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
127
+ response.should be_kind_of(Array)
128
+ response.should have(3).items
129
+ response.first.should equal(200)
130
+ JSON.parse(response.last).should eql(%w[jay phillips].map(&:reverse).reverse)
131
+ end
132
+
133
+ it "should return a 400 when no data is POSTed" do
134
+ env = {"rack.input" => StringIO.new(""), "REQUEST_URI" => "/foobar"}
135
+ RESTFUL_RPC::RESTFUL_API_HANDLER.call(env).first.should equal(400)
136
+ end
137
+
138
+ it "should work with a high level test of a successful method invocation" do
139
+
140
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
141
+
142
+ mock(Adhearsion::Components).component_manager { component_manager }
143
+
144
+ component_manager.load_code '
145
+ methods_for(:rpc) do
146
+ def rofl(one,two)
147
+ "Hai! #{one} #{two}"
148
+ end
149
+ end'
150
+
151
+ env = {
152
+ "CONTENT_LENGTH" => "12",
153
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
154
+ "GATEWAY_INTERFACE" => "CGI/1.1",
155
+ "HTTP_ACCEPT" => "application/xml",
156
+ "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
157
+ "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
158
+ "HTTP_HOST" => "localhost:5000",
159
+ "HTTP_VERSION" => "HTTP/1.1",
160
+ "PATH_INFO" => "/rofl",
161
+ "QUERY_STRING" => "",
162
+ "rack.errors" => StringIO.new(""),
163
+ "rack.input" => StringIO.new('["o","hai!"]'),
164
+ "rack.multiprocess" => false,
165
+ "rack.multithread" => true,
166
+ "rack.run_once" => false,
167
+ "rack.url_scheme" => "http",
168
+ "rack.version" => [0, 1],
169
+ "REMOTE_ADDR" => "::1",
170
+ "REMOTE_HOST" => "localhost",
171
+ "REMOTE_USER" => "jicksta",
172
+ "REQUEST_METHOD" => "POST",
173
+ "REQUEST_PATH" => "/",
174
+ "REQUEST_URI" => "http://localhost:5000/rofl",
175
+ "SCRIPT_NAME" => "",
176
+ "SERVER_NAME" => "localhost",
177
+ "SERVER_PORT" => "5000",
178
+ "SERVER_PROTOCOL" => "HTTP/1.1",
179
+ "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)" }
180
+
181
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
182
+ JSON.parse(response.last).should == ["Hai! o hai!"]
183
+
184
+ end
185
+
186
+ it "should contain backtrace information when show_errors is enabled and an exception occurs" do
187
+ mock_component_config_with :restful_api => {"show_errors" => true}
188
+ pending
189
+ end
190
+
191
+ end
192
+
193
+ describe 'the ip_allowed?() method' do
194
+
195
+ before :each do
196
+ @method = RESTFUL_RPC.helper_method :ip_allowed?
197
+ end
198
+
199
+ it 'should raise a ConfigurationError if "access" is not one of "everyone", "whitelist" or "blacklist"' do
200
+ good_access_values = %w[everyone whitelist blacklist]
201
+ bad_access_values = %w[foo bar qaz qwerty everone blaclist whitlist]
202
+
203
+ good_access_values.each do |access_value|
204
+ mock_component_config_with :restful_rpc => {"access" => access_value, "whitelist" => [], "blacklist" => []}
205
+ lambda { @method.call("10.0.0.1") }.should_not raise_error
206
+ end
207
+
208
+ bad_access_values.each do |access_value|
209
+ mock_component_config_with :restful_rpc => {"access" => access_value, "authentication" => false}
210
+ lambda { @method.call("10.0.0.1") }.should raise_error(Adhearsion::Components::ConfigurationError)
211
+ end
212
+ end
213
+
214
+ describe 'whitelists' do
215
+
216
+ it "should parse *'s as wildcards" do
217
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["10.*.*.*"]}
218
+ @method.call("10.1.2.3").should be_true
219
+ end
220
+
221
+ it "should allow IPs which are explictly specified" do
222
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["4.3.2.1"]}
223
+ @method.call("4.3.2.1").should be_true
224
+ end
225
+
226
+ it "should not allow IPs which are not explicitly specified" do
227
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => %w[ 1.2.3.4 4.3.2.1]}
228
+ @method.call("2.2.2.2").should be_false
229
+ end
230
+
231
+ end
232
+
233
+ describe 'blacklists' do
234
+
235
+ it "should parse *'s as wildcards" do
236
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.*.*.*"]}
237
+ @method.call("10.1.2.3").should be_false
238
+ end
239
+
240
+ it "should not allow IPs which are explicitly specified" do
241
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["9.8.7.6", "9.8.7.5"]}
242
+ @method.call("9.8.7.5").should be_false
243
+ end
244
+
245
+ it "should allow IPs which are not explicitly specified" do
246
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.20.30.40"]}
247
+ @method.call("1.1.1.1").should be_true
248
+ end
249
+
250
+ end
251
+
252
+ describe '"everyone" access' do
253
+ it "should return true for any IP given, irrespective of the configuration" do
254
+ ip_addresses = %w[100.200.100.200 0.0.0.0 *.0.0.*]
255
+ ip_addresses.each do |address|
256
+ RESTFUL_RPC.helper_method(:ip_allowed?).call(address).should equal(true)
257
+ end
258
+ end
259
+ end
260
+
261
+ end
262
+
263
+ end