summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
authorRuslan Mustakov <endragor@users.noreply.github.com>2017-05-02 14:25:50 +0700
committerAndreas Rumpf <rumpf_a@web.de>2017-05-02 09:25:50 +0200
commitecf278c4672aa680803ee82e1a59a686c765f226 (patch)
treea36d3a10aca0718b191379737653a8fb75b569f2 /tests
parent6377b52d8e1e7bbf0ff4a8d71081d44368ea1d94 (diff)
downloadNim-ecf278c4672aa680803ee82e1a59a686c765f226.tar.gz
Implement dial, support IPv6 in httpclient (#5763)
* Implement dial, support IPv6 in httpclient

Added ``dial`` procedure to networking modules: ``net``, ``asyncdispatch``,
``asyncnet``. It merges socket creation, address resolution, and connection
into single step. When using ``dial``, you don't have to worry about
IPv4 vs IPv6 problem.

Fixed addrInfo loop in connect to behave properly.
Previously it would stop on first non-immediate failure, instead of
continuing and trying the remaining addresses.

Fixed newAsyncNativeSocket to raise proper error if socket creation
fails.

Fixes: #3811

* Check domain during connect() only on non-Windows

This is how it was in the previous implementation of connect().

* Call 'osLastError' before 'close' in net.dial

* Record osLastError before freeAddrInfo in net.dial

* Add missing docs for 'dial' proc

* Optimize dial to create one FD per domain, add tests

And make async IPv6 servers work on Windows.

* Add IPv6 test to uri module

* Fix getAddrString error handling
Diffstat (limited to 'tests')
-rw-r--r--tests/async/tasyncdial.nim53
-rw-r--r--tests/stdlib/thttpclient.nim40
-rw-r--r--tests/stdlib/tnetdial.nim60
3 files changed, 151 insertions, 2 deletions
diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim
new file mode 100644
index 000000000..d70e14020
--- /dev/null
+++ b/tests/async/tasyncdial.nim
@@ -0,0 +1,53 @@
+discard """
+  file: "tasyncdial.nim"
+  output: '''
+OK AF_INET
+OK AF_INET6
+'''
+"""
+
+import
+  nativesockets, os, asyncdispatch
+
+proc setupServerSocket(hostname: string, port: Port, domain: Domain): AsyncFD =
+  ## Creates a socket, binds it to the specified address, and starts listening for connecitons.
+  ## Registers the descriptor with the dispatcher of the current thread
+  ## Raises OSError in case of an error.
+  let fd = newNativeSocket(domain)
+  setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
+  var aiList = getAddrInfo(hostname, port, domain)
+  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+    freeAddrInfo(aiList)
+    raiseOSError(osLastError())
+  freeAddrInfo(aiList)
+  if listen(fd) != 0:
+    raiseOSError(osLastError())
+  setBlocking(fd, false)
+  result = fd.AsyncFD
+  register(result)
+
+proc doTest(domain: static[Domain]) {.async.} =
+  const
+    testHost = when domain == Domain.AF_INET6: "::1" else: "127.0.0.1"
+    testPort = Port(17384)
+  let serverFd = setupServerSocket(testHost, testPort, domain)
+  let acceptFut = serverFd.accept()
+  let clientFdFut = dial(testHost, testPort)
+
+  let serverClientFd = await acceptFut
+  serverFd.closeSocket()
+
+  let clientFd = await clientFdFut
+
+  let recvFut = serverClientFd.recv(2)
+  await clientFd.send("Hi")
+  let msg = await recvFut
+
+  serverClientFd.closeSocket()
+  clientFd.closeSocket()
+
+  if msg == "Hi":
+    echo "OK ", domain
+
+waitFor(doTest(Domain.AF_INET))
+waitFor(doTest(Domain.AF_INET6))
diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim
index 62c1ebee7..40324d92a 100644
--- a/tests/stdlib/thttpclient.nim
+++ b/tests/stdlib/thttpclient.nim
@@ -1,11 +1,13 @@
 discard """
   cmd: "nim c --threads:on -d:ssl $file"
+  exitcode: 0
+  output: "OK"
 """
 
 import strutils
 from net import TimeoutError
 
-import httpclient, asyncdispatch
+import nativesockets, os, httpclient, asyncdispatch
 
 const manualTests = false
 
@@ -112,6 +114,40 @@ proc syncTest() =
   except:
     doAssert false, "TimeoutError should have been raised."
 
-syncTest()
+proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
+  let fd = newNativeSocket(AF_INET6)
+  setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
+  var aiList = getAddrInfo(hostname, port, AF_INET6)
+  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+    freeAddrInfo(aiList)
+    raiseOSError(osLastError())
+  freeAddrInfo(aiList)
+  if listen(fd) != 0:
+    raiseOSError(osLastError())
+  setBlocking(fd, false)
+
+  var serverFd = fd.AsyncFD
+  register(serverFd)
+  result = serverFd
+
+  proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} =
+    if not fut.failed:
+      let clientFd = fut.read()
+      clientFd.send("HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L").callback = proc() =
+        clientFd.closeSocket()
+      serverFd.accept().callback = onAccept
+  serverFd.accept().callback = onAccept
+
+proc ipv6Test() =
+  var client = newAsyncHttpClient()
+  let serverFd = makeIPv6HttpServer("::1", Port(18473))
+  var resp = waitFor client.request("http://[::1]:18473/")
+  doAssert(resp.status == "200 OK")
+  serverFd.closeSocket()
+  client.close()
 
+syncTest()
 waitFor(asyncTest())
+ipv6Test()
+
+echo "OK"
diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim
new file mode 100644
index 000000000..da6088d70
--- /dev/null
+++ b/tests/stdlib/tnetdial.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: "nim c --threads:on $file"
+  exitcode: 0
+  output: "OK"
+"""
+
+import os, net, nativesockets, asyncdispatch
+
+## Test for net.dial
+
+const port = Port(28431)
+
+proc initIPv6Server(hostname: string, port: Port): AsyncFD =
+  let fd = newNativeSocket(AF_INET6)
+  setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
+  var aiList = getAddrInfo(hostname, port, AF_INET6)
+  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+    freeAddrInfo(aiList)
+    raiseOSError(osLastError())
+  freeAddrInfo(aiList)
+  if listen(fd) != 0:
+    raiseOSError(osLastError())
+  setBlocking(fd, false)
+
+  var serverFd = fd.AsyncFD
+  register(serverFd)
+  result = serverFd
+
+# Since net.dial is synchronous, we use main thread to setup server,
+# and dial to it from another thread.
+
+proc testThread() {.thread.} =
+  let fd = net.dial("::1", port)
+  var s = newString(5)
+  doAssert fd.recv(addr s[0], 5) == 5
+  if s == "Hello":
+    echo "OK"
+  fd.close()
+
+proc test() =
+  var t: Thread[void]
+  createThread(t, testThread)
+
+  let serverFd = initIPv6Server("::1", port)
+  var done = false
+
+  serverFd.accept().callback = proc(fut: Future[AsyncFD]) =
+    serverFd.closeSocket()
+    if not fut.failed:
+      let fd = fut.read()
+      fd.send("Hello").callback = proc() =
+        fd.closeSocket()
+        done = true
+
+  while not done:
+    poll()
+
+  joinThread(t)
+
+test()