Files
go-sip/internal/asterisk/answer.go
T

68 lines
2.0 KiB
Go

package asterisk
import (
"context"
"errors"
"fmt"
"strings"
"github.com/CyCoreSystems/ari/v5"
)
// awaitStasisStart only marks the exact originated channel answered after it
// enters our ARI application. A generic Up event is not proof of media setup.
func awaitStasisStart(ctx context.Context, subscription ari.Subscription, channelID string) error {
if ctx == nil || subscription == nil || channelID == "" {
return errors.New("ARI answer subscription and channel identity required")
}
recent := make([]string, 0, 8)
for {
select {
case <-ctx.Done():
return fmt.Errorf("ARI answer deadline or cancellation (recent_events=%s): %w", strings.Join(recent, ","), ctx.Err())
case event, open := <-subscription.Events():
if !open {
return errors.New("ARI event subscription closed before answer")
}
if event == nil {
return errors.New("ARI answer subscription delivered an empty event")
}
typ := event.GetType()
if len(recent) == cap(recent) {
recent = recent[1:]
}
recent = append(recent, typ)
if !eventBelongsToChannel(event, channelID) {
continue
}
switch typ {
case "StasisStart":
if _, ok := event.(*ari.StasisStart); ok {
return nil
}
case "StasisEnd":
return errors.New("ARI channel left the application before StasisStart")
case "ChannelHangupRequest":
if hangup, ok := event.(*ari.ChannelHangupRequest); ok {
return fmt.Errorf("ARI channel ended before StasisStart: hangup_cause=%d", hangup.Cause)
}
return errors.New("ARI channel ended before StasisStart: hangup request")
case "ChannelDestroyed":
if destroyed, ok := event.(*ari.ChannelDestroyed); ok {
return fmt.Errorf("ARI channel ended before StasisStart: destroy_cause=%d", destroyed.Cause)
}
return errors.New("ARI channel ended before StasisStart: destroyed")
}
}
}
}
func eventBelongsToChannel(event ari.Event, channelID string) bool {
for _, key := range event.Keys() {
if key != nil && key.Kind == "channel" && key.ID == channelID {
return true
}
}
return false
}