2021-05-10 07:26:36 +00:00
|
|
|
#
|
|
|
|
# Chronos HTTP/S body reader/writer
|
|
|
|
# (c) Copyright 2021-Present
|
|
|
|
# Status Research & Development GmbH
|
|
|
|
#
|
|
|
|
# Licensed under either of
|
|
|
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
|
|
|
# MIT license (LICENSE-MIT)
|
2023-11-21 10:01:44 +00:00
|
|
|
|
|
|
|
{.push raises: [].}
|
|
|
|
|
2021-05-10 07:26:36 +00:00
|
|
|
import ../../asyncloop, ../../asyncsync
|
|
|
|
import ../../streams/[asyncstream, boundstream]
|
2021-08-06 10:13:55 +00:00
|
|
|
import httpcommon
|
2021-05-10 07:26:36 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
HttpBodyReaderTrackerName* = "http.body.reader"
|
|
|
|
## HTTP body reader leaks tracker name
|
|
|
|
HttpBodyWriterTrackerName* = "http.body.writer"
|
|
|
|
## HTTP body writer leaks tracker name
|
|
|
|
|
|
|
|
type
|
|
|
|
HttpBodyReader* = ref object of AsyncStreamReader
|
2021-08-06 10:13:55 +00:00
|
|
|
bstate*: HttpState
|
2021-05-10 07:26:36 +00:00
|
|
|
streams*: seq[AsyncStreamReader]
|
|
|
|
|
|
|
|
HttpBodyWriter* = ref object of AsyncStreamWriter
|
2021-08-06 10:13:55 +00:00
|
|
|
bstate*: HttpState
|
2021-05-10 07:26:36 +00:00
|
|
|
streams*: seq[AsyncStreamWriter]
|
|
|
|
|
|
|
|
proc newHttpBodyReader*(streams: varargs[AsyncStreamReader]): HttpBodyReader =
|
|
|
|
## HttpBodyReader is AsyncStreamReader which holds references to all the
|
|
|
|
## ``streams``. Also on close it will close all the ``streams``.
|
|
|
|
##
|
|
|
|
## First stream in sequence will be used as a source.
|
|
|
|
doAssert(len(streams) > 0, "At least one stream must be added")
|
2021-08-06 10:13:55 +00:00
|
|
|
var res = HttpBodyReader(bstate: HttpState.Alive, streams: @streams)
|
2021-05-10 07:26:36 +00:00
|
|
|
res.init(streams[0])
|
2023-07-14 10:35:08 +00:00
|
|
|
trackCounter(HttpBodyReaderTrackerName)
|
2021-05-10 07:26:36 +00:00
|
|
|
res
|
|
|
|
|
2023-11-21 10:01:44 +00:00
|
|
|
proc closeWait*(bstream: HttpBodyReader) {.async: (raises: []).} =
|
2021-05-10 07:26:36 +00:00
|
|
|
## Close and free resource allocated by body reader.
|
2021-08-06 10:13:55 +00:00
|
|
|
if bstream.bstate == HttpState.Alive:
|
|
|
|
bstream.bstate = HttpState.Closing
|
2024-01-04 15:17:42 +00:00
|
|
|
var res = newSeq[Future[void].Raising([])]()
|
2021-08-06 10:13:55 +00:00
|
|
|
# We closing streams in reversed order because stream at position [0], uses
|
|
|
|
# data from stream at position [1].
|
|
|
|
for index in countdown((len(bstream.streams) - 1), 0):
|
|
|
|
res.add(bstream.streams[index].closeWait())
|
2023-09-15 16:38:39 +00:00
|
|
|
res.add(procCall(closeWait(AsyncStreamReader(bstream))))
|
|
|
|
await noCancel(allFutures(res))
|
2021-08-06 10:13:55 +00:00
|
|
|
bstream.bstate = HttpState.Closed
|
2023-07-14 10:35:08 +00:00
|
|
|
untrackCounter(HttpBodyReaderTrackerName)
|
2021-05-10 07:26:36 +00:00
|
|
|
|
|
|
|
proc newHttpBodyWriter*(streams: varargs[AsyncStreamWriter]): HttpBodyWriter =
|
|
|
|
## HttpBodyWriter is AsyncStreamWriter which holds references to all the
|
|
|
|
## ``streams``. Also on close it will close all the ``streams``.
|
|
|
|
##
|
|
|
|
## First stream in sequence will be used as a destination.
|
|
|
|
doAssert(len(streams) > 0, "At least one stream must be added")
|
2021-08-06 10:13:55 +00:00
|
|
|
var res = HttpBodyWriter(bstate: HttpState.Alive, streams: @streams)
|
2021-05-10 07:26:36 +00:00
|
|
|
res.init(streams[0])
|
2023-07-14 10:35:08 +00:00
|
|
|
trackCounter(HttpBodyWriterTrackerName)
|
2021-05-10 07:26:36 +00:00
|
|
|
res
|
|
|
|
|
2023-11-21 10:01:44 +00:00
|
|
|
proc closeWait*(bstream: HttpBodyWriter) {.async: (raises: []).} =
|
2021-05-10 07:26:36 +00:00
|
|
|
## Close and free all the resources allocated by body writer.
|
2021-08-06 10:13:55 +00:00
|
|
|
if bstream.bstate == HttpState.Alive:
|
|
|
|
bstream.bstate = HttpState.Closing
|
2024-01-04 15:17:42 +00:00
|
|
|
var res = newSeq[Future[void].Raising([])]()
|
2021-08-06 10:13:55 +00:00
|
|
|
for index in countdown(len(bstream.streams) - 1, 0):
|
|
|
|
res.add(bstream.streams[index].closeWait())
|
2023-09-15 16:38:39 +00:00
|
|
|
await noCancel(allFutures(res))
|
2021-08-06 10:13:55 +00:00
|
|
|
await procCall(closeWait(AsyncStreamWriter(bstream)))
|
|
|
|
bstream.bstate = HttpState.Closed
|
2023-07-14 10:35:08 +00:00
|
|
|
untrackCounter(HttpBodyWriterTrackerName)
|
2021-05-10 07:26:36 +00:00
|
|
|
|
2023-11-21 10:01:44 +00:00
|
|
|
proc hasOverflow*(bstream: HttpBodyReader): bool =
|
2021-05-10 07:26:36 +00:00
|
|
|
if len(bstream.streams) == 1:
|
|
|
|
# If HttpBodyReader has only one stream it has ``BoundedStreamReader``, in
|
|
|
|
# such case its impossible to get more bytes then expected amount.
|
|
|
|
false
|
|
|
|
else:
|
|
|
|
# If HttpBodyReader has two or more streams, we check if
|
|
|
|
# ``BoundedStreamReader`` at EOF.
|
|
|
|
if bstream.streams[0].atEof():
|
|
|
|
for i in 1 ..< len(bstream.streams):
|
2023-03-16 09:46:24 +00:00
|
|
|
if not(bstream.streams[i].atEof()):
|
2021-05-10 07:26:36 +00:00
|
|
|
return true
|
|
|
|
false
|
|
|
|
else:
|
|
|
|
false
|
2021-08-06 10:13:55 +00:00
|
|
|
|
2023-11-21 10:01:44 +00:00
|
|
|
proc closed*(bstream: HttpBodyReader | HttpBodyWriter): bool =
|
2021-08-06 10:13:55 +00:00
|
|
|
bstream.bstate != HttpState.Alive
|