diff options
author | Ruslan Mustakov <endragor@users.noreply.github.com> | 2017-05-02 14:25:50 +0700 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-05-02 09:25:50 +0200 |
commit | ecf278c4672aa680803ee82e1a59a686c765f226 (patch) | |
tree | a36d3a10aca0718b191379737653a8fb75b569f2 /tests | |
parent | 6377b52d8e1e7bbf0ff4a8d71081d44368ea1d94 (diff) | |
download | Nim-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.nim | 53 | ||||
-rw-r--r-- | tests/stdlib/thttpclient.nim | 40 | ||||
-rw-r--r-- | tests/stdlib/tnetdial.nim | 60 |
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() |