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

add getPort to resolve Port(0) #17559

Merged
merged 4 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down
42 changes: 27 additions & 15 deletions lib/pure/asynchttpserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -41,6 +41,7 @@ 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
Expand Down Expand Up @@ -70,21 +71,32 @@ type
maxBody: int ## The maximum content-length that will be read for the body.
maxFDs: int

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.
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:
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:
from std/asyncdispatch import Port
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
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
let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1]
doAssert uint16(port) > 0
# note: a more direct way to get the port is `getPort`.
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 =
Expand Down
3 changes: 2 additions & 1 deletion testament/lib/stdtest/netutils.nim
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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))
name.sin_port = htons(uint16(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]
3 changes: 1 addition & 2 deletions tests/stdlib/tasynchttpserver_transferencoding.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down