mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-04 02:44:44 +00:00
Fixes socket problems on Windows and normalises some names.
Ref #2976. Ref #2003. See news.txt for details.
This commit is contained in:
@@ -483,7 +483,7 @@ when defined(windows) or defined(nimdoc):
|
||||
RemoteSockaddr, RemoteSockaddrLength)
|
||||
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af = rawsockets.AF_INET): Future[void] =
|
||||
domain = rawsockets.AF_INET): Future[void] =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
## Returns a ``Future`` which will complete when the connection succeeds
|
||||
@@ -492,14 +492,14 @@ when defined(windows) or defined(nimdoc):
|
||||
var retFuture = newFuture[void]("connect")
|
||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
||||
var saddr: Sockaddr_in
|
||||
saddr.sin_family = int16(toInt(af))
|
||||
saddr.sin_family = int16(toInt(domain))
|
||||
saddr.sin_port = 0
|
||||
saddr.sin_addr.s_addr = INADDR_ANY
|
||||
if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)),
|
||||
sizeof(saddr).SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
var aiList = getAddrInfo(address, port, af)
|
||||
var aiList = getAddrInfo(address, port, domain)
|
||||
var success = false
|
||||
var lastError: OSErrorCode
|
||||
var it = aiList
|
||||
@@ -855,17 +855,17 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
return retFuture
|
||||
|
||||
proc newAsyncRawSocket*(domain, typ, protocol: cint): AsyncFD =
|
||||
proc newAsyncRawSocket*(domain, sockType, protocol: cint): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result = newRawSocket(domain, sockType, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = rawsockets.AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result = newRawSocket(domain, sockType, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
@@ -928,17 +928,18 @@ else:
|
||||
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
|
||||
p.selector.register(fd.SocketHandle, {}, data.RootRef)
|
||||
|
||||
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
proc newAsyncRawSocket*(domain: cint, sockType: cint,
|
||||
protocol: cint): AsyncFD =
|
||||
result = newRawSocket(domain, sockType, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
register(result)
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result = newRawSocket(domain, sockType, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
@@ -1009,7 +1010,7 @@ else:
|
||||
processTimers(p)
|
||||
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af_unused = AF_INET): Future[void] =
|
||||
domain = AF_INET): Future[void] =
|
||||
var retFuture = newFuture[void]("connect")
|
||||
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
@@ -1017,8 +1018,8 @@ else:
|
||||
retFuture.complete()
|
||||
return true
|
||||
|
||||
var sockDomain = getSockDomain(socket.SocketHandle)
|
||||
var aiList = getAddrInfo(address, port, sockDomain)
|
||||
assert getSockDomain(socket.SocketHandle) == domain
|
||||
var aiList = getAddrInfo(address, port, domain)
|
||||
var success = false
|
||||
var lastError: OSErrorCode
|
||||
var it = aiList
|
||||
@@ -1496,8 +1497,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
result[6] = outerProcBody
|
||||
|
||||
#echo(treeRepr(result))
|
||||
#if prc[0].getName == "test":
|
||||
# echo(toStrLit(result))
|
||||
if prc[0].getName == "getAsync":
|
||||
echo(toStrLit(result))
|
||||
|
||||
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
|
||||
@@ -85,35 +85,44 @@ type
|
||||
bioIn: BIO
|
||||
bioOut: BIO
|
||||
of false: nil
|
||||
domain: Domain
|
||||
sockType: SockType
|
||||
protocol: Protocol
|
||||
AsyncSocket* = ref AsyncSocketDesc
|
||||
|
||||
{.deprecated: [PAsyncSocket: AsyncSocket].}
|
||||
|
||||
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||
|
||||
proc newAsyncSocket*(fd: AsyncFD, buffered = true): AsyncSocket =
|
||||
proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
|
||||
sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
|
||||
## Creates a new ``AsyncSocket`` based on the supplied params.
|
||||
assert fd != osInvalidSocket.AsyncFD
|
||||
new(result)
|
||||
result.fd = fd.SocketHandle
|
||||
result.isBuffered = buffered
|
||||
result.domain = domain
|
||||
result.sockType = sockType
|
||||
result.protocol = protocol
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newAsyncSocket*(domain: Domain = AF_INET6, typ: SockType = SOCK_STREAM,
|
||||
proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
|
||||
## Creates a new asynchronous socket.
|
||||
##
|
||||
## This procedure will also create a brand new file descriptor for
|
||||
## this socket.
|
||||
result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
|
||||
result = newAsyncSocket(newAsyncRawSocket(domain, sockType, protocol), domain,
|
||||
sockType, protocol, buffered)
|
||||
|
||||
proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): AsyncSocket =
|
||||
proc newAsyncSocket*(domain, sockType, protocol: cint,
|
||||
buffered = true): AsyncSocket =
|
||||
## Creates a new asynchronous socket.
|
||||
##
|
||||
## This procedure will also create a brand new file descriptor for
|
||||
## this socket.
|
||||
result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
|
||||
result = newAsyncSocket(newAsyncRawSocket(domain, sockType, protocol),
|
||||
Domain(domain), SockType(sockType), Protocol(protocol), buffered)
|
||||
|
||||
when defined(ssl):
|
||||
proc getSslError(handle: SslPtr, err: cint): cint =
|
||||
@@ -169,13 +178,12 @@ when defined(ssl):
|
||||
let err = getSslError(socket.sslHandle, opResult.cint)
|
||||
yield appeaseSsl(socket, flags, err.cint)
|
||||
|
||||
proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||
af = AF_INET) {.async.} =
|
||||
proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
## Returns a ``Future`` which will complete when the connection succeeds
|
||||
## or an error occurs.
|
||||
await connect(socket.fd.AsyncFD, address, port, af)
|
||||
await connect(socket.fd.AsyncFD, address, port, socket.domain)
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
let flags = {SocketFlag.SafeDisconn}
|
||||
@@ -287,7 +295,8 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
|
||||
retFuture.fail(future.readError)
|
||||
else:
|
||||
let resultTup = (future.read.address,
|
||||
newAsyncSocket(future.read.client, socket.isBuffered))
|
||||
newAsyncSocket(future.read.client, socket.domain,
|
||||
socket.sockType, socket.protocol, socket.isBuffered))
|
||||
retFuture.complete(resultTup)
|
||||
return retFuture
|
||||
|
||||
@@ -423,17 +432,15 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
|
||||
## Binds ``address``:``port`` to the socket.
|
||||
##
|
||||
## If ``address`` is "" then ADDR_ANY will be bound.
|
||||
var sockDomain = getSockDomain(socket.fd)
|
||||
|
||||
var realaddr = address
|
||||
if realaddr == "":
|
||||
case sockDomain
|
||||
case socket.domain
|
||||
of AF_INET6: realaddr = "::"
|
||||
of AF_INET: realaddr = "0.0.0.0"
|
||||
else:
|
||||
raiseOSError("Unknown socket address family and no address specified to bindAddr")
|
||||
|
||||
var aiList = getAddrInfo(realaddr, port, sockDomain)
|
||||
var aiList = getAddrInfo(realaddr, port, socket.domain)
|
||||
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||
dealloc(aiList)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
@@ -64,6 +64,9 @@ type
|
||||
sslPeekChar: char
|
||||
of false: nil
|
||||
lastError: OSErrorCode ## stores the last error on this socket
|
||||
domain: Domain
|
||||
sockType: SockType
|
||||
protocol: Protocol
|
||||
|
||||
Socket* = ref SocketImpl
|
||||
|
||||
@@ -124,33 +127,39 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||
result = result or MSG_PEEK
|
||||
of SocketFlag.SafeDisconn: continue
|
||||
|
||||
proc newSocket*(fd: SocketHandle, buffered = true): Socket =
|
||||
proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET,
|
||||
sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
|
||||
## Creates a new socket as specified by the params.
|
||||
assert fd != osInvalidSocket
|
||||
new(result)
|
||||
result.fd = fd
|
||||
result.isBuffered = buffered
|
||||
result.domain = domain
|
||||
result.sockType = sockType
|
||||
result.protocol = protocol
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
|
||||
proc newSocket*(domain, sockType, protocol: cint, buffered = true): Socket =
|
||||
## Creates a new socket.
|
||||
##
|
||||
## If an error occurs EOS will be raised.
|
||||
let fd = newRawSocket(domain, typ, protocol)
|
||||
let fd = newRawSocket(domain, sockType, protocol)
|
||||
if fd == osInvalidSocket:
|
||||
raiseOSError(osLastError())
|
||||
result = newSocket(fd, buffered)
|
||||
result = newSocket(fd, domain.Domain, sockType.SockType, protocol.Protocol,
|
||||
buffered)
|
||||
|
||||
proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
|
||||
## Creates a new socket.
|
||||
##
|
||||
## If an error occurs EOS will be raised.
|
||||
let fd = newRawSocket(domain, typ, protocol)
|
||||
let fd = newRawSocket(domain, sockType, protocol)
|
||||
if fd == osInvalidSocket:
|
||||
raiseOSError(osLastError())
|
||||
result = newSocket(fd, buffered)
|
||||
result = newSocket(fd, domain, sockType, protocol, buffered)
|
||||
|
||||
when defined(ssl):
|
||||
CRYPTO_malloc_init()
|
||||
@@ -371,7 +380,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
|
||||
sizeof(name).SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
var aiList = getAddrInfo(address, port, AF_INET)
|
||||
var aiList = getAddrInfo(address, port, socket.domain)
|
||||
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
|
||||
dealloc(aiList)
|
||||
raiseOSError(osLastError())
|
||||
@@ -532,15 +541,15 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||
|
||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||
proc connect*(socket: Socket, address: string,
|
||||
port = Port(0)) {.tags: [ReadIOEffect].} =
|
||||
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
|
||||
## host name. If ``address`` is a host name, this function will try each IP
|
||||
## of that host name. ``htons`` is already performed on ``port`` so you must
|
||||
## not do it.
|
||||
##
|
||||
## If ``socket`` is an SSL socket a handshake will be automatically performed.
|
||||
var aiList = getAddrInfo(address, port, af)
|
||||
var aiList = getAddrInfo(address, port, socket.domain)
|
||||
# try all possibilities:
|
||||
var success = false
|
||||
var lastError: OSErrorCode
|
||||
@@ -992,15 +1001,15 @@ proc connectAsync(socket: Socket, name: string, port = Port(0),
|
||||
dealloc(aiList)
|
||||
if not success: raiseOSError(lastError)
|
||||
|
||||
proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
||||
af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
## Connects to server as specified by ``address`` on port specified by ``port``.
|
||||
##
|
||||
## The ``timeout`` paremeter specifies the time in milliseconds to allow for
|
||||
## the connection to the server to be made.
|
||||
socket.fd.setBlocking(false)
|
||||
|
||||
socket.connectAsync(address, port, af)
|
||||
socket.connectAsync(address, port, socket.domain)
|
||||
var s = @[socket.fd]
|
||||
if selectWrite(s, timeout) != 1:
|
||||
raise newException(TimeoutError, "Call to 'connect' timed out.")
|
||||
|
||||
@@ -157,17 +157,17 @@ else:
|
||||
result = cint(ord(p))
|
||||
|
||||
|
||||
proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
proc newRawSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): SocketHandle =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
socket(toInt(domain), toInt(typ), toInt(protocol))
|
||||
socket(toInt(domain), toInt(sockType), toInt(protocol))
|
||||
|
||||
proc newRawSocket*(domain: cint, typ: cint, protocol: cint): SocketHandle =
|
||||
proc newRawSocket*(domain: cint, sockType: cint, protocol: cint): SocketHandle =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
##
|
||||
## Use this overload if one of the enums specified above does
|
||||
## not contain what you need.
|
||||
socket(domain, typ, protocol)
|
||||
socket(domain, sockType, protocol)
|
||||
|
||||
proc close*(socket: SocketHandle) =
|
||||
## closes a socket.
|
||||
@@ -190,16 +190,17 @@ proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEff
|
||||
else:
|
||||
result = posix.listen(socket, cint(backlog))
|
||||
|
||||
proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
|
||||
proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
|
||||
sockType: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): ptr AddrInfo =
|
||||
##
|
||||
##
|
||||
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
||||
var hints: AddrInfo
|
||||
result = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(typ)
|
||||
hints.ai_protocol = toInt(prot)
|
||||
hints.ai_family = toInt(domain)
|
||||
hints.ai_socktype = toInt(sockType)
|
||||
hints.ai_protocol = toInt(protocol)
|
||||
hints.ai_flags = AI_V4MAPPED
|
||||
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
|
||||
if gaiResult != 0'i32:
|
||||
|
||||
@@ -28,7 +28,12 @@ News
|
||||
``--gc:none``. ``--os:standalone`` without ``--gc:none`` is now a version
|
||||
that doesn't depend on any OS but includes the GC. However this version
|
||||
is currently untested!
|
||||
|
||||
- All procedures which construct a ``Socket``/``AsyncSocket`` now need to
|
||||
specify the socket domain, type and protocol. The param name
|
||||
``typ: SockType`` (in ``newSocket``/``newAsyncSocket`` procs) was also
|
||||
renamed to ``sockType``. The param ``af`` in the ``connect`` procs was
|
||||
removed. This affects ``asyncnet``, ``asyncdispatch``, ``net``, and
|
||||
``rawsockets``.
|
||||
|
||||
Library additions
|
||||
-----------------
|
||||
|
||||
Reference in New Issue
Block a user