Browse Source

Make URI parser more like the RFC.

pull/2/head
Justine Alexandra Roberts Tunney 11 years ago
parent
commit
8eb29b1cf4
3 changed files with 1919 additions and 2213 deletions
  1. +1816
    -2134
      sip/uri_parse.go
  2. +93
    -68
      sip/uri_parse.rl
  3. +10
    -11
      sip/uri_test.go

+ 1816
- 2134
sip/uri_parse.go
File diff suppressed because it is too large
View File


+ 93
- 68
sip/uri_parse.rl View File

@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"strconv"
)
%% machine uri;
@ -34,25 +33,62 @@ func ParseURIBytes(data []byte) (uri *URI, err error) {
var hex byte
%%{
action strStart { amt = 0 }
action strChar { buf[amt] = fc; amt++ }
action strLower { buf[amt] = fc + 0x20; amt++ }
action hexHi { hex = unhex(fc) * 16 }
action hexLo { hex += unhex(fc)
buf[amt] = hex; amt++ }
action user { uri.User = string(buf[0:amt]) }
action pass { uri.Pass = string(buf[0:amt]) }
action host { uri.Host = string(buf[0:amt]) }
action scheme { uri.Scheme = string(buf[0:amt]) }
action b1 { b1 = string(buf[0:amt]); amt = 0 }
action b2 { b2 = string(buf[0:amt]); amt = 0 }
action start {
amt = 0
}
action append {
buf[amt] = fc
amt++
}
action hexHi {
hex = unhex(fc) * 16
}
action hexLo {
hex += unhex(fc)
buf[amt] = hex
amt++
}
action scheme {
uri.Scheme = string(buf[0:amt])
}
action user {
uri.User = string(buf[0:amt])
}
action pass {
uri.Pass = string(buf[0:amt])
}
action host {
uri.Host = string(buf[0:amt])
}
action port {
if amt > 0 {
port, err := strconv.ParseUint(string(buf[0:amt]), 10, 16)
if err != nil { goto fail }
uri.Port = uint16(port)
uri.Port = uri.Port * 10 + uint16(fc - 0x30)
}
action b1 {
b1 = string(buf[0:amt])
amt = 0
}
action b2 {
b2 = string(buf[0:amt])
amt = 0
}
action lower {
if 'A' <= fc && fc <= 'Z' {
buf[amt] = fc + 0x20
} else {
buf[amt] = fc
}
amt++
}
action param {
@ -69,53 +105,45 @@ func ParseURIBytes(data []byte) (uri *URI, err error) {
uri.Headers[b1] = b2
}
# TODO(jart): Use BNF from SIP RFC: https://tools.ietf.org/html/rfc3261#section-25.1
# Define what a single character is allowed to be.
toxic = ( cntrl | 127 ) ;
scary = ( toxic | space | "\"" | "#" | "%" | "<" | ">" | "=" ) ;
schmchars = ( lower | digit | "+" | "-" | "." ) ;
authdelims = ( "/" | "?" | "#" | ":" | "@" | ";" | "[" | "]" | "&" ) ;
userchars = any -- ( authdelims | scary ) ;
passchars = userchars ;
hostchars = passchars -- upper;
hostcharsEsc = ( hostchars | ":" ) -- upper;
portchars = digit ;
paramchars = userchars -- upper ;
headerchars = userchars ;
# Define how characters trigger actions.
escape = "%" xdigit xdigit ;
unescape = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ;
schmfirst = ( upper @strLower ) | ( lower @strChar ) ;
schmchar = ( upper @strLower ) | ( schmchars @strChar ) ;
userchar = unescape | ( userchars @strChar ) ;
passchar = unescape | ( passchars @strChar ) ;
hostchar = unescape | ( upper @strLower ) | ( hostchars @strChar ) ;
hostcharEsc = unescape | ( upper @strLower ) | ( hostcharsEsc @strChar ) ;
portchar = unescape | ( portchars @strChar ) ;
paramchar = unescape | ( upper @strLower ) | ( paramchars @strChar ) ;
headerchar = unescape | ( headerchars @strChar ) ;
# Define multi-character patterns.
scheme = ( schmfirst schmchar* ) >strStart %scheme ;
user = userchar+ >strStart %user ;
pass = passchar+ >strStart %pass ;
hostPlain = hostchar+ >strStart %host ;
hostQuoted = "[" ( hostcharEsc+ >strStart %host ) "]" ;
host = hostQuoted | hostPlain ;
port = portchar* >strStart %port ;
paramkey = paramchar+ >strStart >b2 %b1 ;
paramval = paramchar+ >strStart %b2 ;
param = space* ";" paramkey ( "=" paramval )? %param ;
headerkey = headerchar+ >strStart >b2 %b1 ;
headerval = headerchar+ >strStart %b2 ;
header = headerkey ( "=" headerval )? %header ;
headers = "?" header ( "&" header )* ;
userpass = user ( ":" pass )? ;
hostport = host ( ":" port )? ;
uriSansUser := space* scheme ":" hostport param* space* headers? space* ;
uriWithUser := space* scheme ":" userpass "@" hostport param* space* headers? space* ;
# Byte character definitions.
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ;
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ;
unreserved = alpha | digit | mark ;
ipv4 = digit | "." ;
ipv6 = xdigit | "." | ":" ;
hostname = alpha | digit | "-" | "." ;
tel = digit | "+" | "-" ;
schmchars = alpha | digit | "+" | "-" | "." ;
userchars = unreserved | "&" | "=" | "+" | "$" | "," | ";" | "?" | "/" ;
passchars = unreserved | "&" | "=" | "+" | "$" | "," ;
paramchars = unreserved | "[" | "]" | "/" | ":" | "&" | "+" | "$" ;
headerchars = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$" ;
# Multibyte character definitions.
escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ;
userchar = escaped | ( userchars @append ) ;
passchar = escaped | ( passchars @append ) ;
paramchar = escaped | ( paramchars @lower ) ;
headerchar = escaped | ( headerchars @append ) ;
# URI component definitions.
scheme = ( alpha schmchars* ) >start @lower %scheme ;
user = userchar+ >start %user ;
pass = passchar+ >start %pass ;
host6 = "[" ( ipv6+ >start @lower %host ) "]" ;
host = host6 | ( ( ipv4 | hostname | tel )+ >start @lower %host ) ;
port = digit+ @port ;
paramkey = paramchar+ >start >b2 %b1 ;
paramval = paramchar+ >start %b2 ;
param = space* ";" paramkey ( "=" paramval )? %param ;
headerkey = headerchar+ >start >b2 %b1 ;
headerval = headerchar+ >start %b2 ;
header = headerkey ( "=" headerval )? %header ;
headers = "?" header ( "&" header )* ;
userpass = user ( ":" pass )? ;
hostport = host ( ":" port )? ;
uriSansUser := space* scheme ":" hostport param* space* headers? space* ;
uriWithUser := space* scheme ":" userpass "@" hostport param* space* headers? space* ;
}%%
%% write init;
@ -128,13 +156,10 @@ func ParseURIBytes(data []byte) (uri *URI, err error) {
if cs < uri_first_final {
if p == pe {
return nil, errors.New(fmt.Sprintf("Unexpected EOF: %s", data))
return nil, errors.New(fmt.Sprintf("Incomplete URI: %s", data))
} else {
return nil, errors.New(fmt.Sprintf("Error in URI at pos %d: %s", p, data))
}
}
return uri, nil
fail:
return nil, errors.New(fmt.Sprintf("Bad URI: %s", data))
}

+ 10
- 11
sip/uri_test.go View File

@ -23,7 +23,7 @@ var uriTests = []uriTest{
uriTest{
s: "sip:",
e: errors.New("Unexpected EOF: sip:"),
e: errors.New("Incomplete URI: sip:"),
},
uriTest{
@ -39,15 +39,6 @@ var uriTests = []uriTest{
},
},
uriTest{
s: "sip:example.com:",
uri: &sip.URI{
Scheme: "sip",
Host: "example.com",
},
skipFormat: true,
},
uriTest{
s: "sip:example.com:5060",
uri: &sip.URI{
@ -204,7 +195,15 @@ var uriTests = []uriTest{
},
},
// TODO(jart): sip:alice;day=tuesday@atlanta.com
uriTest{
s: "sip:alice;day=tuesday@atlanta.com",
uri: &sip.URI{
Scheme: "sip",
User: "alice;day=tuesday",
Host: "atlanta.com",
},
skipFormat: true, // TODO(jart): Fix this.
},
}
func TestParseURI(t *testing.T) {


Loading…
Cancel
Save