Skip to content

Commit e4cb480

Browse files
authored
Fix inability to change httpclient's internal buffer size. (#531)
Add test. Address #529.
1 parent 0d050d5 commit e4cb480

File tree

2 files changed

+103
-23
lines changed

2 files changed

+103
-23
lines changed

chronos/apps/http/httpclient.nim

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ type
159159
redirectCount: int
160160
timestamp*: Moment
161161
duration*: Duration
162+
headersBuffer: seq[byte]
162163

163164
HttpClientRequestRef* = ref HttpClientRequest
164165

@@ -859,6 +860,7 @@ proc closeWait*(request: HttpClientRequestRef) {.async: (raises: []).} =
859860
await noCancel(allFutures(pending))
860861
request.session = nil
861862
request.error = nil
863+
request.headersBuffer.reset()
862864
request.state = HttpReqRespState.Closed
863865
untrackCounter(HttpClientRequestTrackerName)
864866

@@ -992,38 +994,40 @@ proc prepareResponse(
992994

993995
proc getResponse(req: HttpClientRequestRef): Future[HttpClientResponseRef] {.
994996
async: (raises: [CancelledError, HttpError]).} =
995-
var buffer: array[HttpMaxHeadersSize, byte]
996997
let timestamp = Moment.now()
997998
req.connection.setTimestamp(timestamp)
998999
let
9991000
bytesRead =
10001001
try:
1001-
await req.connection.reader.readUntil(addr buffer[0],
1002-
len(buffer), HeadersMark).wait(
1002+
await req.connection.reader.readUntil(addr req.headersBuffer[0],
1003+
len(req.headersBuffer),
1004+
HeadersMark).wait(
10031005
req.session.headersTimeout)
10041006
except AsyncTimeoutError:
10051007
raiseHttpReadError("Reading response headers timed out")
10061008
except AsyncStreamError as exc:
10071009
raiseHttpReadError(
10081010
"Could not read response headers, reason: " & $exc.msg)
10091011

1010-
let response = prepareResponse(req, buffer.toOpenArray(0, bytesRead - 1))
1011-
if response.isErr():
1012-
raiseHttpProtocolError(response.error())
1013-
let res = response.get()
1014-
res.setTimestamp(timestamp)
1015-
return res
1012+
let response =
1013+
prepareResponse(req,
1014+
req.headersBuffer.toOpenArray(0, bytesRead - 1)).valueOr:
1015+
raiseHttpProtocolError(error)
1016+
response.setTimestamp(timestamp)
1017+
response
10161018

10171019
proc new*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10181020
ha: HttpAddress, meth: HttpMethod = MethodGet,
10191021
version: HttpVersion = HttpVersion11,
10201022
flags: set[HttpClientRequestFlag] = {},
1023+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10211024
headers: openArray[HttpHeaderTuple] = [],
10221025
body: openArray[byte] = []): HttpClientRequestRef =
10231026
let res = HttpClientRequestRef(
10241027
state: HttpReqRespState.Ready, session: session, meth: meth,
10251028
version: version, flags: flags, headers: HttpTable.init(headers),
1026-
address: ha, bodyFlag: HttpClientBodyFlag.Custom, buffer: @body
1029+
address: ha, bodyFlag: HttpClientBodyFlag.Custom, buffer: @body,
1030+
headersBuffer: newSeq[byte](max(maxResponseHeadersSize, HttpMaxHeadersSize))
10271031
)
10281032
trackCounter(HttpClientRequestTrackerName)
10291033
res
@@ -1032,62 +1036,74 @@ proc new*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10321036
url: string, meth: HttpMethod = MethodGet,
10331037
version: HttpVersion = HttpVersion11,
10341038
flags: set[HttpClientRequestFlag] = {},
1039+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10351040
headers: openArray[HttpHeaderTuple] = [],
10361041
body: openArray[byte] = []): HttpResult[HttpClientRequestRef] =
10371042
let address = ? session.getAddress(parseUri(url))
10381043
let res = HttpClientRequestRef(
10391044
state: HttpReqRespState.Ready, session: session, meth: meth,
10401045
version: version, flags: flags, headers: HttpTable.init(headers),
1041-
address: address, bodyFlag: HttpClientBodyFlag.Custom, buffer: @body
1046+
address: address, bodyFlag: HttpClientBodyFlag.Custom, buffer: @body,
1047+
headersBuffer: newSeq[byte](max(maxResponseHeadersSize, HttpMaxHeadersSize))
10421048
)
10431049
trackCounter(HttpClientRequestTrackerName)
10441050
ok(res)
10451051

10461052
proc get*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10471053
url: string, version: HttpVersion = HttpVersion11,
10481054
flags: set[HttpClientRequestFlag] = {},
1055+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10491056
headers: openArray[HttpHeaderTuple] = []
10501057
): HttpResult[HttpClientRequestRef] =
1051-
HttpClientRequestRef.new(session, url, MethodGet, version, flags, headers)
1058+
HttpClientRequestRef.new(session, url, MethodGet, version, flags,
1059+
maxResponseHeadersSize, headers)
10521060

10531061
proc get*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10541062
ha: HttpAddress, version: HttpVersion = HttpVersion11,
10551063
flags: set[HttpClientRequestFlag] = {},
1064+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10561065
headers: openArray[HttpHeaderTuple] = []
10571066
): HttpClientRequestRef =
1058-
HttpClientRequestRef.new(session, ha, MethodGet, version, flags, headers)
1067+
HttpClientRequestRef.new(session, ha, MethodGet, version, flags,
1068+
maxResponseHeadersSize, headers)
10591069

10601070
proc post*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10611071
url: string, version: HttpVersion = HttpVersion11,
10621072
flags: set[HttpClientRequestFlag] = {},
1073+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10631074
headers: openArray[HttpHeaderTuple] = [],
10641075
body: openArray[byte] = []
10651076
): HttpResult[HttpClientRequestRef] =
1066-
HttpClientRequestRef.new(session, url, MethodPost, version, flags, headers,
1067-
body)
1077+
HttpClientRequestRef.new(session, url, MethodPost, version, flags,
1078+
maxResponseHeadersSize, headers, body)
10681079

10691080
proc post*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10701081
url: string, version: HttpVersion = HttpVersion11,
10711082
flags: set[HttpClientRequestFlag] = {},
1083+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10721084
headers: openArray[HttpHeaderTuple] = [],
10731085
body: openArray[char] = []): HttpResult[HttpClientRequestRef] =
1074-
HttpClientRequestRef.new(session, url, MethodPost, version, flags, headers,
1086+
HttpClientRequestRef.new(session, url, MethodPost, version, flags,
1087+
maxResponseHeadersSize, headers,
10751088
body.toOpenArrayByte(0, len(body) - 1))
10761089

10771090
proc post*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10781091
ha: HttpAddress, version: HttpVersion = HttpVersion11,
10791092
flags: set[HttpClientRequestFlag] = {},
1093+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10801094
headers: openArray[HttpHeaderTuple] = [],
10811095
body: openArray[byte] = []): HttpClientRequestRef =
1082-
HttpClientRequestRef.new(session, ha, MethodPost, version, flags, headers,
1083-
body)
1096+
HttpClientRequestRef.new(session, ha, MethodPost, version, flags,
1097+
maxResponseHeadersSize, headers, body)
10841098

10851099
proc post*(t: typedesc[HttpClientRequestRef], session: HttpSessionRef,
10861100
ha: HttpAddress, version: HttpVersion = HttpVersion11,
10871101
flags: set[HttpClientRequestFlag] = {},
1102+
maxResponseHeadersSize: int = HttpMaxHeadersSize,
10881103
headers: openArray[HttpHeaderTuple] = [],
10891104
body: openArray[char] = []): HttpClientRequestRef =
1090-
HttpClientRequestRef.new(session, ha, MethodPost, version, flags, headers,
1105+
HttpClientRequestRef.new(session, ha, MethodPost, version, flags,
1106+
maxResponseHeadersSize, headers,
10911107
body.toOpenArrayByte(0, len(body) - 1))
10921108

10931109
proc prepareRequest(request: HttpClientRequestRef): string =
@@ -1454,8 +1470,10 @@ proc redirect*(request: HttpClientRequestRef,
14541470
var res = request.headers
14551471
res.set(HostHeader, ha.hostname)
14561472
res
1457-
var res = HttpClientRequestRef.new(request.session, ha, request.meth,
1458-
request.version, request.flags, headers.toList(), request.buffer)
1473+
var res =
1474+
HttpClientRequestRef.new(request.session, ha, request.meth,
1475+
request.version, request.flags, headers = headers.toList(),
1476+
body = request.buffer)
14591477
res.redirectCount = redirectCount
14601478
ok(res)
14611479

@@ -1478,8 +1496,10 @@ proc redirect*(request: HttpClientRequestRef,
14781496
var res = request.headers
14791497
res.set(HostHeader, address.hostname)
14801498
res
1481-
var res = HttpClientRequestRef.new(request.session, address, request.meth,
1482-
request.version, request.flags, headers.toList(), request.buffer)
1499+
var res =
1500+
HttpClientRequestRef.new(request.session, address, request.meth,
1501+
request.version, request.flags, headers = headers.toList(),
1502+
body = request.buffer)
14831503
res.redirectCount = redirectCount
14841504
ok(res)
14851505

tests/testhttpclient.nim

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,3 +1518,63 @@ suite "HTTP client testing suite":
15181518
res.isErr() and res.error == HttpAddressErrorType.NameLookupFailed
15191519
res.error.isRecoverableError()
15201520
not(res.error.isCriticalError())
1521+
1522+
asyncTest "HTTPS response headers buffer size test":
1523+
const HeadersSize = HttpMaxHeadersSize
1524+
let expectValue =
1525+
string.fromBytes(createBigMessage("HEADERSTEST", HeadersSize))
1526+
proc process(r: RequestFence): Future[HttpResponseRef] {.
1527+
async: (raises: [CancelledError]).} =
1528+
if r.isOk():
1529+
let request = r.get()
1530+
try:
1531+
case request.uri.path
1532+
of "/test":
1533+
let headers = HttpTable.init([("big-header", expectValue)])
1534+
await request.respond(Http200, "ok", headers)
1535+
else:
1536+
await request.respond(Http404, "Page not found")
1537+
except HttpWriteError as exc:
1538+
defaultResponse(exc)
1539+
else:
1540+
defaultResponse()
1541+
1542+
var server = createServer(initTAddress("127.0.0.1:0"), process, false)
1543+
server.start()
1544+
let
1545+
address = server.instance.localAddress()
1546+
ha = getAddress(address, HttpClientScheme.NonSecure, "/test")
1547+
session = HttpSessionRef.new()
1548+
let
1549+
req1 = HttpClientRequestRef.new(session, ha)
1550+
req2 =
1551+
HttpClientRequestRef.new(session, ha,
1552+
maxResponseHeadersSize = HttpMaxHeadersSize * 2)
1553+
res1 =
1554+
try:
1555+
let res {.used.} = await send(req1)
1556+
await closeWait(req1)
1557+
await closeWait(res)
1558+
false
1559+
except HttpReadError:
1560+
true
1561+
except HttpError:
1562+
await closeWait(req1)
1563+
false
1564+
except CancelledError:
1565+
await closeWait(req1)
1566+
false
1567+
1568+
res2 = await send(req2)
1569+
1570+
check:
1571+
res1 == true
1572+
res2.status == 200
1573+
res2.headers.getString("big-header") == expectValue
1574+
1575+
await req1.closeWait()
1576+
await req2.closeWait()
1577+
await res2.closeWait()
1578+
await session.closeWait()
1579+
await server.stop()
1580+
await server.closeWait()

0 commit comments

Comments
 (0)