add support for DNS resolution (#81)
* add support for DNS resolution - reworked API to be more consistent - string addresses and Uri types will be now resolved - made the API more consistent * log failed connection attempt * agent string can't contain spaces * add websock topic (#83) * style Co-authored-by: Tanguy Cizain <tanguycizain@gmail.com>
This commit is contained in:
parent
fec0f2bac1
commit
06ae75cf7f
|
@ -19,19 +19,18 @@ const
|
||||||
# so we are using different port
|
# so we are using different port
|
||||||
when defined tls:
|
when defined tls:
|
||||||
const
|
const
|
||||||
agent = "websock secure client"
|
agent = "websock-secure-client"
|
||||||
secure = true
|
secure = true
|
||||||
serverPort = 9002
|
serverPort = 9002
|
||||||
else:
|
else:
|
||||||
const
|
const
|
||||||
agent = "websock client"
|
agent = "websock-client"
|
||||||
secure = false
|
secure = false
|
||||||
serverPort = 9001
|
serverPort = 9001
|
||||||
|
|
||||||
proc connectServer(path: string, factories: seq[ExtFactory] = @[]): Future[WSSession] {.async.} =
|
proc connectServer(path: string, factories: seq[ExtFactory] = @[]): Future[WSSession] {.async.} =
|
||||||
let ws = await WebSocket.connect(
|
let ws = await WebSocket.connect(
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1:$1" % [$serverPort],
|
||||||
port = Port(serverPort),
|
|
||||||
path = path,
|
path = path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
flags=clientFlags,
|
flags=clientFlags,
|
||||||
|
|
|
@ -17,14 +17,12 @@ import ../websock/websock
|
||||||
proc main() {.async.} =
|
proc main() {.async.} =
|
||||||
let ws = when defined tls:
|
let ws = when defined tls:
|
||||||
await WebSocket.connect(
|
await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/wss",
|
path = "/wss",
|
||||||
flags = {TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName})
|
flags = {TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName})
|
||||||
else:
|
else:
|
||||||
await WebSocket.connect(
|
await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/ws")
|
path = "/ws")
|
||||||
|
|
||||||
trace "Websocket client: ", State = ws.readyState
|
trace "Websocket client: ", State = ws.readyState
|
||||||
|
|
|
@ -48,8 +48,7 @@ suite "permessage deflate compression":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let client = await WebSocket.connect(
|
let client = await WebSocket.connect(
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1:8888",
|
||||||
port = Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
factories = @[deflateFactory]
|
factories = @[deflateFactory]
|
||||||
|
@ -89,8 +88,7 @@ suite "permessage deflate compression":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let client = await WebSocket.connect(
|
let client = await WebSocket.connect(
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1:8888",
|
||||||
port = Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
factories = @[deflateFactory]
|
factories = @[deflateFactory]
|
||||||
|
|
|
@ -43,8 +43,7 @@ suite "multiple extensions flow":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let client = await WebSocket.connect(
|
let client = await WebSocket.connect(
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1:8888",
|
||||||
port = Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
factories = @[hexFactory, base64Factory]
|
factories = @[hexFactory, base64Factory]
|
||||||
|
@ -76,8 +75,7 @@ suite "multiple extensions flow":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let client = await WebSocket.connect(
|
let client = await WebSocket.connect(
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1:8888",
|
||||||
port = Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
factories = @[base64Factory, hexFactory]
|
factories = @[base64Factory, hexFactory]
|
||||||
|
|
|
@ -96,7 +96,7 @@ proc connectClient*(
|
||||||
rng: Rng = nil): Future[WSSession] {.async.} =
|
rng: Rng = nil): Future[WSSession] {.async.} =
|
||||||
let secure = when defined secure: true else: false
|
let secure = when defined secure: true else: false
|
||||||
return await WebSocket.connect(
|
return await WebSocket.connect(
|
||||||
address = address,
|
host = address,
|
||||||
flags = flags,
|
flags = flags,
|
||||||
path = path,
|
path = path,
|
||||||
secure = secure,
|
secure = secure,
|
||||||
|
|
|
@ -110,8 +110,7 @@ suite "UTF-8 validator in action":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let session = await WebSocket.connect(
|
let session = await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
)
|
)
|
||||||
|
@ -152,8 +151,7 @@ suite "UTF-8 validator in action":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let session = await WebSocket.connect(
|
let session = await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"],
|
protocols = @["proto"],
|
||||||
)
|
)
|
||||||
|
@ -178,8 +176,7 @@ suite "UTF-8 validator in action":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let session = await WebSocket.connect(
|
let session = await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"]
|
protocols = @["proto"]
|
||||||
)
|
)
|
||||||
|
@ -204,8 +201,7 @@ suite "UTF-8 validator in action":
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
let session = await WebSocket.connect(
|
let session = await WebSocket.connect(
|
||||||
"127.0.0.1",
|
"127.0.0.1:8888",
|
||||||
Port(8888),
|
|
||||||
path = "/ws",
|
path = "/ws",
|
||||||
protocols = @["proto"]
|
protocols = @["proto"]
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,6 @@ suite "Test handshake":
|
||||||
test "Should not select incorrect protocol":
|
test "Should not select incorrect protocol":
|
||||||
proc handle(request: HttpRequest) {.async.} =
|
proc handle(request: HttpRequest) {.async.} =
|
||||||
check request.uri.path == WSPath
|
check request.uri.path == WSPath
|
||||||
|
|
||||||
let
|
let
|
||||||
server = WSServer.new(protos = ["proto"])
|
server = WSServer.new(protos = ["proto"])
|
||||||
ws = await server.handleRequest(request)
|
ws = await server.handleRequest(request)
|
||||||
|
|
|
@ -103,9 +103,6 @@ proc request*(
|
||||||
else:
|
else:
|
||||||
url
|
url
|
||||||
|
|
||||||
if requestUrl.scheme == "":
|
|
||||||
raise newException(HttpError, "No uri scheme supplied.")
|
|
||||||
|
|
||||||
let headerString = generateHeaders(requestUrl, httpMethod, client.version, headers)
|
let headerString = generateHeaders(requestUrl, httpMethod, client.version, headers)
|
||||||
|
|
||||||
await client.stream.writer.write(headerString)
|
await client.stream.writer.write(headerString)
|
||||||
|
@ -129,7 +126,8 @@ proc connect*(
|
||||||
version = HttpVersion11,
|
version = HttpVersion11,
|
||||||
tlsFlags: set[TLSFlags] = {},
|
tlsFlags: set[TLSFlags] = {},
|
||||||
tlsMinVersion = TLSVersion.TLS11,
|
tlsMinVersion = TLSVersion.TLS11,
|
||||||
tlsMaxVersion = TLSVersion.TLS12): Future[T] {.async.} =
|
tlsMaxVersion = TLSVersion.TLS12,
|
||||||
|
hostName = ""): Future[T] {.async.} =
|
||||||
|
|
||||||
let transp = await connect(address)
|
let transp = await connect(address)
|
||||||
let client = T(
|
let client = T(
|
||||||
|
@ -150,7 +148,7 @@ proc connect*(
|
||||||
let tlsStream = newTLSClientAsyncStream(
|
let tlsStream = newTLSClientAsyncStream(
|
||||||
stream.reader,
|
stream.reader,
|
||||||
stream.writer,
|
stream.writer,
|
||||||
address.host,
|
serverName = hostName,
|
||||||
minVersion = tlsMinVersion,
|
minVersion = tlsMinVersion,
|
||||||
maxVersion = tlsMaxVersion,
|
maxVersion = tlsMaxVersion,
|
||||||
flags = tlsFlags)
|
flags = tlsFlags)
|
||||||
|
@ -167,16 +165,27 @@ proc connect*(
|
||||||
proc connect*(
|
proc connect*(
|
||||||
T: typedesc[HttpClient | TlsHttpClient],
|
T: typedesc[HttpClient | TlsHttpClient],
|
||||||
host: string,
|
host: string,
|
||||||
port: int = 80,
|
|
||||||
version = HttpVersion11,
|
version = HttpVersion11,
|
||||||
tlsFlags: set[TLSFlags] = {},
|
tlsFlags: set[TLSFlags] = {},
|
||||||
tlsMinVersion = TLSVersion.TLS11,
|
tlsMinVersion = TLSVersion.TLS11,
|
||||||
tlsMaxVersion = TLSVersion.TLS12): Future[T]
|
tlsMaxVersion = TLSVersion.TLS12): Future[T]
|
||||||
{.raises: [Defect, HttpError].} =
|
{.async, raises: [Defect, HttpError].} =
|
||||||
|
|
||||||
let address = try:
|
let hostPort = host.split(":")
|
||||||
initTAddress(host, port)
|
let addrs = resolveTAddress(host)
|
||||||
except TransportAddressError as exc:
|
for a in addrs:
|
||||||
raise newException(HttpError, exc.msg)
|
try:
|
||||||
|
let conn = await T.connect(
|
||||||
|
a,
|
||||||
|
version,
|
||||||
|
tlsFlags,
|
||||||
|
tlsMinVersion,
|
||||||
|
tlsMaxVersion,
|
||||||
|
hostName = hostPort[0])
|
||||||
|
|
||||||
return T.connect(address, version, tlsFlags, tlsMinVersion, tlsMaxVersion)
|
return conn
|
||||||
|
except TransportError as exc:
|
||||||
|
trace "Error connecting to address", address = $a, exc = exc.msg
|
||||||
|
|
||||||
|
raise newException(HttpError,
|
||||||
|
"Unable to connect to host on any address!")
|
||||||
|
|
|
@ -103,8 +103,8 @@ type
|
||||||
|
|
||||||
ExtFactoryProc* = proc(
|
ExtFactoryProc* = proc(
|
||||||
isServer: bool,
|
isServer: bool,
|
||||||
args: seq[ExtParam]): Result[Ext, string] {.
|
args: seq[ExtParam]): Result[Ext, string]
|
||||||
gcsafe, raises: [Defect].}
|
{.gcsafe, raises: [Defect].}
|
||||||
|
|
||||||
ExtFactory* = object
|
ExtFactory* = object
|
||||||
name*: string
|
name*: string
|
||||||
|
|
|
@ -105,9 +105,11 @@ proc selectExt(isServer: bool,
|
||||||
|
|
||||||
proc connect*(
|
proc connect*(
|
||||||
_: type WebSocket,
|
_: type WebSocket,
|
||||||
uri: Uri,
|
host: string | TransportAddress,
|
||||||
|
path: string,
|
||||||
protocols: seq[string] = @[],
|
protocols: seq[string] = @[],
|
||||||
factories: seq[ExtFactory] = @[],
|
factories: seq[ExtFactory] = @[],
|
||||||
|
secure = false,
|
||||||
flags: set[TLSFlags] = {},
|
flags: set[TLSFlags] = {},
|
||||||
version = WSDefaultVersion,
|
version = WSDefaultVersion,
|
||||||
frameSize = WSDefaultFrameSize,
|
frameSize = WSDefaultFrameSize,
|
||||||
|
@ -115,22 +117,15 @@ proc connect*(
|
||||||
onPong: ControlCb = nil,
|
onPong: ControlCb = nil,
|
||||||
onClose: CloseCb = nil,
|
onClose: CloseCb = nil,
|
||||||
rng: Rng = nil): Future[WSSession] {.async.} =
|
rng: Rng = nil): Future[WSSession] {.async.} =
|
||||||
## create a new websockets client
|
|
||||||
##
|
|
||||||
|
|
||||||
var rng = if isNil(rng): newRng() else: rng
|
let
|
||||||
var key = Base64Pad.encode(genWebSecKey(rng))
|
rng = if isNil(rng): newRng() else: rng
|
||||||
var uri = uri
|
key = Base64Pad.encode(genWebSecKey(rng))
|
||||||
let client = case uri.scheme:
|
|
||||||
of "wss":
|
let client = if secure:
|
||||||
uri.scheme = "https"
|
await TlsHttpClient.connect(host, tlsFlags = flags)
|
||||||
await TlsHttpClient.connect(uri.hostname, uri.port.parseInt(), tlsFlags = flags)
|
|
||||||
of "ws":
|
|
||||||
uri.scheme = "http"
|
|
||||||
await HttpClient.connect(uri.hostname, uri.port.parseInt())
|
|
||||||
else:
|
else:
|
||||||
raise newException(WSWrongUriSchemeError,
|
await HttpClient.connect(host)
|
||||||
"uri scheme has to be 'ws' or 'wss'")
|
|
||||||
|
|
||||||
let headerData = [
|
let headerData = [
|
||||||
("Connection", "Upgrade"),
|
("Connection", "Upgrade"),
|
||||||
|
@ -138,7 +133,7 @@ proc connect*(
|
||||||
("Cache-Control", "no-cache"),
|
("Cache-Control", "no-cache"),
|
||||||
("Sec-WebSocket-Version", $version),
|
("Sec-WebSocket-Version", $version),
|
||||||
("Sec-WebSocket-Key", key),
|
("Sec-WebSocket-Key", key),
|
||||||
("Host", uri.hostname & ":" & uri.port)]
|
("Host", $host)]
|
||||||
|
|
||||||
var headers = HttpTable.init(headerData)
|
var headers = HttpTable.init(headerData)
|
||||||
if protocols.len > 0:
|
if protocols.len > 0:
|
||||||
|
@ -154,7 +149,7 @@ proc connect*(
|
||||||
headers.add("Sec-WebSocket-Extensions", extOffer)
|
headers.add("Sec-WebSocket-Extensions", extOffer)
|
||||||
|
|
||||||
let response = try:
|
let response = try:
|
||||||
await client.request(uri, headers = headers)
|
await client.request(path, headers = headers)
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
trace "Websocket failed during handshake", exc = exc.msg
|
trace "Websocket failed during handshake", exc = exc.msg
|
||||||
await client.close()
|
await client.close()
|
||||||
|
@ -195,63 +190,35 @@ proc connect*(
|
||||||
|
|
||||||
proc connect*(
|
proc connect*(
|
||||||
_: type WebSocket,
|
_: type WebSocket,
|
||||||
address: TransportAddress,
|
uri: Uri,
|
||||||
path: string,
|
|
||||||
protocols: seq[string] = @[],
|
protocols: seq[string] = @[],
|
||||||
factories: seq[ExtFactory] = @[],
|
factories: seq[ExtFactory] = @[],
|
||||||
secure = false,
|
|
||||||
flags: set[TLSFlags] = {},
|
flags: set[TLSFlags] = {},
|
||||||
version = WSDefaultVersion,
|
version = WSDefaultVersion,
|
||||||
frameSize = WSDefaultFrameSize,
|
frameSize = WSDefaultFrameSize,
|
||||||
onPing: ControlCb = nil,
|
onPing: ControlCb = nil,
|
||||||
onPong: ControlCb = nil,
|
onPong: ControlCb = nil,
|
||||||
onClose: CloseCb = nil,
|
onClose: CloseCb = nil,
|
||||||
rng: Rng = nil): Future[WSSession] {.async.} =
|
rng: Rng = nil): Future[WSSession]
|
||||||
|
{.raises: [Defect, WSWrongUriSchemeError].} =
|
||||||
## Create a new websockets client
|
## Create a new websockets client
|
||||||
## using a string path
|
## using a Uri
|
||||||
##
|
##
|
||||||
|
|
||||||
var uri = if secure:
|
let secure = case uri.scheme:
|
||||||
&"wss://"
|
of "wss": true
|
||||||
|
of "ws": false
|
||||||
else:
|
else:
|
||||||
&"ws://"
|
raise newException(WSWrongUriSchemeError,
|
||||||
|
"uri scheme has to be 'ws' or 'wss'")
|
||||||
|
|
||||||
uri &= address.host & ":" & $address.port
|
var uri = uri
|
||||||
if path.startsWith("/"):
|
if uri.port.len <= 0:
|
||||||
uri.add path
|
uri.port = if secure: "443" else: "80"
|
||||||
else:
|
|
||||||
uri.add &"/{path}"
|
|
||||||
|
|
||||||
return await WebSocket.connect(
|
return WebSocket.connect(
|
||||||
uri = parseUri(uri),
|
host = uri.hostname & ":" & uri.port,
|
||||||
protocols = protocols,
|
path = uri.path,
|
||||||
factories = factories,
|
|
||||||
flags = flags,
|
|
||||||
version = version,
|
|
||||||
frameSize = frameSize,
|
|
||||||
onPing = onPing,
|
|
||||||
onPong = onPong,
|
|
||||||
onClose = onClose)
|
|
||||||
|
|
||||||
proc connect*(
|
|
||||||
_: type WebSocket,
|
|
||||||
host: string,
|
|
||||||
port: Port,
|
|
||||||
path: string,
|
|
||||||
protocols: seq[string] = @[],
|
|
||||||
factories: seq[ExtFactory] = @[],
|
|
||||||
secure = false,
|
|
||||||
flags: set[TLSFlags] = {},
|
|
||||||
version = WSDefaultVersion,
|
|
||||||
frameSize = WSDefaultFrameSize,
|
|
||||||
onPing: ControlCb = nil,
|
|
||||||
onPong: ControlCb = nil,
|
|
||||||
onClose: CloseCb = nil,
|
|
||||||
rng: Rng = nil): Future[WSSession] {.async.} =
|
|
||||||
|
|
||||||
return await WebSocket.connect(
|
|
||||||
address = initTAddress(host, port),
|
|
||||||
path = path,
|
|
||||||
protocols = protocols,
|
protocols = protocols,
|
||||||
factories = factories,
|
factories = factories,
|
||||||
secure = secure,
|
secure = secure,
|
||||||
|
|
Loading…
Reference in New Issue