mirror of
https://github.com/logos-storage/nim-chronos.git
synced 2026-01-06 23:43:06 +00:00
Refactor preferredContentType proc & add tests
Fix of the following bug: In case of multiple accept headers with same preference `preferredContentType` used to select the first match within content types provided by the application. For example, if user specifies accept headers `application/octet-stream, application/json` and application provides `application/json, application/octet-stream`, `application/octet-stream` will be returned, and that is a bug. Now the procedure returns the most suitable match according to the application preferences.
This commit is contained in:
parent
519ca463df
commit
1f37dac810
@ -488,7 +488,7 @@ proc preferredContentMediaType*(acceptHeader: string): MediaType =
|
|||||||
MediaType.init("*", "*")
|
MediaType.init("*", "*")
|
||||||
|
|
||||||
proc preferredContentType*(acceptHeader: string,
|
proc preferredContentType*(acceptHeader: string,
|
||||||
types: openArray[string] = []): Result[string, cstring] =
|
types: varargs[MediaType]): Result[MediaType, cstring] =
|
||||||
## Match or obtain preferred content-type using ``Accept`` header specified by
|
## Match or obtain preferred content-type using ``Accept`` header specified by
|
||||||
## string ``acceptHeader``.
|
## string ``acceptHeader``.
|
||||||
##
|
##
|
||||||
@ -505,18 +505,18 @@ proc preferredContentType*(acceptHeader: string,
|
|||||||
if len(types) == 0:
|
if len(types) == 0:
|
||||||
if len(acceptHeader) == 0:
|
if len(acceptHeader) == 0:
|
||||||
# If `Accept` header is missing, return `*/*`.
|
# If `Accept` header is missing, return `*/*`.
|
||||||
ok("*/*")
|
ok(wildCardMediaType)
|
||||||
else:
|
else:
|
||||||
let res = getAcceptInfo(acceptHeader)
|
let res = getAcceptInfo(acceptHeader)
|
||||||
if res.isErr():
|
if res.isErr():
|
||||||
# If `Accept` header is incorrect, client accepts any type of content.
|
# If `Accept` header is incorrect, client accepts any type of content.
|
||||||
ok("*/*")
|
ok(wildCardMediaType)
|
||||||
else:
|
else:
|
||||||
let mediaTypes = res.get().data
|
let mediaTypes = res.get().data
|
||||||
if len(mediaTypes) > 0:
|
if len(mediaTypes) > 0:
|
||||||
ok($mediaTypes[0].mediaType)
|
ok(mediaTypes[0].mediaType)
|
||||||
else:
|
else:
|
||||||
ok("*/*")
|
ok(wildCardMediaType)
|
||||||
else:
|
else:
|
||||||
if len(acceptHeader) == 0:
|
if len(acceptHeader) == 0:
|
||||||
# If `Accept` header is missing, client accepts any type of content.
|
# If `Accept` header is missing, client accepts any type of content.
|
||||||
@ -527,17 +527,7 @@ proc preferredContentType*(acceptHeader: string,
|
|||||||
# If `Accept` header is incorrect, client accepts any type of content.
|
# If `Accept` header is incorrect, client accepts any type of content.
|
||||||
ok(types[0])
|
ok(types[0])
|
||||||
else:
|
else:
|
||||||
let mediaTypes =
|
selectContentType(ares.get().data, types)
|
||||||
block:
|
|
||||||
var res: seq[MediaType]
|
|
||||||
for item in types:
|
|
||||||
res.add(MediaType.init(item))
|
|
||||||
res
|
|
||||||
for item in ares.get().data:
|
|
||||||
for expect in mediaTypes:
|
|
||||||
if expect == item.mediaType:
|
|
||||||
return ok($expect)
|
|
||||||
err("Preferred content type not found")
|
|
||||||
|
|
||||||
proc preferredContentMediaType*(request: HttpRequestRef): MediaType =
|
proc preferredContentMediaType*(request: HttpRequestRef): MediaType =
|
||||||
## Returns preferred content-type using ``Accept`` header specified by
|
## Returns preferred content-type using ``Accept`` header specified by
|
||||||
@ -545,7 +535,7 @@ proc preferredContentMediaType*(request: HttpRequestRef): MediaType =
|
|||||||
preferredContentMediaType(request.headers.getString(AcceptHeaderName))
|
preferredContentMediaType(request.headers.getString(AcceptHeaderName))
|
||||||
|
|
||||||
proc preferredContentType*(request: HttpRequestRef,
|
proc preferredContentType*(request: HttpRequestRef,
|
||||||
types: varargs[string]): Result[string, cstring] =
|
types: varargs[MediaType]): Result[MediaType, cstring] =
|
||||||
## Match or obtain preferred content-type using ``Accept`` header specified by
|
## Match or obtain preferred content-type using ``Accept`` header specified by
|
||||||
## client in request ``request``.
|
## client in request ``request``.
|
||||||
preferredContentType(request.headers.getString(AcceptHeaderName), types)
|
preferredContentType(request.headers.getString(AcceptHeaderName), types)
|
||||||
|
|||||||
@ -856,6 +856,12 @@ suite "HTTP server testing suite":
|
|||||||
check toString(table) == vector[2]
|
check toString(table) == vector[2]
|
||||||
|
|
||||||
test "Preferred Accept handling test":
|
test "Preferred Accept handling test":
|
||||||
|
const
|
||||||
|
jsonMediaType = MediaType.init("application/json")
|
||||||
|
sszMediaType = MediaType.init("application/octet-stream")
|
||||||
|
plainTextMediaType = MediaType.init("text/plain")
|
||||||
|
imageMediaType = MediaType.init("image/jpg")
|
||||||
|
|
||||||
proc createRequest(acceptHeader: string): HttpRequestRef =
|
proc createRequest(acceptHeader: string): HttpRequestRef =
|
||||||
let headers = HttpTable.init([("accept", acceptHeader)])
|
let headers = HttpTable.init([("accept", acceptHeader)])
|
||||||
HttpRequestRef(headers: headers)
|
HttpRequestRef(headers: headers)
|
||||||
@ -863,49 +869,305 @@ suite "HTTP server testing suite":
|
|||||||
proc createRequest(): HttpRequestRef =
|
proc createRequest(): HttpRequestRef =
|
||||||
HttpRequestRef(headers: HttpTable.init())
|
HttpRequestRef(headers: HttpTable.init())
|
||||||
|
|
||||||
var requests = @[
|
var singleHeader = @[
|
||||||
(
|
(
|
||||||
createRequest("application/json;q=0.9,application/octet-stream"),
|
createRequest("application/json"),
|
||||||
@[
|
@[
|
||||||
"application/octet-stream",
|
"application/json"
|
||||||
"application/octet-stream",
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
var complexHeaders = @[
|
||||||
|
(
|
||||||
|
createRequest(),
|
||||||
|
@[
|
||||||
|
"*/*",
|
||||||
|
"application/json",
|
||||||
"application/octet-stream",
|
"application/octet-stream",
|
||||||
"application/json",
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
createRequest(""),
|
createRequest(""),
|
||||||
@[
|
@[
|
||||||
"*/*",
|
"*/*",
|
||||||
"*/*",
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/json, application/octet-stream"),
|
||||||
|
@[
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
"application/json",
|
"application/json",
|
||||||
"application/json"
|
"application/json"
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
createRequest(),
|
createRequest("application/octet-stream, application/json"),
|
||||||
|
@[
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/json;q=0.9, application/octet-stream"),
|
||||||
|
@[
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/json, application/octet-stream;q=0.9"),
|
||||||
|
@[
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/json;q=0.9, application/octet-stream;q=0.8"),
|
||||||
|
@[
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/json;q=0.8, application/octet-stream;q=0.9"),
|
||||||
|
@[
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/octet-stream"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/plain, application/octet-stream, application/json"),
|
||||||
|
@[
|
||||||
|
"text/plain",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/plain, application/json;q=0.8, application/octet-stream;q=0.8"),
|
||||||
|
@[
|
||||||
|
"text/plain",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"text/plain",
|
||||||
|
"text/plain"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/plain, application/json;q=0.8, application/octet-stream;q=0.5"),
|
||||||
|
@[
|
||||||
|
"text/plain",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"text/plain",
|
||||||
|
"text/plain"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/plain;q=0.8, application/json, application/octet-stream;q=0.8"),
|
||||||
|
@[
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/*, application/json;q=0.8, application/octet-stream;q=0.8"),
|
||||||
|
@[
|
||||||
|
"text/*",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"text/plain",
|
||||||
|
"text/plain"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("text/*, application/json;q=0.8, application/octet-stream;q=0.5"),
|
||||||
|
@[
|
||||||
|
"text/*",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/json",
|
||||||
|
"text/plain",
|
||||||
|
"text/plain"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(createRequest("image/jpg, text/plain, application/octet-stream, application/json"),
|
||||||
|
@[
|
||||||
|
"image/jpg",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(createRequest("image/jpg;q=1, text/plain;q=0.2, application/octet-stream;q=0.2, application/json;q=0.2"),
|
||||||
|
@[
|
||||||
|
"image/jpg",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("*/*, application/json;q=0.8, application/octet-stream;q=0.5"),
|
||||||
@[
|
@[
|
||||||
"*/*",
|
"*/*",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("*/*"),
|
||||||
|
@[
|
||||||
"*/*",
|
"*/*",
|
||||||
"application/json",
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"image/jpg"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
createRequest("application/*"),
|
||||||
|
@[
|
||||||
|
"application/*",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/json",
|
||||||
"application/json"
|
"application/json"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
for req in requests:
|
for req in singleHeader:
|
||||||
check $req[0].preferredContentMediaType() == req[1][1]
|
check $req[0].preferredContentMediaType() == req[1][0]
|
||||||
let r0 = req[0].preferredContentType()
|
let r0 = req[0].preferredContentType()
|
||||||
let r1 = req[0].preferredContentType("application/json",
|
let r1 = req[0].preferredContentType(jsonMediaType)
|
||||||
"application/octet-stream")
|
let r2 = req[0].preferredContentType(sszMediaType)
|
||||||
let r2 = req[0].preferredContentType("application/json")
|
let r3 = req[0].preferredContentType(jsonMediaType,
|
||||||
|
sszMediaType)
|
||||||
|
let r4 = req[0].preferredContentType(sszMediaType,
|
||||||
|
jsonMediaType)
|
||||||
|
let r5 = req[0].preferredContentType(jsonMediaType,
|
||||||
|
sszMediaType,
|
||||||
|
plainTextMediaType)
|
||||||
|
let r6 = req[0].preferredContentType(imageMediaType,
|
||||||
|
jsonMediaType,
|
||||||
|
sszMediaType,
|
||||||
|
plainTextMediaType)
|
||||||
|
check:
|
||||||
|
r0.isOk() == true
|
||||||
|
r1.isOk() == true
|
||||||
|
r2.isErr() == true
|
||||||
|
r3.isOk() == true
|
||||||
|
r4.isOk() == true
|
||||||
|
r5.isOk() == true
|
||||||
|
r6.isOk() == true
|
||||||
|
r0.get() == MediaType.init(req[1][0])
|
||||||
|
r1.get() == MediaType.init(req[1][0])
|
||||||
|
r3.get() == MediaType.init(req[1][0])
|
||||||
|
r4.get() == MediaType.init(req[1][0])
|
||||||
|
r5.get() == MediaType.init(req[1][0])
|
||||||
|
r6.get() == MediaType.init(req[1][0])
|
||||||
|
|
||||||
|
for req in complexHeaders:
|
||||||
|
let r0 = req[0].preferredContentType()
|
||||||
|
let r1 = req[0].preferredContentType(jsonMediaType)
|
||||||
|
let r2 = req[0].preferredContentType(sszMediaType)
|
||||||
|
let r3 = req[0].preferredContentType(jsonMediaType,
|
||||||
|
sszMediaType)
|
||||||
|
let r4 = req[0].preferredContentType(sszMediaType,
|
||||||
|
jsonMediaType)
|
||||||
|
let r5 = req[0].preferredContentType(jsonMediaType,
|
||||||
|
sszMediaType,
|
||||||
|
plainTextMediaType)
|
||||||
|
let r6 = req[0].preferredContentType(imageMediaType,
|
||||||
|
jsonMediaType,
|
||||||
|
sszMediaType,
|
||||||
|
plainTextMediaType)
|
||||||
check:
|
check:
|
||||||
r0.isOk() == true
|
r0.isOk() == true
|
||||||
r1.isOk() == true
|
r1.isOk() == true
|
||||||
r2.isOk() == true
|
r2.isOk() == true
|
||||||
r0.get() == req[1][0]
|
r3.isOk() == true
|
||||||
r1.get() == req[1][2]
|
r4.isOk() == true
|
||||||
r2.get() == req[1][3]
|
r5.isOk() == true
|
||||||
|
r6.isOk() == true
|
||||||
|
r0.get() == MediaType.init(req[1][0])
|
||||||
|
r1.get() == MediaType.init(req[1][1])
|
||||||
|
r2.get() == MediaType.init(req[1][2])
|
||||||
|
r3.get() == MediaType.init(req[1][3])
|
||||||
|
r4.get() == MediaType.init(req[1][4])
|
||||||
|
r5.get() == MediaType.init(req[1][5])
|
||||||
|
r6.get() == MediaType.init(req[1][6])
|
||||||
|
|
||||||
test "SSE server-side events stream test":
|
test "SSE server-side events stream test":
|
||||||
proc testPostMultipart2(address: TransportAddress): Future[bool] {.async.} =
|
proc testPostMultipart2(address: TransportAddress): Future[bool] {.async.} =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user