|
|
|
@ -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)) |
|
|
|
} |