make it gc-safe #2692
This commit is contained in:
parent
cfe5447e95
commit
443ec02a92
1 changed files with 94 additions and 93 deletions
187
lib/pure/net.nim
187
lib/pure/net.nim
|
|
@ -102,8 +102,7 @@ type
|
||||||
## case of IPv4
|
## case of IPv4
|
||||||
{.deprecated: [TIpAddress: IpAddress].}
|
{.deprecated: [TIpAddress: IpAddress].}
|
||||||
|
|
||||||
proc isIpAddress*(address_str: string): bool {.tags: [].}
|
|
||||||
proc parseIpAddress*(address_str: string): IpAddress
|
|
||||||
proc socketError*(socket: Socket, err: int = -1, async = false,
|
proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||||
lastError = (-1).OSErrorCode): void
|
lastError = (-1).OSErrorCode): void
|
||||||
|
|
||||||
|
|
@ -548,40 +547,6 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
|
||||||
var valuei = cint(if value: 1 else: 0)
|
var valuei = cint(if value: 1 else: 0)
|
||||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||||
|
|
||||||
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, socket.domain)
|
|
||||||
# try all possibilities:
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else: lastError = osLastError()
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
dealloc(aiList)
|
|
||||||
if not success: raiseOSError(lastError)
|
|
||||||
|
|
||||||
when defined(ssl):
|
|
||||||
if socket.isSSL:
|
|
||||||
# RFC3546 for SNI specifies that IP addresses are not allowed.
|
|
||||||
if not isIpAddress(address):
|
|
||||||
# Discard result in case OpenSSL version doesn't support SNI, or we're
|
|
||||||
# not using TLSv1+
|
|
||||||
discard SSL_set_tlsext_host_name(socket.sslHandle, address)
|
|
||||||
|
|
||||||
let ret = SSLConnect(socket.sslHandle)
|
|
||||||
socketError(socket, ret)
|
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
|
proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
## This proc needs to be called on a socket after it connects. This is
|
## This proc needs to be called on a socket after it connects. This is
|
||||||
|
|
@ -971,61 +936,6 @@ proc sendTo*(socket: Socket, address: string, port: Port,
|
||||||
## This is the high-level version of the above ``sendTo`` function.
|
## This is the high-level version of the above ``sendTo`` function.
|
||||||
result = socket.sendTo(address, port, cstring(data), data.len)
|
result = socket.sendTo(address, port, cstring(data), data.len)
|
||||||
|
|
||||||
proc connectAsync(socket: Socket, name: string, port = Port(0),
|
|
||||||
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
|
||||||
## A variant of ``connect`` for non-blocking sockets.
|
|
||||||
##
|
|
||||||
## This procedure will immediately return, it will not block until a connection
|
|
||||||
## is made. It is up to the caller to make sure the connection has been established
|
|
||||||
## by checking (using ``select``) whether the socket is writeable.
|
|
||||||
##
|
|
||||||
## **Note**: For SSL sockets, the ``handshake`` procedure must be called
|
|
||||||
## whenever the socket successfully connects to a server.
|
|
||||||
var aiList = getAddrInfo(name, port, af)
|
|
||||||
# try all possibilities:
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen)
|
|
||||||
if ret == 0'i32:
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = osLastError()
|
|
||||||
when useWinVersion:
|
|
||||||
# Windows EINTR doesn't behave same as POSIX.
|
|
||||||
if lastError.int32 == WSAEWOULDBLOCK:
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
dealloc(aiList)
|
|
||||||
if not success: raiseOSError(lastError)
|
|
||||||
|
|
||||||
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, socket.domain)
|
|
||||||
var s = @[socket.fd]
|
|
||||||
if selectWrite(s, timeout) != 1:
|
|
||||||
raise newException(TimeoutError, "Call to 'connect' timed out.")
|
|
||||||
else:
|
|
||||||
when defined(ssl):
|
|
||||||
if socket.isSSL:
|
|
||||||
socket.fd.setBlocking(true)
|
|
||||||
doAssert socket.handshake()
|
|
||||||
socket.fd.setBlocking(true)
|
|
||||||
|
|
||||||
proc isSsl*(socket: Socket): bool =
|
proc isSsl*(socket: Socket): bool =
|
||||||
## Determines whether ``socket`` is a SSL socket.
|
## Determines whether ``socket`` is a SSL socket.
|
||||||
|
|
@ -1295,6 +1205,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"Invalid IP Address. The address consists of too many groups")
|
"Invalid IP Address. The address consists of too many groups")
|
||||||
|
|
||||||
|
|
||||||
proc parseIpAddress(address_str: string): IpAddress =
|
proc parseIpAddress(address_str: string): IpAddress =
|
||||||
## Parses an IP address
|
## Parses an IP address
|
||||||
## Raises EInvalidValue on error
|
## Raises EInvalidValue on error
|
||||||
|
|
@ -1305,8 +1216,7 @@ proc parseIpAddress(address_str: string): IpAddress =
|
||||||
else:
|
else:
|
||||||
return parseIPv4Address(address_str)
|
return parseIPv4Address(address_str)
|
||||||
|
|
||||||
|
proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||||
proc isIpAddress(address_str: string): bool =
|
|
||||||
## Checks if a string is an IP address
|
## Checks if a string is an IP address
|
||||||
## Returns true if it is, false otherwise
|
## Returns true if it is, false otherwise
|
||||||
try:
|
try:
|
||||||
|
|
@ -1314,3 +1224,94 @@ proc isIpAddress(address_str: string): bool =
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
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, socket.domain)
|
||||||
|
# try all possibilities:
|
||||||
|
var success = false
|
||||||
|
var lastError: OSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
while it != nil:
|
||||||
|
if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
else: lastError = osLastError()
|
||||||
|
it = it.ai_next
|
||||||
|
|
||||||
|
dealloc(aiList)
|
||||||
|
if not success: raiseOSError(lastError)
|
||||||
|
|
||||||
|
when defined(ssl):
|
||||||
|
if socket.isSSL:
|
||||||
|
# RFC3546 for SNI specifies that IP addresses are not allowed.
|
||||||
|
if not isIpAddress(address):
|
||||||
|
# Discard result in case OpenSSL version doesn't support SNI, or we're
|
||||||
|
# not using TLSv1+
|
||||||
|
discard SSL_set_tlsext_host_name(socket.sslHandle, address)
|
||||||
|
|
||||||
|
let ret = SSLConnect(socket.sslHandle)
|
||||||
|
socketError(socket, ret)
|
||||||
|
|
||||||
|
proc connectAsync(socket: Socket, name: string, port = Port(0),
|
||||||
|
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||||
|
## A variant of ``connect`` for non-blocking sockets.
|
||||||
|
##
|
||||||
|
## This procedure will immediately return, it will not block until a connection
|
||||||
|
## is made. It is up to the caller to make sure the connection has been established
|
||||||
|
## by checking (using ``select``) whether the socket is writeable.
|
||||||
|
##
|
||||||
|
## **Note**: For SSL sockets, the ``handshake`` procedure must be called
|
||||||
|
## whenever the socket successfully connects to a server.
|
||||||
|
var aiList = getAddrInfo(name, port, af)
|
||||||
|
# try all possibilities:
|
||||||
|
var success = false
|
||||||
|
var lastError: OSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
while it != nil:
|
||||||
|
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen)
|
||||||
|
if ret == 0'i32:
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lastError = osLastError()
|
||||||
|
when useWinVersion:
|
||||||
|
# Windows EINTR doesn't behave same as POSIX.
|
||||||
|
if lastError.int32 == WSAEWOULDBLOCK:
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
|
||||||
|
it = it.ai_next
|
||||||
|
|
||||||
|
dealloc(aiList)
|
||||||
|
if not success: raiseOSError(lastError)
|
||||||
|
|
||||||
|
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, socket.domain)
|
||||||
|
var s = @[socket.fd]
|
||||||
|
if selectWrite(s, timeout) != 1:
|
||||||
|
raise newException(TimeoutError, "Call to 'connect' timed out.")
|
||||||
|
else:
|
||||||
|
when defined(ssl):
|
||||||
|
if socket.isSSL:
|
||||||
|
socket.fd.setBlocking(true)
|
||||||
|
doAssert socket.handshake()
|
||||||
|
socket.fd.setBlocking(true)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue