Merge pull request #1653 from def-/name-fixes-2

Fix some deprecation warnings caused by renames
This commit is contained in:
Andreas Rumpf 2014-11-13 22:09:02 +01:00
commit c507fe3d9d
12 changed files with 237 additions and 236 deletions

View file

@ -9,7 +9,7 @@
{.deadCodeElim:on.} {.deadCodeElim:on.}
from posix import TSocketHandle from posix import SocketHandle
const const
EPOLLIN* = 0x00000001 EPOLLIN* = 0x00000001
@ -58,7 +58,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
## Same as epoll_create but with an FLAGS parameter. The unused SIZE ## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped. ## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {. proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".} importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success, ## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the ## -1 in case of error ( the "errno" variable will contain the

View file

@ -242,7 +242,7 @@ proc echoOriginalStackTrace[T](future: Future[T]) =
proc read*[T](future: Future[T]): T = proc read*[T](future: Future[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``EInvalidValue`` exception. ## this function will fail with a ``ValueError`` exception.
## ##
## If the result of the future is an error then that error will be raised. ## If the result of the future is an error then that error will be raised.
if future.finished: if future.finished:
@ -808,13 +808,13 @@ else:
TAsyncFD* = distinct cint TAsyncFD* = distinct cint
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.} TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.}
PData* = ref object of PObject PData* = ref object of RootRef
fd: TAsyncFD fd: TAsyncFD
readCBs: seq[TCallback] readCBs: seq[TCallback]
writeCBs: seq[TCallback] writeCBs: seq[TCallback]
PDispatcher* = ref object of PDispatcherBase PDispatcher* = ref object of PDispatcherBase
selector: PSelector selector: Selector
proc `==`*(x, y: TAsyncFD): bool {.borrow.} proc `==`*(x, y: TAsyncFD): bool {.borrow.}
@ -828,7 +828,7 @@ else:
if gDisp.isNil: gDisp = newDispatcher() if gDisp.isNil: gDisp = newDispatcher()
result = gDisp result = gDisp
proc update(fd: TAsyncFD, events: set[TEvent]) = proc update(fd: TAsyncFD, events: set[Event]) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
assert fd.SocketHandle in p.selector assert fd.SocketHandle in p.selector
discard p.selector.update(fd.SocketHandle, events) discard p.selector.update(fd.SocketHandle, events)
@ -836,16 +836,16 @@ else:
proc register*(fd: TAsyncFD) = proc register*(fd: TAsyncFD) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = PData(fd: fd, readCBs: @[], writeCBs: @[]) var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
p.selector.register(fd.SocketHandle, {}, data.PObject) p.selector.register(fd.SocketHandle, {}, data.RootRef)
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD = proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc newAsyncRawSocket*(domain: TDomain = AF_INET, proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: TType = SOCK_STREAM, typ: SockType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TAsyncFD = protocol: Protocol = IPPROTO_TCP): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
@ -861,14 +861,14 @@ else:
proc addRead*(fd: TAsyncFD, cb: TCallback) = proc addRead*(fd: TAsyncFD, cb: TCallback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector: if fd.SocketHandle notin p.selector:
raise newException(EInvalidValue, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb) p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvRead}) update(fd, p.selector[fd.SocketHandle].events + {EvRead})
proc addWrite*(fd: TAsyncFD, cb: TCallback) = proc addWrite*(fd: TAsyncFD, cb: TCallback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector: if fd.SocketHandle notin p.selector:
raise newException(EInvalidValue, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb) p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvWrite}) update(fd, p.selector[fd.SocketHandle].events + {EvWrite})
@ -898,7 +898,7 @@ else:
data.writeCBs.add(cb) data.writeCBs.add(cb)
if info.key in p.selector: if info.key in p.selector:
var newEvents: set[TEvent] var newEvents: set[Event]
if data.readCBs.len != 0: newEvents = {EvRead} if data.readCBs.len != 0: newEvents = {EvRead}
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
if newEvents != info.key.events: if newEvents != info.key.events:
@ -910,7 +910,7 @@ else:
processTimers(p) processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: TPort, proc connect*(socket: TAsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
@ -921,7 +921,7 @@ else:
var aiList = getAddrInfo(address, port, af) var aiList = getAddrInfo(address, port, af)
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
var ret = connect(socket.SocketHandle, it.ai_addr, it.ai_addrlen.Socklen) var ret = connect(socket.SocketHandle, it.ai_addr, it.ai_addrlen.Socklen)
@ -942,11 +942,11 @@ else:
dealloc(aiList) dealloc(aiList)
if not success: if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: TAsyncFD, size: int,
flags = {TSocketFlags.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv") var retFuture = newFuture[string]("recv")
var readBuffer = newString(size) var readBuffer = newString(size)
@ -962,7 +962,7 @@ else:
if flags.isDisconnectionError(lastError): if flags.isDisconnectionError(lastError):
retFuture.complete("") retFuture.complete("")
else: else:
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else: else:
result = false # We still want this callback to be called. result = false # We still want this callback to be called.
elif res == 0: elif res == 0:
@ -977,7 +977,7 @@ else:
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {TSocketFlags.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
var written = 0 var written = 0
@ -994,7 +994,7 @@ else:
if flags.isDisconnectionError(lastError): if flags.isDisconnectionError(lastError):
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else: else:
result = false # We still want this callback to be called. result = false # We still want this callback to be called.
else: else:
@ -1008,7 +1008,7 @@ else:
addWrite(socket, cb) addWrite(socket, cb)
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: TAsyncFD]] =
var retFuture = newFuture[tuple[address: string, var retFuture = newFuture[tuple[address: string,
client: TAsyncFD]]("acceptAddr") client: TAsyncFD]]("acceptAddr")
@ -1027,7 +1027,7 @@ else:
if flags.isDisconnectionError(lastError): if flags.isDisconnectionError(lastError):
return false return false
else: else:
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else: else:
register(client.TAsyncFD) register(client.TAsyncFD)
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
@ -1302,7 +1302,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
# -> createCb(retFuture) # -> createCb(retFuture)
var cbName = newIdentNode("cb") #var cbName = newIdentNode("cb")
var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym, var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName)) newStrLitNode(prc[0].getName))
outerProcBody.add procCb outerProcBody.add procCb
@ -1372,7 +1372,7 @@ proc runForever*() =
while true: while true:
poll() poll()
proc waitFor*[T](fut: PFuture[T]): T = proc waitFor*[T](fut: Future[T]): T =
## **Blocks** the current thread until the specified future completes. ## **Blocks** the current thread until the specified future completes.
while not fut.finished: while not fut.finished:
poll() poll()

View file

@ -180,10 +180,10 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
if result.socket == invalidSocket: raiseOSError(osLastError()) if result.socket == invalidSocket: raiseOSError(osLastError())
result.socket.setBlocking(false) result.socket.setBlocking(false)
proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): PAsyncSocket = proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
## Wraps an already initialized ``TSocket`` into a PAsyncSocket. ## Wraps an already initialized ``TSocket`` into a AsyncSocket.
## This is useful if you want to use an already connected TSocket as an ## This is useful if you want to use an already connected TSocket as an
## asynchronous PAsyncSocket in asyncio's event loop. ## asynchronous AsyncSocket in asyncio's event loop.
## ##
## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be ## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be
## adjusted properly. By default it will be assumed that the socket is ## adjusted properly. By default it will be assumed that the socket is
@ -201,7 +201,7 @@ proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): PAsyncSo
## SockUDPBound Socket is a UDP socket which is listening for data. ## SockUDPBound Socket is a UDP socket which is listening for data.
## ================ ================================================================ ## ================ ================================================================
## ##
## **Warning**: If ``state`` is set incorrectly the resulting ``PAsyncSocket`` ## **Warning**: If ``state`` is set incorrectly the resulting ``AsyncSocket``
## object may not work properly. ## object may not work properly.
## ##
## **Note**: This will set ``sock`` to be non-blocking. ## **Note**: This will set ``sock`` to be non-blocking.
@ -213,8 +213,8 @@ proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): PAsyncSo
proc asyncSockHandleRead(h: RootRef) = proc asyncSockHandleRead(h: RootRef) =
when defined(ssl): when defined(ssl):
if PAsyncSocket(h).socket.isSSL and not if AsyncSocket(h).socket.isSSL and not
PAsyncSocket(h).socket.gotHandshake: AsyncSocket(h).socket.gotHandshake:
return return
if AsyncSocket(h).info != SockListening: if AsyncSocket(h).info != SockListening:
@ -226,8 +226,8 @@ proc asyncSockHandleRead(h: RootRef) =
proc close*(sock: AsyncSocket) {.gcsafe.} proc close*(sock: AsyncSocket) {.gcsafe.}
proc asyncSockHandleWrite(h: RootRef) = proc asyncSockHandleWrite(h: RootRef) =
when defined(ssl): when defined(ssl):
if PAsyncSocket(h).socket.isSSL and not if AsyncSocket(h).socket.isSSL and not
PAsyncSocket(h).socket.gotHandshake: AsyncSocket(h).socket.gotHandshake:
return return
if AsyncSocket(h).info == SockConnecting: if AsyncSocket(h).info == SockConnecting:
@ -266,17 +266,17 @@ proc asyncSockHandleWrite(h: RootRef) =
when defined(ssl): when defined(ssl):
proc asyncSockDoHandshake(h: PObject) {.gcsafe.} = proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
if PAsyncSocket(h).socket.isSSL and not if AsyncSocket(h).socket.isSSL and not
PAsyncSocket(h).socket.gotHandshake: AsyncSocket(h).socket.gotHandshake:
if PAsyncSocket(h).sslNeedAccept: if AsyncSocket(h).sslNeedAccept:
var d = "" var d = ""
let ret = PAsyncSocket(h).socket.acceptAddrSSL(PAsyncSocket(h).socket, d) let ret = AsyncSocket(h).socket.acceptAddrSSL(AsyncSocket(h).socket, d)
assert ret != AcceptNoClient assert ret != AcceptNoClient
if ret == AcceptSuccess: if ret == AcceptSuccess:
PAsyncSocket(h).info = SockConnected AsyncSocket(h).info = SockConnected
else: else:
# handshake will set socket's ``sslNoHandshake`` field. # handshake will set socket's ``sslNoHandshake`` field.
discard PAsyncSocket(h).socket.handshake() discard AsyncSocket(h).socket.handshake()
proc asyncSockTask(h: RootRef) = proc asyncSockTask(h: RootRef) =
@ -453,7 +453,7 @@ proc setHandleWrite*(s: AsyncSocket,
## ##
## To remove this event you should use the ``delHandleWrite`` function. ## To remove this event you should use the ``delHandleWrite`` function.
## It is advised to use that function instead of just setting the event to ## It is advised to use that function instead of just setting the event to
## ``proc (s: PAsyncSocket) = nil`` as that would mean that that function ## ``proc (s: AsyncSocket) = nil`` as that would mean that that function
## would be called constantly. ## would be called constantly.
s.deleg.mode = fmReadWrite s.deleg.mode = fmReadWrite
s.handleWrite = handleWrite s.handleWrite = handleWrite
@ -655,10 +655,10 @@ proc len*(disp: Dispatcher): int =
when isMainModule: when isMainModule:
proc testConnect(s: PAsyncSocket, no: int) = proc testConnect(s: AsyncSocket, no: int) =
echo("Connected! " & $no) echo("Connected! " & $no)
proc testRead(s: PAsyncSocket, no: int) = proc testRead(s: AsyncSocket, no: int) =
echo("Reading! " & $no) echo("Reading! " & $no)
var data = "" var data = ""
if not s.readLine(data): return if not s.readLine(data): return
@ -668,15 +668,15 @@ when isMainModule:
echo(data) echo(data)
echo("Finished reading! " & $no) echo("Finished reading! " & $no)
proc testAccept(s: PAsyncSocket, disp: PDispatcher, no: int) = proc testAccept(s: AsyncSocket, disp: PDispatcher, no: int) =
echo("Accepting client! " & $no) echo("Accepting client! " & $no)
var client: PAsyncSocket var client: AsyncSocket
new(client) new(client)
var address = "" var address = ""
s.acceptAddr(client, address) s.acceptAddr(client, address)
echo("Accepted ", address) echo("Accepted ", address)
client.handleRead = client.handleRead =
proc (s: PAsyncSocket) = proc (s: AsyncSocket) =
testRead(s, 2) testRead(s, 2)
disp.register(client) disp.register(client)
@ -686,16 +686,16 @@ when isMainModule:
var s = asyncSocket() var s = asyncSocket()
s.connect("amber.tenthbit.net", TPort(6667)) s.connect("amber.tenthbit.net", TPort(6667))
s.handleConnect = s.handleConnect =
proc (s: PAsyncSocket) = proc (s: AsyncSocket) =
testConnect(s, 1) testConnect(s, 1)
s.handleRead = s.handleRead =
proc (s: PAsyncSocket) = proc (s: AsyncSocket) =
testRead(s, 1) testRead(s, 1)
d.register(s) d.register(s)
var server = asyncSocket() var server = asyncSocket()
server.handleAccept = server.handleAccept =
proc (s: PAsyncSocket) = proc (s: AsyncSocket) =
testAccept(s, d, 78) testAccept(s, d, 78)
server.bindAddr(TPort(5555)) server.bindAddr(TPort(5555))
server.listen() server.listen()

View file

@ -67,7 +67,7 @@ when defined(ssl):
type type
# TODO: I would prefer to just do: # TODO: I would prefer to just do:
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work. # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work.
AsyncSocketDesc = object AsyncSocketDesc = object
fd*: SocketHandle fd*: SocketHandle
closed*: bool ## determines whether this socket has been closed closed*: bool ## determines whether this socket has been closed
@ -91,7 +91,7 @@ type
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect. # TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket = proc newSocket(fd: TAsyncFD, isBuff: bool): AsyncSocket =
assert fd != osInvalidSocket.TAsyncFD assert fd != osInvalidSocket.TAsyncFD
new(result) new(result)
result.fd = fd.SocketHandle result.fd = fd.SocketHandle
@ -99,12 +99,12 @@ proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket =
if isBuff: if isBuff:
result.currPos = 0 result.currPos = 0
proc newAsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket = protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
## Creates a new asynchronous socket. ## Creates a new asynchronous socket.
result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered) result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): PAsyncSocket = proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): AsyncSocket =
## Creates a new asynchronous socket. ## Creates a new asynchronous socket.
result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered) result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
@ -126,7 +126,7 @@ when defined(ssl):
else: raiseSSLError("Unknown Error") else: raiseSSLError("Unknown Error")
proc sendPendingSslData(socket: AsyncSocket, proc sendPendingSslData(socket: AsyncSocket,
flags: set[TSocketFlags]) {.async.} = flags: set[SocketFlag]) {.async.} =
let len = bioCtrlPending(socket.bioOut) let len = bioCtrlPending(socket.bioOut)
if len > 0: if len > 0:
var data = newStringOfCap(len) var data = newStringOfCap(len)
@ -137,7 +137,7 @@ when defined(ssl):
data.setLen(read) data.setLen(read)
await socket.fd.TAsyncFd.send(data, flags) await socket.fd.TAsyncFd.send(data, flags)
proc appeaseSsl(socket: AsyncSocket, flags: set[TSocketFlags], proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
sslError: cint) {.async.} = sslError: cint) {.async.} =
case sslError case sslError
of SSL_ERROR_WANT_WRITE: of SSL_ERROR_WANT_WRITE:
@ -150,7 +150,7 @@ when defined(ssl):
else: else:
raiseSSLError("Cannot appease SSL.") raiseSSLError("Cannot appease SSL.")
template sslLoop(socket: AsyncSocket, flags: set[TSocketFlags], template sslLoop(socket: AsyncSocket, flags: set[SocketFlag],
op: expr) = op: expr) =
var opResult {.inject.} = -1.cint var opResult {.inject.} = -1.cint
while opResult < 0: while opResult < 0:
@ -162,21 +162,21 @@ when defined(ssl):
let err = getSslError(socket.sslHandle, opResult.cint) let err = getSslError(socket.sslHandle, opResult.cint)
yield appeaseSsl(socket, flags, err.cint) yield appeaseSsl(socket, flags, err.cint)
proc connect*(socket: PAsyncSocket, address: string, port: TPort, proc connect*(socket: AsyncSocket, address: string, port: Port,
af = AF_INET) {.async.} = af = AF_INET) {.async.} =
## Connects ``socket`` to server at ``address:port``. ## Connects ``socket`` to server at ``address:port``.
## ##
## Returns a ``Future`` which will complete when the connection succeeds ## Returns a ``Future`` which will complete when the connection succeeds
## or an error occurs. ## or an error occurs.
await connect(socket.fd.TAsyncFD, address, port, af) await connect(socket.fd.TAsyncFD, address, port, af)
let flags = {TSocketFlags.SafeDisconn}
if socket.isSsl: if socket.isSsl:
when defined(ssl): when defined(ssl):
let flags = {SocketFlag.SafeDisconn}
sslSetConnectState(socket.sslHandle) sslSetConnectState(socket.sslHandle)
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
proc readInto(buf: cstring, size: int, socket: PAsyncSocket, proc readInto(buf: cstring, size: int, socket: AsyncSocket,
flags: set[TSocketFlags]): Future[int] {.async.} = flags: set[SocketFlag]): Future[int] {.async.} =
if socket.isSsl: if socket.isSsl:
when defined(ssl): when defined(ssl):
# SSL mode. # SSL mode.
@ -190,14 +190,14 @@ proc readInto(buf: cstring, size: int, socket: PAsyncSocket,
# Not in SSL mode. # Not in SSL mode.
result = data.len result = data.len
proc readIntoBuf(socket: PAsyncSocket, proc readIntoBuf(socket: AsyncSocket,
flags: set[TSocketFlags]): Future[int] {.async.} = flags: set[SocketFlag]): Future[int] {.async.} =
result = await readInto(addr socket.buffer[0], BufferSize, socket, flags) result = await readInto(addr socket.buffer[0], BufferSize, socket, flags)
socket.currPos = 0 socket.currPos = 0
socket.bufLen = result socket.bufLen = result
proc recv*(socket: PAsyncSocket, size: int, proc recv*(socket: AsyncSocket, size: int,
flags = {TSocketFlags.SafeDisconn}): Future[string] {.async.} = flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
## Reads **up to** ``size`` bytes from ``socket``. ## Reads **up to** ``size`` bytes from ``socket``.
## ##
## For buffered sockets this function will attempt to read all the requested ## For buffered sockets this function will attempt to read all the requested
@ -218,7 +218,7 @@ proc recv*(socket: PAsyncSocket, size: int,
let originalBufPos = socket.currPos let originalBufPos = socket.currPos
if socket.bufLen == 0: if socket.bufLen == 0:
let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
if res == 0: if res == 0:
result.setLen(0) result.setLen(0)
return return
@ -226,10 +226,10 @@ proc recv*(socket: PAsyncSocket, size: int,
var read = 0 var read = 0
while read < size: while read < size:
if socket.currPos >= socket.bufLen: if socket.currPos >= socket.bufLen:
if TSocketFlags.Peek in flags: if SocketFlag.Peek in flags:
# We don't want to get another buffer if we're peeking. # We don't want to get another buffer if we're peeking.
break break
let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
if res == 0: if res == 0:
break break
@ -238,7 +238,7 @@ proc recv*(socket: PAsyncSocket, size: int,
read.inc(chunk) read.inc(chunk)
socket.currPos.inc(chunk) socket.currPos.inc(chunk)
if TSocketFlags.Peek in flags: if SocketFlag.Peek in flags:
# Restore old buffer cursor position. # Restore old buffer cursor position.
socket.currPos = originalBufPos socket.currPos = originalBufPos
result.setLen(read) result.setLen(read)
@ -247,8 +247,8 @@ proc recv*(socket: PAsyncSocket, size: int,
let read = await readInto(addr result[0], size, socket, flags) let read = await readInto(addr result[0], size, socket, flags)
result.setLen(read) result.setLen(read)
proc send*(socket: PAsyncSocket, data: string, proc send*(socket: AsyncSocket, data: string,
flags = {TSocketFlags.SafeDisconn}) {.async.} = flags = {SocketFlag.SafeDisconn}) {.async.} =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
assert socket != nil assert socket != nil
@ -261,12 +261,12 @@ proc send*(socket: PAsyncSocket, data: string,
else: else:
await send(socket.fd.TAsyncFD, data, flags) await send(socket.fd.TAsyncFD, data, flags)
proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}): proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: PAsyncSocket]] = Future[tuple[address: string, client: AsyncSocket]] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]("asyncnet.acceptAddr") var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
var fut = acceptAddr(socket.fd.TAsyncFD, flags) var fut = acceptAddr(socket.fd.TAsyncFD, flags)
fut.callback = fut.callback =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) = proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
@ -279,15 +279,15 @@ proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}):
retFuture.complete(resultTup) retFuture.complete(resultTup)
return retFuture return retFuture
proc accept*(socket: PAsyncSocket, proc accept*(socket: AsyncSocket,
flags = {TSocketFlags.SafeDisconn}): Future[PAsyncSocket] = flags = {SocketFlag.SafeDisconn}): Future[AsyncSocket] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFut = newFuture[PAsyncSocket]("asyncnet.accept") var retFut = newFuture[AsyncSocket]("asyncnet.accept")
var fut = acceptAddr(socket, flags) var fut = acceptAddr(socket, flags)
fut.callback = fut.callback =
proc (future: Future[tuple[address: string, client: PAsyncSocket]]) = proc (future: Future[tuple[address: string, client: AsyncSocket]]) =
assert future.finished assert future.finished
if future.failed: if future.failed:
retFut.fail(future.readError) retFut.fail(future.readError)
@ -295,8 +295,8 @@ proc accept*(socket: PAsyncSocket,
retFut.complete(future.read.client) retFut.complete(future.read.client)
return retFut return retFut
proc recvLine*(socket: PAsyncSocket, proc recvLine*(socket: AsyncSocket,
flags = {TSocketFlags.SafeDisconn}): Future[string] {.async.} = flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs. ## a full line is read or an error occurs.
## ##
@ -317,7 +317,7 @@ proc recvLine*(socket: PAsyncSocket,
template addNLIfEmpty(): stmt = template addNLIfEmpty(): stmt =
if result.len == 0: if result.len == 0:
result.add("\c\L") result.add("\c\L")
assert TSocketFlags.Peek notin flags ## TODO: assert SocketFlag.Peek notin flags ## TODO:
if socket.isBuffered: if socket.isBuffered:
result = "" result = ""
if socket.bufLen == 0: if socket.bufLen == 0:
@ -365,7 +365,7 @@ proc recvLine*(socket: PAsyncSocket,
return return
add(result.string, c) add(result.string, c)
proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
@ -373,7 +373,7 @@ proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].}
## Raises an EOS error upon failure. ## Raises an EOS error upon failure.
if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError()) if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError())
proc bindAddr*(socket: PAsyncSocket, port = Port(0), address = "") {. proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## Binds ``address``:``port`` to the socket. ## Binds ``address``:``port`` to the socket.
## ##
@ -397,7 +397,7 @@ proc bindAddr*(socket: PAsyncSocket, port = Port(0), address = "") {.
raiseOSError(osLastError()) raiseOSError(osLastError())
dealloc(aiList) dealloc(aiList)
proc close*(socket: PAsyncSocket) = proc close*(socket: AsyncSocket) =
## Closes the socket. ## Closes the socket.
socket.fd.TAsyncFD.closeSocket() socket.fd.TAsyncFD.closeSocket()
when defined(ssl): when defined(ssl):
@ -419,7 +419,7 @@ when defined(ssl):
## prone to security vulnerabilities. ## prone to security vulnerabilities.
socket.isSsl = true socket.isSsl = true
socket.sslContext = ctx socket.sslContext = ctx
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext)) socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
if socket.sslHandle == nil: if socket.sslHandle == nil:
raiseSslError() raiseSslError()
@ -449,7 +449,7 @@ when isMainModule:
when test == HighClient: when test == HighClient:
proc main() {.async.} = proc main() {.async.} =
var sock = newAsyncSocket() var sock = newAsyncSocket()
await sock.connect("irc.freenode.net", TPort(6667)) await sock.connect("irc.freenode.net", Port(6667))
while true: while true:
let line = await sock.recvLine() let line = await sock.recvLine()
if line == "": if line == "":
@ -460,7 +460,7 @@ when isMainModule:
asyncCheck main() asyncCheck main()
elif test == LowClient: elif test == LowClient:
var sock = newAsyncSocket() var sock = newAsyncSocket()
var f = connect(sock, "irc.freenode.net", TPort(6667)) var f = connect(sock, "irc.freenode.net", Port(6667))
f.callback = f.callback =
proc (future: Future[void]) = proc (future: Future[void]) =
echo("Connected in future!") echo("Connected in future!")
@ -471,9 +471,9 @@ when isMainModule:
echo("Read ", future.read.len, ": ", future.read.repr) echo("Read ", future.read.len, ": ", future.read.repr)
elif test == LowServer: elif test == LowServer:
var sock = newAsyncSocket() var sock = newAsyncSocket()
sock.bindAddr(TPort(6667)) sock.bindAddr(Port(6667))
sock.listen() sock.listen()
proc onAccept(future: Future[PAsyncSocket]) = proc onAccept(future: Future[AsyncSocket]) =
let client = future.read let client = future.read
echo "Accepted ", client.fd.cint echo "Accepted ", client.fd.cint
var t = send(client, "test\c\L") var t = send(client, "test\c\L")

View file

@ -18,17 +18,17 @@ type
{.deprecated: [TQueue: Queue].} {.deprecated: [TQueue: Queue].}
proc initQueue*[T](initialSize=4): TQueue[T] = proc initQueue*[T](initialSize=4): Queue[T] =
## creates a new queue. `initialSize` needs to be a power of 2. ## creates a new queue. `initialSize` needs to be a power of 2.
assert isPowerOfTwo(initialSize) assert isPowerOfTwo(initialSize)
result.mask = initialSize-1 result.mask = initialSize-1
newSeq(result.data, initialSize) newSeq(result.data, initialSize)
proc len*[T](q: TQueue[T]): int = proc len*[T](q: Queue[T]): int =
## returns the number of elements of `q`. ## returns the number of elements of `q`.
result = q.count result = q.count
iterator items*[T](q: TQueue[T]): T = iterator items*[T](q: Queue[T]): T =
## yields every element of `q`. ## yields every element of `q`.
var i = q.rd var i = q.rd
var c = q.count var c = q.count
@ -37,7 +37,7 @@ iterator items*[T](q: TQueue[T]): T =
yield q.data[i] yield q.data[i]
i = (i + 1) and q.mask i = (i + 1) and q.mask
proc add*[T](q: var TQueue[T], item: T) = proc add*[T](q: var Queue[T], item: T) =
## adds an `item` to the end of the queue `q`. ## adds an `item` to the end of the queue `q`.
var cap = q.mask+1 var cap = q.mask+1
if q.count >= cap: if q.count >= cap:
@ -55,18 +55,18 @@ proc add*[T](q: var TQueue[T], item: T) =
q.data[q.wr] = item q.data[q.wr] = item
q.wr = (q.wr + 1) and q.mask q.wr = (q.wr + 1) and q.mask
proc enqueue*[T](q: var TQueue[T], item: T) = proc enqueue*[T](q: var Queue[T], item: T) =
## alias for the ``add`` operation. ## alias for the ``add`` operation.
add(q, item) add(q, item)
proc dequeue*[T](q: var TQueue[T]): T = proc dequeue*[T](q: var Queue[T]): T =
## removes and returns the first element of the queue `q`. ## removes and returns the first element of the queue `q`.
assert q.count > 0 assert q.count > 0
dec q.count dec q.count
result = q.data[q.rd] result = q.data[q.rd]
q.rd = (q.rd + 1) and q.mask q.rd = (q.rd + 1) and q.mask
proc `$`*[T](q: TQueue[T]): string = proc `$`*[T](q: Queue[T]): string =
## turns a queue into its string representation. ## turns a queue into its string representation.
result = "[" result = "["
for x in items(q): for x in items(q):

View file

@ -57,7 +57,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
s.prevProcKernel = procKernel s.prevProcKernel = procKernel
s.prevProcUser = procUser s.prevProcUser = procUser
elif defined(linux): elif defined(linux):
proc fscanf(c: TFile, frmt: cstring) {.varargs, importc, proc fscanf(c: File, frmt: cstring) {.varargs, importc,
header: "<stdio.h>".} header: "<stdio.h>".}
var f = open("/proc/loadavg") var f = open("/proc/loadavg")

View file

@ -101,7 +101,7 @@ type
body: string] body: string]
Proxy* = ref object Proxy* = ref object
url*: TUrl url*: Url
auth*: string auth*: string
ProtocolError* = object of IOError ## exception that is raised when server ProtocolError* = object of IOError ## exception that is raised when server
@ -130,7 +130,7 @@ proc fileError(msg: string) =
e.msg = msg e.msg = msg
raise e raise e
proc parseChunks(s: TSocket, timeout: int): string = proc parseChunks(s: Socket, timeout: int): string =
result = "" result = ""
var ri = 0 var ri = 0
while true: while true:
@ -168,7 +168,7 @@ proc parseChunks(s: TSocket, timeout: int): string =
# Trailer headers will only be sent if the request specifies that we want # Trailer headers will only be sent if the request specifies that we want
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1 # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string = proc parseBody(s: Socket, headers: StringTableRef, timeout: int): string =
result = "" result = ""
if headers["Transfer-Encoding"] == "chunked": if headers["Transfer-Encoding"] == "chunked":
result = parseChunks(s, timeout) result = parseChunks(s, timeout)
@ -203,7 +203,7 @@ proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string =
buf.setLen(r) buf.setLen(r)
result.add(buf) result.add(buf)
proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse = proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
var parsedStatus = false var parsedStatus = false
var linei = 0 var linei = 0
var fullyRead = false var fullyRead = false
@ -272,20 +272,20 @@ type
{.deprecated: [THttpMethod: HttpMethod].} {.deprecated: [THttpMethod: HttpMethod].}
when not defined(ssl): when not defined(ssl):
type PSSLContext = ref object type SSLContext = ref object
let defaultSSLContext: PSSLContext = nil let defaultSSLContext: SSLContext = nil
else: else:
let defaultSSLContext = newContext(verifyMode = CVerifyNone) let defaultSSLContext = newContext(verifyMode = CVerifyNone)
proc newProxy*(url: string, auth = ""): PProxy = proc newProxy*(url: string, auth = ""): Proxy =
## Constructs a new ``TProxy`` object. ## Constructs a new ``TProxy`` object.
result = PProxy(url: parseUrl(url), auth: auth) result = Proxy(url: parseUrl(url), auth: auth)
proc request*(url: string, httpMethod = httpGET, extraHeaders = "", proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
body = "", body = "",
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): TResponse = proxy: Proxy = nil): Response =
## | Requests ``url`` with the specified ``httpMethod``. ## | Requests ``url`` with the specified ``httpMethod``.
## | Extra headers can be specified and must be seperated by ``\c\L`` ## | Extra headers can be specified and must be seperated by ``\c\L``
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
@ -310,16 +310,16 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
var s = socket() var s = socket()
if s == invalidSocket: raiseOSError(osLastError()) if s == invalidSocket: raiseOSError(osLastError())
var port = sockets.TPort(80) var port = sockets.Port(80)
if r.scheme == "https": if r.scheme == "https":
when defined(ssl): when defined(ssl):
sslContext.wrapSocket(s) sslContext.wrapSocket(s)
port = sockets.TPort(443) port = sockets.Port(443)
else: else:
raise newException(EHttpRequestErr, raise newException(HttpRequestError,
"SSL support is not available. Cannot connect over SSL.") "SSL support is not available. Cannot connect over SSL.")
if r.port != "": if r.port != "":
port = sockets.TPort(r.port.parseInt) port = sockets.Port(r.port.parseInt)
if timeout == -1: if timeout == -1:
s.connect(r.hostname, port) s.connect(r.hostname, port)
@ -338,7 +338,7 @@ proc redirection(status: string): bool =
if status.startsWith(i): if status.startsWith(i):
return true return true
proc getNewLocation(lastUrl: string, headers: PStringTable): string = proc getNewLocation(lastUrl: string, headers: StringTableRef): string =
result = headers["Location"] result = headers["Location"]
if result == "": httpError("location header expected") if result == "": httpError("location header expected")
# Relative URLs. (Not part of the spec, but soon will be.) # Relative URLs. (Not part of the spec, but soon will be.)
@ -348,10 +348,10 @@ proc getNewLocation(lastUrl: string, headers: PStringTable): string =
result = origParsed.hostname & "/" & r.path result = origParsed.hostname & "/" & r.path
proc get*(url: string, extraHeaders = "", maxRedirects = 5, proc get*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): TResponse = proxy: Proxy = nil): Response =
## | GETs the ``url`` and returns a ``TResponse`` object ## | GETs the ``url`` and returns a ``Response`` object
## | This proc also handles redirection ## | This proc also handles redirection
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
@ -367,9 +367,9 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5,
lastUrl = redirectTo lastUrl = redirectTo
proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): string = proxy: Proxy = nil): string =
## | GETs the body and returns it as a string. ## | GETs the body and returns it as a string.
## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
@ -378,16 +378,16 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent,
proxy) proxy)
if r.status[0] in {'4','5'}: if r.status[0] in {'4','5'}:
raise newException(EHTTPRequestErr, r.status) raise newException(HttpRequestError, r.status)
else: else:
return r.body return r.body
proc post*(url: string, extraHeaders = "", body = "", proc post*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): TResponse = proxy: Proxy = nil): Response =
## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object. ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
## | This proc adds the necessary Content-Length header. ## | This proc adds the necessary Content-Length header.
## | This proc also handles redirection. ## | This proc also handles redirection.
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
@ -407,9 +407,9 @@ proc post*(url: string, extraHeaders = "", body = "",
proc postContent*(url: string, extraHeaders = "", body = "", proc postContent*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): string = proxy: Proxy = nil): string =
## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | POSTs ``body`` to ``url`` and returns the response's body as a string
## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
@ -418,18 +418,18 @@ proc postContent*(url: string, extraHeaders = "", body = "",
var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
userAgent, proxy) userAgent, proxy)
if r.status[0] in {'4','5'}: if r.status[0] in {'4','5'}:
raise newException(EHTTPRequestErr, r.status) raise newException(HttpRequestError, r.status)
else: else:
return r.body return r.body
proc downloadFile*(url: string, outputFilename: string, proc downloadFile*(url: string, outputFilename: string,
sslContext: PSSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil) = proxy: Proxy = nil) =
## | Downloads ``url`` and saves it to ``outputFilename`` ## | Downloads ``url`` and saves it to ``outputFilename``
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
var f: TFile var f: File
if open(f, outputFilename, fmWrite): if open(f, outputFilename, fmWrite):
f.write(getContent(url, sslContext = sslContext, timeout = timeout, f.write(getContent(url, sslContext = sslContext, timeout = timeout,
userAgent = userAgent, proxy = proxy)) userAgent = userAgent, proxy = proxy))
@ -437,8 +437,8 @@ proc downloadFile*(url: string, outputFilename: string,
else: else:
fileError("Unable to open file") fileError("Unable to open file")
proc generateHeaders(r: TURL, httpMethod: THttpMethod, proc generateHeaders(r: Url, httpMethod: HttpMethod,
headers: PStringTable): string = headers: StringTableRef): string =
result = substr($httpMethod, len("http")) result = substr($httpMethod, len("http"))
# TODO: Proxies # TODO: Proxies
result.add(" /" & r.path & r.query) result.add(" /" & r.path & r.query)
@ -455,7 +455,7 @@ type
AsyncHttpClient* = ref object AsyncHttpClient* = ref object
socket: AsyncSocket socket: AsyncSocket
connected: bool connected: bool
currentURL: TURL ## Where we are currently connected. currentURL: Url ## Where we are currently connected.
headers*: StringTableRef headers*: StringTableRef
maxRedirects: int maxRedirects: int
userAgent: string userAgent: string
@ -466,7 +466,7 @@ type
proc newAsyncHttpClient*(userAgent = defUserAgent, proc newAsyncHttpClient*(userAgent = defUserAgent,
maxRedirects = 5, sslContext = defaultSslContext): AsyncHttpClient = maxRedirects = 5, sslContext = defaultSslContext): AsyncHttpClient =
## Creates a new PAsyncHttpClient instance. ## Creates a new AsyncHttpClient instance.
## ##
## ``userAgent`` specifies the user agent that will be used when making ## ``userAgent`` specifies the user agent that will be used when making
## requests. ## requests.
@ -488,7 +488,7 @@ proc close*(client: AsyncHttpClient) =
client.socket.close() client.socket.close()
client.connected = false client.connected = false
proc recvFull(socket: PAsyncSocket, size: int): Future[string] {.async.} = proc recvFull(socket: AsyncSocket, size: int): Future[string] {.async.} =
## Ensures that all the data requested is read and returned. ## Ensures that all the data requested is read and returned.
result = "" result = ""
while true: while true:
@ -497,7 +497,7 @@ proc recvFull(socket: PAsyncSocket, size: int): Future[string] {.async.} =
if data == "": break # We've been disconnected. if data == "": break # We've been disconnected.
result.add data result.add data
proc parseChunks(client: PAsyncHttpClient): Future[string] {.async.} = proc parseChunks(client: AsyncHttpClient): Future[string] {.async.} =
result = "" result = ""
var ri = 0 var ri = 0
while true: while true:
@ -529,8 +529,8 @@ proc parseChunks(client: PAsyncHttpClient): Future[string] {.async.} =
# Trailer headers will only be sent if the request specifies that we want # Trailer headers will only be sent if the request specifies that we want
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1 # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
proc parseBody(client: PAsyncHttpClient, proc parseBody(client: AsyncHttpClient,
headers: PStringTable): Future[string] {.async.} = headers: StringTableRef): Future[string] {.async.} =
result = "" result = ""
if headers["Transfer-Encoding"] == "chunked": if headers["Transfer-Encoding"] == "chunked":
result = await parseChunks(client) result = await parseChunks(client)
@ -559,8 +559,8 @@ proc parseBody(client: PAsyncHttpClient,
if buf == "": break if buf == "": break
result.add(buf) result.add(buf)
proc parseResponse(client: PAsyncHttpClient, proc parseResponse(client: AsyncHttpClient,
getBody: bool): Future[TResponse] {.async.} = getBody: bool): Future[Response] {.async.} =
var parsedStatus = false var parsedStatus = false
var linei = 0 var linei = 0
var fullyRead = false var fullyRead = false
@ -607,34 +607,34 @@ proc parseResponse(client: PAsyncHttpClient,
else: else:
result.body = "" result.body = ""
proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} = proc newConnection(client: AsyncHttpClient, url: Url) {.async.} =
if client.currentURL.hostname != url.hostname or if client.currentURL.hostname != url.hostname or
client.currentURL.scheme != url.scheme: client.currentURL.scheme != url.scheme:
if client.connected: client.close() if client.connected: client.close()
client.socket = newAsyncSocket() client.socket = newAsyncSocket()
# TODO: I should be able to write 'net.TPort' here... # TODO: I should be able to write 'net.Port' here...
let port = let port =
if url.port == "": if url.port == "":
if url.scheme.toLower() == "https": if url.scheme.toLower() == "https":
rawsockets.TPort(443) rawsockets.Port(443)
else: else:
rawsockets.TPort(80) rawsockets.Port(80)
else: rawsockets.TPort(url.port.parseInt) else: rawsockets.Port(url.port.parseInt)
if url.scheme.toLower() == "https": if url.scheme.toLower() == "https":
when defined(ssl): when defined(ssl):
client.sslContext.wrapSocket(client.socket) client.sslContext.wrapSocket(client.socket)
else: else:
raise newException(EHttpRequestErr, raise newException(HttpRequestError,
"SSL support is not available. Cannot connect over SSL.") "SSL support is not available. Cannot connect over SSL.")
await client.socket.connect(url.hostname, port) await client.socket.connect(url.hostname, port)
client.currentURL = url client.currentURL = url
client.connected = true client.connected = true
proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET, proc request*(client: AsyncHttpClient, url: string, httpMethod = httpGET,
body = ""): Future[TResponse] {.async.} = body = ""): Future[Response] {.async.} =
## Connects to the hostname specified by the URL and performs a request ## Connects to the hostname specified by the URL and performs a request
## using the method specified. ## using the method specified.
## ##
@ -657,7 +657,7 @@ proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
result = await parseResponse(client, httpMethod != httpHEAD) result = await parseResponse(client, httpMethod != httpHEAD)
proc get*(client: PAsyncHttpClient, url: string): Future[TResponse] {.async.} = proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} =
## Connects to the hostname specified by the URL and performs a GET request. ## Connects to the hostname specified by the URL and performs a GET request.
## ##
## This procedure will follow redirects up to a maximum number of redirects ## This procedure will follow redirects up to a maximum number of redirects

View file

@ -15,12 +15,12 @@
## import strutils, sockets, httpserver ## import strutils, sockets, httpserver
## ##
## var counter = 0 ## var counter = 0
## proc handleRequest(client: TSocket, path, query: string): bool {.procvar.} = ## proc handleRequest(client: Socket, path, query: string): bool {.procvar.} =
## inc(counter) ## inc(counter)
## client.send("Hello for the $#th time." % $counter & wwwNL) ## client.send("Hello for the $#th time." % $counter & wwwNL)
## return false # do not stop processing ## return false # do not stop processing
## ##
## run(handleRequest, TPort(80)) ## run(handleRequest, Port(80))
## ##
import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio
@ -31,14 +31,14 @@ const
# --------------- output messages -------------------------------------------- # --------------- output messages --------------------------------------------
proc sendTextContentType(client: TSocket) = proc sendTextContentType(client: Socket) =
send(client, "Content-type: text/html" & wwwNL) send(client, "Content-type: text/html" & wwwNL)
send(client, wwwNL) send(client, wwwNL)
proc sendStatus(client: TSocket, status: string) = proc sendStatus(client: Socket, status: string) =
send(client, "HTTP/1.1 " & status & wwwNL) send(client, "HTTP/1.1 " & status & wwwNL)
proc badRequest(client: TSocket) = proc badRequest(client: Socket) =
# Inform the client that a request it has made has a problem. # Inform the client that a request it has made has a problem.
send(client, "HTTP/1.1 400 Bad Request" & wwwNL) send(client, "HTTP/1.1 400 Bad Request" & wwwNL)
sendTextContentType(client) sendTextContentType(client)
@ -46,18 +46,18 @@ proc badRequest(client: TSocket) =
"such as a POST without a Content-Length.</p>" & wwwNL) "such as a POST without a Content-Length.</p>" & wwwNL)
when false: when false:
proc cannotExec(client: TSocket) = proc cannotExec(client: Socket) =
send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
sendTextContentType(client) sendTextContentType(client)
send(client, "<P>Error prohibited CGI execution." & wwwNL) send(client, "<P>Error prohibited CGI execution." & wwwNL)
proc headers(client: TSocket, filename: string) = proc headers(client: Socket, filename: string) =
# XXX could use filename to determine file type # XXX could use filename to determine file type
send(client, "HTTP/1.1 200 OK" & wwwNL) send(client, "HTTP/1.1 200 OK" & wwwNL)
send(client, ServerSig) send(client, ServerSig)
sendTextContentType(client) sendTextContentType(client)
proc notFound(client: TSocket) = proc notFound(client: Socket) =
send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL) send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL)
send(client, ServerSig) send(client, ServerSig)
sendTextContentType(client) sendTextContentType(client)
@ -67,7 +67,7 @@ proc notFound(client: TSocket) =
send(client, "is unavailable or nonexistent.</p>" & wwwNL) send(client, "is unavailable or nonexistent.</p>" & wwwNL)
send(client, "</body></html>" & wwwNL) send(client, "</body></html>" & wwwNL)
proc unimplemented(client: TSocket) = proc unimplemented(client: Socket) =
send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL) send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL)
send(client, ServerSig) send(client, ServerSig)
sendTextContentType(client) sendTextContentType(client)
@ -79,11 +79,11 @@ proc unimplemented(client: TSocket) =
# ----------------- file serving --------------------------------------------- # ----------------- file serving ---------------------------------------------
when false: when false:
proc discardHeaders(client: TSocket) = skip(client) proc discardHeaders(client: Socket) = skip(client)
proc serveFile*(client: TSocket, filename: string) = proc serveFile*(client: Socket, filename: string) =
## serves a file to the client. ## serves a file to the client.
var f: TFile var f: File
if open(f, filename): if open(f, filename):
headers(client, filename) headers(client, filename)
const bufSize = 8000 # != 8K might be good for memory manager const bufSize = 8000 # != 8K might be good for memory manager
@ -108,7 +108,7 @@ when false:
type type
TRequestMethod = enum reqGet, reqPost TRequestMethod = enum reqGet, reqPost
proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) =
var env = newStringTable(modeCaseInsensitive) var env = newStringTable(modeCaseInsensitive)
var contentLength = -1 var contentLength = -1
case meth case meth
@ -158,7 +158,7 @@ when false:
# --------------- Server Setup ----------------------------------------------- # --------------- Server Setup -----------------------------------------------
proc acceptRequest(client: TSocket) = proc acceptRequest(client: Socket) =
var cgi = false var cgi = false
var query = "" var query = ""
var buf = TaintedString"" var buf = TaintedString""
@ -208,21 +208,21 @@ when false:
executeCgi(client, path, query, meth) executeCgi(client, path, query, meth)
type type
TServer* = object of TObject ## contains the current server state TServer* = object of RootObj ## contains the current server state
socket: TSocket socket: Socket
port: TPort port: Port
client*: TSocket ## the socket to write the file data to client*: Socket ## the socket to write the file data to
reqMethod*: string ## Request method. GET or POST. reqMethod*: string ## Request method. GET or POST.
path*, query*: string ## path and query the client requested path*, query*: string ## path and query the client requested
headers*: PStringTable ## headers with which the client made the request headers*: StringTableRef ## headers with which the client made the request
body*: string ## only set with POST requests body*: string ## only set with POST requests
ip*: string ## ip address of the requesting client ip*: string ## ip address of the requesting client
PAsyncHTTPServer* = ref TAsyncHTTPServer PAsyncHTTPServer* = ref TAsyncHTTPServer
TAsyncHTTPServer = object of TServer TAsyncHTTPServer = object of TServer
asyncSocket: PAsyncSocket asyncSocket: AsyncSocket
proc open*(s: var TServer, port = TPort(80), reuseAddr = false) = proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
## creates a new server at port `port`. If ``port == 0`` a free port is ## creates a new server at port `port`. If ``port == 0`` a free port is
## acquired that can be accessed later by the ``port`` proc. ## acquired that can be accessed later by the ``port`` proc.
s.socket = socket(AF_INET) s.socket = socket(AF_INET)
@ -232,7 +232,7 @@ proc open*(s: var TServer, port = TPort(80), reuseAddr = false) =
bindAddr(s.socket, port) bindAddr(s.socket, port)
listen(s.socket) listen(s.socket)
if port == TPort(0): if port == Port(0):
s.port = getSockName(s.socket) s.port = getSockName(s.socket)
else: else:
s.port = port s.port = port
@ -243,13 +243,13 @@ proc open*(s: var TServer, port = TPort(80), reuseAddr = false) =
s.query = "" s.query = ""
s.headers = {:}.newStringTable() s.headers = {:}.newStringTable()
proc port*(s: var TServer): TPort = proc port*(s: var TServer): Port =
## get the port number the server has acquired. ## get the port number the server has acquired.
result = s.port result = s.port
proc next*(s: var TServer) = proc next*(s: var TServer) =
## proceed to the first/next request. ## proceed to the first/next request.
var client: TSocket var client: Socket
new(client) new(client)
var ip: string var ip: string
acceptAddr(s.socket, client, ip) acceptAddr(s.socket, client, ip)
@ -358,9 +358,9 @@ proc close*(s: TServer) =
## closes the server (and the socket the server uses). ## closes the server (and the socket the server uses).
close(s.socket) close(s.socket)
proc run*(handleRequest: proc (client: TSocket, proc run*(handleRequest: proc (client: Socket,
path, query: string): bool {.closure.}, path, query: string): bool {.closure.},
port = TPort(80)) = port = Port(80)) =
## encapsulates the server object and main loop ## encapsulates the server object and main loop
var s: TServer var s: TServer
open(s, port) open(s, port)
@ -375,7 +375,7 @@ proc run*(handleRequest: proc (client: TSocket,
proc nextAsync(s: PAsyncHTTPServer) = proc nextAsync(s: PAsyncHTTPServer) =
## proceed to the first/next request. ## proceed to the first/next request.
var client: TSocket var client: Socket
new(client) new(client)
var ip: string var ip: string
acceptAddr(getSocket(s.asyncSocket), client, ip) acceptAddr(getSocket(s.asyncSocket), client, ip)
@ -474,16 +474,16 @@ proc nextAsync(s: PAsyncHTTPServer) =
s.query = "" s.query = ""
s.path = data.substr(i, last-1) s.path = data.substr(i, last-1)
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSocket, proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
path, query: string): bool {.closure, gcsafe.}, path, query: string): bool {.closure, gcsafe.},
port = TPort(80), address = "", port = Port(80), address = "",
reuseAddr = false): PAsyncHTTPServer = reuseAddr = false): PAsyncHTTPServer =
## Creates an Asynchronous HTTP server at ``port``. ## Creates an Asynchronous HTTP server at ``port``.
var capturedRet: PAsyncHTTPServer var capturedRet: PAsyncHTTPServer
new(capturedRet) new(capturedRet)
capturedRet.asyncSocket = asyncSocket() capturedRet.asyncSocket = asyncSocket()
capturedRet.asyncSocket.handleAccept = capturedRet.asyncSocket.handleAccept =
proc (s: PAsyncSocket) = proc (s: AsyncSocket) =
nextAsync(capturedRet) nextAsync(capturedRet)
let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path, let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path,
capturedRet.query) capturedRet.query)
@ -493,7 +493,7 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSo
capturedRet.asyncSocket.bindAddr(port, address) capturedRet.asyncSocket.bindAddr(port, address)
capturedRet.asyncSocket.listen() capturedRet.asyncSocket.listen()
if port == TPort(0): if port == Port(0):
capturedRet.port = getSockName(capturedRet.asyncSocket) capturedRet.port = getSockName(capturedRet.asyncSocket)
else: else:
capturedRet.port = port capturedRet.port = port
@ -506,8 +506,8 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSo
capturedRet.headers = {:}.newStringTable() capturedRet.headers = {:}.newStringTable()
result = capturedRet result = capturedRet
proc register*(d: PDispatcher, s: PAsyncHTTPServer) = proc register*(d: Dispatcher, s: PAsyncHTTPServer) =
## Registers a ``PAsyncHTTPServer`` with a ``PDispatcher``. ## Registers a ``PAsyncHTTPServer`` with a ``Dispatcher``.
d.register(s.asyncSocket) d.register(s.asyncSocket)
proc close*(h: PAsyncHTTPServer) = proc close*(h: PAsyncHTTPServer) =
@ -518,7 +518,7 @@ when isMainModule:
var counter = 0 var counter = 0
var s: TServer var s: TServer
open(s, TPort(0)) open(s, Port(0))
echo("httpserver running on port ", s.port) echo("httpserver running on port ", s.port)
while true: while true:
next(s) next(s)

View file

@ -196,7 +196,6 @@ when not defined(JS):
## computes x to power raised of y. ## computes x to power raised of y.
# C procs: # C procs:
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>".} proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>".}
proc rand(): cint {.importc: "rand", header: "<stdlib.h>".} proc rand(): cint {.importc: "rand", header: "<stdlib.h>".}
@ -331,6 +330,8 @@ proc standardDeviation*(s: RunningStat): float =
{.pop.} {.pop.}
when isMainModule and not defined(JS): when isMainModule and not defined(JS):
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
# Verifies random seed initialization. # Verifies random seed initialization.
let seed = gettime(nil) let seed = gettime(nil)
randomize(seed) randomize(seed)

View file

@ -54,8 +54,8 @@ type
when defined(ssl): when defined(ssl):
case isSsl*: bool case isSsl*: bool
of true: of true:
sslHandle*: PSSL sslHandle*: SSLPtr
sslContext*: PSSLContext sslContext*: SSLContext
sslNoHandshake*: bool # True if needs handshake. sslNoHandshake*: bool # True if needs handshake.
sslHasPeekChar*: bool sslHasPeekChar*: bool
sslPeekChar*: char sslPeekChar*: char
@ -147,11 +147,11 @@ when defined(ssl):
raise newException(SSLError, $errStr) raise newException(SSLError, $errStr)
# http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
proc loadCertificates(ctx: PSSL_CTX, certFile, keyFile: string) = proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) =
if certFile != "" and not existsFile(certFile): if certFile != "" and not existsFile(certFile):
raise newException(system.EIO, "Certificate file could not be found: " & certFile) raise newException(system.IOError, "Certificate file could not be found: " & certFile)
if keyFile != "" and not existsFile(keyFile): if keyFile != "" and not existsFile(keyFile):
raise newException(system.EIO, "Key file could not be found: " & keyFile) raise newException(system.IOError, "Key file could not be found: " & keyFile)
if certFile != "": if certFile != "":
var ret = SSLCTXUseCertificateChainFile(ctx, certFile) var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
@ -168,7 +168,7 @@ when defined(ssl):
raiseSSLError("Verification of private key file failed.") raiseSSLError("Verification of private key file failed.")
proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
certFile = "", keyFile = ""): PSSLContext = certFile = "", keyFile = ""): SSLContext =
## Creates an SSL context. ## Creates an SSL context.
## ##
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
@ -184,7 +184,7 @@ when defined(ssl):
## path, a server socket will most likely not work without these. ## path, a server socket will most likely not work without these.
## Certificates can be generated using the following command: ## Certificates can be generated using the following command:
## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``. ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``.
var newCTX: PSSL_CTX var newCTX: SSL_CTX
case protVersion case protVersion
of protSSLv23: of protSSLv23:
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
@ -210,9 +210,9 @@ when defined(ssl):
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY) discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
newCTX.loadCertificates(certFile, keyFile) newCTX.loadCertificates(certFile, keyFile)
return PSSLContext(newCTX) return SSLContext(newCTX)
proc wrapSocket*(ctx: PSSLContext, socket: PSocket) = proc wrapSocket*(ctx: SSLContext, socket: Socket) =
## Wraps a socket in an SSL context. This function effectively turns ## Wraps a socket in an SSL context. This function effectively turns
## ``socket`` into an SSL socket. ## ``socket`` into an SSL socket.
## ##
@ -221,7 +221,7 @@ when defined(ssl):
socket.isSSL = true socket.isSSL = true
socket.sslContext = ctx socket.sslContext = ctx
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext)) socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
socket.sslNoHandshake = false socket.sslNoHandshake = false
socket.sslHasPeekChar = false socket.sslHasPeekChar = false
if socket.sslHandle == nil: if socket.sslHandle == nil:
@ -350,9 +350,9 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
address = $inet_ntoa(sockAddress.sin_addr) address = $inet_ntoa(sockAddress.sin_addr)
when false: #defined(ssl): when false: #defined(ssl):
proc acceptAddrSSL*(server: PSocket, client: var PSocket, proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): TSSLAcceptResult {. address: var string): TSSLAcceptResult {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## This procedure should only be used for non-blocking **SSL** sockets. ## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values: ## It will immediately return with one of the following values:
## ##
@ -480,7 +480,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
socketError(socket, ret) socketError(socket, ret)
when defined(ssl): when defined(ssl):
proc handshake*(socket: PSocket): bool {.tags: [FReadIO, FWriteIO].} = 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
## only applicable when using ``connectAsync``. ## only applicable when using ``connectAsync``.
## This proc performs the SSL handshake. ## This proc performs the SSL handshake.
@ -510,7 +510,7 @@ when defined(ssl):
else: else:
raiseSSLError("Socket is not an SSL socket.") raiseSSLError("Socket is not an SSL socket.")
proc gotHandshake*(socket: PSocket): bool = proc gotHandshake*(socket: Socket): bool =
## Determines whether a handshake has occurred between a client (``socket``) ## Determines whether a handshake has occurred between a client (``socket``)
## and the server that ``socket`` is connected to. ## and the server that ``socket`` is connected to.
## ##

View file

@ -54,12 +54,12 @@ type
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].} {.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
proc debugSend(smtp: TSMTP, cmd: string) = proc debugSend(smtp: Smtp, cmd: string) =
if smtp.debug: if smtp.debug:
echo("C:" & cmd) echo("C:" & cmd)
smtp.sock.send(cmd) smtp.sock.send(cmd)
proc debugRecv(smtp: var TSMTP): TaintedString = proc debugRecv(smtp: var Smtp): TaintedString =
var line = TaintedString"" var line = TaintedString""
smtp.sock.readLine(line) smtp.sock.readLine(line)
@ -67,11 +67,11 @@ proc debugRecv(smtp: var TSMTP): TaintedString =
echo("S:" & line.string) echo("S:" & line.string)
return line return line
proc quitExcpt(smtp: TSMTP, msg: string) = proc quitExcpt(smtp: Smtp, msg: string) =
smtp.debugSend("QUIT") smtp.debugSend("QUIT")
raise newException(EInvalidReply, msg) raise newException(ReplyError, msg)
proc checkReply(smtp: var TSMTP, reply: string) = proc checkReply(smtp: var Smtp, reply: string) =
var line = smtp.debugRecv() var line = smtp.debugRecv()
if not line.string.startswith(reply): if not line.string.startswith(reply):
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string) quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
@ -86,9 +86,9 @@ else:
proc connect*(address: string, port = Port(25), proc connect*(address: string, port = Port(25),
ssl = false, debug = false, ssl = false, debug = false,
sslContext = defaultSSLContext): TSMTP = sslContext = defaultSSLContext): Smtp =
## Establishes a connection with a SMTP server. ## Establishes a connection with a SMTP server.
## May fail with EInvalidReply or with a socket error. ## May fail with ReplyError or with a socket error.
result.sock = newSocket() result.sock = newSocket()
if ssl: if ssl:
when compiledWithSsl: when compiledWithSsl:
@ -103,10 +103,10 @@ proc connect*(address: string, port = Port(25),
result.debugSend("HELO " & address & "\c\L") result.debugSend("HELO " & address & "\c\L")
result.checkReply("250") result.checkReply("250")
proc auth*(smtp: var TSMTP, username, password: string) = proc auth*(smtp: var Smtp, username, password: string) =
## Sends an AUTH command to the server to login as the `username` ## Sends an AUTH command to the server to login as the `username`
## using `password`. ## using `password`.
## May fail with EInvalidReply. ## May fail with ReplyError.
smtp.debugSend("AUTH LOGIN\c\L") smtp.debugSend("AUTH LOGIN\c\L")
smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
@ -117,11 +117,11 @@ proc auth*(smtp: var TSMTP, username, password: string) =
smtp.debugSend(encode(password) & "\c\L") smtp.debugSend(encode(password) & "\c\L")
smtp.checkReply("235") # Check whether the authentification was successful. smtp.checkReply("235") # Check whether the authentification was successful.
proc sendmail*(smtp: var TSMTP, fromaddr: string, proc sendmail*(smtp: var Smtp, fromaddr: string,
toaddrs: seq[string], msg: string) = toaddrs: seq[string], msg: string) =
## Sends `msg` from `fromaddr` to `toaddr`. ## Sends `msg` from `fromaddr` to `toaddr`.
## Messages may be formed using ``createMessage`` by converting the ## Messages may be formed using ``createMessage`` by converting the
## TMessage into a string. ## Message into a string.
smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L") smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L")
smtp.checkReply("250") smtp.checkReply("250")
@ -136,13 +136,13 @@ proc sendmail*(smtp: var TSMTP, fromaddr: string,
smtp.debugSend(".\c\L") smtp.debugSend(".\c\L")
smtp.checkReply("250") smtp.checkReply("250")
proc close*(smtp: TSMTP) = proc close*(smtp: Smtp) =
## Disconnects from the SMTP server and closes the socket. ## Disconnects from the SMTP server and closes the socket.
smtp.debugSend("QUIT\c\L") smtp.debugSend("QUIT\c\L")
smtp.sock.close() smtp.sock.close()
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
otherHeaders: openarray[tuple[name, value: string]]): TMessage = otherHeaders: openarray[tuple[name, value: string]]): Message =
## Creates a new MIME compliant message. ## Creates a new MIME compliant message.
result.msgTo = mTo result.msgTo = mTo
result.msgCc = mCc result.msgCc = mCc
@ -153,7 +153,7 @@ proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
result.msgOtherHeaders[n] = v result.msgOtherHeaders[n] = v
proc createMessage*(mSubject, mBody: string, mTo, proc createMessage*(mSubject, mBody: string, mTo,
mCc: seq[string] = @[]): TMessage = mCc: seq[string] = @[]): Message =
## Alternate version of the above. ## Alternate version of the above.
result.msgTo = mTo result.msgTo = mTo
result.msgCc = mCc result.msgCc = mCc
@ -161,8 +161,8 @@ proc createMessage*(mSubject, mBody: string, mTo,
result.msgBody = mBody result.msgBody = mBody
result.msgOtherHeaders = newStringTable() result.msgOtherHeaders = newStringTable()
proc `$`*(msg: TMessage): string = proc `$`*(msg: Message): string =
## stringify for ``TMessage``. ## stringify for ``Message``.
result = "" result = ""
if msg.msgTo.len() > 0: if msg.msgTo.len() > 0:
result = "TO: " & msg.msgTo.join(", ") & "\c\L" result = "TO: " & msg.msgTo.join(", ") & "\c\L"
@ -192,7 +192,7 @@ proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
raise newException(ESystem, raise newException(ESystem,
"SMTP module compiled without SSL support") "SMTP module compiled without SSL support")
proc quitExcpt(smtp: AsyncSmtp, msg: string): PFuture[void] = proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
var retFuture = newFuture[void]() var retFuture = newFuture[void]()
var sendFut = smtp.sock.send("QUIT") var sendFut = smtp.sock.send("QUIT")
sendFut.callback = sendFut.callback =
@ -208,7 +208,7 @@ proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} =
proc connect*(smtp: AsyncSmtp) {.async.} = proc connect*(smtp: AsyncSmtp) {.async.} =
## Establishes a connection with a SMTP server. ## Establishes a connection with a SMTP server.
## May fail with EInvalidReply or with a socket error. ## May fail with ReplyError or with a socket error.
await smtp.sock.connect(smtp.address, smtp.port) await smtp.sock.connect(smtp.address, smtp.port)
await smtp.checkReply("220") await smtp.checkReply("220")
@ -218,7 +218,7 @@ proc connect*(smtp: AsyncSmtp) {.async.} =
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
## Sends an AUTH command to the server to login as the `username` ## Sends an AUTH command to the server to login as the `username`
## using `password`. ## using `password`.
## May fail with EInvalidReply. ## May fail with ReplyError.
await smtp.sock.send("AUTH LOGIN\c\L") await smtp.sock.send("AUTH LOGIN\c\L")
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
@ -233,7 +233,7 @@ proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
toAddrs: seq[string], msg: string) {.async.} = toAddrs: seq[string], msg: string) {.async.} =
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
## Messages may be formed using ``createMessage`` by converting the ## Messages may be formed using ``createMessage`` by converting the
## TMessage into a string. ## Message into a string.
await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L") await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L")
await smtp.checkReply("250") await smtp.checkReply("250")

View file

@ -55,7 +55,7 @@ when defined(ssl):
SSLProtVersion* = enum SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23 protSSLv2, protSSLv3, protTLSv1, protSSLv23
SSLContext* = distinct PSSLCTX SSLContext* = distinct SSLCTX
SSLAcceptResult* = enum SSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
@ -79,7 +79,7 @@ type
when defined(ssl): when defined(ssl):
case isSsl: bool case isSsl: bool
of true: of true:
sslHandle: PSSL sslHandle: SSLPtr
sslContext: SSLContext sslContext: SSLContext
sslNoHandshake: bool # True if needs handshake. sslNoHandshake: bool # True if needs handshake.
sslHasPeekChar: bool sslHasPeekChar: bool
@ -254,21 +254,21 @@ when defined(ssl):
proc raiseSSLError(s = "") = proc raiseSSLError(s = "") =
if s != "": if s != "":
raise newException(ESSL, s) raise newException(SSLError, s)
let err = ErrPeekLastError() let err = ErrPeekLastError()
if err == 0: if err == 0:
raise newException(ESSL, "No error reported.") raise newException(SSLError, "No error reported.")
if err == -1: if err == -1:
raiseOSError(osLastError()) raiseOSError(osLastError())
var errStr = ErrErrorString(err, nil) var errStr = ErrErrorString(err, nil)
raise newException(ESSL, $errStr) raise newException(SSLError, $errStr)
# http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
proc loadCertificates(ctx: PSSL_CTX, certFile, keyFile: string) = proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) =
if certFile != "" and not existsFile(certFile): if certFile != "" and not existsFile(certFile):
raise newException(system.EIO, "Certificate file could not be found: " & certFile) raise newException(system.IOError, "Certificate file could not be found: " & certFile)
if keyFile != "" and not existsFile(keyFile): if keyFile != "" and not existsFile(keyFile):
raise newException(system.EIO, "Key file could not be found: " & keyFile) raise newException(system.IOError, "Key file could not be found: " & keyFile)
if certFile != "": if certFile != "":
var ret = SSLCTXUseCertificateChainFile(ctx, certFile) var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
@ -285,7 +285,7 @@ when defined(ssl):
raiseSslError("Verification of private key file failed.") raiseSslError("Verification of private key file failed.")
proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
certFile = "", keyFile = ""): PSSLContext = certFile = "", keyFile = ""): SSLContext =
## Creates an SSL context. ## Creates an SSL context.
## ##
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
@ -301,7 +301,7 @@ when defined(ssl):
## path, a server socket will most likely not work without these. ## path, a server socket will most likely not work without these.
## Certificates can be generated using the following command: ## Certificates can be generated using the following command:
## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``. ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``.
var newCTX: PSSL_CTX var newCTX: SSL_CTX
case protVersion case protVersion
of protSSLv23: of protSSLv23:
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
@ -327,9 +327,9 @@ when defined(ssl):
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY) discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
newCTX.loadCertificates(certFile, keyFile) newCTX.loadCertificates(certFile, keyFile)
return PSSLContext(newCTX) return SSLContext(newCTX)
proc wrapSocket*(ctx: PSSLContext, socket: TSocket) = proc wrapSocket*(ctx: SSLContext, socket: Socket) =
## Wraps a socket in an SSL context. This function effectively turns ## Wraps a socket in an SSL context. This function effectively turns
## ``socket`` into an SSL socket. ## ``socket`` into an SSL socket.
## ##
@ -338,7 +338,7 @@ when defined(ssl):
socket.isSSL = true socket.isSSL = true
socket.sslContext = ctx socket.sslContext = ctx
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext)) socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
socket.sslNoHandshake = false socket.sslNoHandshake = false
socket.sslHasPeekChar = false socket.sslHasPeekChar = false
if socket.sslHandle == nil: if socket.sslHandle == nil:
@ -561,9 +561,9 @@ proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.}
## Sets blocking mode on socket ## Sets blocking mode on socket
when defined(ssl): when defined(ssl):
proc acceptAddrSSL*(server: TSocket, client: var TSocket, proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): TSSLAcceptResult {. address: var string): SSLAcceptResult {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## This procedure should only be used for non-blocking **SSL** sockets. ## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values: ## It will immediately return with one of the following values:
## ##
@ -886,7 +886,7 @@ proc connectAsync*(socket: Socket, name: string, port = Port(0),
socket.sslNoHandshake = true socket.sslNoHandshake = true
when defined(ssl): when defined(ssl):
proc handshake*(socket: TSocket): bool {.tags: [FReadIO, FWriteIO].} = 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
## only applicable when using ``connectAsync``. ## only applicable when using ``connectAsync``.
## This proc performs the SSL handshake. ## This proc performs the SSL handshake.
@ -916,7 +916,7 @@ when defined(ssl):
else: else:
raiseSslError("Socket is not an SSL socket.") raiseSslError("Socket is not an SSL socket.")
proc gotHandshake*(socket: TSocket): bool = proc gotHandshake*(socket: Socket): bool =
## Determines whether a handshake has occurred between a client (``socket``) ## Determines whether a handshake has occurred between a client (``socket``)
## and the server that ``socket`` is connected to. ## and the server that ``socket`` is connected to.
## ##
@ -1689,7 +1689,7 @@ proc setBlocking(s: Socket, blocking: bool) =
raiseOSError(osLastError()) raiseOSError(osLastError())
s.nonblocking = not blocking s.nonblocking = not blocking
discard """ proc setReuseAddr*(s: TSocket) = discard """ proc setReuseAddr*(s: Socket) =
var blah: int = 1 var blah: int = 1
var mode = SO_REUSEADDR var mode = SO_REUSEADDR
if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1: if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1: