commit 2611483e348541c03d648c6455415a50298bda3e
parent ff1c5bccc7b8e868d34831a859fa962c408f104a
Author: Oliver Lowe <o@olowe.co>
Date: Sun, 30 Jun 2024 12:58:43 +1000
sdp: implement parsing some (not all) optional fields
Diffstat:
2 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/sdp/sdp.go b/sdp/sdp.go
@@ -18,10 +18,12 @@ type Session struct {
Origin Origin
Name string
- Info string
- URI *url.URL
- Email *mail.Address
- Phone string
+ Info string
+ URI *url.URL
+ Email *mail.Address
+ // TODO(otl): can we do any sanitisation here? at least delete spaces or something...?
+ // The number "+1 617 555-6011" is semantically equal to "+16175556011"
+ Phone string
// TODO(otl): add rest of fields
}
@@ -31,13 +33,17 @@ type Origin struct {
Version int
Network string // TODO(otl): only "IN" is valid... so int type?
AddressType string // TODO(otl): only "IP4", "IP6" valid... new int type?
- Address string
+ Address string // IPv4, IPv6 literal or a hostname
}
+var fchars = [...]string{"i", "u", "e", "p"}
+
func ReadSession(rd io.Reader) (*Session, error) {
sc := bufio.NewScanner(rd)
next := "v"
var session Session
+First:
+ // read the mandatory fields first
for sc.Scan() {
if sc.Text() == "" {
continue // TODO(otl): empty lines allowed?
@@ -72,25 +78,49 @@ func ReadSession(rd io.Reader) (*Session, error) {
return nil, fmt.Errorf("empty name")
}
session.Name = v
+ break First
+ default:
+ return nil, fmt.Errorf("expected key %q, found %q", next, v)
+ }
+ }
+
+ // Time for the optional fields. We keep a slice...
+ // TODO(otl): document in plain language what's going on here.
+ onext := fchars[:]
+ for sc.Scan() {
+ if onext == nil {
+ break
+ }
+ if sc.Text() == "" {
+ continue // TODO(otl): empty lines allowed?
+ }
+ k, v, found := strings.Cut(sc.Text(), "=")
+ if !found {
+ return nil, fmt.Errorf("parse field %q: missing %q", next, "=")
+ }
+ switch k {
case "i":
session.Info = v
- // TODO(otl): what do we expect next?
+ onext = fchars[1:]
case "u":
u, err := url.Parse(v)
if err != nil {
return nil, fmt.Errorf("parse uri: %w", err)
}
session.URI = u
- // TODO(otl): what do we expect next?
+ onext = fchars[2:]
case "e":
addr, err := parseEmail(v)
if err != nil {
return nil, fmt.Errorf("parse email: %w", err)
}
session.Email = addr
- // TODO(otl): what do we expect next?
+ onext = fchars[3:]
+ case "p":
+ session.Phone = v
+ onext = nil
default:
- return &session, fmt.Errorf("unsupported field %q", k)
+ return nil, fmt.Errorf("expected one of %v, found %q", onext, k)
}
}
return &session, sc.Err()
diff --git a/sdp/sdp_test.go b/sdp/sdp_test.go
@@ -1,6 +1,9 @@
package sdp
import (
+ "net/mail"
+ "net/url"
+ "reflect"
"strings"
"testing"
)
@@ -21,11 +24,30 @@ m=audio 49180 RTP/AVP 0
m=video 51372 RTP/AVP 99
c=IN IP6 2001:db8::2
a=rtpmap:99 h263-1998/90000`
+ want := Session{
+ Name: "Call to John Smith",
+ Origin: Origin{
+ Username: "jdoe",
+ ID: 3724394400,
+ Version: 3724394405,
+ Network: "IN",
+ AddressType: "IP4",
+ Address: "198.51.100.1",
+ },
+ Info: "SDP Offer #1",
+ URI: &url.URL{Scheme: "http",
+ Host: "www.jdoe.example.com",
+ Path: "/home.html",
+ },
+ Email: &mail.Address{"Jane Doe", "jane@jdoe.example.com"},
+ Phone: "+1 617 555-6011",
+ }
session, err := ReadSession(strings.NewReader(s))
if err != nil {
t.Fatalf("read session: %v", err)
}
- if session.Name != "Call to John Smith" {
- t.Errorf("bad name bla bla TODO")
+ if !reflect.DeepEqual(*session, want) {
+ t.Errorf("got %+v\nwant %+v\n", *session, want)
}
+ t.Errorf("TODO still not parsing all fields")
}