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
|
||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||
# MIT license (LICENSE-MIT)
|
||||
import apps/http/httpserver
|
||||
export httpserver
|
||||
import apps/http/httpserver, apps/http/shttpserver
|
||||
export httpserver, shttpserver
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
import std/[tables, options, uri, strutils]
|
||||
import stew/[results, base10], httputils
|
||||
import ../../asyncloop, ../../asyncsync
|
||||
import ../../streams/[asyncstream, boundstream, chunkstream, tlsstream]
|
||||
import ../../streams/[asyncstream, boundstream, chunkstream]
|
||||
import httptable, httpcommon, multipart
|
||||
export httptable, httpcommon, httputils, multipart, tlsstream, asyncstream,
|
||||
export httptable, httpcommon, httputils, multipart, asyncstream,
|
||||
uri, tables, options, results
|
||||
|
||||
type
|
||||
|
@ -46,6 +46,10 @@ type
|
|||
HttpProcessCallback* =
|
||||
proc(req: RequestFence): Future[HttpResponseRef] {.gcsafe.}
|
||||
|
||||
HttpConnectionCallback* =
|
||||
proc(server: HttpServerRef,
|
||||
transp: StreamTransport): Future[HttpConnectionRef] {.gcsafe.}
|
||||
|
||||
HttpServer* = object of RootObj
|
||||
instance*: StreamServer
|
||||
address*: TransportAddress
|
||||
|
@ -56,16 +60,15 @@ type
|
|||
serverIdent*: string
|
||||
flags*: set[HttpServerFlags]
|
||||
socketFlags*: set[ServerFlags]
|
||||
secureFlags*: set[TLSFlags]
|
||||
connections*: Table[string, Future[void]]
|
||||
acceptLoop*: Future[void]
|
||||
lifetime*: Future[void]
|
||||
headersTimeout: Duration
|
||||
bufferSize: int
|
||||
maxHeadersSize: int
|
||||
maxRequestBodySize: int
|
||||
processCallback: HttpProcessCallback
|
||||
tlsPrivateKey: TLSPrivateKey
|
||||
tlsCertificate: TLSCertificate
|
||||
createConnCallback: HttpConnectionCallback
|
||||
|
||||
HttpServerRef* = ref HttpServer
|
||||
|
||||
|
@ -103,9 +106,8 @@ type
|
|||
HttpConnection* = object of RootObj
|
||||
server*: HttpServerRef
|
||||
transp: StreamTransport
|
||||
mainReader: AsyncStreamReader
|
||||
mainWriter: AsyncStreamWriter
|
||||
tlsStream: TLSAsyncStream
|
||||
mainReader*: AsyncStreamReader
|
||||
mainWriter*: AsyncStreamWriter
|
||||
reader*: AsyncStreamReader
|
||||
writer*: AsyncStreamWriter
|
||||
buffer: seq[byte]
|
||||
|
@ -119,6 +121,50 @@ proc init(htype: typedesc[HttpProcessError], error: HTTPServerError,
|
|||
code: HttpCode): HttpProcessError {.raises: [Defect].} =
|
||||
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],
|
||||
address: TransportAddress,
|
||||
processCallback: HttpProcessCallback,
|
||||
|
@ -126,9 +172,6 @@ proc new*(htype: typedesc[HttpServerRef],
|
|||
socketFlags: set[ServerFlags] = {ReuseAddr},
|
||||
serverUri = Uri(),
|
||||
serverIdent = "",
|
||||
tlsPrivateKey: TLSPrivateKey = nil,
|
||||
tlsCertificate: TLSCertificate = nil,
|
||||
secureFlags: set[TLSFlags] = {},
|
||||
maxConnections: int = -1,
|
||||
bufferSize: int = 4096,
|
||||
backlogSize: int = 100,
|
||||
|
@ -136,50 +179,30 @@ proc new*(htype: typedesc[HttpServerRef],
|
|||
maxHeadersSize: int = 8192,
|
||||
maxRequestBodySize: int = 1_048_576): HttpResult[HttpServerRef] =
|
||||
|
||||
if HttpServerFlags.Secure in serverFlags:
|
||||
if isNil(tlsPrivateKey) or isNil(tlsCertificate):
|
||||
return err("PrivateKey or Certificate is missing")
|
||||
let serverUri =
|
||||
if len(serverUri.hostname) > 0:
|
||||
serverUri
|
||||
else:
|
||||
try:
|
||||
parseUri("http://" & $address & "/")
|
||||
except TransportAddressError as exc:
|
||||
return err(exc.msg)
|
||||
|
||||
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 =
|
||||
let serverInstance =
|
||||
try:
|
||||
if len(serverUri.hostname) > 0 and isAbsolute(serverUri):
|
||||
serverUri
|
||||
else:
|
||||
if HttpServerFlags.Secure in serverFlags:
|
||||
parseUri("https://" & $address & "/")
|
||||
else:
|
||||
parseUri("http://" & $address & "/")
|
||||
except TransportAddressError as exc:
|
||||
createStreamServer(address, flags = socketFlags, bufferSize = bufferSize,
|
||||
backlog = backlogSize)
|
||||
except TransportOsError as exc:
|
||||
return err(exc.msg)
|
||||
except CatchableError as exc:
|
||||
return err(exc.msg)
|
||||
|
||||
try:
|
||||
res.instance = createStreamServer(address, flags = socketFlags,
|
||||
bufferSize = bufferSize,
|
||||
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:
|
||||
return err(exc.msg)
|
||||
except CatchableError as exc:
|
||||
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].} =
|
||||
if req.response.isNone():
|
||||
|
@ -450,47 +473,32 @@ proc getRequest(conn: HttpConnectionRef): Future[HttpRequestRef] {.async.} =
|
|||
except AsyncStreamLimitError:
|
||||
raiseHttpCriticalError("Maximum size of request headers reached", Http431)
|
||||
|
||||
proc new(ht: typedesc[HttpConnectionRef], server: HttpServerRef,
|
||||
transp: StreamTransport): HttpConnectionRef =
|
||||
let mainReader = newAsyncStreamReader(transp)
|
||||
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,
|
||||
proc init*(value: var HttpConnection, server: HttpServerRef,
|
||||
transp: StreamTransport) =
|
||||
value = HttpConnection(
|
||||
server: server,
|
||||
transp: transp,
|
||||
buffer: newSeq[byte](server.maxHeadersSize),
|
||||
mainReader: mainReader,
|
||||
mainWriter: mainWriter,
|
||||
tlsStream: tlsStream,
|
||||
reader: reader,
|
||||
writer: writer
|
||||
mainReader: newAsyncStreamReader(transp),
|
||||
mainWriter: newAsyncStreamWriter(transp)
|
||||
)
|
||||
|
||||
proc closeWait(conn: HttpConnectionRef) {.async.} =
|
||||
if HttpServerFlags.Secure in conn.server.flags:
|
||||
# First we will close TLS streams.
|
||||
await allFutures(conn.reader.closeWait(), conn.writer.closeWait())
|
||||
proc new(ht: typedesc[HttpConnectionRef], server: HttpServerRef,
|
||||
transp: StreamTransport): HttpConnectionRef =
|
||||
var res = HttpConnectionRef()
|
||||
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.
|
||||
await allFutures(conn.mainReader.closeWait(), conn.mainWriter.closeWait(),
|
||||
conn.transp.closeWait())
|
||||
|
@ -505,20 +513,7 @@ proc closeWait(req: HttpRequestRef) {.async.} =
|
|||
proc createConnection(server: HttpServerRef,
|
||||
transp: StreamTransport): Future[HttpConnectionRef] {.
|
||||
async.} =
|
||||
var conn = 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")
|
||||
return HttpConnectionRef.new(server, transp)
|
||||
|
||||
proc processLoop(server: HttpServerRef, transp: StreamTransport) {.async.} =
|
||||
var
|
||||
|
@ -527,10 +522,9 @@ proc processLoop(server: HttpServerRef, transp: StreamTransport) {.async.} =
|
|||
runLoop = false
|
||||
|
||||
try:
|
||||
conn = await createConnection(server, transp)
|
||||
conn = await server.createConnCallback(server, transp)
|
||||
runLoop = true
|
||||
except CancelledError:
|
||||
# We could be cancelled only when we perform TLS handshake, connection
|
||||
server.connections.del(transp.getId())
|
||||
await transp.closeWait()
|
||||
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)
|
||||
import testmacro, testsync, testsoon, testtime, testfut, testsignal,
|
||||
testaddress, testdatagram, teststream, testserver, testbugs, testnet,
|
||||
testasyncstream, testhttpserver
|
||||
testasyncstream, testhttpserver, testshttpserver
|
||||
import testutils
|
||||
|
|
|
@ -6,68 +6,10 @@
|
|||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||
# MIT license (LICENSE-MIT)
|
||||
import std/[strutils, unittest, algorithm, strutils]
|
||||
import ../chronos, ../chronos/apps
|
||||
import ../chronos, ../chronos/apps/http/httpserver
|
||||
import stew/base10
|
||||
|
||||
# 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-----
|
||||
"""
|
||||
when defined(nimHasUsed): {.used.}
|
||||
|
||||
suite "HTTP server testing suite":
|
||||
type
|
||||
|
@ -89,34 +31,6 @@ suite "HTTP server testing suite":
|
|||
if not(isNil(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,
|
||||
operation: TooBigTest): Future[bool] {.async.} =
|
||||
var serverRes = false
|
||||
|
@ -606,82 +520,6 @@ suite "HTTP server testing suite":
|
|||
|
||||
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":
|
||||
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