From 9c27421ecd6892c0462ae4ec2d65f31626114d3c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 13 Jan 2021 18:46:24 -0800 Subject: [PATCH 1/4] add getPort to resolve Port(0) --- changelog.md | 2 ++ lib/pure/asynchttpserver.nim | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7452e318228fd..c234bf174e3a5 100644 --- a/changelog.md +++ b/changelog.md @@ -71,6 +71,8 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors. +- Added to `asynchttpserver` `getPort` and `getSocket`. + - `--gc:orc` is now 10% faster than previously for common workloads. If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 38be4ceac699a..d22ef362ec786 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -41,10 +41,14 @@ runnableExamples("-r:off"): import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore +from nativesockets import getLocalAddr, AF_INET import std/private/since export httpcore except parseHeader +since (1, 5, 1): + export Port + const maxLine = 8*1024 @@ -70,17 +74,27 @@ type maxBody: int ## The maximum content-length that will be read for the body. maxFDs: int +proc getPort*(a: AsyncHttpServer): Port {.since: (1, 5, 1).} = + ## Returns the port `a` was bound do to. + ## Useful when `listen(Port(0))` was called which assigns a port automatically. + runnableExamples: + let server = newAsyncHttpServer() + server.listen(Port(0)) + doAssert server.getPort.uint16 > 0 + server.close() + result = getLocalAddr(a.socket.getFd, AF_INET)[1] + func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = ## Returns the `AsyncHttpServer`s internal `AsyncSocket` instance. ## ## Useful for identifying what port the AsyncHttpServer is bound to, if it ## was chosen automatically. runnableExamples: - from std/asyncdispatch import Port from std/asyncnet import getFd from std/nativesockets import getLocalAddr, AF_INET let server = newAsyncHttpServer() server.listen(Port(0)) # Socket is not bound until this point + # note: a more direct way to get the port is `getPort`. let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] doAssert uint16(port) > 0 server.close() From 263143804be61a305601bd5115e829aa43b07ad0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 28 Mar 2021 14:18:04 -0700 Subject: [PATCH 2/4] fixup --- lib/pure/asynchttpserver.nim | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index d22ef362ec786..b696d87e89be7 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -74,31 +74,31 @@ type maxBody: int ## The maximum content-length that will be read for the body. maxFDs: int -proc getPort*(a: AsyncHttpServer): Port {.since: (1, 5, 1).} = - ## Returns the port `a` was bound do to. - ## Useful when `listen(Port(0))` was called which assigns a port automatically. +proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = + ## Returns the port `self` was bound to. + ## + ## Useful for identifying what port `self` is bound to, if it + ## was chosen automatically, for example via `listen(Port(0))`. runnableExamples: let server = newAsyncHttpServer() server.listen(Port(0)) - doAssert server.getPort.uint16 > 0 + assert server.getPort.uint16 > 0 server.close() - result = getLocalAddr(a.socket.getFd, AF_INET)[1] + result = getLocalAddr(self.socket.getFd, AF_INET)[1] -func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = - ## Returns the `AsyncHttpServer`s internal `AsyncSocket` instance. - ## - ## Useful for identifying what port the AsyncHttpServer is bound to, if it - ## was chosen automatically. +func getSocket*(self: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = + ## Field accessor. runnableExamples: from std/asyncnet import getFd from std/nativesockets import getLocalAddr, AF_INET let server = newAsyncHttpServer() server.listen(Port(0)) # Socket is not bound until this point # note: a more direct way to get the port is `getPort`. - let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] - doAssert uint16(port) > 0 + let (laddr, port) = getLocalAddr(server.getSocket.getFd, AF_INET) + assert uint16(port) > 0 + assert laddr == "0.0.0.0" server.close() - a.socket + self.socket proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = From e0f1b8936d46f761a89adf0a5c5cbaf56e690595 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 28 Mar 2021 14:37:26 -0700 Subject: [PATCH 3/4] use getPort in examples + tests --- lib/pure/asynchttpserver.nim | 10 +++++----- testament/lib/stdtest/netutils.nim | 3 ++- tests/stdlib/tasynchttpserver_transferencoding.nim | 3 +-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index b696d87e89be7..a3513223790c4 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -15,20 +15,20 @@ ## instead of allowing users to connect directly to this server. runnableExamples("-r:off"): - # This example will create an HTTP server on port 8080. The server will - # respond to all requests with a `200 OK` response code and "Hello World" + # This example will create an HTTP server on an automatically chosen port. + # It will respond to all requests with a `200 OK` response code and "Hello World" # as the response body. import std/asyncdispatch proc main {.async.} = - const port = 8080 var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = echo (req.reqMethod, req.url, req.headers) let headers = {"Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - echo "test this with: curl localhost:" & $port & "/" - server.listen(Port(port)) + server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port. + let port = server.getPort + echo "test this with: curl localhost:" & $port.uint16 & "/" while true: if server.shouldAcceptRequest(): await server.acceptRequest(cb) diff --git a/testament/lib/stdtest/netutils.nim b/testament/lib/stdtest/netutils.nim index eb913a56a93f8..5115390e09c7d 100644 --- a/testament/lib/stdtest/netutils.nim +++ b/testament/lib/stdtest/netutils.nim @@ -1,6 +1,7 @@ import std/[nativesockets, asyncdispatch, os] proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = + ## See also `asynchttpserver.getPort`. block: var name: Sockaddr_in name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) @@ -8,5 +9,5 @@ proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(handle, cast[ptr SockAddr](addr(name)), sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) + raiseOSError(osLastError(), $port) result = getLocalAddr(handle, AF_INET)[1] diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim index 34f3cef11b727..bfa0e2f6913b1 100644 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -27,10 +27,9 @@ template genTest(input, expected) = let server = newAsyncHttpServer() waitFor runSleepLoop(server) - let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] let data = postBegin & input var socket = newSocket() - socket.connect("127.0.0.1", port) + socket.connect("127.0.0.1", server.getPort) socket.send(data) waitFor sleepAsync(10) socket.close() From c2c97c87101a2556ce854a8d303cc65d86d84061 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 30 Mar 2021 08:49:23 -0700 Subject: [PATCH 4/4] address comments: do not re-export Port --- lib/pure/asynchttpserver.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index a3513223790c4..ec464682e9cf8 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -46,9 +46,6 @@ import std/private/since export httpcore except parseHeader -since (1, 5, 1): - export Port - const maxLine = 8*1024 @@ -80,6 +77,7 @@ proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = ## Useful for identifying what port `self` is bound to, if it ## was chosen automatically, for example via `listen(Port(0))`. runnableExamples: + from std/nativesockets import Port let server = newAsyncHttpServer() server.listen(Port(0)) assert server.getPort.uint16 > 0 @@ -90,7 +88,7 @@ func getSocket*(self: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = ## Field accessor. runnableExamples: from std/asyncnet import getFd - from std/nativesockets import getLocalAddr, AF_INET + from std/nativesockets import getLocalAddr, AF_INET, Port let server = newAsyncHttpServer() server.listen(Port(0)) # Socket is not bound until this point # note: a more direct way to get the port is `getPort`.