Split HTTPS and HTTP servers. (#165)
* Split HTTPS and HTTP servers. * Fix review commens
This commit is contained in:
parent
0b78606e41
commit
c8eefb9382
|
@ -6,5 +6,5 @@
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
import apps/http/httpserver
|
import apps/http/httpserver, apps/http/shttpserver
|
||||||
export httpserver
|
export httpserver, shttpserver
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
import std/[tables, options, uri, strutils]
|
import std/[tables, options, uri, strutils]
|
||||||
import stew/[results, base10], httputils
|
import stew/[results, base10], httputils
|
||||||
import ../../asyncloop, ../../asyncsync
|
import ../../asyncloop, ../../asyncsync
|
||||||
import ../../streams/[asyncstream, boundstream, chunkstream, tlsstream]
|
import ../../streams/[asyncstream, boundstream, chunkstream]
|
||||||
import httptable, httpcommon, multipart
|
import httptable, httpcommon, multipart
|
||||||
export httptable, httpcommon, httputils, multipart, tlsstream, asyncstream,
|
export httptable, httpcommon, httputils, multipart, asyncstream,
|
||||||
uri, tables, options, results
|
uri, tables, options, results
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -46,6 +46,10 @@ type
|
||||||
HttpProcessCallback* =
|
HttpProcessCallback* =
|
||||||
proc(req: RequestFence): Future[HttpResponseRef] {.gcsafe.}
|
proc(req: RequestFence): Future[HttpResponseRef] {.gcsafe.}
|
||||||
|
|
||||||
|
HttpConnectionCallback* =
|
||||||
|
proc(server: HttpServerRef,
|
||||||
|
transp: StreamTransport): Future[HttpConnectionRef] {.gcsafe.}
|
||||||
|
|
||||||
HttpServer* = object of RootObj
|
HttpServer* = object of RootObj
|
||||||
instance*: StreamServer
|
instance*: StreamServer
|
||||||
address*: TransportAddress
|
address*: TransportAddress
|
||||||
|
@ -56,16 +60,15 @@ type
|
||||||
serverIdent*: string
|
serverIdent*: string
|
||||||
flags*: set[HttpServerFlags]
|
flags*: set[HttpServerFlags]
|
||||||
socketFlags*: set[ServerFlags]
|
socketFlags*: set[ServerFlags]
|
||||||
secureFlags*: set[TLSFlags]
|
|
||||||
connections*: Table[string, Future[void]]
|
connections*: Table[string, Future[void]]
|
||||||
acceptLoop*: Future[void]
|
acceptLoop*: Future[void]
|
||||||
lifetime*: Future[void]
|
lifetime*: Future[void]
|
||||||
headersTimeout: Duration
|
headersTimeout: Duration
|
||||||
|
bufferSize: int
|
||||||
maxHeadersSize: int
|
maxHeadersSize: int
|
||||||
maxRequestBodySize: int
|
maxRequestBodySize: int
|
||||||
processCallback: HttpProcessCallback
|
processCallback: HttpProcessCallback
|
||||||
tlsPrivateKey: TLSPrivateKey
|
createConnCallback: HttpConnectionCallback
|
||||||
tlsCertificate: TLSCertificate
|
|
||||||
|
|
||||||
HttpServerRef* = ref HttpServer
|
HttpServerRef* = ref HttpServer
|
||||||
|
|
||||||
|
@ -103,9 +106,8 @@ type
|
||||||
HttpConnection* = object of RootObj
|
HttpConnection* = object of RootObj
|
||||||
server*: HttpServerRef
|
server*: HttpServerRef
|
||||||
transp: StreamTransport
|
transp: StreamTransport
|
||||||
mainReader: AsyncStreamReader
|
mainReader*: AsyncStreamReader
|
||||||
mainWriter: AsyncStreamWriter
|
mainWriter*: AsyncStreamWriter
|
||||||
tlsStream: TLSAsyncStream
|
|
||||||
reader*: AsyncStreamReader
|
reader*: AsyncStreamReader
|
||||||
writer*: AsyncStreamWriter
|
writer*: AsyncStreamWriter
|
||||||
buffer: seq[byte]
|
buffer: seq[byte]
|
||||||
|
@ -119,6 +121,50 @@ proc init(htype: typedesc[HttpProcessError], error: HTTPServerError,
|
||||||
code: HttpCode): HttpProcessError {.raises: [Defect].} =
|
code: HttpCode): HttpProcessError {.raises: [Defect].} =
|
||||||
HttpProcessError(error: error, exc: exc, remote: remote, code: code)
|
HttpProcessError(error: error, exc: exc, remote: remote, code: code)
|
||||||
|
|
||||||
|
proc init*(value: var HttpServer,
|
||||||
|
address: TransportAddress,
|
||||||
|
server: StreamServer,
|
||||||
|
processCallback: HttpProcessCallback,
|
||||||
|
createConnCallback: HttpConnectionCallback,
|
||||||
|
serverUri: Uri,
|
||||||
|
serverFlags: set[HttpServerFlags] = {},
|
||||||
|
socketFlags: set[ServerFlags] = {ReuseAddr},
|
||||||
|
serverIdent = "",
|
||||||
|
maxConnections: int = -1,
|
||||||
|
bufferSize: int = 4096,
|
||||||
|
backlogSize: int = 100,
|
||||||
|
httpHeadersTimeout = 10.seconds,
|
||||||
|
maxHeadersSize: int = 8192,
|
||||||
|
maxRequestBodySize: int = 1_048_576) =
|
||||||
|
|
||||||
|
value = HttpServer(
|
||||||
|
address: address,
|
||||||
|
instance: server,
|
||||||
|
processCallback: processCallback,
|
||||||
|
createConnCallback: createConnCallback,
|
||||||
|
baseUri: serverUri,
|
||||||
|
serverIdent: serverIdent,
|
||||||
|
flags: serverFlags,
|
||||||
|
socketFlags: socketFlags,
|
||||||
|
maxConnections: maxConnections,
|
||||||
|
bufferSize: bufferSize,
|
||||||
|
backlogSize: backlogSize,
|
||||||
|
headersTimeout: httpHeadersTimeout,
|
||||||
|
maxHeadersSize: maxHeadersSize,
|
||||||
|
maxRequestBodySize: maxRequestBodySize,
|
||||||
|
# semaphore:
|
||||||
|
# if maxConnections > 0:
|
||||||
|
# newAsyncSemaphore(maxConnections)
|
||||||
|
# else:
|
||||||
|
# nil
|
||||||
|
lifetime: newFuture[void]("http.server.lifetime"),
|
||||||
|
connections: initTable[string, Future[void]]()
|
||||||
|
)
|
||||||
|
|
||||||
|
proc createConnection(server: HttpServerRef,
|
||||||
|
transp: StreamTransport): Future[HttpConnectionRef] {.
|
||||||
|
gcsafe.}
|
||||||
|
|
||||||
proc new*(htype: typedesc[HttpServerRef],
|
proc new*(htype: typedesc[HttpServerRef],
|
||||||
address: TransportAddress,
|
address: TransportAddress,
|
||||||
processCallback: HttpProcessCallback,
|
processCallback: HttpProcessCallback,
|
||||||
|
@ -126,9 +172,6 @@ proc new*(htype: typedesc[HttpServerRef],
|
||||||
socketFlags: set[ServerFlags] = {ReuseAddr},
|
socketFlags: set[ServerFlags] = {ReuseAddr},
|
||||||
serverUri = Uri(),
|
serverUri = Uri(),
|
||||||
serverIdent = "",
|
serverIdent = "",
|
||||||
tlsPrivateKey: TLSPrivateKey = nil,
|
|
||||||
tlsCertificate: TLSCertificate = nil,
|
|
||||||
secureFlags: set[TLSFlags] = {},
|
|
||||||
maxConnections: int = -1,
|
maxConnections: int = -1,
|
||||||
bufferSize: int = 4096,
|
bufferSize: int = 4096,
|
||||||
backlogSize: int = 100,
|
backlogSize: int = 100,
|
||||||
|
@ -136,51 +179,31 @@ proc new*(htype: typedesc[HttpServerRef],
|
||||||
maxHeadersSize: int = 8192,
|
maxHeadersSize: int = 8192,
|
||||||
maxRequestBodySize: int = 1_048_576): HttpResult[HttpServerRef] =
|
maxRequestBodySize: int = 1_048_576): HttpResult[HttpServerRef] =
|
||||||
|
|
||||||
if HttpServerFlags.Secure in serverFlags:
|
let serverUri =
|
||||||
if isNil(tlsPrivateKey) or isNil(tlsCertificate):
|
if len(serverUri.hostname) > 0:
|
||||||
return err("PrivateKey or Certificate is missing")
|
|
||||||
|
|
||||||
var res = HttpServerRef(
|
|
||||||
address: address,
|
|
||||||
serverIdent: serverIdent,
|
|
||||||
maxConnections: maxConnections,
|
|
||||||
headersTimeout: httpHeadersTimeout,
|
|
||||||
maxHeadersSize: maxHeadersSize,
|
|
||||||
maxRequestBodySize: maxRequestBodySize,
|
|
||||||
processCallback: processCallback,
|
|
||||||
backLogSize: backLogSize,
|
|
||||||
flags: serverFlags,
|
|
||||||
socketFlags: socketFlags,
|
|
||||||
tlsPrivateKey: tlsPrivateKey,
|
|
||||||
tlsCertificate: tlsCertificate
|
|
||||||
)
|
|
||||||
|
|
||||||
res.baseUri =
|
|
||||||
try:
|
|
||||||
if len(serverUri.hostname) > 0 and isAbsolute(serverUri):
|
|
||||||
serverUri
|
serverUri
|
||||||
else:
|
else:
|
||||||
if HttpServerFlags.Secure in serverFlags:
|
try:
|
||||||
parseUri("https://" & $address & "/")
|
|
||||||
else:
|
|
||||||
parseUri("http://" & $address & "/")
|
parseUri("http://" & $address & "/")
|
||||||
except TransportAddressError as exc:
|
except TransportAddressError as exc:
|
||||||
return err(exc.msg)
|
return err(exc.msg)
|
||||||
|
|
||||||
|
let serverInstance =
|
||||||
try:
|
try:
|
||||||
res.instance = createStreamServer(address, flags = socketFlags,
|
createStreamServer(address, flags = socketFlags, bufferSize = bufferSize,
|
||||||
bufferSize = bufferSize,
|
|
||||||
backlog = backlogSize)
|
backlog = backlogSize)
|
||||||
# if maxConnections > 0:
|
|
||||||
# res.semaphore = newAsyncSemaphore(maxConnections)
|
|
||||||
res.lifetime = newFuture[void]("http.server.lifetime")
|
|
||||||
res.connections = initTable[string, Future[void]]()
|
|
||||||
return ok(res)
|
|
||||||
except TransportOsError as exc:
|
except TransportOsError as exc:
|
||||||
return err(exc.msg)
|
return err(exc.msg)
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
return err(exc.msg)
|
return err(exc.msg)
|
||||||
|
|
||||||
|
var res = HttpServerRef()
|
||||||
|
res[].init(address, serverInstance, processCallback, createConnection,
|
||||||
|
serverUri, serverFlags, socketFlags, serverIdent, maxConnections,
|
||||||
|
bufferSize, backLogSize, httpHeadersTimeout, maxHeadersSize,
|
||||||
|
maxRequestBodySize)
|
||||||
|
ok(res)
|
||||||
|
|
||||||
proc getResponse*(req: HttpRequestRef): HttpResponseRef {.raises: [Defect].} =
|
proc getResponse*(req: HttpRequestRef): HttpResponseRef {.raises: [Defect].} =
|
||||||
if req.response.isNone():
|
if req.response.isNone():
|
||||||
var resp = HttpResponseRef(
|
var resp = HttpResponseRef(
|
||||||
|
@ -450,47 +473,32 @@ proc getRequest(conn: HttpConnectionRef): Future[HttpRequestRef] {.async.} =
|
||||||
except AsyncStreamLimitError:
|
except AsyncStreamLimitError:
|
||||||
raiseHttpCriticalError("Maximum size of request headers reached", Http431)
|
raiseHttpCriticalError("Maximum size of request headers reached", Http431)
|
||||||
|
|
||||||
proc new(ht: typedesc[HttpConnectionRef], server: HttpServerRef,
|
proc init*(value: var HttpConnection, server: HttpServerRef,
|
||||||
transp: StreamTransport): HttpConnectionRef =
|
transp: StreamTransport) =
|
||||||
let mainReader = newAsyncStreamReader(transp)
|
value = HttpConnection(
|
||||||
let mainWriter = newAsyncStreamWriter(transp)
|
|
||||||
let tlsStream =
|
|
||||||
if HttpServerFlags.Secure in server.flags:
|
|
||||||
newTLSServerAsyncStream(mainReader, mainWriter, server.tlsPrivateKey,
|
|
||||||
server.tlsCertificate,
|
|
||||||
minVersion = TLSVersion.TLS12,
|
|
||||||
flags = server.secureFlags)
|
|
||||||
else:
|
|
||||||
nil
|
|
||||||
|
|
||||||
let reader =
|
|
||||||
if isNil(tlsStream):
|
|
||||||
mainReader
|
|
||||||
else:
|
|
||||||
cast[AsyncStreamReader](tlsStream.reader)
|
|
||||||
|
|
||||||
let writer =
|
|
||||||
if isNil(tlsStream):
|
|
||||||
mainWriter
|
|
||||||
else:
|
|
||||||
cast[AsyncStreamWriter](tlsStream.writer)
|
|
||||||
|
|
||||||
HttpConnectionRef(
|
|
||||||
transp: transp,
|
|
||||||
server: server,
|
server: server,
|
||||||
|
transp: transp,
|
||||||
buffer: newSeq[byte](server.maxHeadersSize),
|
buffer: newSeq[byte](server.maxHeadersSize),
|
||||||
mainReader: mainReader,
|
mainReader: newAsyncStreamReader(transp),
|
||||||
mainWriter: mainWriter,
|
mainWriter: newAsyncStreamWriter(transp)
|
||||||
tlsStream: tlsStream,
|
|
||||||
reader: reader,
|
|
||||||
writer: writer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
proc closeWait(conn: HttpConnectionRef) {.async.} =
|
proc new(ht: typedesc[HttpConnectionRef], server: HttpServerRef,
|
||||||
if HttpServerFlags.Secure in conn.server.flags:
|
transp: StreamTransport): HttpConnectionRef =
|
||||||
# First we will close TLS streams.
|
var res = HttpConnectionRef()
|
||||||
await allFutures(conn.reader.closeWait(), conn.writer.closeWait())
|
res[].init(server, transp)
|
||||||
|
res.reader = res.mainReader
|
||||||
|
res.writer = res.mainWriter
|
||||||
|
res
|
||||||
|
|
||||||
|
proc closeWait*(conn: HttpConnectionRef) {.async.} =
|
||||||
|
var pending: seq[Future[void]]
|
||||||
|
if conn.reader != conn.mainReader:
|
||||||
|
pending.add(conn.reader.closeWait())
|
||||||
|
if conn.writer != conn.mainWriter:
|
||||||
|
pending.add(conn.writer.closeWait())
|
||||||
|
if len(pending) > 0:
|
||||||
|
await allFutures(pending)
|
||||||
# After we going to close everything else.
|
# After we going to close everything else.
|
||||||
await allFutures(conn.mainReader.closeWait(), conn.mainWriter.closeWait(),
|
await allFutures(conn.mainReader.closeWait(), conn.mainWriter.closeWait(),
|
||||||
conn.transp.closeWait())
|
conn.transp.closeWait())
|
||||||
|
@ -505,20 +513,7 @@ proc closeWait(req: HttpRequestRef) {.async.} =
|
||||||
proc createConnection(server: HttpServerRef,
|
proc createConnection(server: HttpServerRef,
|
||||||
transp: StreamTransport): Future[HttpConnectionRef] {.
|
transp: StreamTransport): Future[HttpConnectionRef] {.
|
||||||
async.} =
|
async.} =
|
||||||
var conn = HttpConnectionRef.new(server, transp)
|
return HttpConnectionRef.new(server, transp)
|
||||||
if HttpServerFlags.Secure notin server.flags:
|
|
||||||
# Non secure connection
|
|
||||||
return conn
|
|
||||||
|
|
||||||
try:
|
|
||||||
await handshake(conn.tlsStream)
|
|
||||||
return conn
|
|
||||||
except CancelledError as exc:
|
|
||||||
await conn.closeWait()
|
|
||||||
raise exc
|
|
||||||
except TLSStreamError:
|
|
||||||
await conn.closeWait()
|
|
||||||
raiseHttpCriticalError("Unable to establish secure connection")
|
|
||||||
|
|
||||||
proc processLoop(server: HttpServerRef, transp: StreamTransport) {.async.} =
|
proc processLoop(server: HttpServerRef, transp: StreamTransport) {.async.} =
|
||||||
var
|
var
|
||||||
|
@ -527,10 +522,9 @@ proc processLoop(server: HttpServerRef, transp: StreamTransport) {.async.} =
|
||||||
runLoop = false
|
runLoop = false
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = await createConnection(server, transp)
|
conn = await server.createConnCallback(server, transp)
|
||||||
runLoop = true
|
runLoop = true
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
# We could be cancelled only when we perform TLS handshake, connection
|
|
||||||
server.connections.del(transp.getId())
|
server.connections.del(transp.getId())
|
||||||
await transp.closeWait()
|
await transp.closeWait()
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
#
|
||||||
|
# Chronos HTTP/S server implementation
|
||||||
|
# (c) Copyright 2021-Present
|
||||||
|
# Status Research & Development GmbH
|
||||||
|
#
|
||||||
|
# Licensed under either of
|
||||||
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
|
# MIT license (LICENSE-MIT)
|
||||||
|
import httpserver
|
||||||
|
import ../../asyncloop, ../../asyncsync
|
||||||
|
import ../../streams/[asyncstream, tlsstream]
|
||||||
|
export httpserver, asyncstream, tlsstream
|
||||||
|
|
||||||
|
type
|
||||||
|
SecureHttpServer* = object of HttpServer
|
||||||
|
secureFlags*: set[TLSFlags]
|
||||||
|
tlsPrivateKey: TLSPrivateKey
|
||||||
|
tlsCertificate: TLSCertificate
|
||||||
|
|
||||||
|
SecureHttpServerRef* = ref SecureHttpServer
|
||||||
|
|
||||||
|
SecureHttpConnection* = object of HttpConnection
|
||||||
|
tlsStream*: TLSAsyncStream
|
||||||
|
|
||||||
|
SecureHttpConnectionRef* = ref SecureHttpConnection
|
||||||
|
|
||||||
|
proc new*(ht: typedesc[SecureHttpConnectionRef], server: SecureHttpServerRef,
|
||||||
|
transp: StreamTransport): SecureHttpConnectionRef =
|
||||||
|
var res = SecureHttpConnectionRef()
|
||||||
|
HttpConnection(res[]).init(HttpServerRef(server), transp)
|
||||||
|
let tlsStream =
|
||||||
|
newTLSServerAsyncStream(res.mainReader, res.mainWriter,
|
||||||
|
server.tlsPrivateKey,
|
||||||
|
server.tlsCertificate,
|
||||||
|
minVersion = TLSVersion.TLS12,
|
||||||
|
flags = server.secureFlags)
|
||||||
|
res.tlsStream = tlsStream
|
||||||
|
res.reader = AsyncStreamReader(tlsStream.reader)
|
||||||
|
res.writer = AsyncStreamWriter(tlsStream.writer)
|
||||||
|
res
|
||||||
|
|
||||||
|
proc createSecConnection(server: HttpServerRef,
|
||||||
|
transp: StreamTransport): Future[HttpConnectionRef] {.
|
||||||
|
async.} =
|
||||||
|
let secureServ = cast[SecureHttpServerRef](server)
|
||||||
|
var sconn = SecureHttpConnectionRef.new(secureServ, transp)
|
||||||
|
try:
|
||||||
|
await handshake(sconn.tlsStream)
|
||||||
|
return HttpConnectionRef(sconn)
|
||||||
|
except CancelledError as exc:
|
||||||
|
await HttpConnectionRef(sconn).closeWait()
|
||||||
|
raise exc
|
||||||
|
except TLSStreamError:
|
||||||
|
await HttpConnectionRef(sconn).closeWait()
|
||||||
|
raiseHttpCriticalError("Unable to establish secure connection")
|
||||||
|
|
||||||
|
proc new*(htype: typedesc[SecureHttpServerRef],
|
||||||
|
address: TransportAddress,
|
||||||
|
processCallback: HttpProcessCallback,
|
||||||
|
tlsPrivateKey: TLSPrivateKey,
|
||||||
|
tlsCertificate: TLSCertificate,
|
||||||
|
serverFlags: set[HttpServerFlags] = {},
|
||||||
|
socketFlags: set[ServerFlags] = {ReuseAddr},
|
||||||
|
serverUri = Uri(),
|
||||||
|
serverIdent = "",
|
||||||
|
secureFlags: set[TLSFlags] = {},
|
||||||
|
maxConnections: int = -1,
|
||||||
|
bufferSize: int = 4096,
|
||||||
|
backlogSize: int = 100,
|
||||||
|
httpHeadersTimeout = 10.seconds,
|
||||||
|
maxHeadersSize: int = 8192,
|
||||||
|
maxRequestBodySize: int = 1_048_576
|
||||||
|
): HttpResult[SecureHttpServerRef] =
|
||||||
|
|
||||||
|
doAssert(not(isNil(tlsPrivateKey)), "TLS private key must not be nil!")
|
||||||
|
doAssert(not(isNil(tlsCertificate)), "TLS certificate must not be nil!")
|
||||||
|
|
||||||
|
let serverUri =
|
||||||
|
if len(serverUri.hostname) > 0:
|
||||||
|
serverUri
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
parseUri("https://" & $address & "/")
|
||||||
|
except TransportAddressError as exc:
|
||||||
|
return err(exc.msg)
|
||||||
|
|
||||||
|
let serverInstance =
|
||||||
|
try:
|
||||||
|
createStreamServer(address, flags = socketFlags, bufferSize = bufferSize,
|
||||||
|
backlog = backlogSize)
|
||||||
|
except TransportOsError as exc:
|
||||||
|
return err(exc.msg)
|
||||||
|
except CatchableError as exc:
|
||||||
|
return err(exc.msg)
|
||||||
|
|
||||||
|
var res = SecureHttpServerRef()
|
||||||
|
HttpServer(res[]).init(address, serverInstance, processCallback,
|
||||||
|
createSecConnection, serverUri, serverFlags,
|
||||||
|
socketFlags, serverIdent, maxConnections,
|
||||||
|
bufferSize, backLogSize, httpHeadersTimeout,
|
||||||
|
maxHeadersSize, maxRequestBodySize)
|
||||||
|
res.tlsCertificate = tlsCertificate
|
||||||
|
res.tlsPrivateKey = tlsPrivateKey
|
||||||
|
res.secureFlags = secureFlags
|
||||||
|
ok(res)
|
|
@ -7,5 +7,5 @@
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
import testmacro, testsync, testsoon, testtime, testfut, testsignal,
|
import testmacro, testsync, testsoon, testtime, testfut, testsignal,
|
||||||
testaddress, testdatagram, teststream, testserver, testbugs, testnet,
|
testaddress, testdatagram, teststream, testserver, testbugs, testnet,
|
||||||
testasyncstream, testhttpserver
|
testasyncstream, testhttpserver, testshttpserver
|
||||||
import testutils
|
import testutils
|
||||||
|
|
|
@ -6,68 +6,10 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
import std/[strutils, unittest, algorithm, strutils]
|
import std/[strutils, unittest, algorithm, strutils]
|
||||||
import ../chronos, ../chronos/apps
|
import ../chronos, ../chronos/apps/http/httpserver
|
||||||
import stew/base10
|
import stew/base10
|
||||||
|
|
||||||
# To create self-signed certificate and key you can use openssl
|
when defined(nimHasUsed): {.used.}
|
||||||
# openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes \
|
|
||||||
# -keyout example-com.key.pem -days 3650 -out example-com.cert.pem
|
|
||||||
const HttpsSelfSignedRsaKey = """
|
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCn7tXGLKMIMzOG
|
|
||||||
tVzUixax1/ftlSLcpEAkZMORuiCCnYjtIJhGZdzRFZC8fBlfAJZpLIAOfX2L2f1J
|
|
||||||
ZuwpwDkOIvNqKMBrl5Mvkl5azPT0rtnjuwrcqN5NFtbmZPKFYvbjex2aXGqjl5MW
|
|
||||||
nQIs/ZA++DVEXmaN9oDxcZsvRMDKfrGQf9iLeoVL47Gx9KpqNqD/JLIn4LpieumV
|
|
||||||
yYidm6ukTOqHRvrWm36y6VvKW4TE97THacULmkeahtTf8zDJbbh4EO+gifgwgJ2W
|
|
||||||
BUS0+5hMcWu8111mXmanlOVlcoW8fH8RmPjL1eK1Z3j3SVHEf7oWZtIVW5gGA0jQ
|
|
||||||
nfA4K51RAgMBAAECggEANZ7/R13tWKrwouy6DWuz/WlWUtgx333atUQvZhKmWs5u
|
|
||||||
cDjeJmxUC7b1FhoSB9GqNT7uTLIpKkSaqZthgRtNnIPwcU890Zz+dEwqMJgNByvl
|
|
||||||
it+oYjjRco/+YmaNQaYN6yjelPE5Y678WlYb4b29Fz4t0/zIhj/VgEKkKH2tiXpS
|
|
||||||
TIicoM7pSOscEUfaW3yp5bS5QwNU6/AaF1wws0feBACd19ZkcdPvr52jopbhxlXw
|
|
||||||
h3XTV/vXIJd5zWGp0h/Jbd4xcD4MVo2GjfkeORKY6SjDaNzt8OGtePcKnnbUVu8b
|
|
||||||
2XlDxukhDQXqJ3g0sHz47mhvo4JeIM+FgymRm+3QmQKBgQDTawrEA3Zy9WvucaC7
|
|
||||||
Zah02oE9nuvpF12lZ7WJh7+tZ/1ss+Fm7YspEKaUiEk7nn1CAVFtem4X4YCXTBiC
|
|
||||||
Oqq/o+ipv1yTur0ae6m4pwLm5wcMWBh3H5zjfQTfrClNN8yjWv8u3/sq8KesHPnT
|
|
||||||
R92/sMAptAChPgTzQphWbxFiYwKBgQDLWFaBqXfZYVnTyUvKX8GorS6jGWc6Eh4l
|
|
||||||
lAFA+2EBWDICrUxsDPoZjEXrWCixdqLhyehaI3KEFIx2bcPv6X2c7yx3IG5lA/Gx
|
|
||||||
TZiKlY74c6jOTstkdLW9RJbg1VUHUVZMf/Owt802YmEfUI5S5v7jFmKW6VG+io+K
|
|
||||||
+5KYeHD1uwKBgQDMf53KPA82422jFwYCPjLT1QduM2q97HwIomhWv5gIg63+l4BP
|
|
||||||
rzYMYq6+vZUYthUy41OAMgyLzPQ1ZMXQMi83b7R9fTxvKRIBq9xfYCzObGnE5vHD
|
|
||||||
SDDZWvR75muM5Yxr9nkfPkgVIPMO6Hg+hiVYZf96V0LEtNjU9HWmJYkLQQKBgQCQ
|
|
||||||
ULGUdGHKtXy7AjH3/t3CiKaAupa4cANVSCVbqQy/l4hmvfdu+AbH+vXkgTzgNgKD
|
|
||||||
nHh7AI1Vj//gTSayLlQn/Nbh9PJkXtg5rYiFUn+VdQBo6yMOuIYDPZqXFtCx0Nge
|
|
||||||
kvCwisHpxwiG4PUhgS+Em259DDonsM8PJFx2OYRx4QKBgEQpGhg71Oi9MhPJshN7
|
|
||||||
dYTowaMS5eLTk2264ARaY+hAIV7fgvUa+5bgTVaWL+Cfs33hi4sMRqlEwsmfds2T
|
|
||||||
cnQiJ4cU20Euldfwa5FLnk6LaWdOyzYt/ICBJnKFRwfCUbS4Bu5rtMEM+3t0wxnJ
|
|
||||||
IgaD04WhoL9EX0Qo3DC1+0kG
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
"""
|
|
||||||
|
|
||||||
# This SSL certificate will expire 13 October 2030.
|
|
||||||
const HttpsSelfSignedRsaCert = """
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDnzCCAoegAwIBAgIUUdcusjDd3XQi3FPM8urdFG3qI+8wDQYJKoZIhvcNAQEL
|
|
||||||
BQAwXzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
|
||||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQz
|
|
||||||
ODA4MB4XDTIwMTAxMjIxNDUwMVoXDTMwMTAxMDIxNDUwMVowXzELMAkGA1UEBhMC
|
|
||||||
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
|
|
||||||
dHMgUHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQzODA4MIIBIjANBgkqhkiG
|
|
||||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp+7VxiyjCDMzhrVc1IsWsdf37ZUi3KRAJGTD
|
|
||||||
kboggp2I7SCYRmXc0RWQvHwZXwCWaSyADn19i9n9SWbsKcA5DiLzaijAa5eTL5Je
|
|
||||||
Wsz09K7Z47sK3KjeTRbW5mTyhWL243sdmlxqo5eTFp0CLP2QPvg1RF5mjfaA8XGb
|
|
||||||
L0TAyn6xkH/Yi3qFS+OxsfSqajag/ySyJ+C6YnrplcmInZurpEzqh0b61pt+sulb
|
|
||||||
yluExPe0x2nFC5pHmobU3/MwyW24eBDvoIn4MICdlgVEtPuYTHFrvNddZl5mp5Tl
|
|
||||||
ZXKFvHx/EZj4y9XitWd490lRxH+6FmbSFVuYBgNI0J3wOCudUQIDAQABo1MwUTAd
|
|
||||||
BgNVHQ4EFgQUBKha84woY5WkFxKw7qx1cONg1H8wHwYDVR0jBBgwFoAUBKha84wo
|
|
||||||
Y5WkFxKw7qx1cONg1H8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
|
||||||
AQEAHZMYt9Ry+Xj3vTbzpGFQzYQVTJlfJWSN6eWNOivRFQE5io9kOBEe5noa8aLo
|
|
||||||
dLkw6ztxRP2QRJmlhGCO9/HwS17ckrkgZp3EC2LFnzxcBmoZu+owfxOT1KqpO52O
|
|
||||||
IKOl8eVohi1pEicE4dtTJVcpI7VCMovnXUhzx1Ci4Vibns4a6H+BQa19a1JSpifN
|
|
||||||
tO8U5jkjJ8Jprs/VPFhJj2O3di53oDHaYSE5eOrm2ZO14KFHSk9cGcOGmcYkUv8B
|
|
||||||
nV5vnGadH5Lvfxb/BCpuONabeRdOxMt9u9yQ89vNpxFtRdZDCpGKZBCfmUP+5m3m
|
|
||||||
N8r5CwGcIX/XPC3lKazzbZ8baA==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
"""
|
|
||||||
|
|
||||||
suite "HTTP server testing suite":
|
suite "HTTP server testing suite":
|
||||||
type
|
type
|
||||||
|
@ -89,34 +31,6 @@ suite "HTTP server testing suite":
|
||||||
if not(isNil(transp)):
|
if not(isNil(transp)):
|
||||||
await closeWait(transp)
|
await closeWait(transp)
|
||||||
|
|
||||||
proc httpsClient(address: TransportAddress,
|
|
||||||
data: string, flags = {NoVerifyHost, NoVerifyServerName}
|
|
||||||
): Future[string] {.async.} =
|
|
||||||
var
|
|
||||||
transp: StreamTransport
|
|
||||||
tlsstream: TlsAsyncStream
|
|
||||||
reader: AsyncStreamReader
|
|
||||||
writer: AsyncStreamWriter
|
|
||||||
|
|
||||||
try:
|
|
||||||
transp = await connect(address)
|
|
||||||
reader = newAsyncStreamReader(transp)
|
|
||||||
writer = newAsyncStreamWriter(transp)
|
|
||||||
tlsstream = newTLSClientAsyncStream(reader, writer, "", flags = flags)
|
|
||||||
if len(data) > 0:
|
|
||||||
await tlsstream.writer.write(data)
|
|
||||||
var rres = await tlsstream.reader.read()
|
|
||||||
return bytesToString(rres)
|
|
||||||
except CatchableError:
|
|
||||||
return "EXCEPTION"
|
|
||||||
finally:
|
|
||||||
if not(isNil(tlsstream)):
|
|
||||||
await allFutures(tlsstream.reader.closeWait(),
|
|
||||||
tlsstream.writer.closeWait())
|
|
||||||
if not(isNil(reader)):
|
|
||||||
await allFutures(reader.closeWait(), writer.closeWait(),
|
|
||||||
transp.closeWait())
|
|
||||||
|
|
||||||
proc testTooBigBodyChunked(address: TransportAddress,
|
proc testTooBigBodyChunked(address: TransportAddress,
|
||||||
operation: TooBigTest): Future[bool] {.async.} =
|
operation: TooBigTest): Future[bool] {.async.} =
|
||||||
var serverRes = false
|
var serverRes = false
|
||||||
|
@ -606,82 +520,6 @@ suite "HTTP server testing suite":
|
||||||
|
|
||||||
check waitFor(testPostMultipart2(initTAddress("127.0.0.1:30080"))) == true
|
check waitFor(testPostMultipart2(initTAddress("127.0.0.1:30080"))) == true
|
||||||
|
|
||||||
test "HTTPS server (successful handshake) test":
|
|
||||||
proc testHTTPS(address: TransportAddress): Future[bool] {.async.} =
|
|
||||||
var serverRes = false
|
|
||||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
|
||||||
async.} =
|
|
||||||
if r.isOk():
|
|
||||||
let request = r.get()
|
|
||||||
serverRes = true
|
|
||||||
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
|
||||||
HttpTable.init())
|
|
||||||
else:
|
|
||||||
serverRes = false
|
|
||||||
return dumbResponse()
|
|
||||||
|
|
||||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
|
||||||
let serverFlags = {Secure}
|
|
||||||
let secureKey = TLSPrivateKey.init(HttpsSelfSignedRsaKey)
|
|
||||||
let secureCert = TLSCertificate.init(HttpsSelfSignedRsaCert)
|
|
||||||
let res = HttpServerRef.new(address, process,
|
|
||||||
socketFlags = socketFlags,
|
|
||||||
serverFlags = serverFlags,
|
|
||||||
tlsPrivateKey = secureKey,
|
|
||||||
tlsCertificate = secureCert)
|
|
||||||
if res.isErr():
|
|
||||||
return false
|
|
||||||
|
|
||||||
let server = res.get()
|
|
||||||
server.start()
|
|
||||||
let message = "GET / HTTP/1.0\r\nHost: https://127.0.0.1:80\r\n\r\n"
|
|
||||||
let data = await httpsClient(address, message)
|
|
||||||
|
|
||||||
await server.stop()
|
|
||||||
await server.closeWait()
|
|
||||||
return serverRes and (data.find("TEST_OK:GET") >= 0)
|
|
||||||
|
|
||||||
check waitFor(testHTTPS(initTAddress("127.0.0.1:30080"))) == true
|
|
||||||
|
|
||||||
test "HTTPS server (failed handshake) test":
|
|
||||||
proc testHTTPS2(address: TransportAddress): Future[bool] {.async.} =
|
|
||||||
var serverRes = false
|
|
||||||
var testFut = newFuture[void]()
|
|
||||||
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
|
||||||
async.} =
|
|
||||||
if r.isOk():
|
|
||||||
let request = r.get()
|
|
||||||
serverRes = false
|
|
||||||
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
|
||||||
HttpTable.init())
|
|
||||||
else:
|
|
||||||
serverRes = true
|
|
||||||
testFut.complete()
|
|
||||||
return dumbResponse()
|
|
||||||
|
|
||||||
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
|
||||||
let serverFlags = {Secure}
|
|
||||||
let secureKey = TLSPrivateKey.init(HttpsSelfSignedRsaKey)
|
|
||||||
let secureCert = TLSCertificate.init(HttpsSelfSignedRsaCert)
|
|
||||||
let res = HttpServerRef.new(address, process,
|
|
||||||
socketFlags = socketFlags,
|
|
||||||
serverFlags = serverFlags,
|
|
||||||
tlsPrivateKey = secureKey,
|
|
||||||
tlsCertificate = secureCert)
|
|
||||||
if res.isErr():
|
|
||||||
return false
|
|
||||||
|
|
||||||
let server = res.get()
|
|
||||||
server.start()
|
|
||||||
let message = "GET / HTTP/1.0\r\nHost: https://127.0.0.1:80\r\n\r\n"
|
|
||||||
let data = await httpsClient(address, message, {NoVerifyServerName})
|
|
||||||
await testFut
|
|
||||||
await server.stop()
|
|
||||||
await server.closeWait()
|
|
||||||
return serverRes and data == "EXCEPTION"
|
|
||||||
|
|
||||||
check waitFor(testHTTPS2(initTAddress("127.0.0.1:30080"))) == true
|
|
||||||
|
|
||||||
test "drop() connections test":
|
test "drop() connections test":
|
||||||
const ClientsCount = 10
|
const ClientsCount = 10
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
# Chronos Test Suite
|
||||||
|
# (c) Copyright 2021-Present
|
||||||
|
# Status Research & Development GmbH
|
||||||
|
#
|
||||||
|
# Licensed under either of
|
||||||
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
|
# MIT license (LICENSE-MIT)
|
||||||
|
import std/[strutils, unittest, strutils]
|
||||||
|
import ../chronos, ../chronos/apps/http/shttpserver
|
||||||
|
import stew/base10
|
||||||
|
|
||||||
|
when defined(nimHasUsed): {.used.}
|
||||||
|
|
||||||
|
# To create self-signed certificate and key you can use openssl
|
||||||
|
# openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes \
|
||||||
|
# -keyout example-com.key.pem -days 3650 -out example-com.cert.pem
|
||||||
|
const HttpsSelfSignedRsaKey = """
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCn7tXGLKMIMzOG
|
||||||
|
tVzUixax1/ftlSLcpEAkZMORuiCCnYjtIJhGZdzRFZC8fBlfAJZpLIAOfX2L2f1J
|
||||||
|
ZuwpwDkOIvNqKMBrl5Mvkl5azPT0rtnjuwrcqN5NFtbmZPKFYvbjex2aXGqjl5MW
|
||||||
|
nQIs/ZA++DVEXmaN9oDxcZsvRMDKfrGQf9iLeoVL47Gx9KpqNqD/JLIn4LpieumV
|
||||||
|
yYidm6ukTOqHRvrWm36y6VvKW4TE97THacULmkeahtTf8zDJbbh4EO+gifgwgJ2W
|
||||||
|
BUS0+5hMcWu8111mXmanlOVlcoW8fH8RmPjL1eK1Z3j3SVHEf7oWZtIVW5gGA0jQ
|
||||||
|
nfA4K51RAgMBAAECggEANZ7/R13tWKrwouy6DWuz/WlWUtgx333atUQvZhKmWs5u
|
||||||
|
cDjeJmxUC7b1FhoSB9GqNT7uTLIpKkSaqZthgRtNnIPwcU890Zz+dEwqMJgNByvl
|
||||||
|
it+oYjjRco/+YmaNQaYN6yjelPE5Y678WlYb4b29Fz4t0/zIhj/VgEKkKH2tiXpS
|
||||||
|
TIicoM7pSOscEUfaW3yp5bS5QwNU6/AaF1wws0feBACd19ZkcdPvr52jopbhxlXw
|
||||||
|
h3XTV/vXIJd5zWGp0h/Jbd4xcD4MVo2GjfkeORKY6SjDaNzt8OGtePcKnnbUVu8b
|
||||||
|
2XlDxukhDQXqJ3g0sHz47mhvo4JeIM+FgymRm+3QmQKBgQDTawrEA3Zy9WvucaC7
|
||||||
|
Zah02oE9nuvpF12lZ7WJh7+tZ/1ss+Fm7YspEKaUiEk7nn1CAVFtem4X4YCXTBiC
|
||||||
|
Oqq/o+ipv1yTur0ae6m4pwLm5wcMWBh3H5zjfQTfrClNN8yjWv8u3/sq8KesHPnT
|
||||||
|
R92/sMAptAChPgTzQphWbxFiYwKBgQDLWFaBqXfZYVnTyUvKX8GorS6jGWc6Eh4l
|
||||||
|
lAFA+2EBWDICrUxsDPoZjEXrWCixdqLhyehaI3KEFIx2bcPv6X2c7yx3IG5lA/Gx
|
||||||
|
TZiKlY74c6jOTstkdLW9RJbg1VUHUVZMf/Owt802YmEfUI5S5v7jFmKW6VG+io+K
|
||||||
|
+5KYeHD1uwKBgQDMf53KPA82422jFwYCPjLT1QduM2q97HwIomhWv5gIg63+l4BP
|
||||||
|
rzYMYq6+vZUYthUy41OAMgyLzPQ1ZMXQMi83b7R9fTxvKRIBq9xfYCzObGnE5vHD
|
||||||
|
SDDZWvR75muM5Yxr9nkfPkgVIPMO6Hg+hiVYZf96V0LEtNjU9HWmJYkLQQKBgQCQ
|
||||||
|
ULGUdGHKtXy7AjH3/t3CiKaAupa4cANVSCVbqQy/l4hmvfdu+AbH+vXkgTzgNgKD
|
||||||
|
nHh7AI1Vj//gTSayLlQn/Nbh9PJkXtg5rYiFUn+VdQBo6yMOuIYDPZqXFtCx0Nge
|
||||||
|
kvCwisHpxwiG4PUhgS+Em259DDonsM8PJFx2OYRx4QKBgEQpGhg71Oi9MhPJshN7
|
||||||
|
dYTowaMS5eLTk2264ARaY+hAIV7fgvUa+5bgTVaWL+Cfs33hi4sMRqlEwsmfds2T
|
||||||
|
cnQiJ4cU20Euldfwa5FLnk6LaWdOyzYt/ICBJnKFRwfCUbS4Bu5rtMEM+3t0wxnJ
|
||||||
|
IgaD04WhoL9EX0Qo3DC1+0kG
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This SSL certificate will expire 13 October 2030.
|
||||||
|
const HttpsSelfSignedRsaCert = """
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDnzCCAoegAwIBAgIUUdcusjDd3XQi3FPM8urdFG3qI+8wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQz
|
||||||
|
ODA4MB4XDTIwMTAxMjIxNDUwMVoXDTMwMTAxMDIxNDUwMVowXzELMAkGA1UEBhMC
|
||||||
|
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
|
||||||
|
dHMgUHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQzODA4MIIBIjANBgkqhkiG
|
||||||
|
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp+7VxiyjCDMzhrVc1IsWsdf37ZUi3KRAJGTD
|
||||||
|
kboggp2I7SCYRmXc0RWQvHwZXwCWaSyADn19i9n9SWbsKcA5DiLzaijAa5eTL5Je
|
||||||
|
Wsz09K7Z47sK3KjeTRbW5mTyhWL243sdmlxqo5eTFp0CLP2QPvg1RF5mjfaA8XGb
|
||||||
|
L0TAyn6xkH/Yi3qFS+OxsfSqajag/ySyJ+C6YnrplcmInZurpEzqh0b61pt+sulb
|
||||||
|
yluExPe0x2nFC5pHmobU3/MwyW24eBDvoIn4MICdlgVEtPuYTHFrvNddZl5mp5Tl
|
||||||
|
ZXKFvHx/EZj4y9XitWd490lRxH+6FmbSFVuYBgNI0J3wOCudUQIDAQABo1MwUTAd
|
||||||
|
BgNVHQ4EFgQUBKha84woY5WkFxKw7qx1cONg1H8wHwYDVR0jBBgwFoAUBKha84wo
|
||||||
|
Y5WkFxKw7qx1cONg1H8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
||||||
|
AQEAHZMYt9Ry+Xj3vTbzpGFQzYQVTJlfJWSN6eWNOivRFQE5io9kOBEe5noa8aLo
|
||||||
|
dLkw6ztxRP2QRJmlhGCO9/HwS17ckrkgZp3EC2LFnzxcBmoZu+owfxOT1KqpO52O
|
||||||
|
IKOl8eVohi1pEicE4dtTJVcpI7VCMovnXUhzx1Ci4Vibns4a6H+BQa19a1JSpifN
|
||||||
|
tO8U5jkjJ8Jprs/VPFhJj2O3di53oDHaYSE5eOrm2ZO14KFHSk9cGcOGmcYkUv8B
|
||||||
|
nV5vnGadH5Lvfxb/BCpuONabeRdOxMt9u9yQ89vNpxFtRdZDCpGKZBCfmUP+5m3m
|
||||||
|
N8r5CwGcIX/XPC3lKazzbZ8baA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
suite "Secure HTTP server testing suite":
|
||||||
|
|
||||||
|
proc httpsClient(address: TransportAddress,
|
||||||
|
data: string, flags = {NoVerifyHost, NoVerifyServerName}
|
||||||
|
): Future[string] {.async.} =
|
||||||
|
var
|
||||||
|
transp: StreamTransport
|
||||||
|
tlsstream: TlsAsyncStream
|
||||||
|
reader: AsyncStreamReader
|
||||||
|
writer: AsyncStreamWriter
|
||||||
|
|
||||||
|
try:
|
||||||
|
transp = await connect(address)
|
||||||
|
reader = newAsyncStreamReader(transp)
|
||||||
|
writer = newAsyncStreamWriter(transp)
|
||||||
|
tlsstream = newTLSClientAsyncStream(reader, writer, "", flags = flags)
|
||||||
|
if len(data) > 0:
|
||||||
|
await tlsstream.writer.write(data)
|
||||||
|
var rres = await tlsstream.reader.read()
|
||||||
|
return bytesToString(rres)
|
||||||
|
except CatchableError:
|
||||||
|
return "EXCEPTION"
|
||||||
|
finally:
|
||||||
|
if not(isNil(tlsstream)):
|
||||||
|
await allFutures(tlsstream.reader.closeWait(),
|
||||||
|
tlsstream.writer.closeWait())
|
||||||
|
if not(isNil(reader)):
|
||||||
|
await allFutures(reader.closeWait(), writer.closeWait(),
|
||||||
|
transp.closeWait())
|
||||||
|
|
||||||
|
test "HTTPS server (successful handshake) test":
|
||||||
|
proc testHTTPS(address: TransportAddress): Future[bool] {.async.} =
|
||||||
|
var serverRes = false
|
||||||
|
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||||
|
async.} =
|
||||||
|
if r.isOk():
|
||||||
|
let request = r.get()
|
||||||
|
serverRes = true
|
||||||
|
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||||
|
HttpTable.init())
|
||||||
|
else:
|
||||||
|
serverRes = false
|
||||||
|
return dumbResponse()
|
||||||
|
|
||||||
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||||
|
let serverFlags = {Secure}
|
||||||
|
let secureKey = TLSPrivateKey.init(HttpsSelfSignedRsaKey)
|
||||||
|
let secureCert = TLSCertificate.init(HttpsSelfSignedRsaCert)
|
||||||
|
let res = SecureHttpServerRef.new(address, process,
|
||||||
|
socketFlags = socketFlags,
|
||||||
|
serverFlags = serverFlags,
|
||||||
|
tlsPrivateKey = secureKey,
|
||||||
|
tlsCertificate = secureCert)
|
||||||
|
if res.isErr():
|
||||||
|
return false
|
||||||
|
|
||||||
|
let server = res.get()
|
||||||
|
server.start()
|
||||||
|
let message = "GET / HTTP/1.0\r\nHost: https://127.0.0.1:80\r\n\r\n"
|
||||||
|
let data = await httpsClient(address, message)
|
||||||
|
|
||||||
|
await server.stop()
|
||||||
|
await server.closeWait()
|
||||||
|
return serverRes and (data.find("TEST_OK:GET") >= 0)
|
||||||
|
|
||||||
|
check waitFor(testHTTPS(initTAddress("127.0.0.1:30080"))) == true
|
||||||
|
|
||||||
|
test "HTTPS server (failed handshake) test":
|
||||||
|
proc testHTTPS2(address: TransportAddress): Future[bool] {.async.} =
|
||||||
|
var serverRes = false
|
||||||
|
var testFut = newFuture[void]()
|
||||||
|
proc process(r: RequestFence): Future[HttpResponseRef] {.
|
||||||
|
async.} =
|
||||||
|
if r.isOk():
|
||||||
|
let request = r.get()
|
||||||
|
serverRes = false
|
||||||
|
return await request.respond(Http200, "TEST_OK:" & $request.meth,
|
||||||
|
HttpTable.init())
|
||||||
|
else:
|
||||||
|
serverRes = true
|
||||||
|
testFut.complete()
|
||||||
|
return dumbResponse()
|
||||||
|
|
||||||
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||||||
|
let serverFlags = {Secure}
|
||||||
|
let secureKey = TLSPrivateKey.init(HttpsSelfSignedRsaKey)
|
||||||
|
let secureCert = TLSCertificate.init(HttpsSelfSignedRsaCert)
|
||||||
|
let res = SecureHttpServerRef.new(address, process,
|
||||||
|
socketFlags = socketFlags,
|
||||||
|
serverFlags = serverFlags,
|
||||||
|
tlsPrivateKey = secureKey,
|
||||||
|
tlsCertificate = secureCert)
|
||||||
|
if res.isErr():
|
||||||
|
return false
|
||||||
|
|
||||||
|
let server = res.get()
|
||||||
|
server.start()
|
||||||
|
let message = "GET / HTTP/1.0\r\nHost: https://127.0.0.1:80\r\n\r\n"
|
||||||
|
let data = await httpsClient(address, message, {NoVerifyServerName})
|
||||||
|
await testFut
|
||||||
|
await server.stop()
|
||||||
|
await server.closeWait()
|
||||||
|
return serverRes and data == "EXCEPTION"
|
||||||
|
|
||||||
|
check waitFor(testHTTPS2(initTAddress("127.0.0.1:30080"))) == true
|
Loading…
Reference in New Issue