nim-chronos/asyncdispatch2/transports/common.nim

190 lines
6.8 KiB
Nim
Raw Normal View History

2018-05-16 08:22:34 +00:00
#
# Asyncdispatch2 Transport Common Types
# (c) Copyright 2018
# Status Research & Development GmbH
#
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import net, nativesockets, strutils
2018-05-16 08:22:34 +00:00
import ../asyncloop, ../asyncsync
const
DefaultStreamBufferSize* = 4096 ## Default buffer size for stream
## transports
DefaultDatagramBufferSize* = 65536 ## Default buffer size for datagram
## transports
type
ServerFlags* = enum
## Server's flags
ReuseAddr, ReusePort, NoAutoRead
2018-05-16 08:22:34 +00:00
TransportAddress* = object
## Transport network address
address*: IpAddress # IP Address
port*: Port # IP port
ServerCommand* = enum
## Server's commands
Start, # Start server
Pause, # Pause server
Stop # Stop server
ServerStatus* = enum
## Server's statuses
Starting, # Server created
Stopped, # Server stopped
Running, # Server running
Paused # Server paused
SocketServer* = ref object of RootRef
## Socket server object
sock*: AsyncFD # Socket
local*: TransportAddress # Address
actEvent*: AsyncEvent # Activation event
action*: ServerCommand # Activation command
status*: ServerStatus # Current server status
udata*: pointer # User-defined pointer
flags*: set[ServerFlags] # Flags
2018-05-28 23:35:15 +00:00
bufferSize*: int # Size of internal transports' buffer
2018-05-16 08:22:34 +00:00
loopFuture*: Future[void] # Server's main Future
TransportError* = object of Exception
## Transport's specific exception
TransportOsError* = object of TransportError
## Transport's OS specific exception
TransportIncompleteError* = object of TransportError
## Transport's `incomplete data received` exception
TransportLimitError* = object of TransportError
## Transport's `data limit reached` exception
TransportState* = enum
## Transport's state
ReadPending, # Read operation pending (Windows)
ReadPaused, # Read operations paused
ReadClosed, # Read operations closed
ReadEof, # Read at EOF
ReadError, # Read error
WritePending, # Writer operation pending (Windows)
WritePaused, # Writer operations paused
WriteClosed, # Writer operations closed
WriteError # Write error
var
AnyAddress* = TransportAddress(
address: IpAddress(family: IpAddressFamily.IPv4), port: Port(0)
) ## Default INADDR_ANY address for IPv4
AnyAddress6* = TransportAddress(
address: IpAddress(family: IpAddressFamily.IPv6), port: Port(0)
) ## Default INADDR_ANY address for IPv6
proc getDomain*(address: IpAddress): Domain =
## Returns OS specific Domain from IP Address.
case address.family
of IpAddressFamily.IPv4:
result = Domain.AF_INET
of IpAddressFamily.IPv6:
result = Domain.AF_INET6
2018-05-28 23:35:15 +00:00
proc getDomain*(address: TransportAddress): Domain =
## Returns OS specific Domain from TransportAddress.
result = address.address.getDomain()
2018-05-16 08:22:34 +00:00
proc `$`*(address: TransportAddress): string =
## Returns string representation of ``address``.
case address.address.family
of IpAddressFamily.IPv4:
result = $address.address
result.add(":")
of IpAddressFamily.IPv6:
result = "[" & $address.address & "]"
result.add(":")
result.add($int(address.port))
proc initTAddress*(address: string): TransportAddress =
2018-05-21 21:52:57 +00:00
## Parses string representation of ``address``.
##
2018-05-21 21:52:57 +00:00
## IPv4 transport address format is ``a.b.c.d:port``.
## IPv6 transport address format is ``[::]:port``.
var parts = address.rsplit(":", maxsplit = 1)
doAssert(len(parts) == 2, "Format is <address>:<port>!")
let port = parseInt(parts[1])
doAssert(port >= 0 and port < 65536, "Illegal port number!")
2018-05-21 21:52:57 +00:00
result.port = Port(port)
if parts[0][0] == '[' and parts[0][^1] == ']':
result.address = parseIpAddress(parts[0][1..^2])
else:
result.address = parseIpAddress(parts[0])
2018-05-16 08:22:34 +00:00
proc initTAddress*(address: string, port: Port): TransportAddress =
## Initialize ``TransportAddress`` with IP address ``address`` and
## port number ``port``.
result.address = parseIpAddress(address)
result.port = port
proc initTAddress*(address: string, port: int): TransportAddress =
## Initialize ``TransportAddress`` with IP address ``address`` and
## port number ``port``.
result.address = parseIpAddress(address)
result.port = Port(Port(port and 0xFFFF))
proc resolveTAddress*(address: string,
family = IpAddressFamily.IPv4): seq[TransportAddress] =
## Resolve string representation of ``address``.
##
## Supported formats are:
## IPv4 numeric address ``a.b.c.d:port``
## IPv6 numeric address ``[::]:port``
## Hostname address ``hostname:port``
##
## If hostname address is detected, then network address translation via DNS
## will be performed.
var
ta: TransportAddress
ap: Port
result = newSeq[TransportAddress]()
var parts = address.rsplit(":", maxsplit = 1)
doAssert(len(parts) == 2, "Format is <address>:<port>!")
let port = parseInt(parts[1])
doAssert(port >= 0 and port < 65536, "Illegal port number!")
if parts[0][0] == '[' and parts[0][^1] == ']':
ta = TransportAddress(address: parseIpAddress(parts[0][1..^2]),
port: Port(port))
result.add(ta)
else:
if isIpAddress(parts[0]):
ta = TransportAddress(address: parseIpAddress(parts[0]),
port: Port(port))
result.add(ta)
else:
var domain = if family == IpAddressFamily.IPv4: Domain(AF_INET) else:
Domain(AF_INET6)
var aiList = getAddrInfo(parts[0], Port(port), domain)
var it = aiList
while it != nil:
fromSockAddr(cast[ptr Sockaddr_storage](it.ai_addr)[],
SockLen(it.ai_addrlen), ta.address, ta.port)
result.add(ta)
it = it.ai_next
freeAddrInfo(aiList)
2018-05-16 08:22:34 +00:00
template checkClosed*(t: untyped) =
if (ReadClosed in (t).state) or (WriteClosed in (t).state):
raise newException(TransportError, "Transport is already closed!")
template getError*(t: untyped): ref Exception =
var err = (t).error
(t).error = nil
err
when defined(windows):
import winlean
const ERROR_OPERATION_ABORTED* = 995
const ERROR_SUCCESS* = 0
2018-05-16 08:22:34 +00:00
proc cancelIo*(hFile: HANDLE): WINBOOL
{.stdcall, dynlib: "kernel32", importc: "CancelIo".}
when isMainModule:
echo $resolveTAddress("localhost:443", IpAddressFamily.IPv6)