Skip to content

Commit

Permalink
Patch mod_rayo to fix idle timeout for connect verb (#971)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwilkie authored Jan 28, 2025
1 parent c50a18b commit 0abe463
Show file tree
Hide file tree
Showing 20 changed files with 333 additions and 61 deletions.
27 changes: 11 additions & 16 deletions .github/workflows/switch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Install native dependencies
run: |
sudo apt-get update
sudo apt-get install libpcre3-dev
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
Expand Down Expand Up @@ -139,10 +144,8 @@ jobs:
docker buildx build --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.APP_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push components/app
docker buildx build --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.WEBSERVER_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.WEBSERVER_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push components/nginx
docker buildx build --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_EVENT_LOGGER_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_EVENT_LOGGER_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push components/freeswitch_event_logger
if [ "${{ matrix.platform }}" = "amd64" ]; then
components/freeswitch/bin/export_tts_voices > components/freeswitch/conf/autoload_configs/tts_voices.xml
docker buildx build --build-arg signalwire_token=${{ secrets.SIGNALWIRE_TOKEN }} --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push components/freeswitch
fi
components/freeswitch/bin/export_tts_voices > components/freeswitch/conf/autoload_configs/tts_voices.xml
docker buildx build --build-arg signalwire_token=${{ secrets.SIGNALWIRE_TOKEN }} --cache-from ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --tag ${{ secrets.ECR_REGISTRY }}/${{ env.FREESWITCH_REPOSITORY_NAME }}:${{ matrix.friendly_image_tag }}-${{ matrix.platform }} --push components/freeswitch
build-manifest:
name: Build Manifest
Expand Down Expand Up @@ -180,12 +183,8 @@ jobs:
declare -a components=("${{ env.APP_REPOSITORY_NAME }}" "${{ env.WEBSERVER_REPOSITORY_NAME }}" "${{ env.FREESWITCH_REPOSITORY_NAME }}" "${{ env.FREESWITCH_EVENT_LOGGER_REPOSITORY_NAME }}")
for component in "${components[@]}"
do
if [ "$component" = "${{ env.FREESWITCH_REPOSITORY_NAME }}" ]; then
docker buildx imagetools create -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.image_tag }} ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-amd64
else
source_images=$(printf "${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}")
docker buildx imagetools create -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.image_tag }} $source_images
fi
source_images=$(printf "${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}")
docker buildx imagetools create -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} -t ${{ secrets.ECR_REGISTRY }}/$component:${{ matrix.image_tag }} $source_images
done
publish_images:
Expand Down Expand Up @@ -242,12 +241,8 @@ jobs:
for component in "${components[@]}"
do
if [ "$component" = "${{ env.FREESWITCH_REPOSITORY_NAME }}" ]; then
docker buildx imagetools create -t ${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} ${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-amd64
else
source_images=$(printf "${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}")
docker buildx imagetools create -t ${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} $source_images
fi
source_images=$(printf "${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }}-%s " "${platforms[@]}")
docker buildx imagetools create -t ${{ secrets.GHCR_REGISTRY }}/$component:${{ matrix.friendly_image_tag }} $source_images
done
deploy:
Expand Down
2 changes: 2 additions & 0 deletions components/app/app/web/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class Application < Sinatra::Base
set :root, __dir__
enable :logging

set :host_authorization, { permitted_hosts: [] }

configure :development do
require "sinatra/reloader"

Expand Down
13 changes: 13 additions & 0 deletions components/app/app/workflows/disconnect_twilio_stream.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class DisconnectTwilioStream < ApplicationWorkflow
attr_reader :call_controller, :phone_call

def initialize(call_controller)
super()
@call_controller = call_controller
@phone_call = call_controller.call
end

def call
phone_call.write_command(Rayo::Command::UpdateCallProgress.new(flag: 0))
end
end
24 changes: 8 additions & 16 deletions components/app/app/workflows/execute_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ def call

on.message do |channel, message|
if event_handler.handle_events_for?(channel, stream_sid)
handle_stream_event(message) { |event| connection.unsubscribe if event.disconnect? }
handle_stream_event(message) do |event|
next unless event.disconnect?

connection.unsubscribe
DisconnectTwilioStream.call(context)
end
elsif call_update_event_handler.handle_events_for?(channel, phone_call.id)
handle_call_update_event(message) { connection.unsubscribe(channel) }
end
Expand Down Expand Up @@ -66,23 +71,10 @@ def create_media_stream(**params)
end

def start_stream!(url:, stream_sid:, custom_parameters:)
context.write_and_await_response(
Rayo::Command::TwilioStream::Start.new(
uuid: phone_call.id,
url:,
metadata: {
call_sid: call_properties.call_sid,
account_sid: call_properties.account_sid,
stream_sid:,
custom_parameters:
}
)
)
StartTwilioStream.call(context, url:, stream_sid:, call_properties:, custom_parameters:)
end

def stop_stream!
context.write_and_await_response(
Rayo::Command::TwilioStream::Stop.new(uuid: phone_call.id)
)
StopTwilioStream.call(context)
end
end
29 changes: 29 additions & 0 deletions components/app/app/workflows/start_twilio_stream.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class StartTwilioStream < ApplicationWorkflow
attr_reader :call_controller, :phone_call, :call_properties, :url, :stream_sid, :custom_parameters

def initialize(call_controller, **options)
super()
@call_controller = call_controller
@phone_call = call_controller.call
@call_properties = options.fetch(:call_properties)
@url = options.fetch(:url)
@stream_sid = options.fetch(:stream_sid)
@custom_parameters = options.fetch(:custom_parameters)
end

def call
call_controller.write_and_await_response(
Rayo::Command::TwilioStream::Start.new(
uuid: phone_call.id,
url:,
metadata: {
call_sid: call_properties.call_sid,
account_sid: call_properties.account_sid,
stream_sid:,
custom_parameters:
}
)
)
phone_call.write_command(Rayo::Command::UpdateCallProgress.new(flag: 1))
end
end
14 changes: 14 additions & 0 deletions components/app/app/workflows/stop_twilio_stream.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class StopTwilioStream < ApplicationWorkflow
attr_reader :call_controller, :phone_call

def initialize(call_controller)
super()
@call_controller = call_controller
@phone_call = call_controller.call
end

def call
call_controller.write_and_await_response(Rayo::Command::TwilioStream::Stop.new(uuid: phone_call.id))
DisconnectTwilioStream.call(call_controller)
end
end
1 change: 1 addition & 0 deletions components/app/lib/rayo/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ module Command
end

require_relative "command/twilio_stream"
require_relative "command/update_call_progress"
15 changes: 15 additions & 0 deletions components/app/lib/rayo/command/update_call_progress.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "adhearsion/rayo/command_node"

module Rayo
module Command
class UpdateCallProgress < Adhearsion::Rayo::CommandNode
register :call_progress, :core

attribute :flag

def rayo_attributes
{ "flag" => flag }
end
end
end
end
17 changes: 17 additions & 0 deletions components/app/spec/lib/rayo/command/update_call_progress_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "spec_helper"

module Rayo
module Command
RSpec.describe UpdateCallProgress do
describe "#to_xml" do
it "serializes to Rayo XML" do
xml = Hash.from_xml(UpdateCallProgress.new(flag: 1).to_xml)
expect(xml.fetch("call_progress")).to include("flag" => "1")

xml = Hash.from_xml(UpdateCallProgress.new(flag: 0).to_xml)
expect(xml.fetch("call_progress")).to include("flag" => "0")
end
end
end
end
end
16 changes: 16 additions & 0 deletions components/app/spec/workflows/disconnect_twilio_stream_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "spec_helper"

RSpec.describe DisconnectTwilioStream, type: :call_controller do
it "disconnects a twilio stream" do
controller = build_controller

DisconnectTwilioStream.call(controller)

expect(controller.call).to have_received(:write_command).with(
have_attributes(
class: Rayo::Command::UpdateCallProgress,
flag: 0
)
)
end
end
30 changes: 10 additions & 20 deletions components/app/spec/workflows/execute_connect_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"bar" => "baz"
}
)
call_platform_client = stub_call_platform_client(stream_sid: "stream-sid")
call_platform_client = stub_call_platform_client
allow(StartTwilioStream).to receive(:call)
controller = build_controller(
stub_voice_commands: :write_and_await_response,
call: build_fake_call(id: "call-id")
)

Expand All @@ -21,7 +21,7 @@
**build_workflow_options(
context: controller,
call_platform_client:,
call_properties: { call_sid: "call-sid", account_sid: "account-sid" }
call_properties: { call_sid: "call-sid" }
)
)

Expand All @@ -34,21 +34,7 @@
"bar" => "baz"
}
)
expect(controller).to have_received(:write_and_await_response) do |command|
expect(command).to have_attributes(
uuid: "call-id",
url: "wss://example.com/audio",
metadata: {
call_sid: "call-sid",
account_sid: "account-sid",
stream_sid: "stream-sid",
custom_parameters: {
"foo" => "bar",
"bar" => "baz"
}
}
)
end
expect(StartTwilioStream).to have_received(:call)
end

it "handles stream disconnects" do
Expand All @@ -60,14 +46,17 @@
event_handler.channel_for("stream-sid") => [ "message" ]
}
)
allow(DisconnectTwilioStream).to receive(:call)
controller = build_controller

ExecuteConnect.call(
verb,
redis_connection: -> { redis_connection },
**build_workflow_options(call_platform_client:, event_handler:)
**build_workflow_options(context: controller, call_platform_client:, event_handler:)
)

expect(event_handler.handled_events).to match_array([ disconnect_event ])
expect(DisconnectTwilioStream).to have_received(:call)
end

it "handles call updates" do
Expand All @@ -81,6 +70,7 @@
event_handler.channel_for("stream-sid") => [ "message" ]
}
)
allow(StopTwilioStream).to receive(:call)
controller = build_controller(
stub_voice_commands: :write_and_await_response,
call: build_fake_call(id: "call-id")
Expand All @@ -99,7 +89,7 @@

expect(event_handler.handled_events).to match_array([ disconnect_event ])
expect(call_update_event_handler.handled_events).to match_array([ call_update_event ])
expect(controller).to have_received(:write_and_await_response).with(an_instance_of(Rayo::Command::TwilioStream::Stop))
expect(StopTwilioStream).to have_received(:call)
end

def stub_fake_redis(channels: {}, poll_for_messages: true)
Expand Down
44 changes: 44 additions & 0 deletions components/app/spec/workflows/start_twilio_stream_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "spec_helper"

RSpec.describe StartTwilioStream, type: :call_controller do
it "starts a twilio stream" do
controller = build_controller(
stub_voice_commands: :write_and_await_response,
call: build_fake_call(id: "call-id")
)

StartTwilioStream.call(
controller,
call_properties: build_call_properties(call_sid: "call-sid", account_sid: "account-sid"),
url: "wss://example.com/audio",
stream_sid: "stream-sid",
custom_parameters: {
"foo" => "bar",
"bar" => "baz"
}
)

expect(controller).to have_received(:write_and_await_response).with(
have_attributes(
class: Rayo::Command::TwilioStream::Start,
uuid: "call-id",
url: "wss://example.com/audio",
metadata: {
call_sid: "call-sid",
account_sid: "account-sid",
stream_sid: "stream-sid",
custom_parameters: {
"foo" => "bar",
"bar" => "baz"
}
}
)
)
expect(controller.call).to have_received(:write_command).with(
have_attributes(
class: Rayo::Command::UpdateCallProgress,
flag: 1
)
)
end
end
19 changes: 19 additions & 0 deletions components/app/spec/workflows/stop_twilio_stream_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require "spec_helper"

RSpec.describe StopTwilioStream, type: :call_controller do
it "stops a twilio stream" do
controller = build_controller(stub_voice_commands: :write_and_await_response)

StopTwilioStream.call(controller)

expect(controller).to have_received(:write_and_await_response).with(
an_instance_of(Rayo::Command::TwilioStream::Stop)
)
expect(controller.call).to have_received(:write_command).with(
have_attributes(
class: Rayo::Command::UpdateCallProgress,
flag: 0
)
)
end
end
Loading

0 comments on commit 0abe463

Please sign in to comment.