From 4237486605e816c046ca08df866ba731b7c7915d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 4 Jul 2021 13:59:37 -0700 Subject: [PATCH 1/2] reusePort = false now default; honored regarless of numThreads --- httpbeast.nimble | 2 +- src/httpbeast.nim | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/httpbeast.nimble b/httpbeast.nimble index ea2be4e..50246aa 100644 --- a/httpbeast.nimble +++ b/httpbeast.nimble @@ -1,6 +1,6 @@ # Package -version = "0.4.0" +version = "0.4.1" author = "Dominik Picheta" description = "A super-fast epoll-backed and parallel HTTP server." license = "MIT" diff --git a/src/httpbeast.nim b/src/httpbeast.nim index 6e9f307..39874eb 100644 --- a/src/httpbeast.nim +++ b/src/httpbeast.nim @@ -54,9 +54,7 @@ type domain*: Domain numThreads: int loggers: seq[Logger] - reusePort: bool - ## controls whether to fail with "Address already in use". - ## Setting this to false will raise when `threads` are on. + reusePort: bool ## controls whether to fail with "Address already in use". HttpBeastDefect* = ref object of Defect @@ -67,7 +65,7 @@ proc initSettings*(port: Port = Port(8080), bindAddr: string = "", numThreads: int = 0, domain = Domain.AF_INET, - reusePort = true): Settings = + reusePort = false): Settings = Settings( port: port, bindAddr: bindAddr, @@ -311,6 +309,12 @@ proc updateDate(fd: AsyncFD): bool = result = false # Returning true signifies we want timer to stop. serverDate = now().utc().format("ddd, dd MMM yyyy HH:mm:ss 'GMT'") +proc newSocketAux(settings: Settings): owned(Socket) = + result = newSocket(settings.domain) + result.setSockOpt(OptReuseAddr, true) + result.setSockOpt(OptReusePort, settings.reusePort) + result.bindAddr(settings.port, settings.bindAddr) + proc eventLoop(params: (OnRequest, Settings)) = let (onRequest, settings) = params @@ -319,12 +323,7 @@ proc eventLoop(params: (OnRequest, Settings)) = let selector = newSelector[Data]() - let server = newSocket(settings.domain) - server.setSockOpt(OptReuseAddr, true) - if compileOption("threads") and not settings.reusePort: - raise HttpBeastDefect(msg: "--threads:on requires reusePort to be enabled in settings") - server.setSockOpt(OptReusePort, settings.reusePort) - server.bindAddr(settings.port, settings.bindAddr) + let server = newSocketAux(settings) server.listen() server.getFd().setBlocking(false) selector.registerHandle(server.getFd(), {Event.Read}, initData(Server)) @@ -477,12 +476,17 @@ proc run*(onRequest: OnRequest, settings: Settings) = echo("Starting ", numThreads, " threads") if numThreads > 1: when compileOption("threads"): + var settings2 = settings + if not settings.reusePort and numThreads > 1: + let server = newSocketAux(settings) + close(server) # binds to a temporary socket to honor `reusePort = false` + settings2.reusePort = true var threads = newSeq[Thread[(OnRequest, Settings)]](numThreads) for i in 0 ..< numThreads: createThread[(OnRequest, Settings)]( - threads[i], eventLoop, (onRequest, settings) + threads[i], eventLoop, (onRequest, settings2) ) - echo("Listening on port ", settings.port) # This line is used in the tester to signal readiness. + echo("Listening on port ", settings2.port) # This line is used in the tester to signal readiness. joinThreads(threads) else: assert false From 3c9adbdb987decb4f7d338f17ca1ff770004d22a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Jul 2021 17:25:07 -0700 Subject: [PATCH 2/2] use failOnExistingPort --- src/httpbeast.nim | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/httpbeast.nim b/src/httpbeast.nim index 39874eb..6c42c90 100644 --- a/src/httpbeast.nim +++ b/src/httpbeast.nim @@ -54,7 +54,11 @@ type domain*: Domain numThreads: int loggers: seq[Logger] - reusePort: bool ## controls whether to fail with "Address already in use". + reusePort: bool + ## controls whether to use `OptReusePort` for sockets + failOnExistingPort: bool + ## Fail with "Address already in use" if port is in use, by attempting + ## to bind to a temporary socket to fail early. HttpBeastDefect* = ref object of Defect @@ -65,7 +69,8 @@ proc initSettings*(port: Port = Port(8080), bindAddr: string = "", numThreads: int = 0, domain = Domain.AF_INET, - reusePort = false): Settings = + reusePort = true, + failOnExistingPort = true): Settings = Settings( port: port, bindAddr: bindAddr, @@ -73,6 +78,7 @@ proc initSettings*(port: Port = Port(8080), numThreads: numThreads, loggers: getHandlers(), reusePort: reusePort, + failOnExistingPort: failOnExistingPort, ) proc initData(fdKind: FdKind, ip = ""): Data = @@ -474,19 +480,18 @@ proc run*(onRequest: OnRequest, settings: Settings) = let numThreads = 1 echo("Starting ", numThreads, " threads") + if settings.failOnExistingPort: + var settings2 = settings + settings2.reusePort = false + close(newSocketAux(settings2)) # attempt to bind to a temporary socket if numThreads > 1: when compileOption("threads"): - var settings2 = settings - if not settings.reusePort and numThreads > 1: - let server = newSocketAux(settings) - close(server) # binds to a temporary socket to honor `reusePort = false` - settings2.reusePort = true var threads = newSeq[Thread[(OnRequest, Settings)]](numThreads) for i in 0 ..< numThreads: createThread[(OnRequest, Settings)]( - threads[i], eventLoop, (onRequest, settings2) + threads[i], eventLoop, (onRequest, settings) ) - echo("Listening on port ", settings2.port) # This line is used in the tester to signal readiness. + echo("Listening on port ", settings.port) # This line is used in the tester to signal readiness. joinThreads(threads) else: assert false