librevox 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +159 -0
- data/Rakefile +6 -0
- data/TODO +24 -0
- data/lib/librevox.rb +35 -0
- data/lib/librevox/applications.rb +127 -0
- data/lib/librevox/command_socket.rb +50 -0
- data/lib/librevox/commands.rb +53 -0
- data/lib/librevox/listener/base.rb +80 -0
- data/lib/librevox/listener/inbound.rb +19 -0
- data/lib/librevox/listener/outbound.rb +83 -0
- data/lib/librevox/response.rb +48 -0
- data/librevox.gemspec +37 -0
- data/spec/helper.rb +6 -0
- data/spec/librevox/listener.rb +130 -0
- data/spec/librevox/listener/spec_inbound.rb +21 -0
- data/spec/librevox/listener/spec_outbound.rb +221 -0
- data/spec/librevox/spec_applications.rb +142 -0
- data/spec/librevox/spec_command_socket.rb +111 -0
- data/spec/librevox/spec_commands.rb +68 -0
- data/spec/librevox/spec_response.rb +56 -0
- metadata +76 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'librevox/response'
|
3
|
+
require 'librevox/commands'
|
4
|
+
|
5
|
+
module Librevox
|
6
|
+
module Listener
|
7
|
+
class Base < EventMachine::Protocols::HeaderAndContentProtocol
|
8
|
+
class << self
|
9
|
+
def hooks
|
10
|
+
@hooks ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def event(event, &block)
|
14
|
+
hooks << [event, block]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# In some cases there are both applications and commands with the same
|
19
|
+
# name, e.g. fifo. But we can't have two `fifo`-methods, so we include
|
20
|
+
# commands in CommandDelegate, and wrap all commands in the `api` call,
|
21
|
+
# which forwards the call to the CommandDelegate instance, which in turn
|
22
|
+
# forwards the #run_cmd-call from the command back to the listener. Yay.
|
23
|
+
class CommandDelegate
|
24
|
+
include Librevox::Commands
|
25
|
+
|
26
|
+
def initialize(listener)
|
27
|
+
@listener = listener
|
28
|
+
end
|
29
|
+
|
30
|
+
def run_cmd(*args, &block)
|
31
|
+
@listener.run_cmd *args, &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def api(cmd, *args, &block)
|
36
|
+
@command_delegate.send(cmd, *args, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_cmd(cmd, &block)
|
40
|
+
send_data "#{cmd}\n\n"
|
41
|
+
@api_queue << (block || lambda {})
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :response
|
45
|
+
alias :event :response
|
46
|
+
|
47
|
+
def post_init
|
48
|
+
@command_delegate = CommandDelegate.new(self)
|
49
|
+
@api_queue = []
|
50
|
+
end
|
51
|
+
|
52
|
+
def receive_request(header, content)
|
53
|
+
@response = Librevox::Response.new(header, content)
|
54
|
+
|
55
|
+
if response.event?
|
56
|
+
on_event
|
57
|
+
invoke_event response.event
|
58
|
+
elsif response.api_response? && @api_queue.any?
|
59
|
+
invoke_api_queue
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# override
|
64
|
+
def on_event
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def invoke_event(event_name)
|
69
|
+
self.class.hooks.each do |name,block|
|
70
|
+
instance_eval(&block) if name == event_name.downcase.to_sym
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def invoke_api_queue
|
75
|
+
block = @api_queue.shift
|
76
|
+
block.call(response)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'librevox/listener/base'
|
2
|
+
|
3
|
+
module Librevox
|
4
|
+
module Listener
|
5
|
+
class Inbound < Base
|
6
|
+
def initialize(args={})
|
7
|
+
super
|
8
|
+
|
9
|
+
@auth = args[:auth] || "ClueCon"
|
10
|
+
end
|
11
|
+
|
12
|
+
def post_init
|
13
|
+
super
|
14
|
+
send_data "auth #{@auth}\n\n"
|
15
|
+
send_data "event plain ALL\n\n"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'librevox/listener/base'
|
2
|
+
require 'librevox/applications'
|
3
|
+
|
4
|
+
module Librevox
|
5
|
+
module Listener
|
6
|
+
class Outbound < Base
|
7
|
+
class << self
|
8
|
+
attr_reader :session_callback
|
9
|
+
|
10
|
+
def session(&block)
|
11
|
+
@session_callback = block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
include Librevox::Applications
|
16
|
+
|
17
|
+
def execute_app(app, args="", params={}, &block)
|
18
|
+
msg = "sendmsg\n"
|
19
|
+
msg << "call-command: execute\n"
|
20
|
+
msg << "execute-app-name: #{app}\n"
|
21
|
+
msg << "execute-app-arg: #{args}\n" unless args.empty?
|
22
|
+
|
23
|
+
send_data "#{msg}\n"
|
24
|
+
|
25
|
+
@read_channel_var = params[:read_var]
|
26
|
+
|
27
|
+
if @read_channel_var
|
28
|
+
@command_queue << lambda {update_session}
|
29
|
+
end
|
30
|
+
|
31
|
+
@command_queue << (block || lambda {})
|
32
|
+
end
|
33
|
+
|
34
|
+
# This should probably be in Application#sendmsg instead.
|
35
|
+
def sendmsg(msg)
|
36
|
+
send_data "sendmsg\n%s" % msg
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :session
|
40
|
+
|
41
|
+
def post_init
|
42
|
+
super
|
43
|
+
@session = nil
|
44
|
+
@command_queue = []
|
45
|
+
|
46
|
+
send_data "connect\n\n"
|
47
|
+
send_data "myevents\n\n"
|
48
|
+
@command_queue << lambda {}
|
49
|
+
send_data "linger\n\n"
|
50
|
+
@command_queue << lambda {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def receive_request(*args)
|
54
|
+
super(*args)
|
55
|
+
|
56
|
+
if session.nil?
|
57
|
+
@session = response
|
58
|
+
instance_eval &self.class.session_callback
|
59
|
+
elsif response.event? && response.event == "CHANNEL_DATA"
|
60
|
+
@session = response
|
61
|
+
resume_with_channel_var
|
62
|
+
elsif response.command_reply? && !response.event?
|
63
|
+
@command_queue.shift.call if @command_queue.any?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def resume_with_channel_var
|
68
|
+
if @read_channel_var
|
69
|
+
variable = "variable_#{@read_channel_var}".to_sym
|
70
|
+
value = @session.content[variable]
|
71
|
+
@command_queue.shift.call(value) if @command_queue.any?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def update_session
|
76
|
+
send_data("api uuid_dump #{session.headers[:unique_id]}\n\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
def session_initiated
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'em/protocols/header_and_content'
|
3
|
+
|
4
|
+
class String
|
5
|
+
alias :each :each_line
|
6
|
+
end
|
7
|
+
|
8
|
+
module Librevox
|
9
|
+
class Response
|
10
|
+
attr_accessor :headers, :content
|
11
|
+
|
12
|
+
def initialize(headers="", content="")
|
13
|
+
self.headers = headers
|
14
|
+
self.content = content
|
15
|
+
end
|
16
|
+
|
17
|
+
def headers=(headers)
|
18
|
+
@headers = headers_2_hash(headers)
|
19
|
+
@headers.each {|k,v| v.chomp! if v.is_a?(String)}
|
20
|
+
end
|
21
|
+
|
22
|
+
def content=(content)
|
23
|
+
@content = content.match(/:/) ? headers_2_hash(content) : content
|
24
|
+
@content.each {|k,v| v.chomp! if v.is_a?(String)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def event?
|
28
|
+
@content.is_a?(Hash) && @content.include?(:event_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def event
|
32
|
+
@content[:event_name] if event?
|
33
|
+
end
|
34
|
+
|
35
|
+
def api_response?
|
36
|
+
@headers[:content_type] == "api/response"
|
37
|
+
end
|
38
|
+
|
39
|
+
def command_reply?
|
40
|
+
@headers[:content_type] == "command/reply"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def headers_2_hash(*args)
|
45
|
+
EM::Protocols::HeaderAndContentProtocol.headers_2_hash *args
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/librevox.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "librevox"
|
3
|
+
s.version = "0.1"
|
4
|
+
s.date = "2009-12-14"
|
5
|
+
s.summary = "Ruby library for interacting with FreeSWITCH."
|
6
|
+
s.email = "harry@vangberg.name"
|
7
|
+
s.homepage = "http://github.com/ichverstehe/librevox"
|
8
|
+
s.description = "EventMachine-based Ruby library for interacting with the
|
9
|
+
open source telephony platform FreeSwitch."
|
10
|
+
s.authors = ["Harry Vangberg"]
|
11
|
+
s.files = [
|
12
|
+
"README.md",
|
13
|
+
"LICENSE",
|
14
|
+
"TODO",
|
15
|
+
"Rakefile",
|
16
|
+
"librevox.gemspec",
|
17
|
+
"lib/librevox.rb",
|
18
|
+
"lib/librevox/applications.rb",
|
19
|
+
"lib/librevox/command_socket.rb",
|
20
|
+
"lib/librevox/commands.rb",
|
21
|
+
"lib/librevox/response.rb",
|
22
|
+
"lib/librevox/listener/base.rb",
|
23
|
+
"lib/librevox/listener/inbound.rb",
|
24
|
+
"lib/librevox/listener/outbound.rb"
|
25
|
+
]
|
26
|
+
s.test_files = [
|
27
|
+
"spec/helper.rb",
|
28
|
+
"spec/librevox/listener.rb",
|
29
|
+
"spec/librevox/spec_applications.rb",
|
30
|
+
"spec/librevox/spec_command_socket.rb",
|
31
|
+
"spec/librevox/spec_commands.rb",
|
32
|
+
"spec/librevox/spec_response.rb",
|
33
|
+
"spec/librevox/listener/spec_inbound.rb",
|
34
|
+
"spec/librevox/listener/spec_outbound.rb"
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
require 'librevox/listener/base'
|
4
|
+
|
5
|
+
class Librevox::Listener::Base
|
6
|
+
attr_accessor :outgoing_data
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@outgoing_data = []
|
10
|
+
super *args
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_data(data)
|
14
|
+
@outgoing_data << data
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_data
|
18
|
+
@outgoing_data.pop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
shared "events" do
|
23
|
+
before do
|
24
|
+
@class = @listener.class
|
25
|
+
|
26
|
+
@class.event(:some_event) {send_data "something"}
|
27
|
+
@class.event(:other_event) {send_data "something else"}
|
28
|
+
|
29
|
+
# Establish session
|
30
|
+
@listener.receive_data("Content-Length: 0\nTest: Testing\n\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
should "add event hook" do
|
34
|
+
@class.hooks.size.should == 2
|
35
|
+
end
|
36
|
+
|
37
|
+
should "execute callback for event" do
|
38
|
+
@listener.receive_data("Content-Length: 23\n\nEvent-Name: OTHER_EVENT\n\n")
|
39
|
+
@listener.read_data.should == "something else"
|
40
|
+
|
41
|
+
@listener.receive_data("Content-Length: 22\n\nEvent-Name: SOME_EVENT\n\n")
|
42
|
+
@listener.read_data.should == "something"
|
43
|
+
end
|
44
|
+
|
45
|
+
should "expose response as event" do
|
46
|
+
@listener.receive_data("Content-Length: 23\n\nEvent-Name: OTHER_EVENT\n\n")
|
47
|
+
@listener.event.class.should == Librevox::Response
|
48
|
+
@listener.event.content[:event_name].should == "OTHER_EVENT"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Librevox::Commands
|
53
|
+
def sample_cmd(cmd, args="", &b)
|
54
|
+
execute_cmd cmd, args, &b
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
shared "api commands" do
|
59
|
+
before do
|
60
|
+
@class = @listener.class
|
61
|
+
|
62
|
+
# Establish session
|
63
|
+
@listener.receive_data("Content-Type: command/reply\nTest: Testing\n\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "multiple api commands" do
|
67
|
+
before do
|
68
|
+
@listener.outgoing_data.clear
|
69
|
+
@class.event(:api_test) {
|
70
|
+
api :sample_cmd, "foo" do
|
71
|
+
api :sample_cmd, "foo", "bar baz"
|
72
|
+
end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
should "only send one command at a time" do
|
77
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 22\n\nEvent-Name: API_TEST\n\n")
|
78
|
+
@listener.read_data.should == "api foo\n\n"
|
79
|
+
@listener.read_data.should == nil
|
80
|
+
|
81
|
+
@listener.receive_data("Content-Type: api/response\nReply-Text: +OK\n\n")
|
82
|
+
@listener.read_data.should == "api foo bar baz\n\n"
|
83
|
+
@listener.read_data.should == nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "flat api commands" do
|
88
|
+
before do
|
89
|
+
@listener.outgoing_data.clear
|
90
|
+
@class.event(:api_flat_test) {
|
91
|
+
api :sample_cmd, "foo"
|
92
|
+
api :sample_cmd, "bar" do
|
93
|
+
api :sample_cmd, "baz"
|
94
|
+
end
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
should "wait for response before calling next proc" do
|
99
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 27\n\nEvent-Name: API_FLAT_TEST\n\n")
|
100
|
+
|
101
|
+
@listener.read_data.should.not == "api baz\n\n"
|
102
|
+
|
103
|
+
# response to "foo"
|
104
|
+
@listener.receive_data("Content-Type: api/response\nContent-Length: 3\n\n+OK\n\n")
|
105
|
+
@listener.read_data.should.not == "api baz\n\n"
|
106
|
+
|
107
|
+
# response to "bar"
|
108
|
+
@listener.receive_data("Content-Type: api/response\nContent-Length: 3\n\n+OK\n\n")
|
109
|
+
@listener.read_data.should == "api baz\n\n"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "api command with block argument" do
|
114
|
+
before do
|
115
|
+
@listener.outgoing_data.clear
|
116
|
+
@class.event(:api_arg_test) {
|
117
|
+
api :sample_cmd, "foo" do |r|
|
118
|
+
send_data "response: #{r.content}"
|
119
|
+
end
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
should "pass response" do
|
124
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 26\n\nEvent-Name: API_ARG_TEST\n\n")
|
125
|
+
@listener.receive_data("Content-Type: api/response\nContent-Length: 3\n\n+OK\n\n")
|
126
|
+
|
127
|
+
@listener.read_data.should == "response: +OK"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require 'spec/librevox/listener'
|
3
|
+
|
4
|
+
require 'librevox/listener/inbound'
|
5
|
+
|
6
|
+
class InboundTestListener < Librevox::Listener::Inbound
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Inbound listener" do
|
10
|
+
before do
|
11
|
+
@listener = InboundTestListener.new(nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
behaves_like "events"
|
15
|
+
behaves_like "api commands"
|
16
|
+
|
17
|
+
should "authorize and subscribe to events" do
|
18
|
+
@listener.outgoing_data.shift.should == "auth ClueCon\n\n"
|
19
|
+
@listener.outgoing_data.shift.should == "event plain ALL\n\n"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require 'spec/librevox/listener'
|
3
|
+
|
4
|
+
require 'librevox/listener/outbound'
|
5
|
+
|
6
|
+
module Librevox::Applications
|
7
|
+
def sample_app(name, *args, &b)
|
8
|
+
execute_app name, args.join(" "), &b
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class OutboundTestListener < Librevox::Listener::Outbound
|
13
|
+
session do
|
14
|
+
send_data "session was initiated"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def receive_event_and_linger_replies
|
19
|
+
@listener.receive_data("Content-Type: command/reply\nReply-Text: +OK Events Enabled\n\n")
|
20
|
+
@listener.receive_data("Content-Type: command/reply\nReply-Text: +OK will linger\n\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Outbound listener" do
|
24
|
+
before do
|
25
|
+
@listener = OutboundTestListener.new(nil)
|
26
|
+
@listener.receive_data("Content-Type: command/reply\nCaller-Caller-ID-Number: 8675309\n\n")
|
27
|
+
receive_event_and_linger_replies
|
28
|
+
end
|
29
|
+
|
30
|
+
should "connect to freeswitch and subscribe to events" do
|
31
|
+
@listener.outgoing_data.shift.should.equal "connect\n\n"
|
32
|
+
@listener.outgoing_data.shift.should.equal "myevents\n\n"
|
33
|
+
@listener.outgoing_data.shift.should.equal "linger\n\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
should "establish a session" do
|
37
|
+
@listener.session.class.should.equal Librevox::Response
|
38
|
+
end
|
39
|
+
|
40
|
+
should "call session callback after establishing new session" do
|
41
|
+
@listener.read_data.should.equal "session was initiated"
|
42
|
+
end
|
43
|
+
|
44
|
+
should "make channel variables available through session" do
|
45
|
+
@listener.session.headers[:caller_caller_id_number].should.equal "8675309"
|
46
|
+
end
|
47
|
+
|
48
|
+
behaves_like "events"
|
49
|
+
behaves_like "api commands"
|
50
|
+
|
51
|
+
should "register app" do
|
52
|
+
@listener.respond_to?(:sample_app).should.be.true?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class OutboundListenerWithNestedApps < Librevox::Listener::Outbound
|
57
|
+
session do
|
58
|
+
sample_app "foo" do
|
59
|
+
sample_app "bar"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "Outbound listener with apps" do
|
65
|
+
before do
|
66
|
+
@listener = OutboundListenerWithNestedApps.new(nil)
|
67
|
+
|
68
|
+
# Establish session and get rid of connect-string
|
69
|
+
@listener.receive_data("Content-Type: command/reply\nEstablish-Session: OK\n\n")
|
70
|
+
receive_event_and_linger_replies
|
71
|
+
3.times {@listener.outgoing_data.shift}
|
72
|
+
end
|
73
|
+
|
74
|
+
should "only send one app at a time" do
|
75
|
+
@listener.read_data.should == "sendmsg\ncall-command: execute\nexecute-app-name: foo\n\n"
|
76
|
+
@listener.read_data.should == nil
|
77
|
+
|
78
|
+
@listener.receive_data("Content-Type: command/reply\nReply-Text: +OK\n\n")
|
79
|
+
@listener.read_data.should == "sendmsg\ncall-command: execute\nexecute-app-name: bar\n\n"
|
80
|
+
@listener.read_data.should == nil
|
81
|
+
end
|
82
|
+
|
83
|
+
should "not be driven forward by events" do
|
84
|
+
@listener.read_data # sample_app "foo"
|
85
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 45\n\nEvent-Name: CHANNEL_EXECUTE\nSession-Var: Some\n\n")
|
86
|
+
@listener.read_data.should == nil
|
87
|
+
end
|
88
|
+
|
89
|
+
should "not be driven forward by api responses" do
|
90
|
+
@listener.read_data # sample_app "foo"
|
91
|
+
@listener.receive_data("Content-Type: api/response\nContent-Length: 3\n\nFoo")
|
92
|
+
@listener.read_data.should == nil
|
93
|
+
end
|
94
|
+
|
95
|
+
should "not be driven forward by disconnect notifications" do
|
96
|
+
@listener.read_data # sample_app "foo"
|
97
|
+
@listener.receive_data("Content-Type: text/disconnect-notice\nContent-Length: 9\n\nLingering")
|
98
|
+
@listener.read_data.should == nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
module Librevox::Applications
|
103
|
+
def reader_app(&b)
|
104
|
+
execute_app 'reader_app', [], {:read_var => 'a_reader_var'}, &b
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class OutboundListenerWithReader < Librevox::Listener::Outbound
|
109
|
+
session do
|
110
|
+
reader_app do |data|
|
111
|
+
send_data "read this: #{data}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "Outbound listener with app reading data" do
|
117
|
+
before do
|
118
|
+
@listener = OutboundListenerWithReader.new(nil)
|
119
|
+
|
120
|
+
# Establish session and get rid of connect-string
|
121
|
+
@listener.receive_data("Content-Type: command/reply\nSession-Var: First\nUnique-ID: 1234\n\n")
|
122
|
+
receive_event_and_linger_replies
|
123
|
+
3.times {@listener.outgoing_data.shift}
|
124
|
+
end
|
125
|
+
|
126
|
+
should "not send anything while missing response" do
|
127
|
+
@listener.read_data # the command executing reader_app
|
128
|
+
@listener.read_data.should == nil
|
129
|
+
end
|
130
|
+
|
131
|
+
should "send uuid_dump to get channel var, after getting response" do
|
132
|
+
@listener.receive_data("Content-Type: command/reply\nReply-Text: +OK\n\n")
|
133
|
+
@listener.read_data.should == "api uuid_dump 1234\n\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
should "update session with new data" do
|
137
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
138
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 44\n\nEvent-Name: CHANNEL_DATA\nSession-Var: Second\n\n")
|
139
|
+
@listener.session.content[:session_var].should == "Second"
|
140
|
+
end
|
141
|
+
|
142
|
+
should "pass value of channel variable to block" do
|
143
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
144
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 59\n\nEvent-Name: CHANNEL_DATA\nvariable_a-reader-var: some value\n\n")
|
145
|
+
@listener.read_data.should == "read this: some value"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class OutboundListenerWithNonNestedApps < Librevox::Listener::Outbound
|
150
|
+
attr_reader :queue
|
151
|
+
session do
|
152
|
+
sample_app "foo"
|
153
|
+
reader_app do |data|
|
154
|
+
send_data "the end: #{data}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "Outbound listener with non-nested apps" do
|
160
|
+
before do
|
161
|
+
@listener = OutboundListenerWithNonNestedApps.new(nil)
|
162
|
+
|
163
|
+
# Establish session and get rid of connect-string
|
164
|
+
@listener.receive_data("Content-Type: command/reply\nSession-Var: First\nUnique-ID: 1234\n\n")
|
165
|
+
receive_event_and_linger_replies
|
166
|
+
3.times {@listener.outgoing_data.shift}
|
167
|
+
end
|
168
|
+
|
169
|
+
should "wait for response before calling next proc" do
|
170
|
+
# response to sample_app
|
171
|
+
@listener.read_data.should.not.match /the end/
|
172
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
173
|
+
|
174
|
+
# response to reader_app
|
175
|
+
@listener.read_data.should.not.match /the end/
|
176
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
177
|
+
|
178
|
+
# response to uuid_dump caused by reader_app
|
179
|
+
@listener.read_data.should.not.match /the end/
|
180
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 59\n\nEvent-Name: CHANNEL_DATA\nvariable_a-reader-var: some value\n\n")
|
181
|
+
|
182
|
+
@listener.read_data.should == "the end: some value"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
module Librevox::Commands
|
187
|
+
def sample_cmd(cmd, *args, &b)
|
188
|
+
execute_cmd cmd, *args, &b
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class OutboundListenerWithAppsAndApi < Librevox::Listener::Outbound
|
193
|
+
session do
|
194
|
+
sample_app "foo" do
|
195
|
+
api :sample_cmd, "bar" do
|
196
|
+
sample_app "baz"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "Outbound listener with both apps and api calls" do
|
203
|
+
before do
|
204
|
+
@listener = OutboundListenerWithAppsAndApi.new(nil)
|
205
|
+
|
206
|
+
# Establish session and get rid of connect-string
|
207
|
+
@listener.receive_data("Content-Type: command/reply\nSession-Var: First\nUnique-ID: 1234\n\n")
|
208
|
+
receive_event_and_linger_replies
|
209
|
+
3.times {@listener.outgoing_data.shift}
|
210
|
+
end
|
211
|
+
|
212
|
+
should "wait for response before calling next proc" do
|
213
|
+
@listener.read_data.should == "sendmsg\ncall-command: execute\nexecute-app-name: foo\n\n"
|
214
|
+
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
215
|
+
|
216
|
+
@listener.read_data.should == "api bar\n\n"
|
217
|
+
@listener.receive_data("Content-Type: api/response\nContent-Length: 3\n\n+OK\n\n")
|
218
|
+
|
219
|
+
@listener.read_data.should == "sendmsg\ncall-command: execute\nexecute-app-name: baz\n\n"
|
220
|
+
end
|
221
|
+
end
|