mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 20:04:18 +00:00
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
@@ -185,13 +185,15 @@ proc isCastable(dst, src: PType): bool =
|
||||
# castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString,
|
||||
# tySequence, tyPointer, tyNil, tyOpenArray,
|
||||
# tyProc, tySet, tyEnum, tyBool, tyChar}
|
||||
if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray:
|
||||
return false
|
||||
var dstSize, srcSize: BiggestInt
|
||||
|
||||
dstSize = computeSize(dst)
|
||||
srcSize = computeSize(src)
|
||||
if dstSize < 0:
|
||||
result = false
|
||||
elif srcSize < 0:
|
||||
elif srcSize < 0:
|
||||
result = false
|
||||
elif not typeAllowed(dst, skParam):
|
||||
result = false
|
||||
|
||||
@@ -524,7 +524,10 @@ when defined(windows) or defined(nimdoc):
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
||||
let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||
@@ -544,13 +547,19 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD):
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
##
|
||||
## The resulting client socket is automatically registered to dispatcher.
|
||||
## The resulting client socket is automatically registered to the
|
||||
## dispatcher.
|
||||
##
|
||||
## The ``accept`` call may result in an error if the connecting socket
|
||||
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr")
|
||||
|
||||
@@ -584,6 +593,18 @@ when defined(windows) or defined(nimdoc):
|
||||
client: clientSock.TAsyncFD)
|
||||
)
|
||||
|
||||
template failAccept(errcode): stmt =
|
||||
if flags.isDisconnectionError(errcode):
|
||||
var newAcceptFut = acceptAddr(socket, flags)
|
||||
newAcceptFut.callback =
|
||||
proc () =
|
||||
if newAcceptFut.failed:
|
||||
retFuture.fail(newAcceptFut.readError)
|
||||
else:
|
||||
retFuture.complete(newAcceptFut.read)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(sock: socket, cb:
|
||||
@@ -592,7 +613,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if errcode == TOSErrorCode(-1):
|
||||
completeAccept()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
failAccept(errcode)
|
||||
)
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
|
||||
@@ -605,7 +626,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if not ret:
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||
failAccept(err)
|
||||
GC_unref(ol)
|
||||
else:
|
||||
completeAccept()
|
||||
@@ -749,7 +770,7 @@ else:
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||
af = AF_INET): PFuture[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
var retFuture = newFuture[void]("connect")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
# We have connected.
|
||||
@@ -784,7 +805,7 @@ else:
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
|
||||
var retFuture = newFuture[string]()
|
||||
var retFuture = newFuture[string]("recv")
|
||||
|
||||
var readBuffer = newString(size)
|
||||
|
||||
@@ -815,7 +836,7 @@ else:
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
||||
var written = 0
|
||||
|
||||
@@ -845,9 +866,10 @@ else:
|
||||
addWrite(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD):
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]()
|
||||
var retFuture = newFuture[tuple[address: string,
|
||||
client: TAsyncFD]]("acceptAddr")
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
result = true
|
||||
var sockAddress: Tsockaddr_in
|
||||
@@ -860,7 +882,10 @@ else:
|
||||
if lastError.int32 == EINTR:
|
||||
return false
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||
if flags.isDisconnectionError(lastError):
|
||||
return false
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||
else:
|
||||
register(client.TAsyncFD)
|
||||
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
|
||||
@@ -875,12 +900,13 @@ proc sleepAsync*(ms: int): PFuture[void] =
|
||||
p.timers.add((epochTime() + (ms / 1000), retFuture))
|
||||
return retFuture
|
||||
|
||||
proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] =
|
||||
proc accept*(socket: TAsyncFD,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[TAsyncFD] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[TAsyncFD]("accept")
|
||||
var fut = acceptAddr(socket)
|
||||
var fut = acceptAddr(socket, flags)
|
||||
fut.callback =
|
||||
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
||||
assert future.finished
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
##
|
||||
## **Note:** This module is still largely experimental.
|
||||
|
||||
import strtabs, asyncnet, asyncdispatch, parseutils, parseurl, strutils
|
||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||
type
|
||||
TRequest* = object
|
||||
client*: PAsyncSocket # TODO: Separate this into a Response object?
|
||||
reqMethod*: string
|
||||
headers*: PStringTable
|
||||
protocol*: tuple[orig: string, major, minor: int]
|
||||
url*: TURL
|
||||
url*: TUri
|
||||
hostname*: string ## The hostname of the client that made the request.
|
||||
body*: string
|
||||
|
||||
@@ -135,7 +135,7 @@ proc processClient(client: PAsyncSocket, address: string,
|
||||
request.headers[kv.key] = kv.value
|
||||
|
||||
request.reqMethod = reqMethod
|
||||
request.url = parseUrl(path)
|
||||
request.url = parseUri(path)
|
||||
try:
|
||||
request.protocol = protocol.parseProtocol()
|
||||
except EInvalidValue:
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
## let client = await server.accept()
|
||||
## clients.add client
|
||||
##
|
||||
## processClient(client)
|
||||
## asyncCheck processClient(client)
|
||||
##
|
||||
## serve()
|
||||
## asyncCheck serve()
|
||||
## runForever()
|
||||
##
|
||||
##
|
||||
@@ -135,13 +135,13 @@ proc send*(socket: PAsyncSocket, data: string,
|
||||
assert socket != nil
|
||||
result = send(socket.fd.TAsyncFD, data, flags)
|
||||
|
||||
proc acceptAddr*(socket: PAsyncSocket):
|
||||
proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}):
|
||||
PFuture[tuple[address: string, client: PAsyncSocket]] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]("asyncnet.acceptAddr")
|
||||
var fut = acceptAddr(socket.fd.TAsyncFD)
|
||||
var fut = acceptAddr(socket.fd.TAsyncFD, flags)
|
||||
fut.callback =
|
||||
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
||||
assert future.finished
|
||||
@@ -153,12 +153,13 @@ proc acceptAddr*(socket: PAsyncSocket):
|
||||
retFuture.complete(resultTup)
|
||||
return retFuture
|
||||
|
||||
proc accept*(socket: PAsyncSocket): PFuture[PAsyncSocket] =
|
||||
proc accept*(socket: PAsyncSocket,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[PAsyncSocket] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[PAsyncSocket]("asyncnet.accept")
|
||||
var fut = acceptAddr(socket)
|
||||
var fut = acceptAddr(socket, flags)
|
||||
fut.callback =
|
||||
proc (future: PFuture[tuple[address: string, client: PAsyncSocket]]) =
|
||||
assert future.finished
|
||||
|
||||
586
lib/pure/net.nim
586
lib/pure/net.nim
@@ -15,288 +15,6 @@ export TPort, `$`
|
||||
|
||||
const useWinVersion = defined(Windows) or defined(nimdoc)
|
||||
|
||||
type
|
||||
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
|
||||
IPv6, ## IPv6 address
|
||||
IPv4 ## IPv4 address
|
||||
|
||||
TIpAddress* = object ## stores an arbitrary IP address
|
||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||
of IpAddressFamily.IPv6:
|
||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv6
|
||||
of IpAddressFamily.IPv4:
|
||||
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv4
|
||||
|
||||
proc IPv4_any*(): TIpAddress =
|
||||
## Returns the IPv4 any address, which can be used to listen on all available
|
||||
## network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [0'u8, 0, 0, 0])
|
||||
|
||||
proc IPv4_loopback*(): TIpAddress =
|
||||
## Returns the IPv4 loopback address (127.0.0.1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [127'u8, 0, 0, 1])
|
||||
|
||||
proc IPv4_broadcast*(): TIpAddress =
|
||||
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [255'u8, 255, 255, 255])
|
||||
|
||||
proc IPv6_any*(): TIpAddress =
|
||||
## Returns the IPv6 any address (::0), which can be used
|
||||
## to listen on all available network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
proc IPv6_loopback*(): TIpAddress =
|
||||
## Returns the IPv6 loopback address (::1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
|
||||
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||
if lhs.family != rhs.family: return false
|
||||
if lhs.family == IpAddressFamily.IPv4:
|
||||
for i in low(lhs.address_v4) .. high(lhs.address_v4):
|
||||
if lhs.address_v4[i] != rhs.address_v4[i]: return false
|
||||
else: # IPv6
|
||||
for i in low(lhs.address_v6) .. high(lhs.address_v6):
|
||||
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||
return true
|
||||
|
||||
proc `$`*(address: TIpAddress): string =
|
||||
## Converts an TIpAddress into the textual representation
|
||||
result = ""
|
||||
case address.family
|
||||
of IpAddressFamily.IPv4:
|
||||
for i in 0 .. 3:
|
||||
if i != 0:
|
||||
result.add('.')
|
||||
result.add($address.address_v4[i])
|
||||
of IpAddressFamily.IPv6:
|
||||
var
|
||||
currentZeroStart = -1
|
||||
currentZeroCount = 0
|
||||
biggestZeroStart = -1
|
||||
biggestZeroCount = 0
|
||||
# Look for the largest block of zeros
|
||||
for i in 0..7:
|
||||
var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0
|
||||
if isZero:
|
||||
if currentZeroStart == -1:
|
||||
currentZeroStart = i
|
||||
currentZeroCount = 1
|
||||
else:
|
||||
currentZeroCount.inc()
|
||||
if currentZeroCount > biggestZeroCount:
|
||||
biggestZeroCount = currentZeroCount
|
||||
biggestZeroStart = currentZeroStart
|
||||
else:
|
||||
currentZeroStart = -1
|
||||
|
||||
if biggestZeroCount == 8: # Special case ::0
|
||||
result.add("::")
|
||||
else: # Print address
|
||||
var printedLastGroup = false
|
||||
for i in 0..7:
|
||||
var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
|
||||
word = word or cast[uint16](address.address_v6[i*2+1])
|
||||
|
||||
if biggestZeroCount != 0 and # Check if group is in skip group
|
||||
(i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)):
|
||||
if i == biggestZeroStart: # skip start
|
||||
result.add("::")
|
||||
printedLastGroup = false
|
||||
else:
|
||||
if printedLastGroup:
|
||||
result.add(':')
|
||||
var
|
||||
afterLeadingZeros = false
|
||||
mask = 0xF000'u16
|
||||
for j in 0'u16..3'u16:
|
||||
var val = (mask and word) shr (4'u16*(3'u16-j))
|
||||
if val != 0 or afterLeadingZeros:
|
||||
if val < 0xA:
|
||||
result.add(chr(uint16(ord('0'))+val))
|
||||
else: # val >= 0xA
|
||||
result.add(chr(uint16(ord('a'))+val-0xA))
|
||||
afterLeadingZeros = true
|
||||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
## 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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(EInvalidValue, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
currentShort = 0
|
||||
byteCount.inc()
|
||||
seperatorValid = false
|
||||
else: # Invalid character
|
||||
raise newException(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress*(address_str: string): TIpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(EInvalidValue, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
when defined(ssl):
|
||||
import openssl
|
||||
|
||||
@@ -569,8 +287,8 @@ proc bindAddr*(socket: PSocket, port = TPort(0), address = "") {.
|
||||
osError(osLastError())
|
||||
dealloc(aiList)
|
||||
|
||||
proc acceptAddr*(server: PSocket, client: var PSocket, address: var string) {.
|
||||
tags: [FReadIO].} =
|
||||
proc acceptAddr*(server: PSocket, client: var PSocket, address: var string,
|
||||
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} =
|
||||
## Blocks until a connection is being made from a client. When a connection
|
||||
## is made sets ``client`` to the client socket and ``address`` to the address
|
||||
## of the connecting client.
|
||||
@@ -581,6 +299,11 @@ proc acceptAddr*(server: PSocket, client: var PSocket, address: var string) {.
|
||||
##
|
||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||
## makes no effort to initialise the ``client`` variable.
|
||||
##
|
||||
## The ``accept`` call may result in an error if the connecting socket
|
||||
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
assert(client != nil)
|
||||
var sockAddress: Tsockaddr_in
|
||||
var addrLen = sizeof(sockAddress).TSocklen
|
||||
@@ -589,6 +312,8 @@ proc acceptAddr*(server: PSocket, client: var PSocket, address: var string) {.
|
||||
|
||||
if sock == osInvalidSocket:
|
||||
let err = osLastError()
|
||||
if flags.isDisconnectionError(err):
|
||||
acceptAddr(server, client, address, flags)
|
||||
osError(err)
|
||||
else:
|
||||
client.fd = sock
|
||||
@@ -658,15 +383,20 @@ when false: #defined(ssl):
|
||||
acceptAddrPlain(AcceptNoClient, AcceptSuccess):
|
||||
doHandshake()
|
||||
|
||||
proc accept*(server: PSocket, client: var PSocket) {.tags: [FReadIO].} =
|
||||
proc accept*(server: PSocket, client: var PSocket,
|
||||
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} =
|
||||
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
|
||||
## socket.
|
||||
##
|
||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||
## makes no effort to initialise the ``client`` variable.
|
||||
|
||||
##
|
||||
## The ``accept`` call may result in an error if the connecting socket
|
||||
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
var addrDummy = ""
|
||||
acceptAddr(server, client, addrDummy)
|
||||
acceptAddr(server, client, addrDummy, flags)
|
||||
|
||||
proc close*(socket: PSocket) =
|
||||
## Closes a socket.
|
||||
@@ -1173,3 +903,285 @@ proc isSSL*(socket: PSocket): bool = return socket.isSSL
|
||||
|
||||
proc getFD*(socket: PSocket): TSocketHandle = return socket.fd
|
||||
## Returns the socket's file descriptor
|
||||
|
||||
type
|
||||
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
|
||||
IPv6, ## IPv6 address
|
||||
IPv4 ## IPv4 address
|
||||
|
||||
TIpAddress* = object ## stores an arbitrary IP address
|
||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||
of IpAddressFamily.IPv6:
|
||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv6
|
||||
of IpAddressFamily.IPv4:
|
||||
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv4
|
||||
|
||||
proc IPv4_any*(): TIpAddress =
|
||||
## Returns the IPv4 any address, which can be used to listen on all available
|
||||
## network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [0'u8, 0, 0, 0])
|
||||
|
||||
proc IPv4_loopback*(): TIpAddress =
|
||||
## Returns the IPv4 loopback address (127.0.0.1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [127'u8, 0, 0, 1])
|
||||
|
||||
proc IPv4_broadcast*(): TIpAddress =
|
||||
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [255'u8, 255, 255, 255])
|
||||
|
||||
proc IPv6_any*(): TIpAddress =
|
||||
## Returns the IPv6 any address (::0), which can be used
|
||||
## to listen on all available network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
proc IPv6_loopback*(): TIpAddress =
|
||||
## Returns the IPv6 loopback address (::1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
|
||||
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||
if lhs.family != rhs.family: return false
|
||||
if lhs.family == IpAddressFamily.IPv4:
|
||||
for i in low(lhs.address_v4) .. high(lhs.address_v4):
|
||||
if lhs.address_v4[i] != rhs.address_v4[i]: return false
|
||||
else: # IPv6
|
||||
for i in low(lhs.address_v6) .. high(lhs.address_v6):
|
||||
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||
return true
|
||||
|
||||
proc `$`*(address: TIpAddress): string =
|
||||
## Converts an TIpAddress into the textual representation
|
||||
result = ""
|
||||
case address.family
|
||||
of IpAddressFamily.IPv4:
|
||||
for i in 0 .. 3:
|
||||
if i != 0:
|
||||
result.add('.')
|
||||
result.add($address.address_v4[i])
|
||||
of IpAddressFamily.IPv6:
|
||||
var
|
||||
currentZeroStart = -1
|
||||
currentZeroCount = 0
|
||||
biggestZeroStart = -1
|
||||
biggestZeroCount = 0
|
||||
# Look for the largest block of zeros
|
||||
for i in 0..7:
|
||||
var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0
|
||||
if isZero:
|
||||
if currentZeroStart == -1:
|
||||
currentZeroStart = i
|
||||
currentZeroCount = 1
|
||||
else:
|
||||
currentZeroCount.inc()
|
||||
if currentZeroCount > biggestZeroCount:
|
||||
biggestZeroCount = currentZeroCount
|
||||
biggestZeroStart = currentZeroStart
|
||||
else:
|
||||
currentZeroStart = -1
|
||||
|
||||
if biggestZeroCount == 8: # Special case ::0
|
||||
result.add("::")
|
||||
else: # Print address
|
||||
var printedLastGroup = false
|
||||
for i in 0..7:
|
||||
var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
|
||||
word = word or cast[uint16](address.address_v6[i*2+1])
|
||||
|
||||
if biggestZeroCount != 0 and # Check if group is in skip group
|
||||
(i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)):
|
||||
if i == biggestZeroStart: # skip start
|
||||
result.add("::")
|
||||
printedLastGroup = false
|
||||
else:
|
||||
if printedLastGroup:
|
||||
result.add(':')
|
||||
var
|
||||
afterLeadingZeros = false
|
||||
mask = 0xF000'u16
|
||||
for j in 0'u16..3'u16:
|
||||
var val = (mask and word) shr (4'u16*(3'u16-j))
|
||||
if val != 0 or afterLeadingZeros:
|
||||
if val < 0xA:
|
||||
result.add(chr(uint16(ord('0'))+val))
|
||||
else: # val >= 0xA
|
||||
result.add(chr(uint16(ord('a'))+val-0xA))
|
||||
afterLeadingZeros = true
|
||||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
## 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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
raise newException(EInvalidValue, "Invalid IP Address")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
currentShort = 0
|
||||
byteCount.inc()
|
||||
seperatorValid = false
|
||||
else: # Invalid character
|
||||
raise newException(EInvalidValue,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "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(EInvalidValue,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
raise newException(EInvalidValue,
|
||||
"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(EInvalidValue,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress*(address_str: string): TIpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(EInvalidValue, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
@@ -41,6 +41,8 @@ News
|
||||
- ``sequtils.distnct`` has been renamed to ``sequtils.deduplicate``.
|
||||
- Added ``algorithm.reversed``
|
||||
- Added ``uri.combine`` and ``uri.parseUri``.
|
||||
- Some sockets procedures now support a ``SafeDisconn`` flag which causes
|
||||
them to handle disconnection errors and not raise them.
|
||||
|
||||
|
||||
2014-04-21 Version 0.9.4 released
|
||||
|
||||
Reference in New Issue
Block a user