Skip to content

Commit 71db2be

Browse files
authored
Improve uri.parseQuery to never raise an error (#16647)
In case of malformed query string where there is `=` on the value, handle this character as part of the value instead of throwing an error. The following query string should no longer crash a program: key=value&key2=x=1 It will be interpreted as [("key", "value"), ("key2", "x=1")] This is correct according to latest WhatWG's HTML5 specification recarding the urlencoded parser: https://url.spec.whatwg.org/#concept-urlencoded-parser Older behavior can be restored using the -d:nimLegacyParseQueryStrict flag.
1 parent bb3c6d0 commit 71db2be

File tree

4 files changed

+38
-27
lines changed

4 files changed

+38
-27
lines changed

changelog.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@
9696
with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.
9797
- Added `socketstream` module that wraps sockets in the stream interface
9898

99+
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
100+
characters in the decoded values. Prior versions would raise an error. This is
101+
no longer the case to comply with the HTML spec and other languages
102+
implementations. Old behavior can be obtained with
103+
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
104+
underlying code is also updated the same way.
105+
106+
107+
108+
99109
- Added `math.signbit`.
100110

101111
- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably)

lib/pure/cgi.nim

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,23 +84,17 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
8484
iterator decodeData*(data: string): tuple[key, value: TaintedString] =
8585
## Reads and decodes CGI data and yields the (name, value) pairs the
8686
## data consists of.
87-
try:
88-
for (key, value) in uri.decodeQuery(data):
89-
yield (key, value)
90-
except UriParseError as e:
91-
cgiError(e.msg)
87+
for (key, value) in uri.decodeQuery(data):
88+
yield (key, value)
9289

9390
iterator decodeData*(allowedMethods: set[RequestMethod] =
9491
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
9592
## Reads and decodes CGI data and yields the (name, value) pairs the
9693
## data consists of. If the client does not use a method listed in the
9794
## `allowedMethods` set, a `CgiError` exception is raised.
9895
let data = getEncodedData(allowedMethods)
99-
try:
100-
for (key, value) in uri.decodeQuery(data):
101-
yield (key, value)
102-
except UriParseError as e:
103-
cgiError(e.msg)
96+
for (key, value) in uri.decodeQuery(data):
97+
yield (key, value)
10498

10599
proc readData*(allowedMethods: set[RequestMethod] =
106100
{methodNone, methodPost, methodGet}): StringTableRef =

lib/pure/uri.nim

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,22 +161,26 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true,
161161
result.add(encodeUrl(val, usePlus))
162162

163163
iterator decodeQuery*(data: string): tuple[key, value: TaintedString] =
164-
## Reads and decodes query string ``data`` and yields the (key, value) pairs the
165-
## data consists of.
164+
## Reads and decodes query string `data` and yields the `(key, value)` pairs
165+
## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`, an
166+
## error is raised when there is an unencoded `=` character in a decoded
167+
## value, which was the behavior in Nim < 1.5.1
166168
runnableExamples:
167-
import std/sugar
168-
let s = collect(newSeq):
169-
for k, v in decodeQuery("foo=1&bar=2"): (k, v)
170-
doAssert s == @[("foo", "1"), ("bar", "2")]
169+
import std/sequtils
170+
doAssert toSeq(decodeQuery("foo=1&bar=2=3")) == @[("foo", "1"), ("bar", "2=3")]
171+
doAssert toSeq(decodeQuery("&a&=b&=&&")) == @[("", ""), ("a", ""), ("", "b"), ("", ""), ("", "")]
171172

172-
proc parseData(data: string, i: int, field: var string): int =
173+
proc parseData(data: string, i: int, field: var string, sep: char): int =
173174
result = i
174175
while result < data.len:
175-
case data[result]
176+
let c = data[result]
177+
case c
176178
of '%': add(field, decodePercent(data, result))
177179
of '+': add(field, ' ')
178-
of '=', '&': break
179-
else: add(field, data[result])
180+
of '&': break
181+
else:
182+
if c == sep: break
183+
else: add(field, data[result])
180184
inc(result)
181185

182186
var i = 0
@@ -185,16 +189,20 @@ iterator decodeQuery*(data: string): tuple[key, value: TaintedString] =
185189
# decode everything in one pass:
186190
while i < data.len:
187191
setLen(name, 0) # reuse memory
188-
i = parseData(data, i, name)
192+
i = parseData(data, i, name, '=')
189193
setLen(value, 0) # reuse memory
190194
if i < data.len and data[i] == '=':
191195
inc(i) # skip '='
192-
i = parseData(data, i, value)
196+
when defined(nimLegacyParseQueryStrict):
197+
i = parseData(data, i, value, '=')
198+
else:
199+
i = parseData(data, i, value, '&')
193200
yield (name.TaintedString, value.TaintedString)
194201
if i < data.len:
195-
if data[i] == '&': inc(i)
196-
else:
197-
uriParseError("'&' expected at index '$#' for '$#'" % [$i, data])
202+
when defined(nimLegacyParseQueryStrict):
203+
if data[i] != '&':
204+
uriParseError("'&' expected at index '$#' for '$#'" % [$i, data])
205+
inc(i)
198206

199207
func parseAuthority(authority: string, result: var Uri) =
200208
var i = 0

tests/stdlib/turi.nim

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,7 @@ template main() =
287287

288288
block: # decodeQuery
289289
doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")]
290-
doAssertRaises(UriParseError):
291-
discard toSeq(decodeQuery("a=1&b=2c=6"))
290+
doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")]
292291

293292
static: main()
294293
main()

0 commit comments

Comments
 (0)