* follow #15357 and move decodeQuery * solve problem one * minor * deprecate decodeData * add changelog and since * add testcase for decodeQuery
This commit is contained in:
parent
2bdc479622
commit
689504081f
4 changed files with 67 additions and 34 deletions
|
|
@ -55,6 +55,7 @@
|
||||||
|
|
||||||
- `writeStackTrace` is available in JS backend now.
|
- `writeStackTrace` is available in JS backend now.
|
||||||
|
|
||||||
|
- Added `decodeQuery` to `std/uri`.
|
||||||
- `strscans.scanf` now supports parsing single characters.
|
- `strscans.scanf` now supports parsing single characters.
|
||||||
- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`.
|
- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,10 @@
|
||||||
import strutils, os, strtabs, cookies, uri
|
import strutils, os, strtabs, cookies, uri
|
||||||
export uri.encodeUrl, uri.decodeUrl
|
export uri.encodeUrl, uri.decodeUrl
|
||||||
|
|
||||||
|
|
||||||
import std/private/decode_helpers
|
import std/private/decode_helpers
|
||||||
|
|
||||||
|
|
||||||
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
||||||
case c
|
case c
|
||||||
of '&': add(dest, "&")
|
of '&': add(dest, "&")
|
||||||
|
|
@ -53,18 +55,15 @@ proc xmlEncode*(s: string): string =
|
||||||
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||||
|
|
||||||
type
|
type
|
||||||
CgiError* = object of IOError ## exception that is raised if a CGI error occurs
|
CgiError* = object of IOError ## Exception that is raised if a CGI error occurs
|
||||||
RequestMethod* = enum ## the used request method
|
RequestMethod* = enum ## the used request method
|
||||||
methodNone, ## no REQUEST_METHOD environment variable
|
methodNone, ## no REQUEST_METHOD environment variable
|
||||||
methodPost, ## query uses the POST method
|
methodPost, ## query uses the POST method
|
||||||
methodGet ## query uses the GET method
|
methodGet ## query uses the GET method
|
||||||
|
|
||||||
proc cgiError*(msg: string) {.noreturn.} =
|
proc cgiError*(msg: string) {.noreturn.} =
|
||||||
## raises an ECgi exception with message `msg`.
|
## Raises a ``CgiError`` exception with message `msg`.
|
||||||
var e: ref CgiError
|
raise newException(CgiError, msg)
|
||||||
new(e)
|
|
||||||
e.msg = msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||||
case getEnv("REQUEST_METHOD").string
|
case getEnv("REQUEST_METHOD").string
|
||||||
|
|
@ -88,40 +87,23 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||||
iterator decodeData*(data: string): tuple[key, value: TaintedString] =
|
iterator decodeData*(data: string): tuple[key, value: TaintedString] =
|
||||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||||
## data consists of.
|
## data consists of.
|
||||||
proc parseData(data: string, i: int, field: var string): int =
|
try:
|
||||||
result = i
|
for (key, value) in uri.decodeQuery(data):
|
||||||
while result < data.len:
|
yield (key, value)
|
||||||
case data[result]
|
except UriParseError as e:
|
||||||
of '%': add(field, decodePercent(data, result))
|
cgiError(e.msg)
|
||||||
of '+': add(field, ' ')
|
|
||||||
of '=', '&': break
|
|
||||||
else: add(field, data[result])
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
var name = ""
|
|
||||||
var value = ""
|
|
||||||
# decode everything in one pass:
|
|
||||||
while i < data.len:
|
|
||||||
setLen(name, 0) # reuse memory
|
|
||||||
i = parseData(data, i, name)
|
|
||||||
setLen(value, 0) # reuse memory
|
|
||||||
if i < data.len and data[i] == '=':
|
|
||||||
inc(i) # skip '='
|
|
||||||
i = parseData(data, i, value)
|
|
||||||
yield (name.TaintedString, value.TaintedString)
|
|
||||||
if i < data.len:
|
|
||||||
if data[i] == '&': inc(i)
|
|
||||||
else: cgiError("'&' expected")
|
|
||||||
|
|
||||||
iterator decodeData*(allowedMethods: set[RequestMethod] =
|
iterator decodeData*(allowedMethods: set[RequestMethod] =
|
||||||
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
|
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
|
||||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||||
## data consists of. If the client does not use a method listed in the
|
## data consists of. If the client does not use a method listed in the
|
||||||
## `allowedMethods` set, an `ECgi` exception is raised.
|
## `allowedMethods` set, a ``CgiError`` exception is raised.
|
||||||
let data = getEncodedData(allowedMethods)
|
let data = getEncodedData(allowedMethods)
|
||||||
for key, value in decodeData(data):
|
try:
|
||||||
|
for (key, value) in uri.decodeQuery(data):
|
||||||
yield (key, value)
|
yield (key, value)
|
||||||
|
except UriParseError as e:
|
||||||
|
cgiError(e.msg)
|
||||||
|
|
||||||
proc readData*(allowedMethods: set[RequestMethod] =
|
proc readData*(allowedMethods: set[RequestMethod] =
|
||||||
{methodNone, methodPost, methodGet}): StringTableRef =
|
{methodNone, methodPost, methodGet}): StringTableRef =
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,13 @@ type
|
||||||
opaque*: bool
|
opaque*: bool
|
||||||
isIpv6: bool # not expose it for compatibility.
|
isIpv6: bool # not expose it for compatibility.
|
||||||
|
|
||||||
|
UriParseError* = object of ValueError
|
||||||
|
|
||||||
|
|
||||||
|
proc uriParseError*(msg: string) {.noreturn.} =
|
||||||
|
## Raises a ``UriParseError`` exception with message `msg`.
|
||||||
|
raise newException(UriParseError, msg)
|
||||||
|
|
||||||
func encodeUrl*(s: string, usePlus = true): string =
|
func encodeUrl*(s: string, usePlus = true): string =
|
||||||
## Encodes a URL according to RFC3986.
|
## Encodes a URL according to RFC3986.
|
||||||
##
|
##
|
||||||
|
|
@ -153,6 +160,42 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true,
|
||||||
result.add('=')
|
result.add('=')
|
||||||
result.add(encodeUrl(val, usePlus))
|
result.add(encodeUrl(val, usePlus))
|
||||||
|
|
||||||
|
iterator decodeQuery*(data: string): tuple[key, value: TaintedString] =
|
||||||
|
## Reads and decodes query string ``data`` and yields the (key, value) pairs the
|
||||||
|
## data consists of.
|
||||||
|
runnableExamples:
|
||||||
|
import std/sugar
|
||||||
|
let s = collect(newSeq):
|
||||||
|
for k, v in decodeQuery("foo=1&bar=2"): (k, v)
|
||||||
|
doAssert s == @[("foo", "1"), ("bar", "2")]
|
||||||
|
|
||||||
|
proc parseData(data: string, i: int, field: var string): int =
|
||||||
|
result = i
|
||||||
|
while result < data.len:
|
||||||
|
case data[result]
|
||||||
|
of '%': add(field, decodePercent(data, result))
|
||||||
|
of '+': add(field, ' ')
|
||||||
|
of '=', '&': break
|
||||||
|
else: add(field, data[result])
|
||||||
|
inc(result)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
var name = ""
|
||||||
|
var value = ""
|
||||||
|
# decode everything in one pass:
|
||||||
|
while i < data.len:
|
||||||
|
setLen(name, 0) # reuse memory
|
||||||
|
i = parseData(data, i, name)
|
||||||
|
setLen(value, 0) # reuse memory
|
||||||
|
if i < data.len and data[i] == '=':
|
||||||
|
inc(i) # skip '='
|
||||||
|
i = parseData(data, i, value)
|
||||||
|
yield (name.TaintedString, value.TaintedString)
|
||||||
|
if i < data.len:
|
||||||
|
if data[i] == '&': inc(i)
|
||||||
|
else:
|
||||||
|
uriParseError("'&' expected at index '$#' for '$#'" % [$i, data])
|
||||||
|
|
||||||
func parseAuthority(authority: string, result: var Uri) =
|
func parseAuthority(authority: string, result: var Uri) =
|
||||||
var i = 0
|
var i = 0
|
||||||
var inPort = false
|
var inPort = false
|
||||||
|
|
|
||||||
7
tests/stdlib/tdecodequery.nim
Normal file
7
tests/stdlib/tdecodequery.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import std/[uri, sequtils]
|
||||||
|
|
||||||
|
|
||||||
|
block:
|
||||||
|
doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")]
|
||||||
|
doAssertRaises(UriParseError):
|
||||||
|
discard toSeq(decodeQuery("a=1&b=2c=6"))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue