141 lines
5.0 KiB
Go
141 lines
5.0 KiB
Go
package asterisk
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func hepTestFrame(cid, sip string) []byte {
|
|
packet := []byte("HEP3\x00\x00")
|
|
for _, chunk := range []struct {
|
|
kind uint16
|
|
data []byte
|
|
}{{11, []byte{1}}, {17, []byte(cid)}, {15, []byte(sip)}} {
|
|
part := make([]byte, 6+len(chunk.data))
|
|
binary.BigEndian.PutUint16(part[2:4], chunk.kind)
|
|
binary.BigEndian.PutUint16(part[4:6], uint16(len(part)))
|
|
copy(part[6:], chunk.data)
|
|
packet = append(packet, part...)
|
|
}
|
|
binary.BigEndian.PutUint16(packet[4:6], uint16(len(packet)))
|
|
return packet
|
|
}
|
|
|
|
func hepTestSIP(callID, method, branch, firstLine string) string {
|
|
return firstLine + "\r\nVia: SIP/2.0/UDP 127.0.0.1:5060;branch=" + branch +
|
|
"\r\nFrom: <sip:caller@localhost>;tag=a\r\nTo: <sip:callee@localhost>\r\nCall-ID: " + callID +
|
|
"\r\nCSeq: 1 " + method + "\r\nContent-Length: 0\r\n\r\n"
|
|
}
|
|
|
|
func TestHEPMirrorKeepsConcurrentSIPTransactionsSeparate(t *testing.T) {
|
|
mirror := NewHEPMirror()
|
|
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := mirror.Bind("exec-b", "sip-id-b"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := mirror.Bind("exec-duplicate", "sip-id-a"); err == nil {
|
|
t.Fatal("one SIP Call-ID cannot belong to two executions")
|
|
}
|
|
var wg sync.WaitGroup
|
|
for _, tc := range []struct {
|
|
id, branch, code string
|
|
}{{"sip-id-a", "z9hG4bK-a", "480 Temporarily Unavailable"}, {"sip-id-b", "z9hG4bK-b", "486 Busy Here"}} {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
invite := hepTestSIP(tc.id, "INVITE", tc.branch, "INVITE sip:callee@localhost SIP/2.0")
|
|
response := hepTestSIP(tc.id, "INVITE", tc.branch, "SIP/2.0 "+tc.code)
|
|
if err := mirror.Observe(hepTestFrame(tc.id, response)); err != nil { // UDP may reorder the response.
|
|
t.Error(err)
|
|
}
|
|
if err := mirror.Observe(hepTestFrame(tc.id, invite)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
for _, tc := range []struct {
|
|
execID, want string
|
|
}{{"exec-a", "SIP/2.0 480 Temporarily Unavailable"}, {"exec-b", "SIP/2.0 486 Busy Here"}} {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
resp, err := mirror.Final(ctx, tc.execID)
|
|
cancel()
|
|
if err != nil || resp.StatusLine != tc.want || !strings.HasPrefix(resp.Raw, tc.want+"\r\n") {
|
|
t.Fatalf("wrong execution response: %+v err=%v", resp, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHEPMirrorRejectsUnmatchedAndMalformedPacketsWithoutGuessing(t *testing.T) {
|
|
mirror := NewHEPMirror()
|
|
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, bad := range [][]byte{nil, []byte("HEP3"), []byte("HEP3\x00\x06"), []byte("HEP3\x00\x0c\x00\x00\x00\x0f\x00\x00")} {
|
|
if err := mirror.Observe(bad); err == nil {
|
|
t.Fatalf("malformed frame accepted: len=%d", len(bad))
|
|
}
|
|
}
|
|
unrelated := hepTestSIP("sip-id-b", "INVITE", "z9hG4bK-b", "SIP/2.0 480 Temporarily Unavailable")
|
|
if err := mirror.Observe(hepTestFrame("sip-id-b", unrelated)); err != nil {
|
|
t.Fatalf("unrelated traffic is not a parser error: %v", err)
|
|
}
|
|
incorrectCID := hepTestSIP("sip-id-b", "INVITE", "z9hG4bK-b", "SIP/2.0 480 Temporarily Unavailable")
|
|
if err := mirror.Observe(hepTestFrame("sip-id-a", incorrectCID)); err == nil {
|
|
t.Fatal("payload Call-ID and HEP CID disagreed")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
|
defer cancel()
|
|
if resp, err := mirror.Final(ctx, "exec-a"); err == nil || resp != nil {
|
|
t.Fatalf("missing observed response was fabricated: %+v err=%v", resp, err)
|
|
}
|
|
}
|
|
|
|
func TestHEPMirrorKeepsOriginalInviteWhenLaterReinviteArrives(t *testing.T) {
|
|
mirror := NewHEPMirror()
|
|
if err := mirror.Bind("exec", "sip-id"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sip := range []string{
|
|
hepTestSIP("sip-id", "INVITE", "z9hG4bK-first", "INVITE sip:callee@localhost SIP/2.0"),
|
|
hepTestSIP("sip-id", "INVITE", "z9hG4bK-first", "SIP/2.0 200 OK"),
|
|
hepTestSIP("sip-id", "INVITE", "z9hG4bK-later", "INVITE sip:callee@localhost SIP/2.0"),
|
|
hepTestSIP("sip-id", "INVITE", "z9hG4bK-later", "SIP/2.0 488 Not Acceptable Here"),
|
|
} {
|
|
if err := mirror.Observe(hepTestFrame("sip-id", sip)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
resp, err := mirror.Final(context.Background(), "exec")
|
|
if err != nil || resp.Code != 200 {
|
|
t.Fatalf("later reINVITE must not replace original INVITE outcome: %+v %v", resp, err)
|
|
}
|
|
}
|
|
|
|
func TestHEPMirrorNeverTreatsProvisionalOrOtherMethodsAsFinalINVITE(t *testing.T) {
|
|
mirror := NewHEPMirror()
|
|
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sip := range []string{
|
|
hepTestSIP("sip-id-a", "INVITE", "z9hG4bK-a", "INVITE sip:callee@localhost SIP/2.0"),
|
|
hepTestSIP("sip-id-a", "INVITE", "z9hG4bK-a", "SIP/2.0 180 Ringing"),
|
|
hepTestSIP("sip-id-a", "BYE", "z9hG4bK-a", "SIP/2.0 200 OK"),
|
|
} {
|
|
if err := mirror.Observe(hepTestFrame("sip-id-a", sip)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
|
defer cancel()
|
|
if resp, err := mirror.Final(ctx, "exec-a"); err == nil || resp != nil {
|
|
t.Fatalf("non-final or non-INVITE response became an outcome: %+v err=%v", resp, err)
|
|
}
|
|
}
|