Enable comma as array delimiter and adding tests. (#191)

* Enable comma as array delimiter and adding tests.

* Bump version to 3.0.4.
This commit is contained in:
Eugene Kabanov 2021-05-17 22:39:24 +03:00 committed by GitHub
parent 67f0f1224f
commit 7ccb170f7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 8 deletions

View File

@ -1,5 +1,5 @@
packageName = "chronos"
version = "3.0.3"
version = "3.0.4"
author = "Status Research & Development GmbH"
description = "Chronos"
license = "Apache License 2.0 or MIT"

View File

@ -53,12 +53,20 @@ type
HttpRedirectError* = object of HttpError
HttpAddressError* = object of HttpError
KeyValueTuple* = tuple
key: string
value: string
TransferEncodingFlags* {.pure.} = enum
Identity, Chunked, Compress, Deflate, Gzip
ContentEncodingFlags* {.pure.} = enum
Identity, Br, Compress, Deflate, Gzip
QueryParamsFlag* {.pure.} = enum
CommaSeparatedArray ## Enable usage of comma symbol as separator of array
## items
proc raiseHttpCriticalError*(msg: string,
code = Http400) {.noinline, noreturn.} =
raise (ref HttpCriticalError)(code: code, msg: msg)
@ -99,7 +107,8 @@ template newHttpReadError*(message: string): ref HttpReadError =
template newHttpWriteError*(message: string): ref HttpWriteError =
newException(HttpWriteError, message)
iterator queryParams*(query: string): tuple[key: string, value: string] {.
iterator queryParams*(query: string,
flags: set[QueryParamsFlag] = {}): KeyValueTuple {.
raises: [Defect].} =
## Iterate over url-encoded query string.
for pair in query.split('&'):
@ -107,7 +116,12 @@ iterator queryParams*(query: string): tuple[key: string, value: string] {.
let k = items[0]
if len(k) > 0:
let v = if len(items) > 1: items[1] else: ""
yield (decodeUrl(k), decodeUrl(v))
if CommaSeparatedArray in flags:
let key = decodeUrl(k)
for av in decodeUrl(v).split(','):
yield (k, av)
else:
yield (decodeUrl(k), decodeUrl(v))
func getTransferEncoding*(ch: openarray[string]): HttpResult[
set[TransferEncodingFlags]] {.

View File

@ -16,7 +16,15 @@ export httptable, httpcommon, httputils, multipart, asyncstream,
type
HttpServerFlags* {.pure.} = enum
Secure, NoExpectHandler, NotifyDisconnect
Secure,
## Internal flag which indicates that server working in secure TLS mode
NoExpectHandler,
## Do not handle `Expect` header automatically
NotifyDisconnect,
## Notify user-callback when remote client disconnects.
QueryCommaSeparatedArray
## Enable usage of comma as an array item delimiter in url-encoded
## entities (e.g. query string or POST body).
HttpServerError* {.pure.} = enum
TimeoutError, CatchableError, RecoverableError, CriticalError,
@ -49,7 +57,8 @@ type
HttpConnectionCallback* =
proc(server: HttpServerRef,
transp: StreamTransport): Future[HttpConnectionRef] {.gcsafe, raises: [Defect].}
transp: StreamTransport): Future[HttpConnectionRef] {.
gcsafe, raises: [Defect].}
HttpServer* = object of RootObj
instance*: StreamServer
@ -275,8 +284,13 @@ proc prepareRequest(conn: HttpConnectionRef,
request.query =
block:
let queryFlags =
if QueryCommaSeparatedArray in conn.server.flags:
{QueryParamsFlag.CommaSeparatedArray}
else:
{}
var table = HttpTable.init()
for key, value in queryParams(request.uri.query):
for key, value in queryParams(request.uri.query, queryFlags):
table.add(key, value)
table
@ -775,6 +789,11 @@ proc post*(req: HttpRequestRef): Future[HttpTable] {.async.} =
return HttpTable.init()
if UrlencodedForm in req.requestFlags:
let queryFlags =
if QueryCommaSeparatedArray in req.connection.server.flags:
{QueryParamsFlag.CommaSeparatedArray}
else:
{}
var table = HttpTable.init()
# getBody() will handle `Expect`.
var body = await req.getBody()
@ -783,7 +802,7 @@ proc post*(req: HttpRequestRef): Future[HttpTable] {.async.} =
var strbody = newString(len(body))
if len(body) > 0:
copyMem(addr strbody[0], addr body[0], len(body))
for key, value in queryParams(strbody):
for key, value in queryParams(strbody, queryFlags):
table.add(key, value)
req.postTable = some(table)
return table

View File

@ -7,7 +7,8 @@
# MIT license (LICENSE-MIT)
import std/[strutils, algorithm, strutils]
import unittest2
import ../chronos, ../chronos/apps/http/httpserver
import ../chronos, ../chronos/apps/http/httpserver,
../chronos/apps/http/httpcommon
import stew/base10
when defined(nimHasUsed): {.used.}
@ -819,6 +820,41 @@ suite "HTTP server testing suite":
getContentEncoding([]).tryGet() == { ContentEncodingFlags.Identity }
getContentEncoding(["", ""]).tryGet() == { ContentEncodingFlags.Identity }
test "queryParams() test":
const Vectors = [
("id=1&id=2&id=3&id=4", {}, "id:1,id:2,id:3,id:4"),
("id=1,2,3,4", {}, "id:1,2,3,4"),
("id=1%2C2%2C3%2C4", {}, "id:1,2,3,4"),
("id=", {}, "id:"),
("id=&id=", {}, "id:,id:"),
("id=1&id=2&id=3&id=4", {QueryParamsFlag.CommaSeparatedArray},
"id:1,id:2,id:3,id:4"),
("id=1,2,3,4", {QueryParamsFlag.CommaSeparatedArray},
"id:1,id:2,id:3,id:4"),
("id=1%2C2%2C3%2C4", {QueryParamsFlag.CommaSeparatedArray},
"id:1,id:2,id:3,id:4"),
("id=", {QueryParamsFlag.CommaSeparatedArray}, "id:"),
("id=&id=", {QueryParamsFlag.CommaSeparatedArray}, "id:,id:"),
("id=,", {QueryParamsFlag.CommaSeparatedArray}, "id:,id:"),
("id=,,", {QueryParamsFlag.CommaSeparatedArray}, "id:,id:,id:"),
("id=1&id=2&id=3,4,5,6&id=7%2C8%2C9%2C10",
{QueryParamsFlag.CommaSeparatedArray},
"id:1,id:2,id:3,id:4,id:5,id:6,id:7,id:8,id:9,id:10")
]
proc toString(ht: HttpTable): string =
var res: seq[string]
for key, value in ht.items():
for item in value:
res.add(key & ":" & item)
res.join(",")
for vector in Vectors:
var table = HttpTable.init()
for key, value in queryParams(vector[0], vector[1]):
table.add(key, value)
check toString(table) == vector[2]
test "Leaks test":
check:
getTracker("async.stream.reader").isLeaked() == false