mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 19:52:36 +00:00
* follow #15357 and move decodeQuery * solve problem one * minor * deprecate decodeData * add changelog and since * add testcase for decodeQuery
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
|
||||
- `writeStackTrace` is available in JS backend now.
|
||||
|
||||
- Added `decodeQuery` to `std/uri`.
|
||||
- `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`.
|
||||
|
||||
|
||||
@@ -32,8 +32,10 @@
|
||||
import strutils, os, strtabs, cookies, uri
|
||||
export uri.encodeUrl, uri.decodeUrl
|
||||
|
||||
|
||||
import std/private/decode_helpers
|
||||
|
||||
|
||||
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
||||
case c
|
||||
of '&': add(dest, "&")
|
||||
@@ -53,18 +55,15 @@ proc xmlEncode*(s: string): string =
|
||||
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||
|
||||
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
|
||||
methodNone, ## no REQUEST_METHOD environment variable
|
||||
methodPost, ## query uses the POST method
|
||||
methodGet ## query uses the GET method
|
||||
|
||||
proc cgiError*(msg: string) {.noreturn.} =
|
||||
## raises an ECgi exception with message `msg`.
|
||||
var e: ref CgiError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
## Raises a ``CgiError`` exception with message `msg`.
|
||||
raise newException(CgiError, msg)
|
||||
|
||||
proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||
case getEnv("REQUEST_METHOD").string
|
||||
@@ -88,40 +87,23 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||
iterator decodeData*(data: string): tuple[key, value: TaintedString] =
|
||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||
## data consists of.
|
||||
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: cgiError("'&' expected")
|
||||
try:
|
||||
for (key, value) in uri.decodeQuery(data):
|
||||
yield (key, value)
|
||||
except UriParseError as e:
|
||||
cgiError(e.msg)
|
||||
|
||||
iterator decodeData*(allowedMethods: set[RequestMethod] =
|
||||
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
|
||||
## 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
|
||||
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||
## `allowedMethods` set, a ``CgiError`` exception is raised.
|
||||
let data = getEncodedData(allowedMethods)
|
||||
for key, value in decodeData(data):
|
||||
yield (key, value)
|
||||
try:
|
||||
for (key, value) in uri.decodeQuery(data):
|
||||
yield (key, value)
|
||||
except UriParseError as e:
|
||||
cgiError(e.msg)
|
||||
|
||||
proc readData*(allowedMethods: set[RequestMethod] =
|
||||
{methodNone, methodPost, methodGet}): StringTableRef =
|
||||
|
||||
@@ -59,6 +59,13 @@ type
|
||||
opaque*: bool
|
||||
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 =
|
||||
## Encodes a URL according to RFC3986.
|
||||
##
|
||||
@@ -153,6 +160,42 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true,
|
||||
result.add('=')
|
||||
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) =
|
||||
var i = 0
|
||||
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"))
|
||||
Reference in New Issue
Block a user