Browse Source

Getting so close!

pull/2/head
Justine Alexandra Roberts Tunney 11 years ago
parent
commit
e9c9bcbfc1
3 changed files with 6615 additions and 3448 deletions
  1. +6016
    -3393
      sip/msg_parse.go
  2. +167
    -36
      sip/msg_parse.rl
  3. +432
    -19
      sip/msg_test.go

+ 6016
- 3393
sip/msg_parse.go
File diff suppressed because it is too large
View File


+ 167
- 36
sip/msg_parse.rl View File

@ -1,4 +1,27 @@
// -*-go-*-
//
// Ragel SIP Message Parser
//
// This file is compiled into Go code by the Ragel State Machine Compiler for
// the purpose of converting SIP messages into a Msg data structure. This
// machine works in tandem with the Ragel machine defined in uri_parse.rl.
//
// Perhaps it would have been better if the authors of this protocol had chosen
// to use a binary serialization format like protocol buffers. But instead they
// chose to create a plaintext protocol that looks similar to HTTP requests,
// but are phenomenally more complicated.
//
// SIP messages are quite insane. Whitespace can be used liberally in a variety
// of different ways. Header values can have line continuations or even
// comments. Header names are case-insensitive and sometimes have shorthand
// notation. Custom headers may be specified. Via and address headers can be
// repeated, or contain repeating values. URIs can be specified with or without
// address angle brackets. URI parameters can belong to either the URI or the
// address. Values can be escaped. String literals can be quoted. Oh the
// humanity. See the torture messages in msg_test.go for examples.
//
// See: http://www.colm.net/files/ragel/ragel-guide-6.9.pdf
// See: http://zedshaw.com/archive/ragel-state-charts/
package sip
@ -41,8 +64,13 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
var hex byte
var value *string
var addr **Addr
var via *Via
%%{
action hold {
fhold;
}
action break {
fbreak;
}
@ -113,31 +141,61 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
msg.Phrase = string(buf[0:amt])
}
action NewVia {
via = new(Via)
}
action Via {
*viap = via
viap = &via.Next
via = nil
}
action ViaProtocol {
via.Protocol = string(data[mark:p])
}
action ViaVersion {
via.Version = string(data[mark:p])
}
action ViaTransport {
via.Transport = string(data[mark:p])
}
action ViaHost {
via.Host = string(data[mark:p])
}
action ViaPort {
via.Port = via.Port * 10 + (uint16(fc) - 0x30)
}
action ViaParam {
if via.Params == nil {
via.Params = Params{}
}
via.Params[name] = string(buf[0:amt])
}
action goto_header {
fgoto header;
}
action goto_value {
fhold;
fgoto value;
}
action goto_xheader {
fhold;
fgoto xheader;
}
# Shorthand notation.
action gxh {
fhold;
fgoto xheader;
}
action store_name {
action name {
name = string(data[mark:p])
}
action store_value {{
action value {{
b := data[mark:p - 1]
if value != nil {
*value = string(b)
@ -204,12 +262,6 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
msg.MinExpires = msg.MinExpires * 10 + (int(fc) - 0x30)
}
action Via {
*viap, err = ParseVia(string(data[mark:p]))
if err != nil { return nil, err }
for *viap != nil { viap = &(*viap).Next }
}
action lookAheadWSP { lookAheadWSP(data, p, pe) }
# https://tools.ietf.org/html/rfc2234
@ -217,11 +269,15 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
HTAB = "\t";
CR = "\r";
LF = "\n" @line;
DQUOTE = "\"";
CRLF = CR LF;
WSP = SP | HTAB;
LWS = ( WSP* ( CR when lookAheadWSP ) LF )? WSP+;
SWS = LWS?;
LWSCRLF_append = ( CR when lookAheadWSP ) @append LF @append;
LWS_append = ( WSP* @append LWSCRLF_append )? WSP+ @append;
UTF8_CONT = 0x80..0xBF @append;
UTF8_NONASCII = 0xC0..0xDF @append UTF8_CONT {1}
| 0xE0..0xEF @append UTF8_CONT {2}
@ -265,11 +321,10 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
HCOLON = WSP* ":" SWS;
LDQUOT = SWS "\"";
RDQUOT = "\"" SWS;
ctext = 0x21..0x27 | 0x2A..0x5B | 0x5D..0x7E | UTF8_NONASCII | LWS;
quoted_pair = "\\" ( 0x00..0x09 | 0x0B..0x0C | 0x0E..0x7F ) @append;
comment = LPAREN ( ctext | quoted_pair )* <: RPAREN; # TODO(jart): Nested parens.
qdtext = UTF8_NONASCII | LWS @append | ( 0x21 | 0x23..0x5B | 0x5D..0x7E ) @append;
escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ;
ipv4 = digit | "." ;
ipv6 = xdigit | "." | ":" ;
hostname = alpha | digit | "-" | "." ;
uric = reserved | unreserved | "%" | "[" | "]";
uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | "&" | "="
| "+" | "$" | "," ;
@ -287,7 +342,41 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
ReasonPhrase = reasonmc+ >start %ReasonPhrase;
hval = ( mUTF8 | LWS )* >mark;
# Address Headers
# Quoted strings can have just about anything, including backslash escapes,
# which aren't quite as fancy as the ones you'd see in programming.
qdtextc = 0x21 | 0x23..0x5B | 0x5D..0x7E;
qdtext = UTF8_NONASCII | LWS_append | qdtextc @append;
quoted_pair = "\\" ( 0x00..0x09 | 0x0B..0x0C | 0x0E..0x7F ) @append;
quoted_content = ( qdtext | quoted_pair )* >start;
quoted_string = SWS DQUOTE quoted_content DQUOTE;
# Via Parsing
#
# This header is defined in a relatively deterministic manner by the SIP
# RFC and as such can be defined rather eloquently in Ragel.
ViaProtocol = token >mark %ViaProtocol;
ViaVersion = token >mark %ViaVersion;
ViaTransport = token >mark %ViaTransport;
ViaSent = ViaProtocol SLASH ViaVersion SLASH ViaTransport;
ViaHostIPv4 = ( digit | "." )+ >mark %ViaHost;
ViaHostIPv6 = "[" ( xdigit | "." | ":" )+ >mark %ViaHost "]";
ViaHostName = ( alnum | "." | "-" )+ >mark %ViaHost;
ViaHost = ViaHostIPv4 | ViaHostIPv6 | ViaHostName;
ViaPort = digit {1,5} @ViaPort;
ViaParamName = token >mark %name;
ViaParamContent = tokenhost >start @append;
ViaParamValue = ViaParamContent | quoted_string;
ViaParamEntry = ViaParamName ( EQUAL ViaParamValue )?;
ViaParam = ViaParamEntry %ViaParam;
ViaSentBy = ViaHost ( COLON ViaPort )?;
ViaEntry = ViaSent LWS ViaSentBy ( SEMI ViaParam )* <: any @hold;
Via = ViaEntry >NewVia %Via;
Vias = Via ( COMMA Via )* <: any @hold;
# Address Header Name Definitions
#
# These headers set the addr pointer to tell the 'value' machine where to
# store the value after using ParseAddrBytes().
aname = ("Contact"i | "m"i) %{addr=&msg.Contact}
| ("From"i | "f"i) %{addr=&msg.From}
| "P-Asserted-Identity"i %{addr=&msg.PAssertedIdentity}
@ -297,7 +386,10 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
| ("To"i | "t"i) %{addr=&msg.To}
;
# String Headers
# String Header Name Definitions
#
# These headers set the value pointer to tell the 'value' machine where to
# store the resulting token string.
sname = "Accept"i %{value=&msg.Accept}
| ("Accept-Contact"i | "a"i) %{value=&msg.AcceptContact}
| "Accept-Encoding"i %{value=&msg.AcceptEncoding}
@ -336,25 +428,64 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
| "WWW-Authenticate"i %{value=&msg.WWWAuthenticate}
;
# Custom Headers
cheader = ("Call-ID"i | "i"i) @!gxh HCOLON cid >mark %CallID
| ("Content-Length"i | "l"i) @!gxh HCOLON digit+ >{clen=0} @ContentLength
| ("Content-Type"i | "c"i) @!gxh HCOLON <: hval %ContentType
| "CSeq"i @!gxh HCOLON (digit+ @CSeq) LWS token >mark %CSeqMethod
| ("Expires"i | "l"i) @!gxh HCOLON digit+ >{msg.Expires=0} @Expires
| ("Max-Forwards"i | "l"i) @!gxh HCOLON digit+ >{msg.MaxForwards=0} @MaxForwards
| ("Min-Expires"i | "l"i) @!gxh HCOLON digit+ >{msg.MinExpires=0} @MinExpires
| ("Via"i | "v"i) @!gxh HCOLON <: hval %Via
# Custom Header Definitions
#
# These headers do not jump to the 'value' machine, but instead specify
# their own special type of parsing.
cheader = ("Call-ID"i | "i"i) $!gxh HCOLON cid >mark %CallID
| ("Content-Length"i | "l"i) $!gxh HCOLON digit+ >{clen=0} @ContentLength
| ("Content-Type"i | "c"i) $!gxh HCOLON <: hval %ContentType
| "CSeq"i $!gxh HCOLON (digit+ @CSeq) LWS token >mark %CSeqMethod
| ("Expires"i | "l"i) $!gxh HCOLON digit+ >{msg.Expires=0} @Expires
| ("Max-Forwards"i | "l"i) $!gxh HCOLON digit+ >{msg.MaxForwards=0} @MaxForwards
| ("Min-Expires"i | "l"i) $!gxh HCOLON digit+ >{msg.MinExpires=0} @MinExpires
| ("Via"i | "v"i) $!gxh HCOLON Vias
;
value := hval <: CRLF @store_value @goto_header;
xheader := token >mark %store_name HCOLON <: any @{value=nil;addr=nil} @goto_value;
# Header Parsing
#
# The header machine parses a single header and then jumps to itself to
# loop. When the final CRLF is observed, we then break out of the Ragel
# parser and let the Go code handle payload extraction.
#
# Parsing standard header names is a prefix trie search in generated code.
# Lookahead to set the mark on the header name. In order to support
# extended headers, we'll use $!gxh to jump to the xheader machine when an
# unrecognized character is detected in the header name.
#
# An independent machine has been created for generic header values, so
# that it doesn't need to be duplicated for each leaf in the prefix
# trie. When the value machine has finished reading a value, it'll be
# parsed and stored based on whether the value/addr pointers are set.
#
# Header values can span multiple lines. Lookahead is used in the LWS
# definition to check for whitespace at the start of the next line upon
# encountering a line feed character, in order to determine if a line
# continuation is present.
#
# In order to concatenate across machines, we use lookahead in conjunction
# with the left-guarded concatenation operator. This pattern works is
# defined as follows: `foo <: any @hold @goto_bar`.
#
# Header names are case insensitive. Each recognized header is assigned to
# a specific field in the Msg data structure. Extended headers are stored
# to a linked list data structure with the casing preserved. This is so
# messages can be reproduced with roughly the same appearance. It is the
# responsibility of the person using Msg.Headers to do case-insensitive
# string comparisons.
value := hval <: CRLF @value @goto_header;
xheader := token %name HCOLON <: any @{value=nil;addr=nil} @hold @goto_value;
sheader = cheader <: CRLF @goto_header
| aname $!gxh HCOLON <: any @{value=nil} @hold @goto_value
| sname $!gxh HCOLON <: any @{addr=nil} @hold @goto_value;
header := CRLF @break
| cheader <: CRLF @goto_header
| aname >mark @err(goto_xheader) HCOLON <: any @{value=nil} @goto_value
| sname >mark @err(goto_xheader) HCOLON <: any @{addr=nil} @goto_value
;
| tokenc @mark @hold sheader;
# Start Line Parsing
#
# The Request and Response definitions are very straightforward, and the
# main machine is the union of the two. Once the line feed character has
# been observed, we then jump to the header machine.
SIPVersion = "SIP/" SIPVersionNo;
Request = Method SP RequestURI SP SIPVersion CRLF @goto_header;
Response = SIPVersion SP StatusCode SP ReasonPhrase CRLF @goto_header;


+ 432
- 19
sip/msg_test.go View File

@ -101,6 +101,71 @@ var msgTests = []msgTest{
},
},
msgTest{
name: "Line Continuation Warning Followed By Extended Header",
s: "SIP/2.0 200 OK\r\n" +
"Warning: Morning and evening\r\n" +
" Maids heard the goblins cry:\r\n" +
" “Come buy our orchard fruits,\r\n" +
" Come buy, come buy:\r\n" +
" Apples and quinces,\r\n" +
" Lemons and oranges\r\n" +
"X-LOL: omfg\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Warning: "Morning and evening\r\n" +
" Maids heard the goblins cry:\r\n" +
" “Come buy our orchard fruits,\r\n" +
" Come buy, come buy:\r\n" +
" Apples and quinces,\r\n" +
" Lemons and oranges",
Headers: sip.Headers{
"X-LOL": "omfg",
},
},
},
msgTest{
name: "Line Continuation Extended Followed By Extended",
s: "SIP/2.0 200 OK\r\n" +
"X-Warning: Come buy our orchard fruits,\r\n" +
" Come buy, come buy\r\n" +
"X-LOL: omfg\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Headers: sip.Headers{
"X-Warning": "Come buy our orchard fruits,\r\n" +
" Come buy, come buy",
"X-LOL": "omfg",
},
},
},
msgTest{
name: "Line Continuation Extended Followed By Extended 2",
s: "SIP/2.0 200 OK\r\n" +
"NewFangledHeader: newfangled value\r\n" +
" continued newfangled value\r\n" +
"UnknownHeaderWithUnusualValue: ;;,,;;,;\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Headers: sip.Headers{
"NewFangledHeader": "newfangled value\r\n" +
" continued newfangled value",
"UnknownHeaderWithUnusualValue": ";;,,;;,;",
},
},
},
msgTest{
name: "Line Continuations Addr",
s: "SIP/2.0 200 OK\r\n" +
@ -127,6 +192,29 @@ var msgTests = []msgTest{
},
},
msgTest{
name: "Extended header looks like standard headers",
s: "SIP/2.0 200 OK\r\n" +
"Proxy-LOL: take\r\n" +
"CSeq2: back\r\n" +
"Contazt: the\r\n" +
"P-Asserted-LOL: dance\r\n" +
"viaz: floor\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Headers: sip.Headers{
"Proxy-LOL": "take",
"CSeq2": "back",
"Contazt": "the",
"P-Asserted-LOL": "dance",
"viaz": "floor",
},
},
},
msgTest{
name: "Address Unquoted Display",
s: "SIP/2.0 200 OK\r\n" +
@ -296,6 +384,190 @@ var msgTests = []msgTest{
},
},
// // TODO(jart): Implement me.
// msgTest{
// name: "Content Type Params",
// s: "SIP/2.0 200 OK\r\n" +
// "Content-Type: multipart/signed;\r\n" +
// " protocol=\"application/pkcs7-signature\";\r\n" +
// " micalg=sha1; boundary=boundary42\r\n" +
// "\r\n",
// msg: sip.Msg{
// VersionMajor: 2,
// Status: 200,
// Phrase: "OK",
// Expires: 666,
// },
// },
msgTest{
name: "Via Line Continuation",
s: "SIP/2.0 200 OK\r\n" +
"Via: SIP/2.0/UDP 10.11.34.37,\r\n" +
" SIP/2.0/UDP 10.11.34.38\r\n" +
"Warning: Maids heard the goblins cry\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Warning: "Maids heard the goblins cry",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.37",
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.38",
},
},
},
},
msgTest{
name: "Via Multiple Lines",
s: "SIP/2.0 200 OK\r\n" +
"Via: SIP/2.0/UDP 10.11.34.37\r\n" +
"Via: SIP/2.0/UDP 10.11.34.38\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.37",
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.38",
},
},
},
},
msgTest{
name: "Via Multiple Lines Continuation",
s: "SIP/2.0 200 OK\r\n" +
"Via: SIP/2.0/UDP 10.11.34.37\r\n" +
"v: SIP/2.0/UDP 10.11.34.38, SIP/2.0/UDP 10.11.34.39\r\n" +
"m: <sip:love.com>\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.37",
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.38",
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.39",
},
},
},
Contact: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "love.com",
},
},
},
},
msgTest{
name: "Via Param",
s: "SIP/2.0 200 OK\r\n" +
"Via: SIP/ 2.0/TCP spindle.example.com ;branch=z9hG4bK9ikj8\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "TCP",
Host: "spindle.example.com",
Params: sip.Params{"branch": "z9hG4bK9ikj8"},
},
},
},
msgTest{
name: "Via Param Torture",
s: "SIP/2.0 200 OK\r\n" +
"v: SIP / 2.0 / TCP spindle.example.com ;\r\n" +
" branch = z9hG4bK9ikj8\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "TCP",
Host: "spindle.example.com",
Params: sip.Params{"branch": "z9hG4bK9ikj8"},
},
},
},
msgTest{
name: "Via Torture",
s: "SIP/2.0 200 OK\r\n" +
"Via : SIP / 2.0\r\n" +
" /UDP\r\n" +
" 192.0.2.2;branch=390skdjuw\r\n" +
"v: SIP / 2.0 / TCP spindle.example.com ;\r\n" +
" branch = z9hG4bK9ikj8 ,\r\n" +
" SIP / 2.0 / UDP 192.168.255.111 ; branch=\r\n" +
" z9hG4bK30239\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
Via: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "192.0.2.2",
Params: sip.Params{"branch": "390skdjuw"},
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "TCP",
Host: "spindle.example.com",
Params: sip.Params{"branch": "z9hG4bK9ikj8"},
Next: &sip.Via{
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "192.168.255.111",
Params: sip.Params{"branch": "z9hG4bK30239"},
},
},
},
},
},
msgTest{
name: "OPTIONS",
s: "OPTIONS sip:10.11.34.37:42367 SIP/2.0\r\n" +
@ -323,11 +595,12 @@ var msgTests = []msgTest{
Port: 42367,
},
Via: &sip.Via{
Version: "2.0",
Proto: "UDP",
Host: "10.11.34.37",
Port: 42367,
Params: sip.Params{"rport": "", "branch": "9dc39c3c3e84"},
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.37",
Port: 42367,
Params: sip.Params{"rport": "", "branch": "9dc39c3c3e84"},
},
To: &sip.Addr{
Uri: &sip.URI{
@ -394,10 +667,11 @@ var msgTests = []msgTest{
Allow: "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH",
Supported: "replaces, timer",
Via: &sip.Via{
Version: "2.0",
Proto: "UDP",
Host: "127.0.0.1",
Port: 52711,
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "127.0.0.1",
Port: 52711,
Params: sip.Params{
"branch": "z9hG4bK-03d1d81e94a0",
"received": "127.0.0.1",
@ -478,11 +752,12 @@ var msgTests = []msgTest{
CSeq: 31109,
CSeqMethod: "INVITE",
Via: &sip.Via{
Version: "2.0",
Proto: "UDP",
Host: "1.2.3.4",
Port: 55345,
Params: sip.Params{"branch": "z9hG4bK-d1d81e94a099"},
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "1.2.3.4",
Port: 55345,
Params: sip.Params{"branch": "z9hG4bK-d1d81e94a099"},
},
From: &sip.Addr{
Uri: &sip.URI{
@ -581,11 +856,12 @@ var msgTests = []msgTest{
Host: "10.11.34.37",
},
Via: &sip.Via{
Version: "2.0",
Proto: "UDP",
Host: "10.11.34.37",
Port: 59516,
Params: sip.Params{"rport": "", "branch": "z9hG4bKS308QB9UUpNyD"},
Protocol: "SIP",
Version: "2.0",
Transport: "UDP",
Host: "10.11.34.37",
Port: 59516,
Params: sip.Params{"rport": "", "branch": "z9hG4bKS308QB9UUpNyD"},
},
To: &sip.Addr{
Uri: &sip.URI{
@ -635,6 +911,140 @@ var msgTests = []msgTest{
},
},
},
// msgTest{
// name: "RFC4475 Torture Message #1",
// s: "INVITE sip:vivekg@chair-dnrc.example.com;unknownparam SIP/2.0\r\n" +
// "TO :\r\n" +
// " sip:vivekg@chair-dnrc.example.com ; tag = 1918181833n\r\n" +
// "from : \"J Rosenberg \\\\\\\"\" <sip:jdrosen@example.com>\r\n" +
// " ;\r\n" +
// " tag = 98asjd8\r\n" +
// "MaX-fOrWaRdS: 0068\r\n" +
// "Call-ID: wsinv.ndaksdj@192.0.2.1\r\n" +
// "Content-Length : 150\r\n" +
// "cseq: 0009\r\n" +
// " INVITE\r\n" +
// "Via : SIP / 2.0\r\n" +
// " /UDP\r\n" +
// " 192.0.2.2;branch=390skdjuw\r\n" +
// "s :\r\n" +
// "NewFangledHeader: newfangled value\r\n" +
// " continued newfangled value\r\n" +
// "UnknownHeaderWithUnusualValue: ;;,,;;,;\r\n" +
// "Content-Type: application/sdp-JART\r\n" +
// "Route:\r\n" +
// " <sip:services.example.com;lr;unknownwith=value;unknown-no-value>\r\n" +
// "v: SIP / 2.0 / TCP spindle.example.com ;\r\n" +
// " branch = z9hG4bK9ikj8 ,\r\n" +
// " SIP / 2.0 / UDP 192.168.255.111 ; branch=\r\n" +
// " z9hG4bK30239\r\n" +
// "m:\"Quoted string \\\"\\\"\" <sip:jdrosen@example.com> ; newparam =\r\n" +
// " newvalue ;\r\n" +
// " secondparam ; q = 0.33\r\n" +
// "\r\n" +
// "v=0\r\n" +
// "o=mhandley 29739 7272939 IN IP4 192.0.2.3\r\n" +
// "s=-\r\n" +
// "c=IN IP4 192.0.2.4\r\n" +
// "t=0 0\r\n" +
// "m=audio 49217 RTP/AVP 0 12\r\n" +
// "m=video 3227 RTP/AVP 31\r\n" +
// "a=rtpmap:31 LPC\r\n",
// msg: sip.Msg{
// Method: "INVITE",
// VersionMajor: 2,
// Request: &sip.URI{
// Scheme: "sip",
// User: "vivekg",
// Host: "chair-dnrc.example.com",
// Params: sip.Params{"unknownparam": ""},
// },
// To: &sip.Addr{
// Uri: &sip.URI{
// Scheme: "sip",
// User: "vivekg",
// Host: "chair-dnrc.example.com",
// },
// Params: sip.Params{"tag": "1918181833n"},
// },
// From: &sip.Addr{
// Display: "J Rosenberg \\\"",
// Uri: &sip.URI{
// Scheme: "sip",
// User: "vivekg",
// Host: "chair-dnrc.example.com",
// Params: sip.Params{"tag": "98asjd8"},
// },
// },
// MaxForwards: 68,
// CallID: "wsinv.ndaksdj@192.0.2.1",
// CSeq: 9,
// CSeqMethod: "INVITE",
// Via: &sip.Via{
// Protocol: "SIP",
// Version: "2.0",
// Transport: "UDP",
// Host: "192.0.2.2",
// Params: sip.Params{"branch": "390skdjuw"},
// Next: &sip.Via{
// Protocol: "SIP",
// Version: "2.0",
// Transport: "TCP",
// Host: "spindle.example.com",
// Params: sip.Params{"branch": "z9hG4bK9ikj8"},
// Next: &sip.Via{
// Protocol: "SIP",
// Version: "2.0",
// Transport: "UDP",
// Host: "192.168.255.111",
// Params: sip.Params{"branch": "z9hG4bK30239"},
// },
// },
// },
// Subject: "",
// Headers: sip.Headers{
// "NewFangledHeader": "newfangled value\r\n" +
// " continued newfangled value",
// "UnknownHeaderWithUnusualValue": ";;,,;;,;",
// },
// Route: &sip.Addr{
// Uri: &sip.URI{
// Scheme: "sip",
// Host: "services.example.com",
// Params: sip.Params{
// "lr": "",
// "unknownwith": "value",
// "unknown-no-value": "",
// },
// },
// },
// Contact: &sip.Addr{
// Display: "Quoted string \"\"",
// Uri: &sip.URI{
// Scheme: "sip",
// User: "jdrosen",
// Host: "example.com",
// },
// Params: sip.Params{
// "newparam": "newvalue",
// "secondparam": "",
// "q": "0.33",
// },
// },
// Payload: &sip.MiscPayload{
// T: "application/sdp-JART",
// D: []byte("v=0\r\n" +
// "o=mhandley 29739 7272939 IN IP4 192.0.2.3\r\n" +
// "s=-\r\n" +
// "c=IN IP4 192.0.2.4\r\n" +
// "t=0 0\r\n" +
// "m=audio 49217 RTP/AVP 0 12\r\n" +
// "m=video 3227 RTP/AVP 31\r\n" +
// "a=rtpmap:31 LPC\r\n"),
// },
// },
// },
}
func TestParseMsg(t *testing.T) {
@ -658,6 +1068,9 @@ func TestParseMsg(t *testing.T) {
}
if !reflect.DeepEqual(test.msg.Via, msg.Via) {
t.Errorf("Via:\n%#v !=\n%#v", test.msg.Via, msg.Via)
t.Errorf("Via #2:\n%#v !=\n%#v", test.msg.Via.Next, msg.Via.Next)
t.Errorf("Via #3:\n%#v !=\n%#v", test.msg.Via.Next.Next, msg.Via.Next.Next)
t.Errorf("Via #4:\n%#v !=\n%#v", test.msg.Via.Next.Next.Next, msg.Via.Next.Next.Next)
}
if !reflect.DeepEqual(test.msg.Request, msg.Request) {
t.Errorf("Request:\n%#v !=\n%#v", test.msg.Request, msg.Request)


Loading…
Cancel
Save