Asyncraises HTTP layer V3 (#482)
* No Critical and Recoverable errors anymore. * Recover raiseHttpCriticalError() * Post-rebase fixes. * Remove deprecated ResponseFence and getResponseFence(). * HttpProcessCallback and 2. * Fix callback holder. * Fix test issue. * Fix backwards compatibility of `HttpResponse.state` field.
This commit is contained in:
parent
e38ceb5378
commit
c41599a6d6
|
@ -43,30 +43,48 @@ const
|
|||
ServerHeader* = "server"
|
||||
LocationHeader* = "location"
|
||||
AuthorizationHeader* = "authorization"
|
||||
ContentDispositionHeader* = "content-disposition"
|
||||
|
||||
UrlEncodedContentType* = MediaType.init("application/x-www-form-urlencoded")
|
||||
MultipartContentType* = MediaType.init("multipart/form-data")
|
||||
|
||||
type
|
||||
HttpMessage* = object
|
||||
code*: HttpCode
|
||||
contentType*: MediaType
|
||||
message*: string
|
||||
|
||||
HttpResult*[T] = Result[T, string]
|
||||
HttpResultCode*[T] = Result[T, HttpCode]
|
||||
HttpResultMessage*[T] = Result[T, HttpMessage]
|
||||
|
||||
HttpDefect* = object of Defect
|
||||
HttpError* = object of AsyncError
|
||||
HttpResponseError* = object of HttpError
|
||||
code*: HttpCode
|
||||
HttpCriticalError* = object of HttpResponseError
|
||||
HttpRecoverableError* = object of HttpResponseError
|
||||
HttpDisconnectError* = object of HttpError
|
||||
HttpConnectionError* = object of HttpError
|
||||
HttpInterruptError* = object of HttpError
|
||||
HttpReadError* = object of HttpError
|
||||
HttpWriteError* = object of HttpError
|
||||
HttpProtocolError* = object of HttpError
|
||||
HttpRedirectError* = object of HttpError
|
||||
HttpAddressError* = object of HttpError
|
||||
HttpUseClosedError* = object of HttpError
|
||||
|
||||
HttpTransportError* = object of HttpError
|
||||
HttpAddressError* = object of HttpTransportError
|
||||
HttpRedirectError* = object of HttpTransportError
|
||||
HttpConnectionError* = object of HttpTransportError
|
||||
HttpReadError* = object of HttpTransportError
|
||||
HttpReadLimitError* = object of HttpReadError
|
||||
HttpDisconnectError* = object of HttpReadError
|
||||
HttpWriteError* = object of HttpTransportError
|
||||
|
||||
HttpProtocolError* = object of HttpError
|
||||
code*: HttpCode
|
||||
|
||||
HttpCriticalError* = object of HttpProtocolError # deprecated
|
||||
HttpRecoverableError* = object of HttpProtocolError # deprecated
|
||||
|
||||
HttpRequestError* = object of HttpProtocolError
|
||||
HttpRequestHeadersError* = object of HttpRequestError
|
||||
HttpRequestBodyError* = object of HttpRequestError
|
||||
HttpRequestHeadersTooLargeError* = object of HttpRequestHeadersError
|
||||
HttpRequestBodyTooLargeError* = object of HttpRequestBodyError
|
||||
HttpResponseError* = object of HttpProtocolError
|
||||
|
||||
HttpInvalidUsageError* = object of HttpError
|
||||
HttpUseClosedError* = object of HttpInvalidUsageError
|
||||
|
||||
KeyValueTuple* = tuple
|
||||
key: string
|
||||
|
@ -127,6 +145,11 @@ func toString*(error: HttpAddressErrorType): string =
|
|||
of HttpAddressErrorType.NoAddressResolved:
|
||||
"No address has been resolved"
|
||||
|
||||
proc raiseHttpRequestBodyTooLargeError*() {.
|
||||
noinline, noreturn, raises: [HttpRequestBodyTooLargeError].} =
|
||||
raise (ref HttpRequestBodyTooLargeError)(
|
||||
code: Http413, msg: MaximumBodySizeError)
|
||||
|
||||
proc raiseHttpCriticalError*(msg: string, code = Http400) {.
|
||||
noinline, noreturn, raises: [HttpCriticalError].} =
|
||||
raise (ref HttpCriticalError)(code: code, msg: msg)
|
||||
|
@ -135,9 +158,6 @@ proc raiseHttpDisconnectError*() {.
|
|||
noinline, noreturn, raises: [HttpDisconnectError].} =
|
||||
raise (ref HttpDisconnectError)(msg: "Remote peer disconnected")
|
||||
|
||||
proc raiseHttpDefect*(msg: string) {.noinline, noreturn.} =
|
||||
raise (ref HttpDefect)(msg: msg)
|
||||
|
||||
proc raiseHttpConnectionError*(msg: string) {.
|
||||
noinline, noreturn, raises: [HttpConnectionError].} =
|
||||
raise (ref HttpConnectionError)(msg: msg)
|
||||
|
@ -152,7 +172,15 @@ proc raiseHttpReadError*(msg: string) {.
|
|||
|
||||
proc raiseHttpProtocolError*(msg: string) {.
|
||||
noinline, noreturn, raises: [HttpProtocolError].} =
|
||||
raise (ref HttpProtocolError)(msg: msg)
|
||||
raise (ref HttpProtocolError)(code: Http400, msg: msg)
|
||||
|
||||
proc raiseHttpProtocolError*(code: HttpCode, msg: string) {.
|
||||
noinline, noreturn, raises: [HttpProtocolError].} =
|
||||
raise (ref HttpProtocolError)(code: code, msg: msg)
|
||||
|
||||
proc raiseHttpProtocolError*(msg: HttpMessage) {.
|
||||
noinline, noreturn, raises: [HttpProtocolError].} =
|
||||
raise (ref HttpProtocolError)(code: msg.code, msg: msg.message)
|
||||
|
||||
proc raiseHttpWriteError*(msg: string) {.
|
||||
noinline, noreturn, raises: [HttpWriteError].} =
|
||||
|
@ -178,6 +206,23 @@ template newHttpWriteError*(message: string): ref HttpWriteError =
|
|||
template newHttpUseClosedError*(): ref HttpUseClosedError =
|
||||
newException(HttpUseClosedError, "Connection was already closed")
|
||||
|
||||
func init*(t: typedesc[HttpMessage], code: HttpCode, message: string,
|
||||
contentType: MediaType): HttpMessage =
|
||||
HttpMessage(code: code, message: message, contentType: contentType)
|
||||
|
||||
func init*(t: typedesc[HttpMessage], code: HttpCode, message: string,
|
||||
contentType: string): HttpMessage =
|
||||
HttpMessage(code: code, message: message,
|
||||
contentType: MediaType.init(contentType))
|
||||
|
||||
func init*(t: typedesc[HttpMessage], code: HttpCode,
|
||||
message: string): HttpMessage =
|
||||
HttpMessage(code: code, message: message,
|
||||
contentType: MediaType.init("text/plain"))
|
||||
|
||||
func init*(t: typedesc[HttpMessage], code: HttpCode): HttpMessage =
|
||||
HttpMessage(code: code)
|
||||
|
||||
iterator queryParams*(query: string,
|
||||
flags: set[QueryParamsFlag] = {}): KeyValueTuple =
|
||||
## Iterate over url-encoded query string.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,7 +18,8 @@ import "."/[httptable, httpcommon, httpbodyrw]
|
|||
export asyncloop, httptable, httpcommon, httpbodyrw, asyncstream, httputils
|
||||
|
||||
const
|
||||
UnableToReadMultipartBody = "Unable to read multipart message body"
|
||||
UnableToReadMultipartBody = "Unable to read multipart message body, reason: "
|
||||
UnableToSendMultipartMessage = "Unable to send multipart message, reason: "
|
||||
|
||||
type
|
||||
MultiPartSource* {.pure.} = enum
|
||||
|
@ -69,7 +70,7 @@ type
|
|||
name*: string
|
||||
filename*: string
|
||||
|
||||
MultipartError* = object of HttpCriticalError
|
||||
MultipartError* = object of HttpProtocolError
|
||||
MultipartEOMError* = object of MultipartError
|
||||
|
||||
BChar* = byte | char
|
||||
|
@ -105,7 +106,7 @@ func setPartNames(part: var MultiPart): HttpResult[void] =
|
|||
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")
|
||||
return err("Content-Disposition header type is incorrect")
|
||||
for k, v in disp.fields(header.toOpenArrayByte(0, len(header) - 1)):
|
||||
case k.toLowerAscii()
|
||||
of "name":
|
||||
|
@ -171,8 +172,17 @@ proc new*[B: BChar](mpt: typedesc[MultiPartReaderRef],
|
|||
stream: stream, offset: 0, boundary: fboundary,
|
||||
buffer: newSeq[byte](partHeadersMaxSize))
|
||||
|
||||
template handleAsyncStreamReaderError(targ, excarg: untyped) =
|
||||
if targ.hasOverflow():
|
||||
raiseHttpRequestBodyTooLargeError()
|
||||
raiseHttpReadError(UnableToReadMultipartBody & $excarg.msg)
|
||||
|
||||
template handleAsyncStreamWriterError(targ, excarg: untyped) =
|
||||
targ.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpWriteError(UnableToSendMultipartMessage & $excarg.msg)
|
||||
|
||||
proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpReadError, HttpProtocolError]).} =
|
||||
doAssert(mpr.kind == MultiPartSource.Stream)
|
||||
if mpr.firstTime:
|
||||
try:
|
||||
|
@ -181,14 +191,11 @@ proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.
|
|||
mpr.firstTime = false
|
||||
if not(startsWith(mpr.buffer.toOpenArray(0, len(mpr.boundary) - 3),
|
||||
mpr.boundary.toOpenArray(2, len(mpr.boundary) - 1))):
|
||||
raiseHttpCriticalError("Unexpected boundary encountered")
|
||||
raiseHttpProtocolError(Http400, "Unexpected boundary encountered")
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
except AsyncStreamError:
|
||||
if mpr.stream.hasOverflow():
|
||||
raiseHttpCriticalError(MaximumBodySizeError, Http413)
|
||||
else:
|
||||
raiseHttpCriticalError(UnableToReadMultipartBody)
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamReaderError(mpr.stream, exc)
|
||||
|
||||
# Reading part's headers
|
||||
try:
|
||||
|
@ -202,9 +209,9 @@ proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.
|
|||
raise newException(MultipartEOMError,
|
||||
"End of multipart message")
|
||||
else:
|
||||
raiseHttpCriticalError("Incorrect multipart header found")
|
||||
raiseHttpProtocolError(Http400, "Incorrect multipart header found")
|
||||
if mpr.buffer[0] != 0x0D'u8 or mpr.buffer[1] != 0x0A'u8:
|
||||
raiseHttpCriticalError("Incorrect multipart boundary found")
|
||||
raiseHttpProtocolError(Http400, "Incorrect multipart boundary found")
|
||||
|
||||
# If two bytes are CRLF we are at the part beginning.
|
||||
# Reading part's headers
|
||||
|
@ -212,7 +219,7 @@ proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.
|
|||
HeadersMark)
|
||||
var headersList = parseHeaders(mpr.buffer.toOpenArray(0, res - 1), false)
|
||||
if headersList.failed():
|
||||
raiseHttpCriticalError("Incorrect multipart's headers found")
|
||||
raiseHttpProtocolError(Http400, "Incorrect multipart's headers found")
|
||||
inc(mpr.counter)
|
||||
|
||||
var part = MultiPart(
|
||||
|
@ -228,45 +235,35 @@ proc readPart*(mpr: MultiPartReaderRef): Future[MultiPart] {.
|
|||
|
||||
let sres = part.setPartNames()
|
||||
if sres.isErr():
|
||||
raiseHttpCriticalError($sres.error)
|
||||
raiseHttpProtocolError(Http400, $sres.error)
|
||||
return part
|
||||
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
except AsyncStreamError:
|
||||
if mpr.stream.hasOverflow():
|
||||
raiseHttpCriticalError(MaximumBodySizeError, Http413)
|
||||
else:
|
||||
raiseHttpCriticalError(UnableToReadMultipartBody)
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamReaderError(mpr.stream, exc)
|
||||
|
||||
proc getBody*(mp: MultiPart): Future[seq[byte]] {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpReadError, HttpProtocolError]).} =
|
||||
## Get multipart's ``mp`` value as sequence of bytes.
|
||||
case mp.kind
|
||||
of MultiPartSource.Stream:
|
||||
try:
|
||||
let res = await mp.stream.read()
|
||||
return res
|
||||
except AsyncStreamError:
|
||||
if mp.breader.hasOverflow():
|
||||
raiseHttpCriticalError(MaximumBodySizeError, Http413)
|
||||
else:
|
||||
raiseHttpCriticalError(UnableToReadMultipartBody)
|
||||
await mp.stream.read()
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamReaderError(mp.breader, exc)
|
||||
of MultiPartSource.Buffer:
|
||||
return mp.buffer
|
||||
mp.buffer
|
||||
|
||||
proc consumeBody*(mp: MultiPart) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpReadError, HttpProtocolError]).} =
|
||||
## Discard multipart's ``mp`` value.
|
||||
case mp.kind
|
||||
of MultiPartSource.Stream:
|
||||
try:
|
||||
discard await mp.stream.consume()
|
||||
except AsyncStreamError:
|
||||
if mp.breader.hasOverflow():
|
||||
raiseHttpCriticalError(MaximumBodySizeError, Http413)
|
||||
else:
|
||||
raiseHttpCriticalError(UnableToReadMultipartBody)
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamReaderError(mp.breader, exc)
|
||||
of MultiPartSource.Buffer:
|
||||
discard
|
||||
|
||||
|
@ -533,7 +530,7 @@ proc new*[B: BChar](mpt: typedesc[MultiPartWriterRef],
|
|||
|
||||
proc prepareHeaders(partMark: openArray[byte], name: string, filename: string,
|
||||
headers: HttpTable): string =
|
||||
const ContentDisposition = "Content-Disposition"
|
||||
const ContentDispositionHeader = "Content-Disposition"
|
||||
let qname =
|
||||
block:
|
||||
let res = quoteCheck(name)
|
||||
|
@ -546,10 +543,10 @@ proc prepareHeaders(partMark: openArray[byte], name: string, filename: string,
|
|||
res.get()
|
||||
var buffer = newString(len(partMark))
|
||||
copyMem(addr buffer[0], unsafeAddr partMark[0], len(partMark))
|
||||
buffer.add(ContentDisposition)
|
||||
buffer.add(ContentDispositionHeader)
|
||||
buffer.add(": ")
|
||||
if ContentDisposition in headers:
|
||||
buffer.add(headers.getString(ContentDisposition))
|
||||
if ContentDispositionHeader in headers:
|
||||
buffer.add(headers.getString(ContentDispositionHeader))
|
||||
buffer.add("\r\n")
|
||||
else:
|
||||
buffer.add("form-data; name=\"")
|
||||
|
@ -562,7 +559,7 @@ proc prepareHeaders(partMark: openArray[byte], name: string, filename: string,
|
|||
buffer.add("\r\n")
|
||||
|
||||
for k, v in headers.stringItems():
|
||||
if k != toLowerAscii(ContentDisposition):
|
||||
if k != ContentDispositionHeader:
|
||||
if len(v) > 0:
|
||||
buffer.add(k)
|
||||
buffer.add(": ")
|
||||
|
@ -572,7 +569,7 @@ proc prepareHeaders(partMark: openArray[byte], name: string, filename: string,
|
|||
buffer
|
||||
|
||||
proc begin*(mpw: MultiPartWriterRef) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Starts multipart message form and write approprate markers to output
|
||||
## stream.
|
||||
doAssert(mpw.kind == MultiPartSource.Stream)
|
||||
|
@ -580,10 +577,9 @@ proc begin*(mpw: MultiPartWriterRef) {.
|
|||
# write "--"
|
||||
try:
|
||||
await mpw.stream.write(mpw.beginMark)
|
||||
except AsyncStreamError:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError("Unable to start multipart message")
|
||||
mpw.state = MultiPartWriterState.MessageStarted
|
||||
mpw.state = MultiPartWriterState.MessageStarted
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc begin*(mpw: var MultiPartWriter) =
|
||||
## Starts multipart message form and write approprate markers to output
|
||||
|
@ -596,7 +592,7 @@ proc begin*(mpw: var MultiPartWriter) =
|
|||
|
||||
proc beginPart*(mpw: MultiPartWriterRef, name: string,
|
||||
filename: string, headers: HttpTable) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Starts part of multipart message and write appropriate ``headers`` to the
|
||||
## output stream.
|
||||
##
|
||||
|
@ -611,9 +607,8 @@ proc beginPart*(mpw: MultiPartWriterRef, name: string,
|
|||
try:
|
||||
await mpw.stream.write(buffer)
|
||||
mpw.state = MultiPartWriterState.PartStarted
|
||||
except AsyncStreamError:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError("Unable to start multipart part")
|
||||
except AsyncStreamError as exc:
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc beginPart*(mpw: var MultiPartWriter, name: string,
|
||||
filename: string, headers: HttpTable) =
|
||||
|
@ -632,7 +627,7 @@ proc beginPart*(mpw: var MultiPartWriter, name: string,
|
|||
mpw.state = MultiPartWriterState.PartStarted
|
||||
|
||||
proc write*(mpw: MultiPartWriterRef, pbytes: pointer, nbytes: int) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Write part's data ``data`` to the output stream.
|
||||
doAssert(mpw.kind == MultiPartSource.Stream)
|
||||
doAssert(mpw.state == MultiPartWriterState.PartStarted)
|
||||
|
@ -640,12 +635,10 @@ proc write*(mpw: MultiPartWriterRef, pbytes: pointer, nbytes: int) {.
|
|||
# write <chunk> of data
|
||||
await mpw.stream.write(pbytes, nbytes)
|
||||
except AsyncStreamError as exc:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError(
|
||||
"Unable to write multipart data, reason: " & $exc.msg)
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc write*(mpw: MultiPartWriterRef, data: seq[byte]) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Write part's data ``data`` to the output stream.
|
||||
doAssert(mpw.kind == MultiPartSource.Stream)
|
||||
doAssert(mpw.state == MultiPartWriterState.PartStarted)
|
||||
|
@ -653,12 +646,10 @@ proc write*(mpw: MultiPartWriterRef, data: seq[byte]) {.
|
|||
# write <chunk> of data
|
||||
await mpw.stream.write(data)
|
||||
except AsyncStreamError as exc:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError(
|
||||
"Unable to write multipart data, reason: " & $exc.msg)
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc write*(mpw: MultiPartWriterRef, data: string) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Write part's data ``data`` to the output stream.
|
||||
doAssert(mpw.kind == MultiPartSource.Stream)
|
||||
doAssert(mpw.state == MultiPartWriterState.PartStarted)
|
||||
|
@ -666,9 +657,7 @@ proc write*(mpw: MultiPartWriterRef, data: string) {.
|
|||
# write <chunk> of data
|
||||
await mpw.stream.write(data)
|
||||
except AsyncStreamError as exc:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError(
|
||||
"Unable to write multipart data, reason: " & $exc.msg)
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc write*(mpw: var MultiPartWriter, pbytes: pointer, nbytes: int) =
|
||||
## Write part's data ``data`` to the output stream.
|
||||
|
@ -692,7 +681,7 @@ proc write*(mpw: var MultiPartWriter, data: openArray[char]) =
|
|||
mpw.buffer.add(data.toOpenArrayByte(0, len(data) - 1))
|
||||
|
||||
proc finishPart*(mpw: MultiPartWriterRef) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Finish multipart's message part and send proper markers to output stream.
|
||||
doAssert(mpw.state == MultiPartWriterState.PartStarted)
|
||||
try:
|
||||
|
@ -700,9 +689,7 @@ proc finishPart*(mpw: MultiPartWriterRef) {.
|
|||
await mpw.stream.write(mpw.finishPartMark)
|
||||
mpw.state = MultiPartWriterState.PartFinished
|
||||
except AsyncStreamError as exc:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError(
|
||||
"Unable to finish multipart message part, reason: " & $exc.msg)
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc finishPart*(mpw: var MultiPartWriter) =
|
||||
## Finish multipart's message part and send proper markers to output stream.
|
||||
|
@ -713,7 +700,7 @@ proc finishPart*(mpw: var MultiPartWriter) =
|
|||
mpw.state = MultiPartWriterState.PartFinished
|
||||
|
||||
proc finish*(mpw: MultiPartWriterRef) {.
|
||||
async: (raises: [CancelledError, HttpCriticalError]).} =
|
||||
async: (raises: [CancelledError, HttpWriteError]).} =
|
||||
## Finish multipart's message form and send finishing markers to the output
|
||||
## stream.
|
||||
doAssert(mpw.kind == MultiPartSource.Stream)
|
||||
|
@ -723,9 +710,7 @@ proc finish*(mpw: MultiPartWriterRef) {.
|
|||
await mpw.stream.write(mpw.finishMark)
|
||||
mpw.state = MultiPartWriterState.MessageFinished
|
||||
except AsyncStreamError as exc:
|
||||
mpw.state = MultiPartWriterState.MessageFailure
|
||||
raiseHttpCriticalError(
|
||||
"Unable to finish multipart message, reason: " & $exc.msg)
|
||||
handleAsyncStreamWriterError(mpw, exc)
|
||||
|
||||
proc finish*(mpw: var MultiPartWriter): seq[byte] =
|
||||
## Finish multipart's message form and send finishing markers to the output
|
||||
|
|
|
@ -164,22 +164,21 @@ proc new*(htype: typedesc[SecureHttpServerRef],
|
|||
maxRequestBodySize: int = 1_048_576,
|
||||
dualstack = DualStackType.Auto
|
||||
): HttpResult[SecureHttpServerRef] {.
|
||||
deprecated: "raises missing from process callback".} =
|
||||
proc processCallback2(req: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
try:
|
||||
await processCallback(req)
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
except HttpResponseError as exc:
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
# Emulate 3.x behavior
|
||||
raise (ref HttpCriticalError)(msg: exc.msg, code: Http503)
|
||||
deprecated: "Callback could raise only CancelledError, annotate with " &
|
||||
"{.async: (raises: [CancelledError]).}".} =
|
||||
|
||||
proc wrap(req: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError]).} =
|
||||
try:
|
||||
await processCallback(req)
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
defaultResponse(exc)
|
||||
|
||||
SecureHttpServerRef.new(
|
||||
address = address,
|
||||
processCallback = processCallback2,
|
||||
processCallback = wrap,
|
||||
tlsPrivateKey = tlsPrivateKey,
|
||||
tlsCertificate = tlsCertificate,
|
||||
serverFlags = serverFlags,
|
||||
|
@ -194,4 +193,4 @@ proc new*(htype: typedesc[SecureHttpServerRef],
|
|||
maxHeadersSize = maxHeadersSize,
|
||||
maxRequestBodySize = maxRequestBodySize,
|
||||
dualstack = dualstack
|
||||
)
|
||||
)
|
||||
|
|
|
@ -85,7 +85,8 @@ suite "HTTP client testing suite":
|
|||
res
|
||||
|
||||
proc createServer(address: TransportAddress,
|
||||
process: HttpProcessCallback2, secure: bool): HttpServerRef =
|
||||
process: HttpProcessCallback2,
|
||||
secure: bool): HttpServerRef =
|
||||
let
|
||||
socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
serverFlags = {HttpServerFlags.Http11Pipeline}
|
||||
|
@ -128,18 +129,24 @@ suite "HTTP client testing suite":
|
|||
(MethodPatch, "/test/patch")
|
||||
]
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test/get", "/test/post", "/test/head", "/test/put",
|
||||
"/test/delete", "/test/trace", "/test/options", "/test/connect",
|
||||
"/test/patch", "/test/error":
|
||||
return await request.respond(Http200, request.uri.path)
|
||||
try:
|
||||
await request.respond(Http200, request.uri.path)
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
await request.respond(Http404, "Page not found")
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -195,7 +202,7 @@ suite "HTTP client testing suite":
|
|||
"LONGCHUNKRESPONSE")
|
||||
]
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
|
@ -203,46 +210,58 @@ suite "HTTP client testing suite":
|
|||
var response = request.getResponse()
|
||||
var data = createBigMessage(ResponseTests[0][4], ResponseTests[0][2])
|
||||
response.status = Http200
|
||||
await response.sendBody(data)
|
||||
return response
|
||||
try:
|
||||
await response.sendBody(data)
|
||||
except HttpWriteError as exc:
|
||||
return defaultResponse(exc)
|
||||
response
|
||||
of "/test/long_size_response":
|
||||
var response = request.getResponse()
|
||||
var data = createBigMessage(ResponseTests[1][4], ResponseTests[1][2])
|
||||
response.status = Http200
|
||||
await response.sendBody(data)
|
||||
return response
|
||||
try:
|
||||
await response.sendBody(data)
|
||||
except HttpWriteError as exc:
|
||||
return defaultResponse(exc)
|
||||
response
|
||||
of "/test/short_chunked_response":
|
||||
var response = request.getResponse()
|
||||
var data = createBigMessage(ResponseTests[2][4], ResponseTests[2][2])
|
||||
response.status = Http200
|
||||
await response.prepare()
|
||||
var offset = 0
|
||||
while true:
|
||||
if len(data) == offset:
|
||||
break
|
||||
let toWrite = min(1024, len(data) - offset)
|
||||
await response.sendChunk(addr data[offset], toWrite)
|
||||
offset = offset + toWrite
|
||||
await response.finish()
|
||||
return response
|
||||
try:
|
||||
await response.prepare()
|
||||
var offset = 0
|
||||
while true:
|
||||
if len(data) == offset:
|
||||
break
|
||||
let toWrite = min(1024, len(data) - offset)
|
||||
await response.sendChunk(addr data[offset], toWrite)
|
||||
offset = offset + toWrite
|
||||
await response.finish()
|
||||
except HttpWriteError as exc:
|
||||
return defaultResponse(exc)
|
||||
response
|
||||
of "/test/long_chunked_response":
|
||||
var response = request.getResponse()
|
||||
var data = createBigMessage(ResponseTests[3][4], ResponseTests[3][2])
|
||||
response.status = Http200
|
||||
await response.prepare()
|
||||
var offset = 0
|
||||
while true:
|
||||
if len(data) == offset:
|
||||
break
|
||||
let toWrite = min(1024, len(data) - offset)
|
||||
await response.sendChunk(addr data[offset], toWrite)
|
||||
offset = offset + toWrite
|
||||
await response.finish()
|
||||
return response
|
||||
try:
|
||||
await response.prepare()
|
||||
var offset = 0
|
||||
while true:
|
||||
if len(data) == offset:
|
||||
break
|
||||
let toWrite = min(1024, len(data) - offset)
|
||||
await response.sendChunk(addr data[offset], toWrite)
|
||||
offset = offset + toWrite
|
||||
await response.finish()
|
||||
except HttpWriteError as exc:
|
||||
return defaultResponse(exc)
|
||||
response
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
defaultResponse()
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -311,21 +330,26 @@ suite "HTTP client testing suite":
|
|||
(MethodPost, "/test/big_request", 262400)
|
||||
]
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test/big_request":
|
||||
if request.hasBody():
|
||||
let body = await request.getBody()
|
||||
let digest = $secureHash(string.fromBytes(body))
|
||||
return await request.respond(Http200, digest)
|
||||
else:
|
||||
return await request.respond(Http400, "Missing content body")
|
||||
try:
|
||||
if request.hasBody():
|
||||
let body = await request.getBody()
|
||||
let digest = $secureHash(string.fromBytes(body))
|
||||
await request.respond(Http200, digest)
|
||||
else:
|
||||
await request.respond(Http400, "Missing content body")
|
||||
except HttpProtocolError as exc:
|
||||
defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
defaultResponse()
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -381,21 +405,27 @@ suite "HTTP client testing suite":
|
|||
(MethodPost, "/test/big_chunk_request", 262400)
|
||||
]
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test/big_chunk_request":
|
||||
if request.hasBody():
|
||||
let body = await request.getBody()
|
||||
let digest = $secureHash(string.fromBytes(body))
|
||||
return await request.respond(Http200, digest)
|
||||
else:
|
||||
return await request.respond(Http400, "Missing content body")
|
||||
try:
|
||||
if request.hasBody():
|
||||
let
|
||||
body = await request.getBody()
|
||||
digest = $secureHash(string.fromBytes(body))
|
||||
await request.respond(Http200, digest)
|
||||
else:
|
||||
await request.respond(Http400, "Missing content body")
|
||||
except HttpProtocolError as exc:
|
||||
defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
defaultResponse()
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -455,23 +485,28 @@ suite "HTTP client testing suite":
|
|||
]
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test/post/urlencoded_size", "/test/post/urlencoded_chunked":
|
||||
if request.hasBody():
|
||||
var postTable = await request.post()
|
||||
let body = postTable.getString("field1") & ":" &
|
||||
postTable.getString("field2") & ":" &
|
||||
postTable.getString("field3")
|
||||
return await request.respond(Http200, body)
|
||||
else:
|
||||
return await request.respond(Http400, "Missing content body")
|
||||
try:
|
||||
if request.hasBody():
|
||||
var postTable = await request.post()
|
||||
let body = postTable.getString("field1") & ":" &
|
||||
postTable.getString("field2") & ":" &
|
||||
postTable.getString("field3")
|
||||
await request.respond(Http200, body)
|
||||
else:
|
||||
await request.respond(Http400, "Missing content body")
|
||||
except HttpTransportError as exc:
|
||||
defaultResponse(exc)
|
||||
except HttpProtocolError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
defaultResponse()
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -554,23 +589,28 @@ suite "HTTP client testing suite":
|
|||
]
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test/post/multipart_size", "/test/post/multipart_chunked":
|
||||
if request.hasBody():
|
||||
var postTable = await request.post()
|
||||
let body = postTable.getString("field1") & ":" &
|
||||
postTable.getString("field2") & ":" &
|
||||
postTable.getString("field3")
|
||||
return await request.respond(Http200, body)
|
||||
else:
|
||||
return await request.respond(Http400, "Missing content body")
|
||||
try:
|
||||
if request.hasBody():
|
||||
var postTable = await request.post()
|
||||
let body = postTable.getString("field1") & ":" &
|
||||
postTable.getString("field2") & ":" &
|
||||
postTable.getString("field3")
|
||||
await request.respond(Http200, body)
|
||||
else:
|
||||
await request.respond(Http400, "Missing content body")
|
||||
except HttpProtocolError as exc:
|
||||
defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
defaultResponse()
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -649,26 +689,29 @@ suite "HTTP client testing suite":
|
|||
var lastAddress: Uri
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/":
|
||||
return await request.redirect(Http302, "/redirect/1")
|
||||
of "/redirect/1":
|
||||
return await request.redirect(Http302, "/next/redirect/2")
|
||||
of "/next/redirect/2":
|
||||
return await request.redirect(Http302, "redirect/3")
|
||||
of "/next/redirect/redirect/3":
|
||||
return await request.redirect(Http302, "next/redirect/4")
|
||||
of "/next/redirect/redirect/next/redirect/4":
|
||||
return await request.redirect(Http302, lastAddress)
|
||||
of "/final/5":
|
||||
return await request.respond(Http200, "ok-5")
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
case request.uri.path
|
||||
of "/":
|
||||
await request.redirect(Http302, "/redirect/1")
|
||||
of "/redirect/1":
|
||||
await request.redirect(Http302, "/next/redirect/2")
|
||||
of "/next/redirect/2":
|
||||
await request.redirect(Http302, "redirect/3")
|
||||
of "/next/redirect/redirect/3":
|
||||
await request.redirect(Http302, "next/redirect/4")
|
||||
of "/next/redirect/redirect/next/redirect/4":
|
||||
await request.redirect(Http302, lastAddress)
|
||||
of "/final/5":
|
||||
await request.respond(Http200, "ok-5")
|
||||
else:
|
||||
await request.respond(Http404, "Page not found")
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -706,8 +749,8 @@ suite "HTTP client testing suite":
|
|||
|
||||
proc testSendCancelLeaksTest(secure: bool): Future[bool] {.async.} =
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
return defaultResponse()
|
||||
async: (raises: [CancelledError]).} =
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -756,8 +799,8 @@ suite "HTTP client testing suite":
|
|||
|
||||
proc testOpenCancelLeaksTest(secure: bool): Future[bool] {.async.} =
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
return defaultResponse()
|
||||
async: (raises: [CancelledError]).} =
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
@ -868,20 +911,23 @@ suite "HTTP client testing suite":
|
|||
(data2.status, data2.data.bytesToString(), count)]
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/keep":
|
||||
let headers = HttpTable.init([("connection", "keep-alive")])
|
||||
return await request.respond(Http200, "ok", headers = headers)
|
||||
of "/drop":
|
||||
let headers = HttpTable.init([("connection", "close")])
|
||||
return await request.respond(Http200, "ok", headers = headers)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
case request.uri.path
|
||||
of "/keep":
|
||||
let headers = HttpTable.init([("connection", "keep-alive")])
|
||||
await request.respond(Http200, "ok", headers = headers)
|
||||
of "/drop":
|
||||
let headers = HttpTable.init([("connection", "close")])
|
||||
await request.respond(Http200, "ok", headers = headers)
|
||||
else:
|
||||
await request.respond(Http404, "Page not found")
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, false)
|
||||
server.start()
|
||||
|
@ -1004,16 +1050,19 @@ suite "HTTP client testing suite":
|
|||
return (data.status, data.data.bytesToString(), 0)
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test":
|
||||
return await request.respond(Http200, "ok")
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
case request.uri.path
|
||||
of "/test":
|
||||
await request.respond(Http200, "ok")
|
||||
else:
|
||||
await request.respond(Http404, "Page not found")
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, false)
|
||||
server.start()
|
||||
|
@ -1064,19 +1113,22 @@ suite "HTTP client testing suite":
|
|||
return (data.status, data.data.bytesToString(), 0)
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
case request.uri.path
|
||||
of "/test":
|
||||
return await request.respond(Http200, "ok")
|
||||
of "/keep-test":
|
||||
let headers = HttpTable.init([("Connection", "keep-alive")])
|
||||
return await request.respond(Http200, "not-alive", headers)
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
case request.uri.path
|
||||
of "/test":
|
||||
await request.respond(Http200, "ok")
|
||||
of "/keep-test":
|
||||
let headers = HttpTable.init([("Connection", "keep-alive")])
|
||||
await request.respond(Http200, "not-alive", headers)
|
||||
else:
|
||||
await request.respond(Http404, "Page not found")
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, false)
|
||||
server.start()
|
||||
|
@ -1180,58 +1232,61 @@ suite "HTTP client testing suite":
|
|||
true
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
if request.uri.path.startsWith("/test/single/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in SingleGoodTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(SingleGoodTests[index][1])
|
||||
return response
|
||||
elif request.uri.path.startsWith("/test/multiple/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in MultipleGoodTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(MultipleGoodTests[index][1])
|
||||
return response
|
||||
elif request.uri.path.startsWith("/test/overflow/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in OverflowTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(OverflowTests[index][1])
|
||||
return response
|
||||
else:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
try:
|
||||
if request.uri.path.startsWith("/test/single/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in SingleGoodTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(SingleGoodTests[index][1])
|
||||
response
|
||||
elif request.uri.path.startsWith("/test/multiple/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in MultipleGoodTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(MultipleGoodTests[index][1])
|
||||
response
|
||||
elif request.uri.path.startsWith("/test/overflow/"):
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for index, value in OverflowTests.pairs():
|
||||
if value[0] == request.uri.path:
|
||||
res = index
|
||||
break
|
||||
res
|
||||
if index < 0:
|
||||
return await request.respond(Http404, "Page not found")
|
||||
var response = request.getResponse()
|
||||
response.status = Http200
|
||||
await response.sendBody(OverflowTests[index][1])
|
||||
response
|
||||
else:
|
||||
defaultResponse()
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
var server = createServer(initTAddress("127.0.0.1:0"), process, secure)
|
||||
server.start()
|
||||
|
|
|
@ -64,7 +64,7 @@ suite "HTTP server testing suite":
|
|||
proc testTooBigBodyChunked(operation: TooBigTest): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
try:
|
||||
|
@ -77,13 +77,15 @@ suite "HTTP server testing suite":
|
|||
let ptable {.used.} = await request.post()
|
||||
of PostMultipartTest:
|
||||
let ptable {.used.} = await request.post()
|
||||
except HttpCriticalError as exc:
|
||||
defaultResponse()
|
||||
except HttpTransportError as exc:
|
||||
defaultResponse(exc)
|
||||
except HttpProtocolError as exc:
|
||||
if exc.code == Http413:
|
||||
serverRes = true
|
||||
# Reraising exception, because processor should properly handle it.
|
||||
raise exc
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -128,14 +130,17 @@ suite "HTTP server testing suite":
|
|||
proc testTimeout(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
if r.error.kind == HttpServerError.TimeoutError:
|
||||
serverRes = true
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"),
|
||||
|
@ -158,14 +163,17 @@ suite "HTTP server testing suite":
|
|||
proc testEmpty(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
if r.error.kind == HttpServerError.CriticalError:
|
||||
if r.error.kind == HttpServerError.ProtocolError:
|
||||
serverRes = true
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"),
|
||||
|
@ -188,14 +196,17 @@ suite "HTTP server testing suite":
|
|||
proc testTooBig(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
if r.error.error == HttpServerError.CriticalError:
|
||||
if r.error.error == HttpServerError.ProtocolError:
|
||||
serverRes = true
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -219,13 +230,11 @@ suite "HTTP server testing suite":
|
|||
proc testTooBigBody(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
if r.isOk():
|
||||
discard
|
||||
else:
|
||||
if r.error.error == HttpServerError.CriticalError:
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isErr():
|
||||
if r.error.error == HttpServerError.ProtocolError:
|
||||
serverRes = true
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -266,7 +275,7 @@ suite "HTTP server testing suite":
|
|||
proc testQuery(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
var kres = newSeq[string]()
|
||||
|
@ -274,11 +283,14 @@ suite "HTTP server testing suite":
|
|||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -296,10 +308,9 @@ suite "HTTP server testing suite":
|
|||
"GET /?a=%D0%9F&%D0%A4=%D0%91&b=%D0%A6&c=%D0%AE HTTP/1.0\r\n\r\n")
|
||||
await server.stop()
|
||||
await server.closeWait()
|
||||
let r = serverRes and
|
||||
(data1.find("TEST_OK:a:1:a:2:b:3:c:4") >= 0) and
|
||||
(data2.find("TEST_OK:a:П:b:Ц:c:Ю:Ф:Б") >= 0)
|
||||
return r
|
||||
serverRes and
|
||||
(data1.find("TEST_OK:a:1:a:2:b:3:c:4") >= 0) and
|
||||
(data2.find("TEST_OK:a:П:b:Ц:c:Ю:Ф:Б") >= 0)
|
||||
|
||||
check waitFor(testQuery()) == true
|
||||
|
||||
|
@ -307,7 +318,7 @@ suite "HTTP server testing suite":
|
|||
proc testHeaders(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
var kres = newSeq[string]()
|
||||
|
@ -315,11 +326,14 @@ suite "HTTP server testing suite":
|
|||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -351,21 +365,30 @@ suite "HTTP server testing suite":
|
|||
proc testPostUrl(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
var kres = newSeq[string]()
|
||||
let request = r.get()
|
||||
if request.meth in PostMethods:
|
||||
let post = await request.post()
|
||||
let post =
|
||||
try:
|
||||
await request.post()
|
||||
except HttpProtocolError as exc:
|
||||
return defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
return defaultResponse(exc)
|
||||
for k, v in post.stringItems():
|
||||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
serverRes = true
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -395,21 +418,30 @@ suite "HTTP server testing suite":
|
|||
proc testPostUrl2(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
var kres = newSeq[string]()
|
||||
let request = r.get()
|
||||
if request.meth in PostMethods:
|
||||
let post = await request.post()
|
||||
let post =
|
||||
try:
|
||||
await request.post()
|
||||
except HttpProtocolError as exc:
|
||||
return defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
return defaultResponse(exc)
|
||||
for k, v in post.stringItems():
|
||||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
serverRes = true
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -440,21 +472,30 @@ suite "HTTP server testing suite":
|
|||
proc testPostMultipart(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
var kres = newSeq[string]()
|
||||
let request = r.get()
|
||||
if request.meth in PostMethods:
|
||||
let post = await request.post()
|
||||
let post =
|
||||
try:
|
||||
await request.post()
|
||||
except HttpProtocolError as exc:
|
||||
return defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
return defaultResponse(exc)
|
||||
for k, v in post.stringItems():
|
||||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
serverRes = true
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -496,21 +537,31 @@ suite "HTTP server testing suite":
|
|||
proc testPostMultipart2(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
var kres = newSeq[string]()
|
||||
let request = r.get()
|
||||
if request.meth in PostMethods:
|
||||
let post = await request.post()
|
||||
let post =
|
||||
try:
|
||||
await request.post()
|
||||
except HttpProtocolError as exc:
|
||||
return defaultResponse(exc)
|
||||
except HttpTransportError as exc:
|
||||
return defaultResponse(exc)
|
||||
for k, v in post.stringItems():
|
||||
kres.add(k & ":" & v)
|
||||
sort(kres)
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -566,16 +617,19 @@ suite "HTTP server testing suite":
|
|||
var count = 0
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
inc(count)
|
||||
if count == ClientsCount:
|
||||
eventWait.fire()
|
||||
await eventContinue.wait()
|
||||
return await request.respond(Http404, "", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http404, "", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -1230,23 +1284,26 @@ suite "HTTP server testing suite":
|
|||
proc testPostMultipart2(): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
let response = request.getResponse()
|
||||
await response.prepareSSE()
|
||||
await response.send("event: event1\r\ndata: data1\r\n\r\n")
|
||||
await response.send("event: event2\r\ndata: data2\r\n\r\n")
|
||||
await response.sendEvent("event3", "data3")
|
||||
await response.sendEvent("event4", "data4")
|
||||
await response.send("data: data5\r\n\r\n")
|
||||
await response.sendEvent("", "data6")
|
||||
await response.finish()
|
||||
serverRes = true
|
||||
return response
|
||||
try:
|
||||
await response.prepareSSE()
|
||||
await response.send("event: event1\r\ndata: data1\r\n\r\n")
|
||||
await response.send("event: event2\r\ndata: data2\r\n\r\n")
|
||||
await response.sendEvent("event3", "data3")
|
||||
await response.sendEvent("event4", "data4")
|
||||
await response.send("data: data5\r\n\r\n")
|
||||
await response.sendEvent("", "data6")
|
||||
await response.finish()
|
||||
serverRes = true
|
||||
response
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let res = HttpServerRef.new(initTAddress("127.0.0.1:0"), process,
|
||||
|
@ -1306,12 +1363,15 @@ suite "HTTP server testing suite":
|
|||
]
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
for test in TestMessages:
|
||||
let
|
||||
|
@ -1360,12 +1420,15 @@ suite "HTTP server testing suite":
|
|||
TestRequest = "GET /httpdebug HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
|
||||
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
proc client(address: TransportAddress,
|
||||
data: string): Future[StreamTransport] {.async.} =
|
||||
|
|
|
@ -108,15 +108,18 @@ suite "Secure HTTP server testing suite":
|
|||
proc testHTTPS(address: TransportAddress): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
serverRes = true
|
||||
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||
HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
serverRes = false
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = false
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let serverFlags = {Secure}
|
||||
|
@ -146,16 +149,18 @@ suite "Secure HTTP server testing suite":
|
|||
var serverRes = false
|
||||
var testFut = newFuture[void]()
|
||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||
async: (raises: [CancelledError, HttpResponseError]).} =
|
||||
async: (raises: [CancelledError]).} =
|
||||
if r.isOk():
|
||||
let request = r.get()
|
||||
serverRes = false
|
||||
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||
HttpTable.init())
|
||||
try:
|
||||
await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||
HttpTable.init())
|
||||
except HttpWriteError as exc:
|
||||
defaultResponse(exc)
|
||||
else:
|
||||
serverRes = true
|
||||
testFut.complete()
|
||||
return defaultResponse()
|
||||
defaultResponse()
|
||||
|
||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||
let serverFlags = {Secure}
|
||||
|
|
Loading…
Reference in New Issue