librevox 0.1
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/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
|