uri.nim: don't rely on zero termination

This commit is contained in:
Andreas Rumpf 2018-04-29 01:46:40 +02:00
commit f4c21305a9

View file

@ -60,7 +60,7 @@ proc encodeUrl*(s: string): string =
else: else:
add(result, '%') add(result, '%')
add(result, toHex(ord(s[i]), 2)) add(result, toHex(ord(s[i]), 2))
proc decodeUrl*(s: string): string = proc decodeUrl*(s: string): string =
## Decodes a value from its HTTP representation: This means that a ``'+'`` ## Decodes a value from its HTTP representation: This means that a ``'+'``
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
@ -72,7 +72,7 @@ proc decodeUrl*(s: string): string =
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: assert(false) else: assert(false)
result = newString(s.len) result = newString(s.len)
var i = 0 var i = 0
var j = 0 var j = 0
@ -94,7 +94,7 @@ proc parseAuthority(authority: string, result: var Uri) =
var i = 0 var i = 0
var inPort = false var inPort = false
var inIPv6 = false var inIPv6 = false
while true: while i < authority.len:
case authority[i] case authority[i]
of '@': of '@':
swap result.password, result.port swap result.password, result.port
@ -111,7 +111,6 @@ proc parseAuthority(authority: string, result: var Uri) =
inIPv6 = true inIPv6 = true
of ']': of ']':
inIPv6 = false inIPv6 = false
of '\0': break
else: else:
if inPort: if inPort:
result.port.add(authority[i]) result.port.add(authority[i])
@ -128,11 +127,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
parseAuthority(result.path, result) parseAuthority(result.path, result)
result.path.setLen(0) result.path.setLen(0)
if uri[i] == '?': if i < uri.len and uri[i] == '?':
i.inc # Skip '?' i.inc # Skip '?'
i.inc parseUntil(uri, result.query, {'#'}, i) i.inc parseUntil(uri, result.query, {'#'}, i)
if uri[i] == '#': if i < uri.len and uri[i] == '#':
i.inc # Skip '#' i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i) i.inc parseUntil(uri, result.anchor, {}, i)
@ -156,7 +155,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Check if this is a reference URI (relative URI) # Check if this is a reference URI (relative URI)
let doubleSlash = uri.len > 1 and uri[1] == '/' let doubleSlash = uri.len > 1 and uri[1] == '/'
if uri[i] == '/': if i < uri.len and uri[i] == '/':
# Make sure ``uri`` doesn't begin with '//'. # Make sure ``uri`` doesn't begin with '//'.
if not doubleSlash: if not doubleSlash:
parsePath(uri, i, result) parsePath(uri, i, result)
@ -164,7 +163,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Scheme # Scheme
i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i) i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i)
if uri[i] != ':' and not doubleSlash: if (i >= uri.len or uri[i] != ':') and not doubleSlash:
# Assume this is a reference URI (relative URI) # Assume this is a reference URI (relative URI)
i = 0 i = 0
result.scheme.setLen(0) result.scheme.setLen(0)
@ -174,7 +173,7 @@ proc parseUri*(uri: string, result: var Uri) =
i.inc # Skip ':' i.inc # Skip ':'
# Authority # Authority
if uri[i] == '/' and uri[i+1] == '/': if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/':
i.inc(2) # Skip // i.inc(2) # Skip //
var authority = "" var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
@ -197,13 +196,13 @@ proc removeDotSegments(path: string): string =
let endsWithSlash = path[path.len-1] == '/' let endsWithSlash = path[path.len-1] == '/'
var i = 0 var i = 0
var currentSegment = "" var currentSegment = ""
while true: while i < path.len:
case path[i] case path[i]
of '/': of '/':
collection.add(currentSegment) collection.add(currentSegment)
currentSegment = "" currentSegment = ""
of '.': of '.':
if path[i+1] == '.' and path[i+2] == '/': if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/':
if collection.len > 0: if collection.len > 0:
discard collection.pop() discard collection.pop()
i.inc 3 i.inc 3
@ -212,13 +211,11 @@ proc removeDotSegments(path: string): string =
i.inc 2 i.inc 2
continue continue
currentSegment.add path[i] currentSegment.add path[i]
of '\0':
if currentSegment != "":
collection.add currentSegment
break
else: else:
currentSegment.add path[i] currentSegment.add path[i]
i.inc i.inc
if currentSegment != "":
collection.add currentSegment
result = collection.join("/") result = collection.join("/")
if endsWithSlash: result.add '/' if endsWithSlash: result.add '/'
@ -320,18 +317,18 @@ proc `/`*(x: Uri, path: string): Uri =
result = x result = x
if result.path.len == 0: if result.path.len == 0:
if path[0] != '/': if path.len == 0 or path[0] != '/':
result.path = "/" result.path = "/"
result.path.add(path) result.path.add(path)
return return
if result.path[result.path.len-1] == '/': if result.path.len > 0 and result.path[result.path.len-1] == '/':
if path[0] == '/': if path.len > 0 and path[0] == '/':
result.path.add(path[1 .. path.len-1]) result.path.add(path[1 .. path.len-1])
else: else:
result.path.add(path) result.path.add(path)
else: else:
if path[0] != '/': if path.len == 0 or path[0] != '/':
result.path.add '/' result.path.add '/'
result.path.add(path) result.path.add(path)
@ -373,7 +370,7 @@ when isMainModule:
const test1 = "abc\L+def xyz" const test1 = "abc\L+def xyz"
doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
doAssert decodeUrl(encodeUrl(test1)) == test1 doAssert decodeUrl(encodeUrl(test1)) == test1
block: block:
let str = "http://localhost" let str = "http://localhost"
let test = parseUri(str) let test = parseUri(str)
@ -464,7 +461,7 @@ when isMainModule:
doAssert test.hostname == "github.com" doAssert test.hostname == "github.com"
doAssert test.port == "dom96" doAssert test.port == "dom96"
doAssert test.path == "/packages" doAssert test.path == "/packages"
block: block:
let str = "file:///foo/bar/baz.txt" let str = "file:///foo/bar/baz.txt"
let test = parseUri(str) let test = parseUri(str)