feat(agent): preserve approved SIP route in ARI originate request
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package asterisk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/CyCoreSystems/ari/v5"
|
||||
)
|
||||
|
||||
var dialedRoute = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
|
||||
|
||||
// approvedOriginateRequest binds an already-approved Agent instruction to one
|
||||
// native ARI channel. The Dispatcher, not the Agent, selected the trunk,
|
||||
// caller and complete dialed number (including any trunk-specific prefix).
|
||||
func approvedOriginateRequest(executionID, trunkID, dialedCallee, callerID string, answerTimeout time.Duration) (ari.OriginateRequest, error) {
|
||||
if !trunkName.MatchString(executionID) || !trunkName.MatchString(trunkID) || !dialedRoute.MatchString(dialedCallee) || strings.TrimSpace(callerID) == "" || strings.ContainsAny(callerID, "\r\n") {
|
||||
return ari.OriginateRequest{}, errors.New("approved ARI channel identity or route is incomplete")
|
||||
}
|
||||
seconds := int(answerTimeout / time.Second)
|
||||
if seconds < 1 {
|
||||
return ari.OriginateRequest{}, fmt.Errorf("approved ARI answer window is shorter than one second: %s", answerTimeout)
|
||||
}
|
||||
return ari.OriginateRequest{
|
||||
ChannelID: executionID, Endpoint: "PJSIP/" + dialedCallee + "@" + trunkID,
|
||||
CallerID: callerID, App: "go-sip-agent", Timeout: seconds,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package asterisk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOriginatePreservesApprovedRouteAndCaller(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
trunk, dialed, caller, expected string
|
||||
}{
|
||||
{"shuqi", "708915000000001", "BD1234", "PJSIP/708915000000001@shuqi"},
|
||||
{"zhongding", "15000000001", "mbkq", "PJSIP/15000000001@zhongding"},
|
||||
{"baiying", "mka75515000000001", "KQ1234", "PJSIP/mka75515000000001@baiying"},
|
||||
} {
|
||||
request, err := approvedOriginateRequest("execution-123", tc.trunk, tc.dialed, tc.caller, 7*time.Second+900*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if request.Endpoint != tc.expected || request.CallerID != tc.caller || request.ChannelID != "execution-123" || request.Timeout != 7 || request.App != "go-sip-agent" {
|
||||
t.Fatalf("Agent changed approved SIP route/caller or exceeded answer deadline: %+v", request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOriginateRejectsMissingOrUnrepresentableSignedRoute(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
executionID, trunk, dialed, caller string
|
||||
answer time.Duration
|
||||
}{
|
||||
{"", "shuqi", "708915000000001", "BD1234", time.Second},
|
||||
{"execution-123", "../shuqi", "708915000000001", "BD1234", time.Second},
|
||||
{"execution-123", "shuqi", "7089/15000000001", "BD1234", time.Second},
|
||||
{"execution-123", "shuqi", "708915000000001", "BD1234\n", time.Second},
|
||||
{"execution-123", "shuqi", "708915000000001", "BD1234", 900 * time.Millisecond},
|
||||
} {
|
||||
if _, err := approvedOriginateRequest(tc.executionID, tc.trunk, tc.dialed, tc.caller, tc.answer); err == nil {
|
||||
t.Fatal("invalid signed route or subsecond answer timeout cannot create a channel")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user