add getPort to resolve Port(0) (#17559)

* add getPort to resolve Port(0)

* fixup

* use getPort in examples + tests

* address comments: do not re-export Port
This commit is contained in:
Timothee Cour 2021-03-30 11:14:39 -07:00 • committed by GitHub
commit 72988509ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 18 deletions

View file

@ -71,6 +71,8 @@
- Added `asyncdispatch.activeDescriptors` that returns the number of currently - Added `asyncdispatch.activeDescriptors` that returns the number of currently
active async event handles/file descriptors. active async event handles/file descriptors.
- Added to `asynchttpserver` `getPort` and `getSocket`.
- `--gc:orc` is now 10% faster than previously for common workloads. If - `--gc:orc` is now 10% faster than previously for common workloads. If
you have trouble with its changed behavior, compile with `-d:nimOldOrc`. you have trouble with its changed behavior, compile with `-d:nimOldOrc`.

View file

@ -15,20 +15,20 @@
## instead of allowing users to connect directly to this server. ## instead of allowing users to connect directly to this server.
runnableExamples("-r:off"): runnableExamples("-r:off"):
# This example will create an HTTP server on port 8080. The server will # This example will create an HTTP server on an automatically chosen port.
# respond to all requests with a `200 OK` response code and "Hello World" # It will respond to all requests with a `200 OK` response code and "Hello World"
# as the response body. # as the response body.
import std/asyncdispatch import std/asyncdispatch
proc main {.async.} = proc main {.async.} =
const port = 8080
var server = newAsyncHttpServer() var server = newAsyncHttpServer()
proc cb(req: Request) {.async.} = proc cb(req: Request) {.async.} =
echo (req.reqMethod, req.url, req.headers) echo (req.reqMethod, req.url, req.headers)
let headers = {"Content-type": "text/plain; charset=utf-8"} let headers = {"Content-type": "text/plain; charset=utf-8"}
await req.respond(Http200, "Hello World", headers.newHttpHeaders()) await req.respond(Http200, "Hello World", headers.newHttpHeaders())
echo "test this with: curl localhost:" & $port & "/" server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port.
server.listen(Port(port)) let port = server.getPort
echo "test this with: curl localhost:" & $port.uint16 & "/"
while true: while true:
if server.shouldAcceptRequest(): if server.shouldAcceptRequest():
await server.acceptRequest(cb) await server.acceptRequest(cb)
@ -41,6 +41,7 @@ runnableExamples("-r:off"):
import asyncnet, asyncdispatch, parseutils, uri, strutils import asyncnet, asyncdispatch, parseutils, uri, strutils
import httpcore import httpcore
from nativesockets import getLocalAddr, AF_INET
import std/private/since import std/private/since
export httpcore except parseHeader export httpcore except parseHeader
@ -70,21 +71,32 @@ type
maxBody: int ## The maximum content-length that will be read for the body. maxBody: int ## The maximum content-length that will be read for the body.
maxFDs: int maxFDs: int
func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} =
## Returns the `AsyncHttpServer`s internal `AsyncSocket` instance. ## Returns the port `self` was bound to.
## ##
## Useful for identifying what port the AsyncHttpServer is bound to, if it ## Useful for identifying what port `self` is bound to, if it
## was chosen automatically. ## 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
server.close()
result = getLocalAddr(self.socket.getFd, AF_INET)[1]
func getSocket*(self: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} =
## Field accessor.
runnableExamples: runnableExamples:
from std/asyncdispatch import Port
from std/asyncnet import getFd from std/asyncnet import getFd
from std/nativesockets import getLocalAddr, AF_INET from std/nativesockets import getLocalAddr, AF_INET, Port
let server = newAsyncHttpServer() let server = newAsyncHttpServer()
server.listen(Port(0)) # Socket is not bound until this point server.listen(Port(0)) # Socket is not bound until this point
let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] # note: a more direct way to get the port is `getPort`.
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() server.close()
a.socket self.socket
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer = maxBody = 8388608): AsyncHttpServer =

View file

@ -1,6 +1,7 @@
import std/[nativesockets, asyncdispatch, os] import std/[nativesockets, asyncdispatch, os]
proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port =
## See also `asynchttpserver.getPort`.
block: block:
var name: Sockaddr_in var name: Sockaddr_in
name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) 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) name.sin_addr.s_addr = htonl(INADDR_ANY)
if bindAddr(handle, cast[ptr SockAddr](addr(name)), if bindAddr(handle, cast[ptr SockAddr](addr(name)),
sizeof(name).Socklen) < 0'i32: sizeof(name).Socklen) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError(), $port)
result = getLocalAddr(handle, AF_INET)[1] result = getLocalAddr(handle, AF_INET)[1]

View file

@ -27,10 +27,9 @@ template genTest(input, expected) =
let server = newAsyncHttpServer() let server = newAsyncHttpServer()
waitFor runSleepLoop(server) waitFor runSleepLoop(server)
let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1]
let data = postBegin & input let data = postBegin & input
var socket = newSocket() var socket = newSocket()
socket.connect("127.0.0.1", port) socket.connect("127.0.0.1", server.getPort)
socket.send(data) socket.send(data)
waitFor sleepAsync(10) waitFor sleepAsync(10)
socket.close() socket.close()