Merge branch 'devel' into araq
This commit is contained in:
commit
c3c37dbb15
29 changed files with 517 additions and 277 deletions
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
include "system/inclrtl"
|
||||
|
||||
import os, oids, tables, strutils, times, heapqueue, options
|
||||
import os, tables, strutils, times, heapqueue, options
|
||||
|
||||
import nativesockets, net, deques
|
||||
|
||||
|
|
@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
|
|
@ -931,6 +936,11 @@ else:
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc update(fd: AsyncFD, events: set[Event]) =
|
||||
let p = getGlobalDispatcher()
|
||||
assert fd.SocketHandle in p.selector
|
||||
|
|
|
|||
|
|
@ -339,13 +339,17 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
|||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == size.int32
|
||||
f.offset.inc(size)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
)
|
||||
# passing -1 here should work according to MSDN, but doesn't. For more
|
||||
# information see
|
||||
# http://stackoverflow.com/questions/33650899/does-asynchronous-file-
|
||||
# appending-in-windows-preserve-order
|
||||
ol.offset = DWord(f.offset and 0xffffffff)
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
f.offset.inc(size)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
|
||||
|
|
@ -364,7 +368,6 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
|||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
else:
|
||||
assert bytesWritten == size.int32
|
||||
f.offset.inc(size)
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
|
@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == data.len.int32
|
||||
f.offset.inc(data.len)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
|
|
@ -420,6 +422,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
)
|
||||
ol.offset = DWord(f.offset and 0xffffffff)
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
f.offset.inc(data.len)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
|
||||
|
|
@ -441,7 +444,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
else:
|
||||
assert bytesWritten == data.len.int32
|
||||
f.offset.inc(data.len)
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
|
|
|||
|
|
@ -647,9 +647,12 @@ when defineSsl:
|
|||
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
|
||||
|
||||
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
|
||||
handshake: SslHandshakeType) =
|
||||
handshake: SslHandshakeType,
|
||||
hostname: string = nil) =
|
||||
## Wraps a connected socket in an SSL context. This function effectively
|
||||
## turns ``socket`` into an SSL socket.
|
||||
## ``hostname`` should be specified so that the client knows which hostname
|
||||
## the server certificate should be validated against.
|
||||
##
|
||||
## This should be called on a connected socket, and will perform
|
||||
## an SSL handshake immediately.
|
||||
|
|
@ -660,6 +663,10 @@ when defineSsl:
|
|||
|
||||
case handshake
|
||||
of handshakeAsClient:
|
||||
if not hostname.isNil and not isIpAddress(hostname):
|
||||
# Set the SNI address for this connection. This call can fail if
|
||||
# we're not using TLSv1+.
|
||||
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
|
||||
sslSetConnectState(socket.sslHandle)
|
||||
of handshakeAsServer:
|
||||
sslSetAcceptState(socket.sslHandle)
|
||||
|
|
|
|||
|
|
@ -269,6 +269,18 @@ proc del*[A, B](t: var Table[A, B], key: A) =
|
|||
## deletes `key` from hash table `t`.
|
||||
delImpl()
|
||||
|
||||
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
||||
## Deletes the ``key`` from the table.
|
||||
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
result = index >= 0
|
||||
if result:
|
||||
shallowCopy(val, t.data[index].val)
|
||||
delImplIdx(t, index)
|
||||
|
||||
proc enlarge[A, B](t: var Table[A, B]) =
|
||||
var n: KeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
|
|
@ -424,6 +436,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
|
|||
## deletes `key` from hash table `t`.
|
||||
t[].del(key)
|
||||
|
||||
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
|
||||
## Deletes the ``key`` from the table.
|
||||
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
result = t[].take(key, val)
|
||||
|
||||
proc newTable*[A, B](initialSize=64): TableRef[A, B] =
|
||||
new(result)
|
||||
result[] = initTable[A, B](initialSize)
|
||||
|
|
@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
|
|||
while ht >= 0 and hs >= 0:
|
||||
var nxtt = t.data[ht].next
|
||||
var nxts = s.data[hs].next
|
||||
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
|
||||
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
|
||||
if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
|
||||
return false
|
||||
ht = nxtt
|
||||
|
|
@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
|
|||
proc clear*[A](t: var CountTable[A]) =
|
||||
## Resets the table so that it is empty.
|
||||
clearImpl()
|
||||
|
||||
|
||||
iterator pairs*[A](t: CountTable[A]): (A, int) =
|
||||
## iterates over any (key, value) pair in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
|
|
@ -1256,17 +1275,17 @@ when isMainModule:
|
|||
var b = newOrderedTable[string, string](initialSize=2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == b
|
||||
|
||||
block: #5482
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string](initialSize=2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == b
|
||||
|
||||
block: #5487
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string]() # notice, default size!
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
|
|
@ -1279,13 +1298,13 @@ when isMainModule:
|
|||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
|
||||
block:
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
block:
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
var c = newOrderedTable[string, string]() # notice, default size!
|
||||
c.add("wrong?", "foo")
|
||||
c.add("wrong?", "foo2")
|
||||
c.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == c
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
raise newException(HttpRequestError,
|
||||
"The proxy server rejected a CONNECT request, " &
|
||||
"so a secure connection could not be established.")
|
||||
sslContext.wrapConnectedSocket(s, handshakeAsClient)
|
||||
sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
|
||||
else:
|
||||
raise newException(HttpRequestError, "SSL support not available. Cannot connect via proxy over SSL")
|
||||
else:
|
||||
|
|
@ -1060,7 +1060,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
|
|||
when defined(ssl):
|
||||
if isSsl:
|
||||
try:
|
||||
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
|
||||
client.sslContext.wrapConnectedSocket(
|
||||
client.socket, handshakeAsClient, url.hostname)
|
||||
except:
|
||||
client.socket.close()
|
||||
raise getCurrentException()
|
||||
|
|
@ -1102,7 +1103,8 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
|
|||
raise newException(HttpRequestError,
|
||||
"The proxy server rejected a CONNECT request, " &
|
||||
"so a secure connection could not be established.")
|
||||
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
|
||||
client.sslContext.wrapConnectedSocket(
|
||||
client.socket, handshakeAsClient, requestUrl.hostname)
|
||||
client.proxy = nil
|
||||
else:
|
||||
raise newException(HttpRequestError,
|
||||
|
|
|
|||
358
lib/pure/net.nim
358
lib/pure/net.nim
|
|
@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
|||
raiseOSError(osLastError())
|
||||
result = newSocket(fd, domain, sockType, protocol, buffered)
|
||||
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
var
|
||||
byteCount = 0
|
||||
currentByte:uint16 = 0
|
||||
seperatorValid = false
|
||||
|
||||
result.family = IpAddressFamily.IPv4
|
||||
|
||||
for i in 0 .. high(address_str):
|
||||
if address_str[i] in strutils.Digits: # Character is a number
|
||||
currentByte = currentByte * 10 +
|
||||
cast[uint16](ord(address_str[i]) - ord('0'))
|
||||
if currentByte > 255'u16:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif address_str[i] == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
currentByte = 0
|
||||
byteCount.inc
|
||||
seperatorValid = false
|
||||
else:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
|
||||
var
|
||||
groupCount = 0
|
||||
currentGroupStart = 0
|
||||
currentShort:uint32 = 0
|
||||
seperatorValid = true
|
||||
dualColonGroup = -1
|
||||
lastWasColon = false
|
||||
v4StartPos = -1
|
||||
byteCount = 0
|
||||
|
||||
for i,c in address_str:
|
||||
if c == ':':
|
||||
if not seperatorValid:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains more than one \"::\" seperator")
|
||||
dualColonGroup = groupCount
|
||||
seperatorValid = false
|
||||
elif i != 0 and i != high(address_str):
|
||||
if groupCount >= 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||
currentShort = 0
|
||||
groupCount.inc()
|
||||
if dualColonGroup != -1: seperatorValid = false
|
||||
elif i == 0: # only valid if address starts with ::
|
||||
if address_str[1] != ':':
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address may not start with \":\"")
|
||||
else: # i == high(address_str) - only valid if address ends with ::
|
||||
if address_str[high(address_str)-1] != ':':
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address may not end with \":\"")
|
||||
lastWasColon = true
|
||||
currentGroupStart = i + 1
|
||||
elif c == '.': # Switch to parse IPv4 mode
|
||||
if i < 3 or not seperatorValid or groupCount >= 7:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
v4StartPos = currentGroupStart
|
||||
currentShort = 0
|
||||
seperatorValid = false
|
||||
break
|
||||
elif c in strutils.HexDigits:
|
||||
if c in strutils.Digits: # Normal digit
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
|
||||
elif c >= 'a' and c <= 'f': # Lower case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
|
||||
else: # Upper case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
|
||||
if currentShort > 65535'u32:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
|
||||
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
|
||||
if seperatorValid: # Copy remaining data
|
||||
if groupCount >= 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||
groupCount.inc()
|
||||
else: # Must parse IPv4 address
|
||||
for i,c in address_str[v4StartPos..high(address_str)]:
|
||||
if c in strutils.Digits: # Character is a number
|
||||
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
|
||||
if currentShort > 255'u32:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
currentShort = 0
|
||||
byteCount.inc()
|
||||
seperatorValid = false
|
||||
else: # Invalid character
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
groupCount += 2
|
||||
|
||||
# Shift and fill zeros in case of ::
|
||||
if groupCount > 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too few groups")
|
||||
var toFill = 8 - groupCount # The number of groups to fill
|
||||
var toShift = groupCount - dualColonGroup # Nr of known groups after ::
|
||||
for i in 0..2*toShift-1: # shift
|
||||
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
|
||||
for i in 0..2*toFill-1: # fill with 0s
|
||||
result.address_v6[dualColonGroup*2+i] = 0
|
||||
elif dualColonGroup != -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress*(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(ValueError, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||
## Checks if a string is an IP address
|
||||
## Returns true if it is, false otherwise
|
||||
try:
|
||||
discard parseIpAddress(address_str)
|
||||
except ValueError:
|
||||
return false
|
||||
return true
|
||||
|
||||
when defineSsl:
|
||||
CRYPTO_malloc_init()
|
||||
SslLibraryInit()
|
||||
|
|
@ -438,9 +612,12 @@ when defineSsl:
|
|||
raiseSSLError()
|
||||
|
||||
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
|
||||
handshake: SslHandshakeType) =
|
||||
handshake: SslHandshakeType,
|
||||
hostname: string = nil) =
|
||||
## Wraps a connected socket in an SSL context. This function effectively
|
||||
## turns ``socket`` into an SSL socket.
|
||||
## ``hostname`` should be specified so that the client knows which hostname
|
||||
## the server certificate should be validated against.
|
||||
##
|
||||
## This should be called on a connected socket, and will perform
|
||||
## an SSL handshake immediately.
|
||||
|
|
@ -450,6 +627,10 @@ when defineSsl:
|
|||
wrapSocket(ctx, socket)
|
||||
case handshake
|
||||
of handshakeAsClient:
|
||||
if not hostname.isNil and not isIpAddress(hostname):
|
||||
# Discard result in case OpenSSL version doesn't support SNI, or we're
|
||||
# not using TLSv1+
|
||||
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
|
||||
let ret = SSLConnect(socket.sslHandle)
|
||||
socketError(socket, ret)
|
||||
of handshakeAsServer:
|
||||
|
|
@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
|
|||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
var
|
||||
byteCount = 0
|
||||
currentByte:uint16 = 0
|
||||
seperatorValid = false
|
||||
|
||||
result.family = IpAddressFamily.IPv4
|
||||
|
||||
for i in 0 .. high(address_str):
|
||||
if address_str[i] in strutils.Digits: # Character is a number
|
||||
currentByte = currentByte * 10 +
|
||||
cast[uint16](ord(address_str[i]) - ord('0'))
|
||||
if currentByte > 255'u16:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif address_str[i] == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
currentByte = 0
|
||||
byteCount.inc
|
||||
seperatorValid = false
|
||||
else:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
|
||||
var
|
||||
groupCount = 0
|
||||
currentGroupStart = 0
|
||||
currentShort:uint32 = 0
|
||||
seperatorValid = true
|
||||
dualColonGroup = -1
|
||||
lastWasColon = false
|
||||
v4StartPos = -1
|
||||
byteCount = 0
|
||||
|
||||
for i,c in address_str:
|
||||
if c == ':':
|
||||
if not seperatorValid:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains more than one \"::\" seperator")
|
||||
dualColonGroup = groupCount
|
||||
seperatorValid = false
|
||||
elif i != 0 and i != high(address_str):
|
||||
if groupCount >= 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||
currentShort = 0
|
||||
groupCount.inc()
|
||||
if dualColonGroup != -1: seperatorValid = false
|
||||
elif i == 0: # only valid if address starts with ::
|
||||
if address_str[1] != ':':
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address may not start with \":\"")
|
||||
else: # i == high(address_str) - only valid if address ends with ::
|
||||
if address_str[high(address_str)-1] != ':':
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address may not end with \":\"")
|
||||
lastWasColon = true
|
||||
currentGroupStart = i + 1
|
||||
elif c == '.': # Switch to parse IPv4 mode
|
||||
if i < 3 or not seperatorValid or groupCount >= 7:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
v4StartPos = currentGroupStart
|
||||
currentShort = 0
|
||||
seperatorValid = false
|
||||
break
|
||||
elif c in strutils.HexDigits:
|
||||
if c in strutils.Digits: # Normal digit
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
|
||||
elif c >= 'a' and c <= 'f': # Lower case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
|
||||
else: # Upper case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
|
||||
if currentShort > 65535'u32:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
|
||||
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
|
||||
if seperatorValid: # Copy remaining data
|
||||
if groupCount >= 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||
groupCount.inc()
|
||||
else: # Must parse IPv4 address
|
||||
for i,c in address_str[v4StartPos..high(address_str)]:
|
||||
if c in strutils.Digits: # Character is a number
|
||||
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
|
||||
if currentShort > 255'u32:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
currentShort = 0
|
||||
byteCount.inc()
|
||||
seperatorValid = false
|
||||
else: # Invalid character
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
groupCount += 2
|
||||
|
||||
# Shift and fill zeros in case of ::
|
||||
if groupCount > 8:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too few groups")
|
||||
var toFill = 8 - groupCount # The number of groups to fill
|
||||
var toShift = groupCount - dualColonGroup # Nr of known groups after ::
|
||||
for i in 0..2*toShift-1: # shift
|
||||
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
|
||||
for i in 0..2*toFill-1: # fill with 0s
|
||||
result.address_v6[dualColonGroup*2+i] = 0
|
||||
elif dualColonGroup != -1:
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
|
||||
proc parseIpAddress*(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(ValueError, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||
## Checks if a string is an IP address
|
||||
## Returns true if it is, false otherwise
|
||||
try:
|
||||
discard parseIpAddress(address_str)
|
||||
except ValueError:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc dial*(address: string, port: Port,
|
||||
protocol = IPPROTO_TCP, buffered = true): Socket
|
||||
{.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
|
|
|
|||
|
|
@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
|
|||
if afterRunEvent != nil: afterRunEvent(i, p)
|
||||
close(p)
|
||||
|
||||
proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
|
||||
proc select*(readfds: var seq[Process], timeout = 500): int
|
||||
{.benign, deprecated.}
|
||||
## `select` with a sensible Nim interface. `timeout` is in milliseconds.
|
||||
## Specify -1 for no timeout. Returns the number of processes that are
|
||||
## ready to read from. The processes that are ready to be read from are
|
||||
|
|
@ -343,6 +344,9 @@ proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
|
|||
##
|
||||
## **Warning**: This function may give unexpected or completely wrong
|
||||
## results on Windows.
|
||||
##
|
||||
## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
|
||||
## and so should not be used in newly written code.
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc execProcess(command: string,
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
|
|||
|
||||
proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
|
||||
## Finds the first occurrence of ``first``, then returns everything from there
|
||||
## up to ``second``(if ``second`` is '\0', then ``first`` is used).
|
||||
## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
|
||||
var i = skipUntil(s, first, start)+1+start
|
||||
result = ""
|
||||
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
||||
|
|
|
|||
|
|
@ -1881,6 +1881,8 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
|||
## of significant digits to be printed.
|
||||
## `precision`'s default value is the maximum number of meaningful digits
|
||||
## after the decimal point for Nim's ``float`` type.
|
||||
##
|
||||
## If ``precision == 0``, it tries to format it nicely.
|
||||
result = formatBiggestFloat(f, format, precision, decimalSep)
|
||||
|
||||
proc trimZeros*(x: var string) {.noSideEffect.} =
|
||||
|
|
|
|||
|
|
@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
|
|||
|
||||
template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
|
||||
when defined(cpp):
|
||||
when sizeof(int) == 8:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
|
||||
header: "<intrin.h>".}
|
||||
else:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
|
||||
header: "<intrin.h>".}
|
||||
else:
|
||||
when sizeof(int) == 8:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
|
||||
else:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
|
||||
|
||||
proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
|
||||
|
||||
|
|
@ -180,6 +194,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
|
|||
result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
result = addAndFetch(memLoc.addr, x)
|
||||
inc(result, x)
|
||||
else:
|
||||
inc(memLoc, x)
|
||||
result = memLoc
|
||||
|
|
@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
|
|||
result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
result = addAndFetch(memLoc.addr, -x)
|
||||
dec(result, x)
|
||||
else:
|
||||
dec(memLoc, x)
|
||||
result = memLoc
|
||||
|
|
|
|||
|
|
@ -299,8 +299,13 @@ proc raiseExceptionAux(e: ref Exception) =
|
|||
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
|
||||
if e.name.isNil: e.name = ename
|
||||
when hasSomeStackTrace:
|
||||
e.trace = ""
|
||||
rawWriteStackTrace(e.trace)
|
||||
if e.trace.isNil:
|
||||
e.trace = ""
|
||||
rawWriteStackTrace(e.trace)
|
||||
elif framePtr != nil:
|
||||
e.trace.add "[[reraised from:\n"
|
||||
auxWriteStackTrace(framePtr, e.trace)
|
||||
e.trace.add "]]\n"
|
||||
raiseExceptionAux(e)
|
||||
|
||||
proc reraiseException() {.compilerRtl.} =
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ elif defined(genode):
|
|||
|
||||
else:
|
||||
type
|
||||
SysLock {.importc: "pthread_mutex_t", pure, final,
|
||||
SysLockObj {.importc: "pthread_mutex_t", pure, final,
|
||||
header: """#include <sys/types.h>
|
||||
#include <pthread.h>""".} = object
|
||||
when defined(linux) and defined(amd64):
|
||||
|
|
@ -111,7 +111,7 @@ else:
|
|||
when defined(linux) and defined(amd64):
|
||||
abi: array[4 div sizeof(cint), cint] # actually a cint
|
||||
|
||||
SysCond {.importc: "pthread_cond_t", pure, final,
|
||||
SysCondObj {.importc: "pthread_cond_t", pure, final,
|
||||
header: """#include <sys/types.h>
|
||||
#include <pthread.h>""".} = object
|
||||
when defined(linux) and defined(amd64):
|
||||
|
|
@ -119,8 +119,62 @@ else:
|
|||
|
||||
SysLockType = distinct cint
|
||||
|
||||
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) {.
|
||||
proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
|
||||
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
|
||||
proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
|
||||
|
||||
proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_lock", header: "<pthread.h>".}
|
||||
proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
|
||||
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||
|
||||
proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
||||
|
||||
when defined(ios):
|
||||
# iOS will behave badly if sync primitives are moved in memory. In order
|
||||
# to prevent this once and for all, we're doing an extra malloc when
|
||||
# initializing the primitive.
|
||||
type
|
||||
SysLock = ptr SysLockObj
|
||||
SysCond = ptr SysCondObj
|
||||
|
||||
when not declared(c_malloc):
|
||||
proc c_malloc(size: csize): pointer {.
|
||||
importc: "malloc", header: "<stdlib.h>".}
|
||||
proc c_free(p: pointer) {.
|
||||
importc: "free", header: "<stdlib.h>".}
|
||||
|
||||
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
|
||||
L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
|
||||
initSysLockAux(L[], attr)
|
||||
|
||||
proc deinitSys(L: var SysLock) =
|
||||
deinitSysAux(L[])
|
||||
c_free(L)
|
||||
|
||||
template acquireSys(L: var SysLock) =
|
||||
acquireSysAux(L[])
|
||||
template tryAcquireSys(L: var SysLock): bool =
|
||||
tryAcquireSysAux(L[]) == 0'i32
|
||||
template releaseSys(L: var SysLock) =
|
||||
releaseSysAux(L[])
|
||||
else:
|
||||
type
|
||||
SysLock = SysLockObj
|
||||
SysCond = SysCondObj
|
||||
|
||||
template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
|
||||
initSysLockAux(L, attr)
|
||||
template deinitSys(L: var SysLock) =
|
||||
deinitSysAux(L)
|
||||
template acquireSys(L: var SysLock) =
|
||||
acquireSysAux(L)
|
||||
template tryAcquireSys(L: var SysLock): bool =
|
||||
tryAcquireSysAux(L) == 0'i32
|
||||
template releaseSys(L: var SysLock) =
|
||||
releaseSysAux(L)
|
||||
|
||||
when insideRLocksModule:
|
||||
proc SysLockType_Reentrant: SysLockType =
|
||||
|
|
@ -130,27 +184,39 @@ else:
|
|||
proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
|
||||
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
|
||||
|
||||
proc acquireSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_lock", header: "<pthread.h>".}
|
||||
proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
|
||||
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
result = tryAcquireSysAux(L) == 0'i32
|
||||
|
||||
proc releaseSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
||||
proc deinitSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
|
||||
|
||||
when not insideRLocksModule:
|
||||
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
|
||||
else:
|
||||
proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
|
||||
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
|
||||
proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
|
||||
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
|
||||
proc signalSysCond(cond: var SysCond) {.
|
||||
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
|
||||
proc deinitSysCond(cond: var SysCond) {.noSideEffect,
|
||||
proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
|
||||
importc: "pthread_cond_destroy", header: "<pthread.h>".}
|
||||
|
||||
proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
|
||||
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
|
||||
proc signalSysCondAux(cond: var SysCondObj) {.
|
||||
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
|
||||
|
||||
when defined(ios):
|
||||
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
|
||||
cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
|
||||
initSysCondAux(cond[], cond_attr)
|
||||
|
||||
proc deinitSysCond(cond: var SysCond) =
|
||||
deinitSysCondAux(cond[])
|
||||
c_free(cond)
|
||||
|
||||
template waitSysCond(cond: var SysCond, lock: var SysLock) =
|
||||
waitSysCondAux(cond[], lock[])
|
||||
template signalSysCond(cond: var SysCond) =
|
||||
signalSysCondAux(cond[])
|
||||
else:
|
||||
template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
|
||||
initSysCondAux(cond, cond_attr)
|
||||
template deinitSysCond(cond: var SysCond) =
|
||||
deinitSysCondAux(cond)
|
||||
|
||||
template waitSysCond(cond: var SysCond, lock: var SysLock) =
|
||||
waitSysCondAux(cond, lock)
|
||||
template signalSysCond(cond: var SysCond) =
|
||||
signalSysCondAux(cond)
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
include "system/inclrtl"
|
||||
|
||||
import os, oids, tables, strutils, times, heapqueue, lists, options
|
||||
import os, tables, strutils, times, heapqueue, lists, options
|
||||
|
||||
import nativesockets, net, deques
|
||||
|
||||
|
|
@ -219,6 +219,11 @@ when defined(windows) or defined(nimdoc):
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
|
|
@ -1080,6 +1085,11 @@ else:
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue