Add `Accept` header handling to httpserver.nim. (#211)
* Add `Accept` header handling to httpserver.nim. Add simple test suite. Bump version to 3.0.6. * Fix compilation error.
This commit is contained in:
parent
3a9cc6bfc9
commit
ef2430d08d
|
@ -1,5 +1,5 @@
|
|||
packageName = "chronos"
|
||||
version = "3.0.5"
|
||||
version = "3.0.6"
|
||||
author = "Status Research & Development GmbH"
|
||||
description = "Chronos"
|
||||
license = "Apache License 2.0 or MIT"
|
||||
|
|
|
@ -856,7 +856,7 @@ proc prepareRequest(request: HttpClientRequestRef): string {.
|
|||
else:
|
||||
request.headers.add(ConnectionHeader, "keep-alive")
|
||||
# We set `Accept` to accept any content if its not set.
|
||||
discard request.headers.hasKeyOrPut(AcceptHeader, "*/*")
|
||||
discard request.headers.hasKeyOrPut(AcceptHeaderName, "*/*")
|
||||
|
||||
# We will send `Authorization` information only if username or password set,
|
||||
# and `Authorization` header is not present in request's headers.
|
||||
|
|
|
@ -22,7 +22,7 @@ const
|
|||
DateHeader* = "date"
|
||||
HostHeader* = "host"
|
||||
ConnectionHeader* = "connection"
|
||||
AcceptHeader* = "accept"
|
||||
AcceptHeaderName* = "accept"
|
||||
ContentLengthHeader* = "content-length"
|
||||
TransferEncodingHeader* = "transfer-encoding"
|
||||
ContentEncodingHeader* = "content-encoding"
|
||||
|
|
|
@ -435,6 +435,82 @@ proc consumeBody*(request: HttpRequestRef): Future[void] {.async.} =
|
|||
finally:
|
||||
await closeWait(res.get())
|
||||
|
||||
proc getAcceptInfo*(request: HttpRequestRef): Result[AcceptInfo, cstring] =
|
||||
## Returns value of `Accept` header as `AcceptInfo` object.
|
||||
##
|
||||
## If ``Accept`` header is missing in request headers, ``*/*`` content
|
||||
## type will be returned.
|
||||
let acceptHeader = request.headers.getString(AcceptHeaderName)
|
||||
getAcceptInfo(acceptHeader)
|
||||
|
||||
proc preferredContentMediaType*(request: HttpRequestRef): MediaType =
|
||||
## Returns preferred content-type using ``Accept`` header specified by
|
||||
## client in request ``request``.
|
||||
let acceptHeader = request.headers.getString(AcceptHeaderName)
|
||||
let res = getAcceptInfo(acceptHeader)
|
||||
if res.isErr():
|
||||
# If `Accept` header is incorrect, client accepts any type of content.
|
||||
MediaType.init("*", "*")
|
||||
else:
|
||||
let mediaTypes = res.get().data
|
||||
if len(mediaTypes) > 0:
|
||||
mediaTypes[0].mediaType
|
||||
else:
|
||||
MediaType.init("*", "*")
|
||||
|
||||
proc preferredContentType*(request: HttpRequestRef,
|
||||
types: varargs[string]): Result[string, cstring] =
|
||||
## Match or obtain preferred content-type using ``Accept`` header specified by
|
||||
## client in request ``request``.
|
||||
##
|
||||
## If ``Accept`` header is missing in client's request - ``types[0]`` or
|
||||
## ``*/*`` value will be returned as result.
|
||||
##
|
||||
## If ``Accept`` header has incorrect format in client's request -
|
||||
## ``types[0]`` or ``*/*`` value will be returned as result.
|
||||
##
|
||||
## If ``Accept`` header is present and has one or more content types supported
|
||||
## by client, the best value will be selected from ``types`` using
|
||||
## quality value (weight) reported in ``Accept`` header. If client do not
|
||||
## support any methods in ``types`` error will be returned.
|
||||
let acceptHeader = request.headers.getString(AcceptHeaderName)
|
||||
if len(types) == 0:
|
||||
if len(acceptHeader) == 0:
|
||||
# If `Accept` header is missing, return `*/*`.
|
||||
ok("*/*")
|
||||
else:
|
||||
let res = getAcceptInfo(acceptHeader)
|
||||
if res.isErr():
|
||||
# If `Accept` header is incorrect, client accepts any type of content.
|
||||
ok("*/*")
|
||||
else:
|
||||
let mediaTypes = res.get().data
|
||||
if len(mediaTypes) > 0:
|
||||
ok($mediaTypes[0].mediaType)
|
||||
else:
|
||||
ok("*/*")
|
||||
else:
|
||||
if len(acceptHeader) == 0:
|
||||
# If `Accept` header is missing, client accepts any type of content.
|
||||
ok(types[0])
|
||||
else:
|
||||
let res = getAcceptInfo(acceptHeader)
|
||||
if res.isErr():
|
||||
# If `Accept` header is incorrect, client accepts any type of content.
|
||||
ok(types[0])
|
||||
else:
|
||||
let mediaTypes =
|
||||
block:
|
||||
var res: seq[MediaType]
|
||||
for item in types:
|
||||
res.add(MediaType.init(item))
|
||||
res
|
||||
for item in res.get().data:
|
||||
for expect in mediaTypes:
|
||||
if expect == item.mediaType:
|
||||
return ok($expect)
|
||||
err("Preferred content type not found")
|
||||
|
||||
proc sendErrorResponse(conn: HttpConnectionRef, version: HttpVersion,
|
||||
code: HttpCode, keepAlive = true,
|
||||
datatype = "text/text",
|
||||
|
|
|
@ -855,6 +855,58 @@ suite "HTTP server testing suite":
|
|||
table.add(key, value)
|
||||
check toString(table) == vector[2]
|
||||
|
||||
test "Preferred Accept handling test":
|
||||
proc createRequest(acceptHeader: string): HttpRequestRef =
|
||||
let headers = HttpTable.init([("accept", acceptHeader)])
|
||||
HttpRequestRef(headers: headers)
|
||||
|
||||
proc createRequest(): HttpRequestRef =
|
||||
HttpRequestRef(headers: HttpTable.init())
|
||||
|
||||
var requests = @[
|
||||
(
|
||||
createRequest("application/json;q=0.9,application/octet-stream"),
|
||||
@[
|
||||
"application/octet-stream",
|
||||
"application/octet-stream",
|
||||
"application/octet-stream",
|
||||
"application/json",
|
||||
]
|
||||
),
|
||||
(
|
||||
createRequest(""),
|
||||
@[
|
||||
"*/*",
|
||||
"*/*",
|
||||
"application/json",
|
||||
"application/json"
|
||||
]
|
||||
),
|
||||
(
|
||||
createRequest(),
|
||||
@[
|
||||
"*/*",
|
||||
"*/*",
|
||||
"application/json",
|
||||
"application/json"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
for req in requests:
|
||||
check $req[0].preferredContentMediaType() == req[1][1]
|
||||
let r0 = req[0].preferredContentType()
|
||||
let r1 = req[0].preferredContentType("application/json",
|
||||
"application/octet-stream")
|
||||
let r2 = req[0].preferredContentType("application/json")
|
||||
check:
|
||||
r0.isOk() == true
|
||||
r1.isOk() == true
|
||||
r2.isOk() == true
|
||||
r0.get() == req[1][0]
|
||||
r1.get() == req[1][2]
|
||||
r2.get() == req[1][3]
|
||||
|
||||
test "Leaks test":
|
||||
check:
|
||||
getTracker("async.stream.reader").isLeaked() == false
|
||||
|
|
Loading…
Reference in New Issue