Browse Source

All tests pass.

pull/2/head
Justine Alexandra Roberts Tunney 11 years ago
parent
commit
af5a023d94
7 changed files with 90 additions and 99 deletions
  1. +59
    -76
      example/echo2/echo2_test.go
  2. +1
    -1
      sdp/sdp.go
  3. +2
    -2
      sdp/sdp_test.go
  4. +7
    -7
      sip/messages.go
  5. +12
    -10
      sip/transport.go
  6. +8
    -0
      sip/uri.go
  7. +1
    -3
      sip/via.go

+ 59
- 76
example/echo2/echo2_test.go View File

@ -14,7 +14,7 @@ import (
)
func TestCallToEchoApp(t *testing.T) {
to := &sip.Addr{Uri: &sip.URI{Host: "127.0.0.1", Port: 5060}}
to := &sip.Addr{Uri: &sip.URI{User: "echo", Host: "127.0.0.1", Port: 5060}}
from := &sip.Addr{Uri: &sip.URI{Host: "127.0.0.1"}}
// Create the SIP UDP transport layer.
@ -24,13 +24,48 @@ func TestCallToEchoApp(t *testing.T) {
}
defer tp.Sock.Close()
// Create an RTP socket.
// Create an RTP session.
rtpsock, err := net.ListenPacket("udp", "108.61.60.146:0")
if err != nil {
t.Fatal("rtp listen:", err)
}
defer rtpsock.Close()
rtpaddr := rtpsock.LocalAddr().(*net.UDPAddr)
rrtpaddrChan := make(chan *net.UDPAddr)
go func() {
var rrtpaddr *net.UDPAddr
frameout := make([]byte, rtp.HeaderSize+160)
rtpHeader := rtp.Header{
PT: sdp.ULAWCodec.PT,
Seq: 666,
TS: 0,
Ssrc: rand.Uint32(),
}
for n := 0; n < 160; n++ {
frameout[rtp.HeaderSize+n] = byte(n)
}
ticker := time.NewTicker(20 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
rtpHeader.Write(frameout)
rtpHeader.TS += 160
rtpHeader.Seq++
if rrtpaddr != nil {
_, err := rtpsock.WriteTo(frameout, rrtpaddr)
if err != nil {
t.Fatal("rtp write", err)
}
}
case rrtpaddr = <-rrtpaddrChan:
if rrtpaddr == nil {
return
}
}
}
}()
defer func() { rrtpaddrChan <- nil }()
// Send an INVITE message with an SDP.
invite := sip.NewRequest(tp, "INVITE", to, from)
@ -41,15 +76,23 @@ func TestCallToEchoApp(t *testing.T) {
}
// Consume provisional messages until we receive answer.
var rrtpaddr *net.UDPAddr
var msg *sip.Msg
for {
tp.Sock.SetDeadline(time.Now().Add(time.Second))
msg := tp.Recv()
msg = tp.Recv()
if msg.Error != nil {
t.Fatal(msg.Error)
}
if msg.Status < sip.StatusOK {
log.Printf("Got provisional %d %s", msg.Status, msg.Phrase)
log.Printf("Provisional %d %s", msg.Status, msg.Phrase)
} else if msg.Status == sip.StatusOK {
log.Printf("Answered!")
}
if msg.Status >= sip.StatusOK {
err = tp.Send(sip.NewAck(invite, msg))
if err != nil {
t.Fatal(err)
}
}
if msg.Headers["Content-Type"] == "application/sdp" {
log.Printf("Establishing media session")
@ -57,71 +100,20 @@ func TestCallToEchoApp(t *testing.T) {
if err != nil {
t.Fatal("failed to parse sdp", err)
}
rrtpaddr = &net.UDPAddr{IP: net.ParseIP(rsdp.Addr), Port: int(rsdp.Audio.Port)}
rrtpaddrChan <- &net.UDPAddr{IP: net.ParseIP(rsdp.Addr), Port: int(rsdp.Audio.Port)}
}
if msg.Status == sip.StatusOK {
log.Printf("Answered!")
break
} else if msg.Status > sip.StatusOK {
t.Fatalf("Got %d %s", msg.Status, msg.Phrase)
NewAck(invite)
}
}
// Figure out where they want us to send RTP.
// Acknowledge the 200 OK to answer the call.
var ack sip.Msg
ack.Request = invite.Request
ack.From = msg.From
ack.To = msg.To
ack.CallID = msg.CallID
ack.Method = "ACK"
ack.CSeq = msg.CSeq
ack.CSeqMethod = "ACK"
ack.Via = msg.Via
b.Reset()
ack.Append(&b)
if amt, err := conn.Write(b.Bytes()); err != nil || amt != b.Len() {
t.Fatal(err)
}
// Send RTP packets containing junk until we get an echo response.
quit := make(chan bool)
go func() {
frameout := make([]byte, rtp.HeaderSize+160)
rtpHeader := rtp.Header{
PT: sdp.ULAWCodec.PT,
Seq: 666,
TS: 0,
Ssrc: rand.Uint32(),
}
for n := 0; n < 160; n++ {
frameout[rtp.HeaderSize+n] = byte(n)
}
ticker := time.NewTicker(20 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
rtpHeader.Write(frameout)
rtpHeader.TS += 160
rtpHeader.Seq++
amt, err = rtpsock.WriteTo(frameout, rrtpaddr)
if err != nil {
t.Fatal("rtp write", err)
}
case <-quit:
return
}
}
}()
defer func() { quit <- true }()
// We're talking to an echo application so they should send us back exactly
// the same audio.
memory := make([]byte, 2048)
rtpsock.SetDeadline(time.Now().Add(5 * time.Second))
amt, _, err = rtpsock.ReadFrom(memory)
amt, _, err := rtpsock.ReadFrom(memory)
if err != nil {
t.Fatal("rtp read", err)
}
@ -140,27 +132,18 @@ func TestCallToEchoApp(t *testing.T) {
}
// Hangup (we'll be lazy and just change up the ack Msg)
ack.Method = "BYE"
ack.CSeqMethod = "BYE"
ack.CSeq++
b.Reset()
ack.Append(&b)
amt, err = conn.Write(b.Bytes())
if err != nil || amt != b.Len() {
err = tp.Send(sip.NewBye(invite, msg))
if err != nil {
t.Fatal(err)
}
// Wait for acknowledgment of hangup.
conn.SetDeadline(time.Now().Add(time.Second))
amt, err = conn.Read(memory)
if err != nil {
t.Fatal(err)
}
msg, err = sip.ParseMsg(string(memory[0:amt]))
if err != nil {
t.Fatal(err)
tp.Sock.SetDeadline(time.Now().Add(time.Second))
msg = tp.Recv()
if msg.Error != nil {
t.Fatal(msg.Error)
}
if !msg.IsResponse || msg.Status != 200 || msg.Phrase != "OK" {
t.Fatal("wanted bye response 200 ok but got:", msg.Status, msg.Phrase)
if msg.Status != 200 || msg.CSeqMethod != "BYE" {
t.Fatal("Wanted BYE 200:", msg)
}
}

+ 1
- 1
sdp/sdp.go View File

@ -267,7 +267,7 @@ func (sdp *SDP) Append(b *bytes.Buffer) error {
return errors.New("sdp lonely no media :[")
}
if sdp.Session == "" {
sdp.Session = "pokémon"
sdp.Session = "my people call themselves dark angels"
}
if sdp.Time == "" {
sdp.Time = "0 0"


+ 2
- 2
sdp/sdp_test.go View File

@ -372,9 +372,9 @@ func sdpCompareMedia(t *testing.T, name string, correct, media *sdp.Media) {
sdpCompareCodecs(t, name, correct.Codecs, media.Codecs)
}
func TestParseSDP(t *testing.T) {
func TestParse(t *testing.T) {
for _, test := range sdpTests {
sdp, err := sdp.ParseSDP(test.s)
sdp, err := sdp.Parse(test.s)
if err != nil {
if test.err == nil {
t.Errorf("%v", err)


+ 7
- 7
sip/messages.go View File

@ -43,17 +43,17 @@ func NewResponse(msg *Msg, status int) *Msg {
}
// http://tools.ietf.org/html/rfc3261#section-17.1.1.3
func NewAck(original, last *Msg) *Msg {
func NewAck(original, msg *Msg) *Msg {
return &Msg{
Method: "ACK",
Request: original.Request,
Via: original.Via.Copy().SetNext(nil),
From: original.From,
To: last.To,
To: msg.To,
CallID: original.CallID,
CSeq: original.CSeq,
CSeqMethod: "ACK",
Route: last.RecordRoute.Reversed(),
Route: msg.RecordRoute.Reversed(),
Headers: DefaultHeaders(),
}
}
@ -71,7 +71,7 @@ func NewCancel(invite *Msg) *Msg {
CallID: invite.CallID,
CSeq: invite.CSeq,
CSeqMethod: "CANCEL",
Route: invite.RecordRoute.Reversed(),
Route: invite.Route,
Headers: DefaultHeaders(),
}
}
@ -81,10 +81,10 @@ func NewBye(invite, last *Msg) *Msg {
Method: "BYE",
Request: last.Contact.Uri,
Via: invite.Via,
From: last.From,
From: invite.From,
To: last.To,
CallID: last.CallID,
CSeq: last.CSeq + 1,
CallID: invite.CallID,
CSeq: invite.CSeq + 1,
CSeqMethod: "BYE",
Route: last.RecordRoute.Reversed(),
Headers: DefaultHeaders(),


+ 12
- 10
sip/transport.go View File

@ -16,7 +16,7 @@ import (
)
var (
tracing = flag.Bool("tracing", false, "Enable SIP message tracing")
tracing = flag.Bool("tracing", true, "Enable SIP message tracing")
timestampTagging = flag.Bool("timestampTagging", false, "Add microsecond timestamps to Via tags")
)
@ -43,10 +43,10 @@ type Transport struct {
// signalling messages.
//
// contact is a SIP address, e.g. "<sip:1.2.3.4>", that tells how to bind
// sockets. If contact.Uri.Port is 0, then a port will be selected randomly.
// This value is also used for contact headers which tell other user-agents
// where to send responses and hence should only contain an IP or canonical
// address.
// sockets. If contact.Uri.Port is 0, it'll be mutated with a randomly selected
// port. This value is also used for contact headers which tell other
// user-agents where to send responses and hence should only contain an IP or
// canonical address.
func NewTransport(contact *Addr) (tp *Transport, err error) {
saddr := util.HostPortToString(contact.Uri.Host, contact.Uri.Port)
sock, err := net.ListenPacket("udp", saddr)
@ -54,6 +54,7 @@ func NewTransport(contact *Addr) (tp *Transport, err error) {
return nil, err
}
addr := sock.LocalAddr().(*net.UDPAddr)
contact.Uri.Port = uint16(addr.Port)
contact = contact.Copy()
contact.Uri.Port = uint16(addr.Port)
contact.Uri.Params["transport"] = addr.Network()
@ -73,7 +74,7 @@ func (tp *Transport) Send(msg *Msg) error {
if err != nil {
return err
}
addr, err := net.ResolveUDPAddr("ip", saddr)
addr, err := net.ResolveUDPAddr("udp", saddr)
if err != nil {
return err
}
@ -130,7 +131,7 @@ func (tp *Transport) Recv() *Msg {
func (tp *Transport) trace(dir, pkt string, addr *net.UDPAddr, t time.Time) {
size := len(pkt)
bar := strings.Repeat("-", 72)
suffix := "\n "
suffix := "\n"
if pkt[len(pkt)-1] == '\n' {
suffix = ""
}
@ -142,7 +143,7 @@ func (tp *Transport) trace(dir, pkt string, addr *net.UDPAddr, t time.Time) {
dir, size, addr.Network(), addr.String(),
t.Format(time.RFC3339Nano),
bar,
strings.Replace(pkt, "\n", "\n ", -1), suffix,
pkt, suffix,
bar)
}
@ -172,7 +173,8 @@ func (tp *Transport) preprocess(msg *Msg) {
log.Printf("Removing our route header: %s", msg.Route)
msg.Route = msg.Route.Next
}
if _, ok := msg.Request.Params["lr"]; ok && msg.Route != nil && tp.Contact.Uri.Compare(msg.Request) {
if msg.Request != nil && msg.Request.Params.Has("lr") && msg.Route != nil && tp.Contact.Uri.Compare(msg.Request) {
// RFC3261 16.4 Route Information Preprocessing
// RFC3261 16.12.1.2: Traversing a Strict-Routing Proxy
var oldReq, newReq *URI
@ -223,7 +225,7 @@ func (tp *Transport) route(old *Msg) (msg *Msg, saddr string, err error) {
if msg.Method == "REGISTER" {
return nil, "", errors.New("Don't loose route register requests")
}
if _, ok := msg.Route.Uri.Params["lr"]; ok {
if msg.Route.Uri.Params.Has("lr") {
// RFC3261 16.12.1.1 Basic SIP Trapezoid
route := msg.Route
msg.Route = msg.Route.Next


+ 8
- 0
sip/uri.go View File

@ -200,3 +200,11 @@ func (params Params) Append(b *bytes.Buffer) {
}
}
}
func (params *Params) Has(k string) bool {
if params == nil {
return false
}
_, ok := (*params)["lr"]
return ok
}

+ 1
- 3
sip/via.go View File

@ -124,9 +124,7 @@ func (via *Via) Compare(other *Via) bool {
func (via *Via) CompareAddr(other *Via) bool {
if via != nil && other != nil {
if via.Version == other.Version &&
via.Proto == other.Proto &&
via.Host == other.Host &&
if via.Host == other.Host &&
via.Port == other.Port {
return true
}


Loading…
Cancel
Save