mirror of
https://github.com/logos-storage/nim-chronos.git
synced 2026-01-02 21:43:06 +00:00
368 lines
14 KiB
Nim
368 lines
14 KiB
Nim
|
|
# 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, algorithm, strutils]
|
||
|
|
import ../chronos, ../chronos/apps
|
||
|
|
|
||
|
|
suite "HTTP server testing suite":
|
||
|
|
proc httpClient(address: TransportAddress,
|
||
|
|
data: string): Future[string] {.async.} =
|
||
|
|
var transp: StreamTransport
|
||
|
|
try:
|
||
|
|
transp = await connect(address)
|
||
|
|
if len(data) > 0:
|
||
|
|
let wres {.used.} = await transp.write(data)
|
||
|
|
var rres = await transp.read()
|
||
|
|
var sres = newString(len(rres))
|
||
|
|
if len(rres) > 0:
|
||
|
|
copyMem(addr sres[0], addr rres[0], len(rres))
|
||
|
|
return sres
|
||
|
|
except CatchableError:
|
||
|
|
return "EXCEPTION"
|
||
|
|
finally:
|
||
|
|
if not(isNil(transp)):
|
||
|
|
await closeWait(transp)
|
||
|
|
|
||
|
|
test "Request headers timeout test":
|
||
|
|
proc testTimeout(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
let request = r.get()
|
||
|
|
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||
|
|
else:
|
||
|
|
if r.error().error == HTTPServerError.TimeoutError:
|
||
|
|
serverRes = true
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process, socketFlags = socketFlags,
|
||
|
|
httpHeadersTimeout = 100.milliseconds)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let data = await httpClient(address, "")
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.startsWith("HTTP/1.1 408"))
|
||
|
|
|
||
|
|
check waitFor(testTimeout(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "Empty headers test":
|
||
|
|
proc testEmpty(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
let request = r.get()
|
||
|
|
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||
|
|
else:
|
||
|
|
if r.error().error == HTTPServerError.CriticalError:
|
||
|
|
serverRes = true
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process, socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let data = await httpClient(address, "\r\n\r\n")
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.startsWith("HTTP/1.1 400"))
|
||
|
|
|
||
|
|
check waitFor(testEmpty(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "Too big headers test":
|
||
|
|
proc testTooBig(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
let request = r.get()
|
||
|
|
return await request.respond(Http200, "TEST_OK", HttpTable.init())
|
||
|
|
else:
|
||
|
|
if r.error().error == HTTPServerError.CriticalError:
|
||
|
|
serverRes = true
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
maxHeadersSize = 10,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let data = await httpClient(address, "GET / HTTP/1.1\r\n\r\n")
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.startsWith("HTTP/1.1 413"))
|
||
|
|
|
||
|
|
check waitFor(testTooBig(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "Query arguments test":
|
||
|
|
proc testQuery(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
let request = r.get()
|
||
|
|
var kres = newSeq[string]()
|
||
|
|
for k, v in request.query.stringItems():
|
||
|
|
kres.add(k & ":" & v)
|
||
|
|
sort(kres)
|
||
|
|
serverRes = true
|
||
|
|
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||
|
|
HttpTable.init())
|
||
|
|
else:
|
||
|
|
serverRes = false
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let data1 = await httpClient(address,
|
||
|
|
"GET /?a=1&a=2&b=3&c=4 HTTP/1.0\r\n\r\n")
|
||
|
|
let data2 = await httpClient(address,
|
||
|
|
"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.close()
|
||
|
|
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
|
||
|
|
|
||
|
|
check waitFor(testQuery(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "Headers test":
|
||
|
|
proc testHeaders(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
let request = r.get()
|
||
|
|
var kres = newSeq[string]()
|
||
|
|
for k, v in request.headers.stringItems():
|
||
|
|
kres.add(k & ":" & v)
|
||
|
|
sort(kres)
|
||
|
|
serverRes = true
|
||
|
|
return await request.respond(Http200, "TEST_OK:" & kres.join(":"),
|
||
|
|
HttpTable.init())
|
||
|
|
else:
|
||
|
|
serverRes = false
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let message =
|
||
|
|
"GET / HTTP/1.0\r\n" &
|
||
|
|
"Host: www.google.com\r\n" &
|
||
|
|
"Content-Type: text/html\r\n" &
|
||
|
|
"Expect: 100-continue\r\n" &
|
||
|
|
"Cookie: 1\r\n" &
|
||
|
|
"Cookie: 2\r\n\r\n"
|
||
|
|
let expect = "TEST_OK:content-type:text/html:cookie:1:cookie:2" &
|
||
|
|
":expect:100-continue:host:www.google.com"
|
||
|
|
let data = await httpClient(address, message)
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.find(expect) >= 0)
|
||
|
|
|
||
|
|
check waitFor(testHeaders(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "POST arguments (application/x-www-form-urlencoded) test":
|
||
|
|
proc testPostUrl(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
var kres = newSeq[string]()
|
||
|
|
let request = r.get()
|
||
|
|
if request.meth in PostMethods:
|
||
|
|
let post = await request.post()
|
||
|
|
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())
|
||
|
|
else:
|
||
|
|
serverRes = false
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let message =
|
||
|
|
"POST / HTTP/1.0\r\n" &
|
||
|
|
"Content-Type: application/x-www-form-urlencoded\r\n" &
|
||
|
|
"Content-Length: 20" &
|
||
|
|
"Cookie: 2\r\n\r\n" &
|
||
|
|
"a=a&b=b&c=c&d=%D0%9F"
|
||
|
|
let data = await httpClient(address, message)
|
||
|
|
let expect = "TEST_OK:a:a:b:b:c:c:d:П"
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.find(expect) >= 0)
|
||
|
|
|
||
|
|
check waitFor(testPostUrl(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "POST arguments (multipart/form-data) test":
|
||
|
|
proc testPostMultipart(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
var kres = newSeq[string]()
|
||
|
|
let request = r.get()
|
||
|
|
if request.meth in PostMethods:
|
||
|
|
let post = await request.post()
|
||
|
|
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())
|
||
|
|
else:
|
||
|
|
serverRes = false
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let message =
|
||
|
|
"POST / HTTP/1.0\r\n" &
|
||
|
|
"Host: 127.0.0.1:30080\r\n" &
|
||
|
|
"User-Agent: curl/7.55.1\r\n" &
|
||
|
|
"Accept: */*\r\n" &
|
||
|
|
"Content-Length: 343\r\n" &
|
||
|
|
"Content-Type: multipart/form-data; " &
|
||
|
|
"boundary=------------------------ab5706ba6f80b795\r\n\r\n" &
|
||
|
|
"--------------------------ab5706ba6f80b795\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key1\"\r\n\r\n" &
|
||
|
|
"value1\r\n" &
|
||
|
|
"--------------------------ab5706ba6f80b795\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key2\"\r\n\r\n" &
|
||
|
|
"value2\r\n" &
|
||
|
|
"--------------------------ab5706ba6f80b795\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key2\"\r\n\r\n" &
|
||
|
|
"value4\r\n" &
|
||
|
|
"--------------------------ab5706ba6f80b795--\r\n"
|
||
|
|
let data = await httpClient(address, message)
|
||
|
|
let expect = "TEST_OK:key1:value1:key2:value2:key2:value4"
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.find(expect) >= 0)
|
||
|
|
|
||
|
|
check waitFor(testPostMultipart(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "POST arguments (multipart/form-data + chunked encoding) test":
|
||
|
|
proc testPostMultipart2(address: TransportAddress): Future[bool] {.async.} =
|
||
|
|
var serverRes = false
|
||
|
|
proc process(r: RequestFence[HttpRequestRef]): Future[HttpResponseRef] {.
|
||
|
|
async.} =
|
||
|
|
if r.isOk():
|
||
|
|
var kres = newSeq[string]()
|
||
|
|
let request = r.get()
|
||
|
|
if request.meth in PostMethods:
|
||
|
|
let post = await request.post()
|
||
|
|
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())
|
||
|
|
else:
|
||
|
|
serverRes = false
|
||
|
|
return dumbResponse()
|
||
|
|
|
||
|
|
let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
|
||
|
|
let res = HttpServerRef.new(address, process,
|
||
|
|
socketFlags = socketFlags)
|
||
|
|
if res.isErr():
|
||
|
|
return false
|
||
|
|
|
||
|
|
let server = res.get()
|
||
|
|
server.start()
|
||
|
|
|
||
|
|
let message =
|
||
|
|
"POST / HTTP/1.0\r\n" &
|
||
|
|
"Host: 127.0.0.1:30080\r\n" &
|
||
|
|
"Transfer-Encoding: chunked\r\n" &
|
||
|
|
"Content-Type: multipart/form-data; boundary=---" &
|
||
|
|
"---------------------f98f0e32c55fa2ae\r\n\r\n" &
|
||
|
|
"271\r\n" &
|
||
|
|
"--------------------------f98f0e32c55fa2ae\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key1\"\r\n\r\n" &
|
||
|
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
|
||
|
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n" &
|
||
|
|
"--------------------------f98f0e32c55fa2ae\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key2\"\r\n\r\n" &
|
||
|
|
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" &
|
||
|
|
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\r\n" &
|
||
|
|
"--------------------------f98f0e32c55fa2ae\r\n" &
|
||
|
|
"Content-Disposition: form-data; name=\"key2\"\r\n\r\n" &
|
||
|
|
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" &
|
||
|
|
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\r\n" &
|
||
|
|
"--------------------------f98f0e32c55fa2ae--\r\n" &
|
||
|
|
"\r\n0\r\n\r\n"
|
||
|
|
|
||
|
|
let data = await httpClient(address, message)
|
||
|
|
let expect = "TEST_OK:key1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
|
||
|
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
|
||
|
|
"AAAAA:key2:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" &
|
||
|
|
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" &
|
||
|
|
"BBB:key2:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" &
|
||
|
|
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
|
||
|
|
await server.stop()
|
||
|
|
await server.close()
|
||
|
|
return serverRes and (data.find(expect) >= 0)
|
||
|
|
|
||
|
|
check waitFor(testPostMultipart2(initTAddress("127.0.0.1:30080"))) == true
|
||
|
|
|
||
|
|
test "Leaks test":
|
||
|
|
check:
|
||
|
|
getTracker("async.stream.reader").isLeaked() == false
|
||
|
|
getTracker("async.stream.writer").isLeaked() == false
|
||
|
|
getTracker("stream.server").isLeaked() == false
|
||
|
|
getTracker("stream.transport").isLeaked() == false
|