Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for #18161 AsyncHttpServer Issue Too many open files. #18198

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions lib/pure/asynchttpserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

import asyncnet, asyncdispatch, parseutils, uri, strutils
import httpcore
from times import DateTime, now, `-`, inSeconds

export httpcore except parseHeader

Expand All @@ -64,18 +65,22 @@ type
url*: Uri
hostname*: string ## The hostname of the client that made the request.
body*: string
disableKeepalive: bool

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

proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
maxBody = 8388608, keepaliveTimeout = 3600): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance.
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort,
maxBody: maxBody, keepaliveTimeout: keepaliveTimeout)

proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers:
Expand Down Expand Up @@ -276,10 +281,11 @@ proc processRequest(
# connection will not be closed and will be kept in the connection pool.

# Persistent connections
if (request.protocol == HttpVer11 and
if not request.disableKeepalive and
(request.protocol == HttpVer11 and
cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
(request.protocol == HttpVer10 and
cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0)):
# In HTTP 1.1 we assume that connection is persistent. Unless connection
# header states otherwise.
# In HTTP 1.0 we assume that the connection should not be persistent.
Expand All @@ -295,15 +301,27 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
var request = newFutureVar[Request]("asynchttpserver.processClient")
request.mget().url = initUri()
request.mget().headers = newHttpHeaders()
request.mget().disableKeepalive = false
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)

let startTimeout = now()
while not client.isClosed:
let fds = activeDescriptors()
# Disables the keepalive if the active file descriptors exceeds the maxFDs value
# or if the maximum keepalive timeout is exceeded.
# The maxFDs should be replaced by the keepaliveConn?
if (fds > server.maxFDs) or ((now() - startTimeout).inSeconds > server.keepaliveTimeout):
request.mget().disableKeepalive = true
Comment on lines +314 to +315
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you shouldn't do this at all. You should check the headers and see whether keep-alive has been turned off.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dom96 can you help me with the code? I want to close the connection when the number of file descriptors is high. Even if the value of keepalive is on I want to close the connection anyway. It is temporary until the number of files descriptors goes down, when the value goes down the connection can remain open. Please help me to solve the issue!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrhdias This isn't what you should do though! Don't close connections when the number of FDs is high, this will just lead to more problems.


let retry = await processRequest(
server, request, client, address, lineFut, callback
)
if not retry: break

client.close() # Close the connection to not increase the number of open files.


const
nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \
## when `maxDescriptors` is not available.
Expand Down Expand Up @@ -339,6 +357,7 @@ proc acceptRequest*(server: AsyncHttpServer,
var (address, client) = await server.socket.acceptAddr()
asyncCheck processClient(server, client, address, callback)


proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
address = "";
Expand All @@ -365,6 +384,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
#echo(f.isNil)
#echo(f.repr)


proc close*(server: AsyncHttpServer) =
## Terminates the async http server instance.
server.socket.close()
Expand Down