nim-websock/ws/http/common.nim
Dmitriy Ryajov 64da1a4344
Rework http (#38)
* wip

* wip

* move http under ws folder

* use asyctest

* wip

* wip

* rework response sending

* make example work with latest changes

* wip request/response

* misc

* fix example to use new http layer

* pass tls flags to client

* more cleanup

* unused imports

* more unsused imports

* better headers

* add helpre sendError

* export sendError

* attach selected proto to session

* move proto to session

* handle unsupported version

* fix tests

* comment out for now

* fix utf8 tests

* allow tests to be ran in tls

* misc

* use Port type

* add tls flags

* better api

* run tls tests

* fix tests on windows

* allow running tests with tls

* mic

* wip

* fix autobahn ci

* handle close

* cleanup

* logging and error handling

* remove old stream
2021-05-31 20:39:14 -06:00

123 lines
3.0 KiB
Nim

{.push raises: [Defect].}
import std/[uri]
import pkg/[
chronos,
httputils,
stew/byteutils,
chronicles]
import pkg/[
chronos/apps/http/httptable,
chronos/streams/tlsstream]
export httputils, httptable, tlsstream, uri
const
MaxHttpHeadersSize* = 8192 # maximum size of HTTP headers in octets
MaxHttpRequestSize* = 128 * 1024 # maximum size of HTTP body in octets
HttpHeadersTimeout* = 120.seconds # timeout for receiving headers (120 sec)
HeaderSep* = @[byte('\c'), byte('\L'), byte('\c'), byte('\L')]
CRLF* = "\r\n"
type
ReqStatus* {.pure.} = enum
Success, Error, ErrorFailure
HttpCommon* = ref object of RootObj
headers*: HttpTable
code*: int
version*: HttpVersion
stream*: AsyncStream
HttpRequest* = ref object of HttpCommon
uri*: Uri
meth*: HttpMethod
# TODO: add useful response params, like body len
HttpResponse* = ref object of HttpCommon
reason*: string
HttpError* = object of CatchableError
HttpHeaderError* = HttpError
proc closeTransp*(transp: StreamTransport) {.async.} =
if not transp.closed():
await transp.closeWait()
proc closeStream*(stream: AsyncStreamRW) {.async.} =
if not stream.closed():
await stream.closeWait()
proc closeWait*(stream: AsyncStream) {.async.} =
await allFutures(
stream.reader.tsource.closeTransp(),
stream.reader.closeStream(),
stream.writer.closeStream()
)
proc sendResponse*(
request: HttpRequest,
code: HttpCode,
headers: HttpTables = HttpTable.init(),
data: seq[byte] = @[],
version = HttpVersion11,
content = "") {.async.} =
## Send response
##
var headers = headers
var response: string = $version
response.add(" ")
response.add($code)
response.add(CRLF)
response.add("Date: " & httpDate() & CRLF)
if data.len > 0:
if headers.getInt("Content-Length").int != data.len:
warn "Wrong content length header, overriding"
headers.set("Content-Length", $data.len)
if headers.getString("Content-Type") != content:
headers.set("Content-Type",
if content.len > 0: content else: "text/html")
for key, val in headers.stringItems(true):
response.add(key)
response.add(": ")
response.add(val)
response.add(CRLF)
response.add(CRLF)
await request.stream.writer.write(
response.toBytes() & data)
proc sendResponse*(
request: HttpRequest,
code: HttpCode,
headers: HttpTables = HttpTable.init(),
data: string,
version = HttpVersion11,
content = ""): Future[void] =
request.sendResponse(code, headers, data.toBytes(), version, content)
proc sendError*(
stream: AsyncStreamWriter,
code: HttpCode,
version = HttpVersion11) {.async.} =
let content = $code
var response: string = $version
response.add(" ")
response.add(content & CRLF)
response.add(CRLF)
await stream.write(
response.toBytes() &
content.toBytes())
proc sendError*(
request: HttpRequest,
code: HttpCode,
version = HttpVersion11): Future[void] =
request.stream.writer.sendError(code, version)