Skip to content

Commit e760f30

Browse files
authored
Fix for nim-lang#18161 AsyncHttpServer Issue: Too many open files, against devel branch.
The client header "connection" if equal to "keep-alive" is disabled whenever the number of file descriptors exceeds the specified maxFDs or exceeds the keep-alive timeout. If the value of header "connection" equal "close", the connection is closed, if it is "keep-alive" it remains open and the number of file descriptors increases and the server die. To avoid this issue the "keep-alive" connection is disabled. It is temporary until the number of files descriptors goes down, when the value goes down the connection can remain open. It also solves the issue when the "connection" is equal to "update".
1 parent e7816a3 commit e760f30

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

lib/pure/asynchttpserver.nim

+18-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import asyncnet, asyncdispatch, parseutils, uri, strutils
4343
import httpcore
4444
from nativesockets import getLocalAddr, AF_INET
4545
import std/private/since
46+
from times import DateTime, now, `-`, inSeconds
4647

4748
export httpcore except parseHeader
4849

@@ -63,13 +64,16 @@ type
6364
url*: Uri
6465
hostname*: string ## The hostname of the client that made the request.
6566
body*: string
67+
disableKeepalive: bool
6668

6769
AsyncHttpServer* = ref object
6870
socket: AsyncSocket
6971
reuseAddr: bool
7072
reusePort: bool
7173
maxBody: int ## The maximum content-length that will be read for the body.
7274
maxFDs: int
75+
keepaliveTimeout: int ## The value for keepalive timeout in seconds and \
76+
## after which the connection will be closed.
7377

7478
proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} =
7579
## Returns the port `self` was bound to.
@@ -85,9 +89,10 @@ proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} =
8589
result = getLocalAddr(self.socket.getFd, AF_INET)[1]
8690

8791
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
88-
maxBody = 8388608): AsyncHttpServer =
92+
maxBody = 8388608, keepaliveTimeout = 3600): AsyncHttpServer =
8993
## Creates a new `AsyncHttpServer` instance.
90-
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
94+
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort,
95+
maxBody: maxBody, keepaliveTimeout: keepaliveTimeout)
9196

9297
proc addHeaders(msg: var string, headers: HttpHeaders) =
9398
for k, v in headers:
@@ -336,7 +341,8 @@ proc processRequest(
336341
# connection will not be closed and will be kept in the connection pool.
337342

338343
# Persistent connections
339-
if (request.protocol == HttpVer11 and
344+
if (not request.disableKeepalive and
345+
request.protocol == HttpVer11 and
340346
cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
341347
(request.protocol == HttpVer10 and
342348
cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
@@ -355,15 +361,24 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
355361
var request = newFutureVar[Request]("asynchttpserver.processClient")
356362
request.mget().url = initUri()
357363
request.mget().headers = newHttpHeaders()
364+
request.mget().disableKeepalive = false
358365
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
359366
lineFut.mget() = newStringOfCap(80)
360367

368+
let startTimeout = now()
361369
while not client.isClosed:
370+
let fds = activeDescriptors()
371+
# The maxFDs should be replaced by the keepaliveConn?
372+
if (fds > server.maxFDs) or ((now() - startTimeout).inSeconds > server.keepaliveTimeout):
373+
request.mget().disableKeepalive = true
374+
362375
let retry = await processRequest(
363376
server, request, client, address, lineFut, callback
364377
)
365378
if not retry: break
366379

380+
client.close() # When in doubt, close the connection to not increase the number of open files.
381+
367382
const
368383
nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \
369384
## when `maxDescriptors` is not available.

0 commit comments

Comments
 (0)