Annotate with `raises:[Defect]`.
This commit is contained in:
parent
45cb009be2
commit
b47fcb3e86
|
@ -54,28 +54,34 @@ proc closeWait*(bstream: HttpBodyReader) {.async.} =
|
|||
await allFutures(res)
|
||||
await procCall(AsyncStreamReader(bstream).closeWait())
|
||||
|
||||
proc atBound*(bstream: HttpBodyReader): bool =
|
||||
proc atBound*(bstream: HttpBodyReader): bool {.
|
||||
raises: [Defect].} =
|
||||
## Returns ``true`` if lowest stream is at EOF.
|
||||
let lreader = bstream.streams[^1]
|
||||
doAssert(lreader of BoundedStreamReader)
|
||||
let breader = cast[BoundedStreamReader](lreader)
|
||||
breader.atEof() and (breader.bytesLeft() == 0)
|
||||
|
||||
proc newHttpDefect*(msg: string): ref HttpDefect =
|
||||
proc newHttpDefect*(msg: string): ref HttpDefect {.
|
||||
raises: [HttpDefect].} =
|
||||
newException(HttpDefect, msg)
|
||||
|
||||
proc newHttpCriticalError*(msg: string, code = Http400): ref HttpCriticalError =
|
||||
proc newHttpCriticalError*(msg: string,
|
||||
code = Http400): ref HttpCriticalError {.
|
||||
raises: [HttpCriticalError].} =
|
||||
var tre = newException(HttpCriticalError, msg)
|
||||
tre.code = code
|
||||
tre
|
||||
|
||||
proc newHttpRecoverableError*(msg: string,
|
||||
code = Http400): ref HttpRecoverableError =
|
||||
code = Http400): ref HttpRecoverableError {.
|
||||
raises: [HttpRecoverableError].} =
|
||||
var tre = newException(HttpRecoverableError, msg)
|
||||
tre.code = code
|
||||
tre
|
||||
|
||||
iterator queryParams*(query: string): tuple[key: string, value: string] =
|
||||
iterator queryParams*(query: string): tuple[key: string, value: string] {.
|
||||
raises: [Defect].} =
|
||||
## Iterate over url-encoded query string.
|
||||
for pair in query.split('&'):
|
||||
let items = pair.split('=', maxsplit = 1)
|
||||
|
@ -85,7 +91,8 @@ iterator queryParams*(query: string): tuple[key: string, value: string] =
|
|||
yield (decodeUrl(k), decodeUrl(v))
|
||||
|
||||
func getTransferEncoding*(ch: openarray[string]): HttpResult[
|
||||
set[TransferEncodingFlags]] =
|
||||
set[TransferEncodingFlags]] {.
|
||||
raises: [Defect].} =
|
||||
## Parse value of multiple HTTP headers ``Transfer-Encoding`` and return
|
||||
## it as set of ``TransferEncodingFlags``.
|
||||
var res: set[TransferEncodingFlags] = {}
|
||||
|
@ -106,6 +113,8 @@ func getTransferEncoding*(ch: openarray[string]): HttpResult[
|
|||
res.incl(TransferEncodingFlags.Deflate)
|
||||
of "gzip":
|
||||
res.incl(TransferEncodingFlags.Gzip)
|
||||
of "x-gzip":
|
||||
res.incl(TransferEncodingFlags.Gzip)
|
||||
of "":
|
||||
res.incl(TransferEncodingFlags.Identity)
|
||||
else:
|
||||
|
@ -113,7 +122,8 @@ func getTransferEncoding*(ch: openarray[string]): HttpResult[
|
|||
ok(res)
|
||||
|
||||
func getContentEncoding*(ch: openarray[string]): HttpResult[
|
||||
set[ContentEncodingFlags]] =
|
||||
set[ContentEncodingFlags]] {.
|
||||
raises: [Defect].} =
|
||||
## Parse value of multiple HTTP headers ``Content-Encoding`` and return
|
||||
## it as set of ``ContentEncodingFlags``.
|
||||
var res: set[ContentEncodingFlags] = {}
|
||||
|
@ -134,13 +144,16 @@ func getContentEncoding*(ch: openarray[string]): HttpResult[
|
|||
res.incl(ContentEncodingFlags.Deflate)
|
||||
of "gzip":
|
||||
res.incl(ContentEncodingFlags.Gzip)
|
||||
of "x-gzip":
|
||||
res.incl(ContentEncodingFlags.Gzip)
|
||||
of "":
|
||||
res.incl(ContentEncodingFlags.Identity)
|
||||
else:
|
||||
return err("Incorrect Content-Encoding value")
|
||||
ok(res)
|
||||
|
||||
func getContentType*(ch: openarray[string]): HttpResult[string] =
|
||||
func getContentType*(ch: openarray[string]): HttpResult[string] {.
|
||||
raises: [Defect].} =
|
||||
## Check and prepare value of ``Content-Type`` header.
|
||||
if len(ch) > 1:
|
||||
err("Multiple Content-Type values found")
|
||||
|
|
|
@ -110,7 +110,7 @@ type
|
|||
|
||||
proc init(htype: typedesc[HttpProcessError], error: HTTPServerError,
|
||||
exc: ref CatchableError, remote: TransportAddress,
|
||||
code: HttpCode): HttpProcessError =
|
||||
code: HttpCode): HttpProcessError {.raises: [Defect].} =
|
||||
HttpProcessError(error: error, exc: exc, remote: remote, code: code)
|
||||
|
||||
proc new*(htype: typedesc[HttpServerRef],
|
||||
|
@ -147,13 +147,16 @@ proc new*(htype: typedesc[HttpServerRef],
|
|||
)
|
||||
|
||||
res.baseUri =
|
||||
if len(serverUri.hostname) > 0 and isAbsolute(serverUri):
|
||||
serverUri
|
||||
else:
|
||||
if HttpServerFlags.Secure in serverFlags:
|
||||
parseUri("https://" & $address & "/")
|
||||
try:
|
||||
if len(serverUri.hostname) > 0 and isAbsolute(serverUri):
|
||||
serverUri
|
||||
else:
|
||||
parseUri("http://" & $address & "/")
|
||||
if HttpServerFlags.Secure in serverFlags:
|
||||
parseUri("https://" & $address & "/")
|
||||
else:
|
||||
parseUri("http://" & $address & "/")
|
||||
except TransportAddressError as exc:
|
||||
return err(exc.msg)
|
||||
|
||||
try:
|
||||
res.instance = createStreamServer(address, flags = socketFlags,
|
||||
|
@ -169,7 +172,7 @@ proc new*(htype: typedesc[HttpServerRef],
|
|||
except CatchableError as exc:
|
||||
return err(exc.msg)
|
||||
|
||||
proc getResponse*(req: HttpRequestRef): HttpResponseRef =
|
||||
proc getResponse*(req: HttpRequestRef): HttpResponseRef {.raises: [Defect].} =
|
||||
if req.response.isNone():
|
||||
var resp = HttpResponseRef(
|
||||
status: Http200,
|
||||
|
@ -184,7 +187,7 @@ proc getResponse*(req: HttpRequestRef): HttpResponseRef =
|
|||
else:
|
||||
req.response.get()
|
||||
|
||||
proc dumbResponse*(): HttpResponseRef =
|
||||
proc dumbResponse*(): HttpResponseRef {.raises: [Defect].} =
|
||||
## Create an empty response to return when request processor got no request.
|
||||
HttpResponseRef(state: HttpResponseState.Dumb, version: HttpVersion11)
|
||||
|
||||
|
@ -192,13 +195,14 @@ proc getId(transp: StreamTransport): string {.inline.} =
|
|||
## Returns string unique transport's identifier as string.
|
||||
$transp.remoteAddress() & "_" & $transp.localAddress()
|
||||
|
||||
proc hasBody*(request: HttpRequestRef): bool =
|
||||
proc hasBody*(request: HttpRequestRef): bool {.raises: [Defect].} =
|
||||
## Returns ``true`` if request has body.
|
||||
request.requestFlags * {HttpRequestFlags.BoundBody,
|
||||
HttpRequestFlags.UnboundBody} != {}
|
||||
|
||||
proc prepareRequest(conn: HttpConnectionRef,
|
||||
req: HttpRequestHeader): HttpResultCode[HttpRequestRef] =
|
||||
req: HttpRequestHeader): HttpResultCode[HttpRequestRef] {.
|
||||
raises: [Defect].}=
|
||||
var request = HttpRequestRef(connection: conn)
|
||||
|
||||
if req.version notin {HttpVersion10, HttpVersion11}:
|
||||
|
@ -659,7 +663,7 @@ proc acceptClientLoop(server: HttpServerRef) {.async.} =
|
|||
if breakLoop:
|
||||
break
|
||||
|
||||
proc state*(server: HttpServerRef): HttpServerState =
|
||||
proc state*(server: HttpServerRef): HttpServerState {.raises: [Defect].} =
|
||||
## Returns current HTTP server's state.
|
||||
if server.lifetime.finished():
|
||||
ServerClosed
|
||||
|
@ -815,22 +819,24 @@ proc `keepalive=`*(resp: HttpResponseRef, value: bool) =
|
|||
else:
|
||||
resp.flags.excl(KeepAlive)
|
||||
|
||||
proc keepalive*(resp: HttpResponseRef): bool =
|
||||
proc keepalive*(resp: HttpResponseRef): bool {.raises: [Defect].} =
|
||||
KeepAlive in resp.flags
|
||||
|
||||
proc setHeader*(resp: HttpResponseRef, key, value: string) =
|
||||
proc setHeader*(resp: HttpResponseRef, key, value: string) {.
|
||||
raises: [Defect].} =
|
||||
doAssert(resp.state == HttpResponseState.Empty)
|
||||
resp.headersTable.set(key, value)
|
||||
|
||||
proc addHeader*(resp: HttpResponseRef, key, value: string) =
|
||||
proc addHeader*(resp: HttpResponseRef, key, value: string) {.
|
||||
raises: [Defect].}=
|
||||
doAssert(resp.state == HttpResponseState.Empty)
|
||||
resp.headersTable.add(key, value)
|
||||
|
||||
proc getHeader*(resp: HttpResponseRef, key: string,
|
||||
default: string = ""): string =
|
||||
default: string = ""): string {.raises: [Defect].} =
|
||||
resp.headersTable.getString(key, default)
|
||||
|
||||
proc hasHeader*(resp: HttpResponseRef, key: string): bool =
|
||||
proc hasHeader*(resp: HttpResponseRef, key: string): bool {.raises: [Defect].} =
|
||||
key in resp.headersTable
|
||||
|
||||
template doHeaderDef(buf, resp, name, default) =
|
||||
|
@ -849,7 +855,8 @@ template checkPending(t: untyped) =
|
|||
if t.state != HttpResponseState.Empty:
|
||||
raise newHttpCriticalError("Response body was already sent")
|
||||
|
||||
proc prepareLengthHeaders(resp: HttpResponseRef, length: int): string =
|
||||
proc prepareLengthHeaders(resp: HttpResponseRef, length: int): string {.
|
||||
raises: [Defect].}=
|
||||
var answer = $(resp.version) & " " & $(resp.status) & "\r\n"
|
||||
answer.doHeaderDef(resp, "Date", httpDate())
|
||||
answer.doHeaderDef(resp, "Content-Type", "text/html; charset=utf-8")
|
||||
|
@ -866,7 +873,8 @@ proc prepareLengthHeaders(resp: HttpResponseRef, length: int): string =
|
|||
answer.add("\r\n")
|
||||
answer
|
||||
|
||||
proc prepareChunkedHeaders(resp: HttpResponseRef): string =
|
||||
proc prepareChunkedHeaders(resp: HttpResponseRef): string {.
|
||||
raises: [Defect].} =
|
||||
var answer = $(resp.version) & " " & $(resp.status) & "\r\n"
|
||||
answer.doHeaderDef(resp, "Date", httpDate())
|
||||
answer.doHeaderDef(resp, "Content-Type", "text/html; charset=utf-8")
|
||||
|
@ -1024,7 +1032,8 @@ proc respond*(req: HttpRequestRef, code: HttpCode, content: string,
|
|||
await response.sendBody(content)
|
||||
return response
|
||||
|
||||
proc requestInfo*(req: HttpRequestRef, contentType = "text/text"): string =
|
||||
proc requestInfo*(req: HttpRequestRef, contentType = "text/text"): string {.
|
||||
raises: [Defect].} =
|
||||
## Returns comprehensive information about request for specific content
|
||||
## type.
|
||||
##
|
||||
|
@ -1100,8 +1109,19 @@ proc requestInfo*(req: HttpRequestRef, contentType = "text/text"): string =
|
|||
res.add(kv(k, v))
|
||||
|
||||
res.add(h("Connection information"))
|
||||
res.add(kv("local.address", $req.connection.transp.localAddress()))
|
||||
res.add(kv("remote.address", $req.connection.transp.remoteAddress()))
|
||||
let localAddress =
|
||||
try:
|
||||
$req.connection.transp.localAddress()
|
||||
except TransportError:
|
||||
"incorrect address"
|
||||
let remoteAddress =
|
||||
try:
|
||||
$req.connection.transp.remoteAddress()
|
||||
except TransportError:
|
||||
"incorrect address"
|
||||
|
||||
res.add(kv("local.address", localAddress))
|
||||
res.add(kv("remote.address", remoteAddress))
|
||||
|
||||
res.add(h("Server configuration"))
|
||||
let maxConn =
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
# MIT license (LICENSE-MIT)
|
||||
import std/[tables, strutils]
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
type
|
||||
HttpTable* = object
|
||||
table: Table[string, seq[string]]
|
||||
|
@ -50,10 +52,11 @@ proc bytesToDec*[T: byte|char](src: openarray[T]): uint64 =
|
|||
v
|
||||
|
||||
proc add*(ht: var HttpTables, key: string, value: string) =
|
||||
var default: seq[string]
|
||||
let lowkey = key.toLowerAscii()
|
||||
var nitem = @[value]
|
||||
if ht.table.hasKeyOrPut(lowkey, nitem):
|
||||
var oitem = ht.table[lowkey]
|
||||
var oitem = ht.table.getOrDefault(lowkey, default)
|
||||
oitem.add(value)
|
||||
ht.table[lowkey] = oitem
|
||||
|
||||
|
@ -64,7 +67,8 @@ proc set*(ht: var HttpTables, key: string, value: string) =
|
|||
let lowkey = key.toLowerAscii()
|
||||
ht.table[lowkey] = @[value]
|
||||
|
||||
proc contains*(ht: var HttpTables, key: string): bool =
|
||||
proc contains*(ht: var HttpTables, key: string): bool {.
|
||||
raises: [Defect].} =
|
||||
ht.table.contains(key.toLowerAscii())
|
||||
|
||||
proc getList*(ht: HttpTables, key: string,
|
||||
|
@ -147,24 +151,24 @@ proc normalizeHeaderName*(value: string): string =
|
|||
res
|
||||
|
||||
iterator stringItems*(ht: HttpTables,
|
||||
normalizeKey = false): tuple[key: string, value: string] =
|
||||
normKey = false): tuple[key: string, value: string] =
|
||||
## Iterate over HttpTable values.
|
||||
##
|
||||
## If ``normalizeKey`` is true, key name value will be normalized using
|
||||
## If ``normKey`` is true, key name value will be normalized using
|
||||
## normalizeHeaderName() procedure.
|
||||
for k, v in ht.table.pairs():
|
||||
let key = if normalizeKey: normalizeHeaderName(k) else: k
|
||||
let key = if normKey: normalizeHeaderName(k) else: k
|
||||
for item in v:
|
||||
yield (key, item)
|
||||
|
||||
iterator items*(ht: HttpTables,
|
||||
normalizeKey = false): tuple[key: string, value: seq[string]] =
|
||||
normKey = false): tuple[key: string, value: seq[string]] =
|
||||
## Iterate over HttpTable values.
|
||||
##
|
||||
## If ``normalizeKey`` is true, key name value will be normalized using
|
||||
## If ``normKey`` is true, key name value will be normalized using
|
||||
## normalizeHeaderName() procedure.
|
||||
for k, v in ht.table.pairs():
|
||||
let key = if normalizeKey: normalizeHeaderName(k) else: k
|
||||
let key = if normKey: normalizeHeaderName(k) else: k
|
||||
yield (key, v)
|
||||
|
||||
proc `$`*(ht: HttpTables): string =
|
||||
|
|
|
@ -50,14 +50,16 @@ type
|
|||
|
||||
BChar* = byte | char
|
||||
|
||||
proc startsWith*(s, prefix: openarray[byte]): bool =
|
||||
proc startsWith(s, prefix: openarray[byte]): bool {.
|
||||
raises: [Defect].} =
|
||||
var i = 0
|
||||
while true:
|
||||
if i >= len(prefix): return true
|
||||
if i >= len(s) or s[i] != prefix[i]: return false
|
||||
inc(i)
|
||||
|
||||
proc parseUntil*(s, until: openarray[byte]): int =
|
||||
proc parseUntil(s, until: openarray[byte]): int {.
|
||||
raises: [Defect].} =
|
||||
var i = 0
|
||||
while i < len(s):
|
||||
if len(until) > 0 and s[i] == until[0]:
|
||||
|
@ -68,9 +70,33 @@ proc parseUntil*(s, until: openarray[byte]): int =
|
|||
inc(i)
|
||||
-1
|
||||
|
||||
func setPartNames(part: var MultiPart): HttpResult[void] {.
|
||||
raises: [Defect].} =
|
||||
if part.headers.count("content-disposition") != 1:
|
||||
return err("Content-Disposition header is incorrect")
|
||||
var header = part.headers.getString("content-disposition")
|
||||
let disp = parseDisposition(header, false)
|
||||
if disp.failed():
|
||||
return err("Content-Disposition header value is incorrect")
|
||||
let dtype = disp.dispositionType(header.toOpenArrayByte(0, len(header) - 1))
|
||||
if dtype.toLowerAscii() != "form-data":
|
||||
return err("Content-Disposition type is incorrect")
|
||||
for k, v in disp.fields(header.toOpenArrayByte(0, len(header) - 1)):
|
||||
case k.toLowerAscii()
|
||||
of "name":
|
||||
part.name = v
|
||||
of "filename":
|
||||
part.filename = v
|
||||
else:
|
||||
discard
|
||||
if len(part.name) == 0:
|
||||
part.name = $part.counter
|
||||
ok()
|
||||
|
||||
proc init*[A: BChar, B: BChar](mpt: typedesc[MultiPartReader],
|
||||
buffer: openarray[A],
|
||||
boundary: openarray[B]): MultiPartReader =
|
||||
boundary: openarray[B]): MultiPartReader {.
|
||||
raises: [Defect].} =
|
||||
## Create new MultiPartReader instance with `buffer` interface.
|
||||
##
|
||||
## ``buffer`` - is buffer which will be used to read data.
|
||||
|
@ -94,7 +120,8 @@ proc init*[A: BChar, B: BChar](mpt: typedesc[MultiPartReader],
|
|||
proc new*[B: BChar](mpt: typedesc[MultiPartReaderRef],
|
||||
stream: HttpBodyReader,
|
||||
boundary: openarray[B],
|
||||
partHeadersMaxSize = 4096): MultiPartReaderRef =
|
||||
partHeadersMaxSize = 4096): MultiPartReaderRef {.
|
||||
raises: [Defect].} =
|
||||
## Create new MultiPartReader instance with `stream` interface.
|
||||
##
|
||||
## ``stream`` is stream used to read data.
|
||||
|
@ -113,28 +140,6 @@ proc new*[B: BChar](mpt: typedesc[MultiPartReaderRef],
|
|||
stream: stream, offset: 0, boundary: fboundary,
|
||||
buffer: newSeq[byte](partHeadersMaxSize))
|
||||
|
||||
func setPartNames(part: var MultiPart): HttpResult[void] =
|
||||
if part.headers.count("content-disposition") != 1:
|
||||
return err("Content-Disposition header is incorrect")
|
||||
var header = part.headers.getString("content-disposition")
|
||||
let disp = parseDisposition(header, false)
|
||||
if disp.failed():
|
||||
return err("Content-Disposition header value is incorrect")
|
||||
let dtype = disp.dispositionType(header.toOpenArrayByte(0, len(header) - 1))
|
||||
if dtype.toLowerAscii() != "form-data":
|
||||
return err("Content-Disposition type is incorrect")
|
||||
for k, v in disp.fields(header.toOpenArrayByte(0, len(header) - 1)):
|
||||
case k.toLowerAscii()
|
||||
of "name":
|
||||
part.name = v
|
||||
of "filename":
|
||||
part.filename = v
|
||||
else:
|
||||
discard
|
||||
if len(part.name) == 0:
|
||||
part.name = $part.counter
|
||||
ok()
|
||||
|
||||
proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.async.} =
|
||||
doAssert(mpr.kind == MultiPartSource.Stream)
|
||||
if mpr.firstTime:
|
||||
|
@ -235,7 +240,8 @@ proc consumeBody*(mp: MultiPart) {.async.} =
|
|||
of MultiPartSource.Buffer:
|
||||
discard
|
||||
|
||||
proc getBodyStream*(mp: MultiPart): HttpResult[AsyncStreamReader] =
|
||||
proc getBodyStream*(mp: MultiPart): HttpResult[AsyncStreamReader] {.
|
||||
raises: [Defect].} =
|
||||
## Get multipart's ``mp`` stream, which can be used to obtain value of the
|
||||
## part.
|
||||
case mp.kind
|
||||
|
@ -260,7 +266,8 @@ proc closeWait*(mpr: MultiPartReaderRef) {.async.} =
|
|||
else:
|
||||
discard
|
||||
|
||||
proc getBytes*(mp: MultiPart): seq[byte] =
|
||||
proc getBytes*(mp: MultiPart): seq[byte] {.
|
||||
raises: [Defect].} =
|
||||
## Returns value for MultiPart ``mp`` as sequence of bytes.
|
||||
case mp.kind
|
||||
of MultiPartSource.Buffer:
|
||||
|
@ -269,7 +276,8 @@ proc getBytes*(mp: MultiPart): seq[byte] =
|
|||
doAssert(not(mp.stream.atEof()), "Value is not obtained yet")
|
||||
mp.buffer
|
||||
|
||||
proc getString*(mp: MultiPart): string =
|
||||
proc getString*(mp: MultiPart): string {.
|
||||
raises: [Defect].} =
|
||||
## Returns value for MultiPart ``mp`` as string.
|
||||
case mp.kind
|
||||
of MultiPartSource.Buffer:
|
||||
|
@ -288,7 +296,8 @@ proc getString*(mp: MultiPart): string =
|
|||
else:
|
||||
""
|
||||
|
||||
proc atEoM*(mpr: var MultiPartReader): bool =
|
||||
proc atEoM*(mpr: var MultiPartReader): bool {.
|
||||
raises: [Defect].} =
|
||||
## Procedure returns ``true`` if MultiPartReader has reached the end of
|
||||
## multipart message.
|
||||
case mpr.kind
|
||||
|
@ -297,7 +306,8 @@ proc atEoM*(mpr: var MultiPartReader): bool =
|
|||
of MultiPartSource.Stream:
|
||||
mpr.stream.atEof()
|
||||
|
||||
proc atEoM*(mpr: MultiPartReaderRef): bool =
|
||||
proc atEoM*(mpr: MultiPartReaderRef): bool {.
|
||||
raises: [Defect].} =
|
||||
## Procedure returns ``true`` if MultiPartReader has reached the end of
|
||||
## multipart message.
|
||||
case mpr.kind
|
||||
|
@ -306,7 +316,8 @@ proc atEoM*(mpr: MultiPartReaderRef): bool =
|
|||
of MultiPartSource.Stream:
|
||||
mpr.stream.atEof()
|
||||
|
||||
proc getPart*(mpr: var MultiPartReader): Result[MultiPart, string] =
|
||||
proc getPart*(mpr: var MultiPartReader): Result[MultiPart, string] {.
|
||||
raises: [Defect].} =
|
||||
## Get multipart part from MultiPartReader instance.
|
||||
##
|
||||
## This procedure will work only for MultiPartReader with buffer source.
|
||||
|
@ -396,11 +407,13 @@ proc getPart*(mpr: var MultiPartReader): Result[MultiPart, string] =
|
|||
else:
|
||||
err("Incorrect multipart form")
|
||||
|
||||
func isEmpty*(mp: MultiPart): bool =
|
||||
func isEmpty*(mp: MultiPart): bool {.
|
||||
raises: [Defect].} =
|
||||
## Returns ``true`` is multipart ``mp`` is not initialized/filled yet.
|
||||
mp.counter == 0
|
||||
|
||||
func getMultipartBoundary*(ch: openarray[string]): HttpResult[string] =
|
||||
func getMultipartBoundary*(ch: openarray[string]): HttpResult[string] {.
|
||||
raises: [Defect].} =
|
||||
## Returns ``multipart/form-data`` boundary value from ``Content-Type``
|
||||
## header.
|
||||
##
|
||||
|
|
Loading…
Reference in New Issue