summary refs log tree commit diff stats
path: root/lib/deprecated/pure
diff options
context:
space:
mode:
authorAdam Strzelecki <ono@java.pl>2015-09-29 22:34:57 +0200
committerAdam Strzelecki <ono@java.pl>2015-09-30 12:26:25 +0200
commit144dc8f8ad209ebcb6591e0adc1bc4cb73528f3a (patch)
tree92a7e34e176cb56e48780b13dce30eb6aed8c09a /lib/deprecated/pure
parent4071219e201d76d75a95ce712027aac1d2ee5281 (diff)
downloadNim-144dc8f8ad209ebcb6591e0adc1bc4cb73528f3a.tar.gz
Move deprecated modules into lib/deprecated/
This gives clear indication what modules are now deprecated and reduce clutter
in non-deprecated module directories.
Diffstat (limited to 'lib/deprecated/pure')
-rw-r--r--lib/deprecated/pure/actors.nim241
-rw-r--r--lib/deprecated/pure/actors.nim.cfg3
-rw-r--r--lib/deprecated/pure/asyncio.nim712
-rw-r--r--lib/deprecated/pure/ftpclient.nim675
-rw-r--r--lib/deprecated/pure/parseopt.nim178
-rw-r--r--lib/deprecated/pure/parseurl.nim114
-rw-r--r--lib/deprecated/pure/sockets.nim1738
7 files changed, 3661 insertions, 0 deletions
diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim
new file mode 100644
index 000000000..f0791f954
--- /dev/null
+++ b/lib/deprecated/pure/actors.nim
@@ -0,0 +1,241 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## `Actor`:idx: support for Nim. An actor is implemented as a thread with
+## a channel as its inbox. This module requires the ``--threads:on``
+## command line switch.
+##
+## Example:
+##
+## .. code-block:: nim
+##
+##      var
+##        a: ActorPool[int, void]
+##      createActorPool(a)
+##      for i in 0 .. < 300:
+##        a.spawn(i, proc (x: int) {.thread.} = echo x)
+##      a.join()
+##
+## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
+## instead.
+
+{.deprecated.}
+
+from os import sleep
+
+type
+  Task*[In, Out] = object{.pure, final.} ## a task
+    when Out isnot void:
+      receiver*: ptr Channel[Out] ## the receiver channel of the response
+    action*: proc (x: In): Out {.thread.} ## action to execute;
+                                            ## sometimes useful
+    shutDown*: bool ## set to tell an actor to shut-down
+    data*: In ## the data to process
+
+  Actor[In, Out] = object{.pure, final.}
+    i: Channel[Task[In, Out]]
+    t: TThread[ptr Actor[In, Out]]
+
+  PActor*[In, Out] = ptr Actor[In, Out] ## an actor
+{.deprecated: [TTask: Task, TActor: Actor].}
+
+proc spawn*[In, Out](action: proc(
+    self: PActor[In, Out]){.thread.}): PActor[In, Out] =
+  ## creates an actor; that is a thread with an inbox. The caller MUST call
+  ## ``join`` because that also frees the actor's associated resources.
+  result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
+  open(result.i)
+  createThread(result.t, action, result)
+
+proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
+  ## gets a pointer to the associated inbox of the actor `self`.
+  result = addr(self.i)
+
+proc running*[In, Out](a: PActor[In, Out]): bool =
+  ## returns true if the actor `a` is running.
+  result = running(a.t)
+
+proc ready*[In, Out](a: PActor[In, Out]): bool =
+  ## returns true if the actor `a` is ready to process new messages.
+  result = ready(a.i)
+
+proc join*[In, Out](a: PActor[In, Out]) =
+  ## joins an actor.
+  joinThread(a.t)
+  close(a.i)
+  deallocShared(a)
+
+proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
+  ## receives a task from `a`'s inbox.
+  result = recv(a.i)
+
+proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
+                            sender: PActor[X, Y]) =
+  ## sends a message to `a`'s inbox.
+  var t: Task[In, Out]
+  t.receiver = addr(sender.i)
+  shallowCopy(t.data, msg)
+  send(receiver.i, t)
+
+proc send*[In, Out](receiver: PActor[In, Out], msg: In,
+                      sender: ptr Channel[Out] = nil) =
+  ## sends a message to `receiver`'s inbox.
+  var t: Task[In, Out]
+  t.receiver = sender
+  shallowCopy(t.data, msg)
+  send(receiver.i, t)
+
+proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
+  ## send a shutdown message to `receiver`.
+  var t: Task[In, Out]
+  t.shutdown = true
+  send(receiver.i, t)
+
+proc reply*[In, Out](t: Task[In, Out], m: Out) =
+  ## sends a message to io's output message box.
+  when Out is void:
+    {.error: "you cannot reply to a void outbox".}
+  assert t.receiver != nil
+  send(t.receiver[], m)
+
+
+# ----------------- actor pools ----------------------------------------------
+
+type
+  ActorPool*[In, Out] = object{.pure, final.}  ## an actor pool
+    actors: seq[PActor[In, Out]]
+    when Out isnot void:
+      outputs: Channel[Out]
+{.deprecated: [TActorPool: ActorPool].}
+
+proc `^`*[T](f: ptr Channel[T]): T =
+  ## alias for 'recv'.
+  result = recv(f[])
+
+proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
+  while true:
+    var m = self.recv
+    if m.shutDown: break
+    when Out is void:
+      m.action(m.data)
+    else:
+      send(m.receiver[], m.action(m.data))
+      #self.reply()
+
+proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
+  ## creates an actor pool.
+  newSeq(a.actors, poolSize)
+  when Out isnot void:
+    open(a.outputs)
+  for i in 0 .. < a.actors.len:
+    a.actors[i] = spawn(poolWorker[In, Out])
+
+proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
+  ## waits for every actor of `a` to finish with its work. Currently this is
+  ## implemented as polling every `polling` ms and has a slight chance
+  ## of failing since we check for every actor to be in `ready` state and not
+  ## for messages still in ether. This will change in a later
+  ## version, however.
+  var allReadyCount = 0
+  while true:
+    var wait = false
+    for i in 0..high(a.actors):
+      if not a.actors[i].i.ready:
+        wait = true
+        allReadyCount = 0
+        break
+    if not wait:
+      # it's possible that some actor sent a message to some other actor but
+      # both appeared to be non-working as the message takes some time to
+      # arrive. We assume that this won't take longer than `polling` and
+      # simply attempt a second time and declare victory then. ;-)
+      inc allReadyCount
+      if allReadyCount > 1: break
+    sleep(polling)
+
+proc terminate*[In, Out](a: var ActorPool[In, Out]) =
+  ## terminates each actor in the actor pool `a` and frees the
+  ## resources attached to `a`.
+  var t: Task[In, Out]
+  t.shutdown = true
+  for i in 0.. <a.actors.len: send(a.actors[i].i, t)
+  for i in 0.. <a.actors.len: join(a.actors[i])
+  when Out isnot void:
+    close(a.outputs)
+  a.actors = nil
+
+proc join*[In, Out](a: var ActorPool[In, Out]) =
+  ## short-cut for `sync` and then `terminate`.
+  sync(a)
+  terminate(a)
+
+template setupTask =
+  t.action = action
+  shallowCopy(t.data, input)
+
+template schedule =
+  # extremely simple scheduler: We always try the first thread first, so that
+  # it remains 'hot' ;-). Round-robin hurts for keeping threads hot.
+  for i in 0..high(p.actors):
+    if p.actors[i].i.ready:
+      p.actors[i].i.send(t)
+      return
+  # no thread ready :-( --> send message to the thread which has the least
+  # messages pending:
+  var minIdx = -1
+  var minVal = high(int)
+  for i in 0..high(p.actors):
+    var curr = p.actors[i].i.peek
+    if curr == 0:
+      # ok, is ready now:
+      p.actors[i].i.send(t)
+      return
+    if curr < minVal and curr >= 0:
+      minVal = curr
+      minIdx = i
+  if minIdx >= 0:
+    p.actors[minIdx].i.send(t)
+  else:
+    raise newException(DeadThreadError, "cannot send message; thread died")
+
+proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
+                       action: proc (input: In): Out {.thread.}
+                       ): ptr Channel[Out] =
+  ## uses the actor pool to run ``action(input)`` concurrently.
+  ## `spawn` is guaranteed to not block.
+  var t: Task[In, Out]
+  setupTask()
+  result = addr(p.outputs)
+  t.receiver = result
+  schedule()
+
+proc spawn*[In](p: var ActorPool[In, void], input: In,
+                 action: proc (input: In) {.thread.}) =
+  ## uses the actor pool to run ``action(input)`` concurrently.
+  ## `spawn` is guaranteed to not block.
+  var t: Task[In, void]
+  setupTask()
+  schedule()
+
+when not defined(testing) and isMainModule:
+  var
+    a: ActorPool[int, void]
+  createActorPool(a)
+  for i in 0 .. < 300:
+    a.spawn(i, proc (x: int) {.thread.} = echo x)
+
+  when false:
+    proc treeDepth(n: PNode): int {.thread.} =
+      var x = a.spawn(treeDepth, n.le)
+      var y = a.spawn(treeDepth, n.ri)
+      result = max(^x, ^y) + 1
+
+  a.join()
+
+
diff --git a/lib/deprecated/pure/actors.nim.cfg b/lib/deprecated/pure/actors.nim.cfg
new file mode 100644
index 000000000..c6bb9c545
--- /dev/null
+++ b/lib/deprecated/pure/actors.nim.cfg
@@ -0,0 +1,3 @@
+# to shut up the tester:
+--threads:on
+
diff --git a/lib/deprecated/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim
new file mode 100644
index 000000000..5fd45b215
--- /dev/null
+++ b/lib/deprecated/pure/asyncio.nim
@@ -0,0 +1,712 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+include "system/inclrtl"
+
+import sockets, os
+
+##
+## **Warning:** This module is deprecated since version 0.10.2.
+## Use the brand new `asyncdispatch <asyncdispatch.html>`_ module together
+## with the `asyncnet <asyncnet.html>`_ module.
+
+## This module implements an asynchronous event loop together with asynchronous
+## sockets which use this event loop.
+## It is akin to Python's asyncore module. Many modules that use sockets
+## have an implementation for this module, those modules should all have a
+## ``register`` function which you should use to add the desired objects to a
+## dispatcher which you created so
+## that you can receive the events associated with that module's object.
+##
+## Once everything is registered in a dispatcher, you need to call the ``poll``
+## function in a while loop.
+##
+## **Note:** Most modules have tasks which need to be ran regularly, this is
+## why you should not call ``poll`` with a infinite timeout, or even a
+## very long one. In most cases the default timeout is fine.
+##
+## **Note:** This module currently only supports select(), this is limited by
+## FD_SETSIZE, which is usually 1024. So you may only be able to use 1024
+## sockets at a time.
+##
+## Most (if not all) modules that use asyncio provide a userArg which is passed
+## on with the events. The type that you set userArg to must be inheriting from
+## ``RootObj``!
+##
+## **Note:** If you want to provide async ability to your module please do not
+## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
+## that in the future this type's fields will not be exported therefore breaking
+## your code.
+##
+## **Warning:** The API of this module is unstable, and therefore is subject
+## to change.
+##
+## Asynchronous sockets
+## ====================
+##
+## For most purposes you do not need to worry about the ``Delegate`` type. The
+## ``AsyncSocket`` is what you are after. It's a reference to
+## the ``AsyncSocketObj`` object. This object defines events which you should
+## overwrite by your own procedures.
+##
+## For server sockets the only event you need to worry about is the ``handleAccept``
+## event, in your handleAccept proc you should call ``accept`` on the server
+## socket which will give you the client which is connecting. You should then
+## set any events that you want to use on that client and add it to your dispatcher
+## using the ``register`` procedure.
+##
+## An example ``handleAccept`` follows:
+##
+## .. code-block:: nim
+##
+##    var disp = newDispatcher()
+##    ...
+##    proc handleAccept(s: AsyncSocket) =
+##      echo("Accepted client.")
+##      var client: AsyncSocket
+##      new(client)
+##      s.accept(client)
+##      client.handleRead = ...
+##      disp.register(client)
+##    ...
+##
+## For client sockets you should only be interested in the ``handleRead`` and
+## ``handleConnect`` events. The former gets called whenever the socket has
+## received messages and can be read from and the latter gets called whenever
+## the socket has established a connection to a server socket; from that point
+## it can be safely written to.
+##
+## Getting a blocking client from an AsyncSocket
+## =============================================
+##
+## If you need a asynchronous server socket but you wish to process the clients
+## synchronously then you can use the ``getSocket`` converter to get
+## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
+## with ``accept`` like so:
+##
+## .. code-block:: nim
+##
+##    proc handleAccept(s: AsyncSocket) =
+##      var client: Socket
+##      getSocket(s).accept(client)
+
+{.deprecated.}
+
+when defined(windows):
+  from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
+    FD_ISSET, select
+else:
+  from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
+    FD_ISSET, select
+
+type
+  DelegateObj* = object
+    fd*: SocketHandle
+    deleVal*: RootRef
+
+    handleRead*: proc (h: RootRef) {.nimcall, gcsafe.}
+    handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.}
+    handleError*: proc (h: RootRef) {.nimcall, gcsafe.}
+    hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.}
+
+    open*: bool
+    task*: proc (h: RootRef) {.nimcall, gcsafe.}
+    mode*: FileMode
+
+  Delegate* = ref DelegateObj
+
+  Dispatcher* = ref DispatcherObj
+  DispatcherObj = object
+    delegates: seq[Delegate]
+
+  AsyncSocket* = ref AsyncSocketObj
+  AsyncSocketObj* = object of RootObj
+    socket: Socket
+    info: SocketStatus
+
+    handleRead*: proc (s: AsyncSocket) {.closure, gcsafe.}
+    handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.}
+    handleConnect*: proc (s:  AsyncSocket) {.closure, gcsafe.}
+
+    handleAccept*: proc (s:  AsyncSocket) {.closure, gcsafe.}
+
+    handleTask*: proc (s: AsyncSocket) {.closure, gcsafe.}
+
+    lineBuffer: TaintedString ## Temporary storage for ``readLine``
+    sendBuffer: string ## Temporary storage for ``send``
+    sslNeedAccept: bool
+    proto: Protocol
+    deleg: Delegate
+
+  SocketStatus* = enum
+    SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
+    SockUDPBound
+
+{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
+  TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
+  TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
+  ].}
+
+
+proc newDelegate*(): Delegate =
+  ## Creates a new delegate.
+  new(result)
+  result.handleRead = (proc (h: RootRef) = discard)
+  result.handleWrite = (proc (h: RootRef) = discard)
+  result.handleError = (proc (h: RootRef) = discard)
+  result.hasDataBuffered = (proc (h: RootRef): bool = return false)
+  result.task = (proc (h: RootRef) = discard)
+  result.mode = fmRead
+
+proc newAsyncSocket(): AsyncSocket =
+  new(result)
+  result.info = SockIdle
+
+  result.handleRead = (proc (s: AsyncSocket) = discard)
+  result.handleWrite = nil
+  result.handleConnect = (proc (s: AsyncSocket) = discard)
+  result.handleAccept = (proc (s: AsyncSocket) = discard)
+  result.handleTask = (proc (s: AsyncSocket) = discard)
+
+  result.lineBuffer = "".TaintedString
+  result.sendBuffer = ""
+
+proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
+                  protocol: Protocol = IPPROTO_TCP,
+                  buffered = true): AsyncSocket =
+  ## Initialises an AsyncSocket object. If a socket cannot be initialised
+  ## EOS is raised.
+  result = newAsyncSocket()
+  result.socket = socket(domain, typ, protocol, buffered)
+  result.proto = protocol
+  if result.socket == invalidSocket: raiseOSError(osLastError())
+  result.socket.setBlocking(false)
+
+proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
+  ## Wraps an already initialized ``Socket`` into a AsyncSocket.
+  ## This is useful if you want to use an already connected Socket as an
+  ## asynchronous AsyncSocket in asyncio's event loop.
+  ##
+  ## ``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
+  ## connected. Please note this is only applicable to TCP client sockets, if
+  ## ``sock`` is a different type of socket ``state`` needs to be adjusted!!!
+  ##
+  ## ================  ================================================================
+  ## Value             Meaning
+  ## ================  ================================================================
+  ##  SockIdle          Socket has only just been initialised, not connected or closed.
+  ##  SockConnected     Socket is connected to a server.
+  ##  SockConnecting    Socket is in the process of connecting to a server.
+  ##  SockListening     Socket is a server socket and is listening for connections.
+  ##  SockClosed        Socket has been closed.
+  ##  SockUDPBound      Socket is a UDP socket which is listening for data.
+  ## ================  ================================================================
+  ##
+  ## **Warning**: If ``state`` is set incorrectly the resulting ``AsyncSocket``
+  ## object may not work properly.
+  ##
+  ## **Note**: This will set ``sock`` to be non-blocking.
+  result = newAsyncSocket()
+  result.socket = sock
+  result.proto = if state == SockUDPBound: IPPROTO_UDP else: IPPROTO_TCP
+  result.socket.setBlocking(false)
+  result.info = state
+
+proc asyncSockHandleRead(h: RootRef) =
+  when defined(ssl):
+    if AsyncSocket(h).socket.isSSL and not
+         AsyncSocket(h).socket.gotHandshake:
+      return
+
+  if AsyncSocket(h).info != SockListening:
+    if AsyncSocket(h).info != SockConnecting:
+      AsyncSocket(h).handleRead(AsyncSocket(h))
+  else:
+    AsyncSocket(h).handleAccept(AsyncSocket(h))
+
+proc close*(sock: AsyncSocket) {.gcsafe.}
+proc asyncSockHandleWrite(h: RootRef) =
+  when defined(ssl):
+    if AsyncSocket(h).socket.isSSL and not
+         AsyncSocket(h).socket.gotHandshake:
+      return
+
+  if AsyncSocket(h).info == SockConnecting:
+    AsyncSocket(h).handleConnect(AsyncSocket(h))
+    AsyncSocket(h).info = SockConnected
+    # Stop receiving write events if there is no handleWrite event.
+    if AsyncSocket(h).handleWrite == nil:
+      AsyncSocket(h).deleg.mode = fmRead
+    else:
+      AsyncSocket(h).deleg.mode = fmReadWrite
+  else:
+    if AsyncSocket(h).sendBuffer != "":
+      let sock = AsyncSocket(h)
+      try:
+        let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
+        if bytesSent == 0:
+          # Apparently the socket cannot be written to. Even though select
+          # just told us that it can be... This used to be an assert. Just
+          # do nothing instead.
+          discard
+        elif bytesSent != sock.sendBuffer.len:
+          sock.sendBuffer = sock.sendBuffer[bytesSent .. ^1]
+        elif bytesSent == sock.sendBuffer.len:
+          sock.sendBuffer = ""
+
+        if AsyncSocket(h).handleWrite != nil:
+          AsyncSocket(h).handleWrite(AsyncSocket(h))
+      except OSError:
+        # Most likely the socket closed before the full buffer could be sent to it.
+        sock.close() # TODO: Provide a handleError for users?
+    else:
+      if AsyncSocket(h).handleWrite != nil:
+        AsyncSocket(h).handleWrite(AsyncSocket(h))
+      else:
+        AsyncSocket(h).deleg.mode = fmRead
+
+when defined(ssl):
+  proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
+    if AsyncSocket(h).socket.isSSL and not
+         AsyncSocket(h).socket.gotHandshake:
+      if AsyncSocket(h).sslNeedAccept:
+        var d = ""
+        let ret = AsyncSocket(h).socket.acceptAddrSSL(AsyncSocket(h).socket, d)
+        assert ret != AcceptNoClient
+        if ret == AcceptSuccess:
+          AsyncSocket(h).info = SockConnected
+      else:
+        # handshake will set socket's ``sslNoHandshake`` field.
+        discard AsyncSocket(h).socket.handshake()
+
+
+proc asyncSockTask(h: RootRef) =
+  when defined(ssl):
+    h.asyncSockDoHandshake()
+
+  AsyncSocket(h).handleTask(AsyncSocket(h))
+
+proc toDelegate(sock: AsyncSocket): Delegate =
+  result = newDelegate()
+  result.deleVal = sock
+  result.fd = getFD(sock.socket)
+  # We need this to get write events, just to know when the socket connects.
+  result.mode = fmReadWrite
+  result.handleRead = asyncSockHandleRead
+  result.handleWrite = asyncSockHandleWrite
+  result.task = asyncSockTask
+  # TODO: Errors?
+  #result.handleError = (proc (h: PObject) = assert(false))
+
+  result.hasDataBuffered =
+    proc (h: RootRef): bool {.nimcall.} =
+      return AsyncSocket(h).socket.hasDataBuffered()
+
+  sock.deleg = result
+  if sock.info notin {SockIdle, SockClosed}:
+    sock.deleg.open = true
+  else:
+    sock.deleg.open = false
+
+proc connect*(sock: AsyncSocket, name: string, port = Port(0),
+                   af: Domain = AF_INET) =
+  ## Begins connecting ``sock`` to ``name``:``port``.
+  sock.socket.connectAsync(name, port, af)
+  sock.info = SockConnecting
+  if sock.deleg != nil:
+    sock.deleg.open = true
+
+proc close*(sock: AsyncSocket) =
+  ## Closes ``sock``. Terminates any current connections.
+  sock.socket.close()
+  sock.info = SockClosed
+  if sock.deleg != nil:
+    sock.deleg.open = false
+
+proc bindAddr*(sock: AsyncSocket, port = Port(0), address = "") =
+  ## Equivalent to ``sockets.bindAddr``.
+  sock.socket.bindAddr(port, address)
+  if sock.proto == IPPROTO_UDP:
+    sock.info = SockUDPBound
+    if sock.deleg != nil:
+      sock.deleg.open = true
+
+proc listen*(sock: AsyncSocket) =
+  ## Equivalent to ``sockets.listen``.
+  sock.socket.listen()
+  sock.info = SockListening
+  if sock.deleg != nil:
+    sock.deleg.open = true
+
+proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket,
+                 address: var string) =
+  ## Equivalent to ``sockets.acceptAddr``. This procedure should be called in
+  ## a ``handleAccept`` event handler **only** once.
+  ##
+  ## **Note**: ``client`` needs to be initialised.
+  assert(client != nil)
+  client = newAsyncSocket()
+  var c: Socket
+  new(c)
+  when defined(ssl):
+    if server.socket.isSSL:
+      var ret = server.socket.acceptAddrSSL(c, address)
+      # The following shouldn't happen because when this function is called
+      # it is guaranteed that there is a client waiting.
+      # (This should be called in handleAccept)
+      assert(ret != AcceptNoClient)
+      if ret == AcceptNoHandshake:
+        client.sslNeedAccept = true
+      else:
+        client.sslNeedAccept = false
+        client.info = SockConnected
+    else:
+      server.socket.acceptAddr(c, address)
+      client.sslNeedAccept = false
+      client.info = SockConnected
+  else:
+    server.socket.acceptAddr(c, address)
+    client.sslNeedAccept = false
+    client.info = SockConnected
+
+  if c == invalidSocket: raiseSocketError(server.socket)
+  c.setBlocking(false) # TODO: Needs to be tested.
+
+  # deleg.open is set in ``toDelegate``.
+
+  client.socket = c
+  client.lineBuffer = "".TaintedString
+  client.sendBuffer = ""
+  client.info = SockConnected
+
+proc accept*(server: AsyncSocket, client: var AsyncSocket) =
+  ## Equivalent to ``sockets.accept``.
+  var dummyAddr = ""
+  server.acceptAddr(client, dummyAddr)
+
+proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket,
+                                              address: string] {.deprecated.} =
+  ## Equivalent to ``sockets.acceptAddr``.
+  ##
+  ## **Deprecated since version 0.9.0:** Please use the function above.
+  var client = newAsyncSocket()
+  var address: string = ""
+  acceptAddr(server, client, address)
+  return (client, address)
+
+proc accept*(server: AsyncSocket): AsyncSocket {.deprecated.} =
+  ## Equivalent to ``sockets.accept``.
+  ##
+  ## **Deprecated since version 0.9.0:** Please use the function above.
+  new(result)
+  var address = ""
+  server.acceptAddr(result, address)
+
+proc newDispatcher*(): Dispatcher =
+  new(result)
+  result.delegates = @[]
+
+proc register*(d: Dispatcher, deleg: Delegate) =
+  ## Registers delegate ``deleg`` with dispatcher ``d``.
+  d.delegates.add(deleg)
+
+proc register*(d: Dispatcher, sock: AsyncSocket): Delegate {.discardable.} =
+  ## Registers async socket ``sock`` with dispatcher ``d``.
+  result = sock.toDelegate()
+  d.register(result)
+
+proc unregister*(d: Dispatcher, deleg: Delegate) =
+  ## Unregisters deleg ``deleg`` from dispatcher ``d``.
+  for i in 0..len(d.delegates)-1:
+    if d.delegates[i] == deleg:
+      d.delegates.del(i)
+      return
+  raise newException(IndexError, "Could not find delegate.")
+
+proc isWriteable*(s: AsyncSocket): bool =
+  ## Determines whether socket ``s`` is ready to be written to.
+  var writeSock = @[s.socket]
+  return selectWrite(writeSock, 1) != 0 and s.socket notin writeSock
+
+converter getSocket*(s: AsyncSocket): Socket =
+  return s.socket
+
+proc isConnected*(s: AsyncSocket): bool =
+  ## Determines whether ``s`` is connected.
+  return s.info == SockConnected
+proc isListening*(s: AsyncSocket): bool =
+  ## Determines whether ``s`` is listening for incoming connections.
+  return s.info == SockListening
+proc isConnecting*(s: AsyncSocket): bool =
+  ## Determines whether ``s`` is connecting.
+  return s.info == SockConnecting
+proc isClosed*(s: AsyncSocket): bool =
+  ## Determines whether ``s`` has been closed.
+  return s.info == SockClosed
+proc isSendDataBuffered*(s: AsyncSocket): bool =
+  ## Determines whether ``s`` has data waiting to be sent, i.e. whether this
+  ## socket's sendBuffer contains data.
+  return s.sendBuffer.len != 0
+
+proc setHandleWrite*(s: AsyncSocket,
+    handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.}) =
+  ## Setter for the ``handleWrite`` event.
+  ##
+  ## To remove this event you should use the ``delHandleWrite`` function.
+  ## It is advised to use that function instead of just setting the event to
+  ## ``proc (s: AsyncSocket) = nil`` as that would mean that that function
+  ## would be called constantly.
+  s.deleg.mode = fmReadWrite
+  s.handleWrite = handleWrite
+
+proc delHandleWrite*(s: AsyncSocket) =
+  ## Removes the ``handleWrite`` event handler on ``s``.
+  s.handleWrite = nil
+
+{.push warning[deprecated]: off.}
+proc recvLine*(s: AsyncSocket, line: var TaintedString): bool {.deprecated.} =
+  ## Behaves similar to ``sockets.recvLine``, however it handles non-blocking
+  ## sockets properly. This function guarantees that ``line`` is a full line,
+  ## if this function can only retrieve some data; it will save this data and
+  ## add it to the result when a full line is retrieved.
+  ##
+  ## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL
+  ## exception if an error occurs.
+  ##
+  ## **Deprecated since version 0.9.2**: This function has been deprecated in
+  ## favour of readLine.
+  setLen(line.string, 0)
+  var dataReceived = "".TaintedString
+  var ret = s.socket.recvLineAsync(dataReceived)
+  case ret
+  of RecvFullLine:
+    if s.lineBuffer.len > 0:
+      string(line).add(s.lineBuffer.string)
+      setLen(s.lineBuffer.string, 0)
+    string(line).add(dataReceived.string)
+    if string(line) == "":
+      line = "\c\L".TaintedString
+    result = true
+  of RecvPartialLine:
+    string(s.lineBuffer).add(dataReceived.string)
+    result = false
+  of RecvDisconnected:
+    result = true
+  of RecvFail:
+    s.raiseSocketError(async = true)
+    result = false
+{.pop.}
+
+proc readLine*(s: AsyncSocket, line: var TaintedString): bool =
+  ## Behaves similar to ``sockets.readLine``, however it handles non-blocking
+  ## sockets properly. This function guarantees that ``line`` is a full line,
+  ## if this function can only retrieve some data; it will save this data and
+  ## add it to the result when a full line is retrieved, when this happens
+  ## False will be returned. True will only be returned if a full line has been
+  ## retrieved or the socket has been disconnected in which case ``line`` will
+  ## be set to "".
+  ##
+  ## This function will raise an EOS exception when a socket error occurs.
+  setLen(line.string, 0)
+  var dataReceived = "".TaintedString
+  var ret = s.socket.readLineAsync(dataReceived)
+  case ret
+  of ReadFullLine:
+    if s.lineBuffer.len > 0:
+      string(line).add(s.lineBuffer.string)
+      setLen(s.lineBuffer.string, 0)
+    string(line).add(dataReceived.string)
+    if string(line) == "":
+      line = "\c\L".TaintedString
+    result = true
+  of ReadPartialLine:
+    string(s.lineBuffer).add(dataReceived.string)
+    result = false
+  of ReadNone:
+    result = false
+  of ReadDisconnected:
+    result = true
+
+proc send*(sock: AsyncSocket, data: string) =
+  ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation
+  ## of ``sockets.sendAsync``.
+  ##
+  ## If ``data`` cannot be sent immediately it will be buffered and sent
+  ## when ``sock`` becomes writeable (during the ``handleWrite`` event).
+  ## It's possible that only a part of ``data`` will be sent immediately, while
+  ## the rest of it will be buffered and sent later.
+  if sock.sendBuffer.len != 0:
+    sock.sendBuffer.add(data)
+    return
+  let bytesSent = sock.socket.sendAsync(data)
+  assert bytesSent >= 0
+  if bytesSent == 0:
+    sock.sendBuffer.add(data)
+    sock.deleg.mode = fmReadWrite
+  elif bytesSent != data.len:
+    sock.sendBuffer.add(data[bytesSent .. ^1])
+    sock.deleg.mode = fmReadWrite
+
+proc timeValFromMilliseconds(timeout = 500): Timeval =
+  if timeout != -1:
+    var seconds = timeout div 1000
+    result.tv_sec = seconds.int32
+    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+
+proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
+  FD_ZERO(fd)
+  for i in items(s):
+    m = max(m, int(i.fd))
+    FD_SET(i.fd, fd)
+
+proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
+  var i = 0
+  var L = s.len
+  while i < L:
+    if FD_ISSET(s[i].fd, fd) != 0'i32:
+      s[i] = s[L-1]
+      dec(L)
+    else:
+      inc(i)
+  setLen(s, L)
+
+proc select(readfds, writefds, exceptfds: var seq[Delegate],
+             timeout = 500): int =
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+
+  var rd, wr, ex: TFdSet
+  var m = 0
+  createFdSet(rd, readfds, m)
+  createFdSet(wr, writefds, m)
+  createFdSet(ex, exceptfds, m)
+
+  if timeout != -1:
+    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
+  else:
+    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
+
+  pruneSocketSet(readfds, (rd))
+  pruneSocketSet(writefds, (wr))
+  pruneSocketSet(exceptfds, (ex))
+
+proc poll*(d: Dispatcher, timeout: int = 500): bool =
+  ## This function checks for events on all the delegates in the `PDispatcher`.
+  ## It then proceeds to call the correct event handler.
+  ##
+  ## This function returns ``True`` if there are file descriptors that are still
+  ## open, otherwise ``False``. File descriptors that have been
+  ## closed are immediately removed from the dispatcher automatically.
+  ##
+  ## **Note:** Each delegate has a task associated with it. This gets called
+  ## after each select() call, if you set timeout to ``-1`` the tasks will
+  ## only be executed after one or more file descriptors becomes readable or
+  ## writeable.
+  result = true
+  var readDg, writeDg, errorDg: seq[Delegate] = @[]
+  var len = d.delegates.len
+  var dc = 0
+
+  while dc < len:
+    let deleg = d.delegates[dc]
+    if (deleg.mode != fmWrite or deleg.mode != fmAppend) and deleg.open:
+      readDg.add(deleg)
+    if (deleg.mode != fmRead) and deleg.open:
+      writeDg.add(deleg)
+    if deleg.open:
+      errorDg.add(deleg)
+      inc dc
+    else:
+      # File/socket has been closed. Remove it from dispatcher.
+      d.delegates[dc] = d.delegates[len-1]
+      dec len
+
+  d.delegates.setLen(len)
+
+  var hasDataBufferedCount = 0
+  for d in d.delegates:
+    if d.hasDataBuffered(d.deleVal):
+      hasDataBufferedCount.inc()
+      d.handleRead(d.deleVal)
+  if hasDataBufferedCount > 0: return true
+
+  if readDg.len() == 0 and writeDg.len() == 0:
+    ## TODO: Perhaps this shouldn't return if errorDg has something?
+    return false
+
+  if select(readDg, writeDg, errorDg, timeout) != 0:
+    for i in 0..len(d.delegates)-1:
+      if i > len(d.delegates)-1: break # One delegate might've been removed.
+      let deleg = d.delegates[i]
+      if not deleg.open: continue # This delegate might've been closed.
+      if (deleg.mode != fmWrite or deleg.mode != fmAppend) and
+          deleg notin readDg:
+        deleg.handleRead(deleg.deleVal)
+      if (deleg.mode != fmRead) and deleg notin writeDg:
+        deleg.handleWrite(deleg.deleVal)
+      if deleg notin errorDg:
+        deleg.handleError(deleg.deleVal)
+
+  # Execute tasks
+  for i in items(d.delegates):
+    i.task(i.deleVal)
+
+proc len*(disp: Dispatcher): int =
+  ## Retrieves the amount of delegates in ``disp``.
+  return disp.delegates.len
+
+when not defined(testing) and isMainModule:
+
+  proc testConnect(s: AsyncSocket, no: int) =
+    echo("Connected! " & $no)
+
+  proc testRead(s: AsyncSocket, no: int) =
+    echo("Reading! " & $no)
+    var data = ""
+    if not s.readLine(data): return
+    if data == "":
+      echo("Closing connection. " & $no)
+      s.close()
+    echo(data)
+    echo("Finished reading! " & $no)
+
+  proc testAccept(s: AsyncSocket, disp: Dispatcher, no: int) =
+    echo("Accepting client! " & $no)
+    var client: AsyncSocket
+    new(client)
+    var address = ""
+    s.acceptAddr(client, address)
+    echo("Accepted ", address)
+    client.handleRead =
+      proc (s: AsyncSocket) =
+        testRead(s, 2)
+    disp.register(client)
+
+  proc main =
+    var d = newDispatcher()
+
+    var s = asyncSocket()
+    s.connect("amber.tenthbit.net", Port(6667))
+    s.handleConnect =
+      proc (s: AsyncSocket) =
+        testConnect(s, 1)
+    s.handleRead =
+      proc (s: AsyncSocket) =
+        testRead(s, 1)
+    d.register(s)
+
+    var server = asyncSocket()
+    server.handleAccept =
+      proc (s: AsyncSocket) =
+        testAccept(s, d, 78)
+    server.bindAddr(Port(5555))
+    server.listen()
+    d.register(server)
+
+    while d.poll(-1): discard
+  main()
diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim
new file mode 100644
index 000000000..229fe4b51
--- /dev/null
+++ b/lib/deprecated/pure/ftpclient.nim
@@ -0,0 +1,675 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Dominik Picheta
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+include "system/inclrtl"
+
+import sockets, strutils, parseutils, times, os, asyncio
+
+from asyncnet import nil
+from rawsockets import nil
+from asyncdispatch import PFuture
+## **Note**: This module is deprecated since version 0.11.3.
+## You should use the async version of this module
+## `asyncftpclient <asyncftpclient.html>`_.
+##
+## ----
+##
+## This module **partially** implements an FTP client as specified
+## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
+##
+## This module provides both a synchronous and asynchronous implementation.
+## The asynchronous implementation requires you to use the ``asyncFTPClient``
+## function. You are then required to register the ``AsyncFTPClient`` with a
+## asyncio dispatcher using the ``register`` function. Take a look at the
+## asyncio module documentation for more information.
+##
+## **Note**: The asynchronous implementation is only asynchronous for long
+## file transfers, calls to functions which use the command socket will block.
+##
+## Here is some example usage of this module:
+##
+## .. code-block:: Nim
+##    var ftp = ftpClient("example.org", user = "user", pass = "pass")
+##    ftp.connect()
+##    ftp.retrFile("file.ext", "file.ext")
+##
+## **Warning:** The API of this module is unstable, and therefore is subject
+## to change.
+
+{.deprecated.}
+
+type
+  FtpBase*[SockType] = ref FtpBaseObj[SockType]
+  FtpBaseObj*[SockType] = object
+    csock*: SockType
+    dsock*: SockType
+    when SockType is asyncio.AsyncSocket:
+      handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
+      disp: Dispatcher
+      asyncDSockID: Delegate
+    user*, pass*: string
+    address*: string
+    when SockType is asyncnet.AsyncSocket:
+      port*: rawsockets.Port
+    else:
+      port*: Port
+
+    jobInProgress*: bool
+    job*: FTPJob[SockType]
+
+    dsockConnected*: bool
+
+  FTPJobType* = enum
+    JRetrText, JRetr, JStore
+
+  FtpJob[T] = ref FtpJobObj[T]
+  FTPJobObj[T] = object
+    prc: proc (ftp: FTPBase[T], async: bool): bool {.nimcall, gcsafe.}
+    case typ*: FTPJobType
+    of JRetrText:
+      lines: string
+    of JRetr, JStore:
+      file: File
+      filename: string
+      total: BiggestInt # In bytes.
+      progress: BiggestInt # In bytes.
+      oneSecond: BiggestInt # Bytes transferred in one second.
+      lastProgressReport: float # Time
+      toStore: string # Data left to upload (Only used with async)
+    else: nil
+
+  FtpClientObj* = FtpBaseObj[Socket]
+  FtpClient* = ref FtpClientObj
+
+  AsyncFtpClient* = ref AsyncFtpClientObj ## Async alternative to TFTPClient.
+  AsyncFtpClientObj* = FtpBaseObj[asyncio.AsyncSocket]
+
+  FTPEventType* = enum
+    EvTransferProgress, EvLines, EvRetr, EvStore
+
+  FTPEvent* = object ## Event
+    filename*: string
+    case typ*: FTPEventType
+    of EvLines:
+      lines*: string ## Lines that have been transferred.
+    of EvRetr, EvStore: ## Retr/Store operation finished.
+      nil
+    of EvTransferProgress:
+      bytesTotal*: BiggestInt     ## Bytes total.
+      bytesFinished*: BiggestInt  ## Bytes transferred.
+      speed*: BiggestInt          ## Speed in bytes/s
+      currentJob*: FTPJobType     ## The current job being performed.
+
+  ReplyError* = object of IOError
+  FTPError* = object of IOError
+
+{.deprecated: [
+  TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient,
+  TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent,
+  EInvalidReply: ReplyError, EFTP: FTPError
+].}
+
+const multiLineLimit = 10000
+
+proc ftpClient*(address: string, port = Port(21),
+                user, pass = ""): FtpClient =
+  ## Create a ``FtpClient`` object.
+  new(result)
+  result.user = user
+  result.pass = pass
+  result.address = address
+  result.port = port
+
+  result.dsockConnected = false
+  result.csock = socket()
+  if result.csock == invalidSocket: raiseOSError(osLastError())
+
+template blockingOperation(sock: Socket, body: stmt) {.immediate.} =
+  body
+
+template blockingOperation(sock: asyncio.AsyncSocket, body: stmt) {.immediate.} =
+  sock.setBlocking(true)
+  body
+  sock.setBlocking(false)
+
+proc expectReply[T](ftp: FtpBase[T]): TaintedString =
+  result = TaintedString""
+  blockingOperation(ftp.csock):
+    when T is Socket:
+      ftp.csock.readLine(result)
+    else:
+      discard ftp.csock.readLine(result)
+    var count = 0
+    while result[3] == '-':
+      ## Multi-line reply.
+      var line = TaintedString""
+      when T is Socket:
+        ftp.csock.readLine(line)
+      else:
+        discard ftp.csock.readLine(line)
+      result.add("\n" & line)
+      count.inc()
+      if count >= multiLineLimit:
+        raise newException(ReplyError, "Reached maximum multi-line reply count.")
+
+proc send*[T](ftp: FtpBase[T], m: string): TaintedString =
+  ## Send a message to the server, and wait for a primary reply.
+  ## ``\c\L`` is added for you.
+  ##
+  ## **Note:** The server may return multiple lines of coded replies.
+  blockingOperation(ftp.csock):
+    ftp.csock.send(m & "\c\L")
+  return ftp.expectReply()
+
+proc assertReply(received: TaintedString, expected: string) =
+  if not received.string.startsWith(expected):
+    raise newException(ReplyError,
+                       "Expected reply '$1' got: $2" % [
+                       expected, received.string])
+
+proc assertReply(received: TaintedString, expected: varargs[string]) =
+  for i in items(expected):
+    if received.string.startsWith(i): return
+  raise newException(ReplyError,
+                     "Expected reply '$1' got: $2" %
+                     [expected.join("' or '"), received.string])
+
+proc createJob[T](ftp: FtpBase[T],
+               prc: proc (ftp: FtpBase[T], async: bool): bool {.
+                          nimcall,gcsafe.},
+               cmd: FTPJobType) =
+  if ftp.jobInProgress:
+    raise newException(FTPError, "Unable to do two jobs at once.")
+  ftp.jobInProgress = true
+  new(ftp.job)
+  ftp.job.prc = prc
+  ftp.job.typ = cmd
+  case cmd
+  of JRetrText:
+    ftp.job.lines = ""
+  of JRetr, JStore:
+    ftp.job.toStore = ""
+
+proc deleteJob[T](ftp: FtpBase[T]) =
+  assert ftp.jobInProgress
+  ftp.jobInProgress = false
+  case ftp.job.typ
+  of JRetrText:
+    ftp.job.lines = ""
+  of JRetr, JStore:
+    ftp.job.file.close()
+  ftp.dsock.close()
+
+proc handleTask(s: AsyncSocket, ftp: AsyncFTPClient) =
+  if ftp.jobInProgress:
+    if ftp.job.typ in {JRetr, JStore}:
+      if epochTime() - ftp.job.lastProgressReport >= 1.0:
+        var r: FTPEvent
+        ftp.job.lastProgressReport = epochTime()
+        r.typ = EvTransferProgress
+        r.bytesTotal = ftp.job.total
+        r.bytesFinished = ftp.job.progress
+        r.speed = ftp.job.oneSecond
+        r.filename = ftp.job.filename
+        r.currentJob = ftp.job.typ
+        ftp.job.oneSecond = 0
+        ftp.handleEvent(ftp, r)
+
+proc handleWrite(s: AsyncSocket, ftp: AsyncFTPClient) =
+  if ftp.jobInProgress:
+    if ftp.job.typ == JStore:
+      assert (not ftp.job.prc(ftp, true))
+
+proc handleConnect(s: AsyncSocket, ftp: AsyncFTPClient) =
+  ftp.dsockConnected = true
+  assert(ftp.jobInProgress)
+  if ftp.job.typ == JStore:
+    s.setHandleWrite(proc (s: AsyncSocket) = handleWrite(s, ftp))
+  else:
+    s.delHandleWrite()
+
+proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
+  assert ftp.jobInProgress
+  assert ftp.job.typ != JStore
+  # This can never return true, because it shouldn't check for code
+  # 226 from csock.
+  assert(not ftp.job.prc(ftp, true))
+
+proc pasv[T](ftp: FtpBase[T]) =
+  ## Negotiate a data connection.
+  when T is Socket:
+    ftp.dsock = socket()
+    if ftp.dsock == invalidSocket: raiseOSError(osLastError())
+  elif T is AsyncSocket:
+    ftp.dsock = asyncSocket()
+    ftp.dsock.handleRead =
+      proc (s: AsyncSocket) =
+        handleRead(s, ftp)
+    ftp.dsock.handleConnect =
+      proc (s: AsyncSocket) =
+        handleConnect(s, ftp)
+    ftp.dsock.handleTask =
+      proc (s: AsyncSocket) =
+        handleTask(s, ftp)
+    ftp.disp.register(ftp.dsock)
+  else:
+    {.fatal: "Incorrect socket instantiation".}
+
+  var pasvMsg = ftp.send("PASV").string.strip.TaintedString
+  assertReply(pasvMsg, "227")
+  var betweenParens = captureBetween(pasvMsg.string, '(', ')')
+  var nums = betweenParens.split(',')
+  var ip = nums[0.. ^3]
+  var port = nums[^2.. ^1]
+  var properPort = port[0].parseInt()*256+port[1].parseInt()
+  ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
+  when T is AsyncSocket:
+    ftp.dsockConnected = false
+  else:
+    ftp.dsockConnected = true
+
+proc normalizePathSep(path: string): string =
+  return replace(path, '\\', '/')
+
+proc connect*[T](ftp: FtpBase[T]) =
+  ## Connect to the FTP server specified by ``ftp``.
+  when T is AsyncSocket:
+    blockingOperation(ftp.csock):
+      ftp.csock.connect(ftp.address, ftp.port)
+  elif T is Socket:
+    ftp.csock.connect(ftp.address, ftp.port)
+  else:
+    {.fatal: "Incorrect socket instantiation".}
+
+  var reply = ftp.expectReply()
+  if reply.startsWith("120"):
+    # 120 Service ready in nnn minutes.
+    # We wait until we receive 220.
+    reply = ftp.expectReply()
+
+  # Handle 220 messages from the server
+  assertReply ftp.expectReply(), "220"
+
+  if ftp.user != "":
+    assertReply(ftp.send("USER " & ftp.user), "230", "331")
+
+  if ftp.pass != "":
+    assertReply ftp.send("PASS " & ftp.pass), "230"
+
+proc pwd*[T](ftp: FtpBase[T]): string =
+  ## Returns the current working directory.
+  var wd = ftp.send("PWD")
+  assertReply wd, "257"
+  return wd.string.captureBetween('"') # "
+
+proc cd*[T](ftp: FtpBase[T], dir: string) =
+  ## Changes the current directory on the remote FTP server to ``dir``.
+  assertReply ftp.send("CWD " & dir.normalizePathSep), "250"
+
+proc cdup*[T](ftp: FtpBase[T]) =
+  ## Changes the current directory to the parent of the current directory.
+  assertReply ftp.send("CDUP"), "200"
+
+proc getLines[T](ftp: FtpBase[T], async: bool = false): bool =
+  ## Downloads text data in ASCII mode
+  ## Returns true if the download is complete.
+  ## It doesn't if `async` is true, because it doesn't check for 226 then.
+  if ftp.dsockConnected:
+    var r = TaintedString""
+    when T is AsyncSocket:
+      if ftp.asyncDSock.readLine(r):
+        if r.string == "":
+          ftp.dsockConnected = false
+        else:
+          ftp.job.lines.add(r.string & "\n")
+    elif T is Socket:
+      assert(not async)
+      ftp.dsock.readLine(r)
+      if r.string == "":
+        ftp.dsockConnected = false
+      else:
+        ftp.job.lines.add(r.string & "\n")
+    else:
+      {.fatal: "Incorrect socket instantiation".}
+
+  if not async:
+    var readSocks: seq[Socket] = @[ftp.csock]
+    # This is only needed here. Asyncio gets this socket...
+    blockingOperation(ftp.csock):
+      if readSocks.select(1) != 0 and ftp.csock in readSocks:
+        assertReply ftp.expectReply(), "226"
+        return true
+
+proc listDirs*[T](ftp: FtpBase[T], dir: string = "",
+               async = false): seq[string] =
+  ## Returns a list of filenames in the given directory. If ``dir`` is "",
+  ## the current directory is used. If ``async`` is true, this
+  ## function will return immediately and it will be your job to
+  ## use asyncio's ``poll`` to progress this operation.
+
+  ftp.createJob(getLines[T], JRetrText)
+  ftp.pasv()
+
+  assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
+
+  if not async:
+    while not ftp.job.prc(ftp, false): discard
+    result = splitLines(ftp.job.lines)
+    ftp.deleteJob()
+  else: return @[]
+
+proc fileExists*(ftp: FtpClient, file: string): bool {.deprecated.} =
+  ## **Deprecated since version 0.9.0:** Please use ``existsFile``.
+  ##
+  ## Determines whether ``file`` exists.
+  ##
+  ## Warning: This function may block. Especially on directories with many
+  ## files, because a full list of file names must be retrieved.
+  var files = ftp.listDirs()
+  for f in items(files):
+    if f.normalizePathSep == file.normalizePathSep: return true
+
+proc existsFile*(ftp: FtpClient, file: string): bool =
+  ## Determines whether ``file`` exists.
+  ##
+  ## Warning: This function may block. Especially on directories with many
+  ## files, because a full list of file names must be retrieved.
+  var files = ftp.listDirs()
+  for f in items(files):
+    if f.normalizePathSep == file.normalizePathSep: return true
+
+proc createDir*[T](ftp: FtpBase[T], dir: string, recursive: bool = false) =
+  ## Creates a directory ``dir``. If ``recursive`` is true, the topmost
+  ## subdirectory of ``dir`` will be created first, following the secondmost...
+  ## etc. this allows you to give a full path as the ``dir`` without worrying
+  ## about subdirectories not existing.
+  if not recursive:
+    assertReply ftp.send("MKD " & dir.normalizePathSep), "257"
+  else:
+    var reply = TaintedString""
+    var previousDirs = ""
+    for p in split(dir, {os.DirSep, os.AltSep}):
+      if p != "":
+        previousDirs.add(p)
+        reply = ftp.send("MKD " & previousDirs)
+        previousDirs.add('/')
+    assertReply reply, "257"
+
+proc chmod*[T](ftp: FtpBase[T], path: string,
+            permissions: set[FilePermission]) =
+  ## Changes permission of ``path`` to ``permissions``.
+  var userOctal = 0
+  var groupOctal = 0
+  var otherOctal = 0
+  for i in items(permissions):
+    case i
+    of fpUserExec: userOctal.inc(1)
+    of fpUserWrite: userOctal.inc(2)
+    of fpUserRead: userOctal.inc(4)
+    of fpGroupExec: groupOctal.inc(1)
+    of fpGroupWrite: groupOctal.inc(2)
+    of fpGroupRead: groupOctal.inc(4)
+    of fpOthersExec: otherOctal.inc(1)
+    of fpOthersWrite: otherOctal.inc(2)
+    of fpOthersRead: otherOctal.inc(4)
+
+  var perm = $userOctal & $groupOctal & $otherOctal
+  assertReply ftp.send("SITE CHMOD " & perm &
+                       " " & path.normalizePathSep), "200"
+
+proc list*[T](ftp: FtpBase[T], dir: string = "", async = false): string =
+  ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
+  ## working directory. If ``async`` is true, this function will return
+  ## immediately and it will be your job to call asyncio's
+  ## ``poll`` to progress this operation.
+  ftp.createJob(getLines[T], JRetrText)
+  ftp.pasv()
+
+  assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
+
+  if not async:
+    while not ftp.job.prc(ftp, false): discard
+    result = ftp.job.lines
+    ftp.deleteJob()
+  else:
+    return ""
+
+proc retrText*[T](ftp: FtpBase[T], file: string, async = false): string =
+  ## Retrieves ``file``. File must be ASCII text.
+  ## If ``async`` is true, this function will return immediately and
+  ## it will be your job to call asyncio's ``poll`` to progress this operation.
+  ftp.createJob(getLines[T], JRetrText)
+  ftp.pasv()
+  assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
+
+  if not async:
+    while not ftp.job.prc(ftp, false): discard
+    result = ftp.job.lines
+    ftp.deleteJob()
+  else:
+    return ""
+
+proc getFile[T](ftp: FtpBase[T], async = false): bool =
+  if ftp.dsockConnected:
+    var r = "".TaintedString
+    var bytesRead = 0
+    var returned = false
+    if async:
+      when T is Socket:
+        raise newException(FTPError, "FTPClient must be async.")
+      else:
+        bytesRead = ftp.dsock.recvAsync(r, BufferSize)
+        returned = bytesRead != -1
+    else:
+      bytesRead = ftp.dsock.recv(r, BufferSize)
+      returned = true
+    let r2 = r.string
+    if r2 != "":
+      ftp.job.progress.inc(r2.len)
+      ftp.job.oneSecond.inc(r2.len)
+      ftp.job.file.write(r2)
+    elif returned and r2 == "":
+      ftp.dsockConnected = false
+
+  when T is Socket:
+    if not async:
+      var readSocks: seq[Socket] = @[ftp.csock]
+      blockingOperation(ftp.csock):
+        if readSocks.select(1) != 0 and ftp.csock in readSocks:
+          assertReply ftp.expectReply(), "226"
+          return true
+
+proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) =
+  ## Downloads ``file`` and saves it to ``dest``. Usage of this function
+  ## asynchronously is recommended to view the progress of the download.
+  ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
+  ## when the download is finished, and the ``filename`` field will be equal
+  ## to ``file``.
+  ftp.createJob(getFile[T], JRetr)
+  ftp.job.file = open(dest, mode = fmWrite)
+  ftp.pasv()
+  var reply = ftp.send("RETR " & file.normalizePathSep)
+  assertReply reply, ["125", "150"]
+  if {'(', ')'} notin reply.string:
+    raise newException(ReplyError, "Reply has no file size.")
+  var fileSize: BiggestInt
+  if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
+    raise newException(ReplyError, "Reply has no file size.")
+
+  ftp.job.total = fileSize
+  ftp.job.lastProgressReport = epochTime()
+  ftp.job.filename = file.normalizePathSep
+
+  if not async:
+    while not ftp.job.prc(ftp, false): discard
+    ftp.deleteJob()
+
+proc doUpload[T](ftp: FtpBase[T], async = false): bool =
+  if ftp.dsockConnected:
+    if ftp.job.toStore.len() > 0:
+      assert(async)
+      let bytesSent = ftp.dsock.sendAsync(ftp.job.toStore)
+      if bytesSent == ftp.job.toStore.len:
+        ftp.job.toStore = ""
+      elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
+        ftp.job.toStore = ftp.job.toStore[bytesSent .. ^1]
+      ftp.job.progress.inc(bytesSent)
+      ftp.job.oneSecond.inc(bytesSent)
+    else:
+      var s = newStringOfCap(4000)
+      var len = ftp.job.file.readBuffer(addr(s[0]), 4000)
+      setLen(s, len)
+      if len == 0:
+        # File finished uploading.
+        ftp.dsock.close()
+        ftp.dsockConnected = false
+
+        if not async:
+          assertReply ftp.expectReply(), "226"
+          return true
+        return false
+
+      if not async:
+        ftp.dsock.send(s)
+      else:
+        let bytesSent = ftp.dsock.sendAsync(s)
+        if bytesSent == 0:
+          ftp.job.toStore.add(s)
+        elif bytesSent != s.len:
+          ftp.job.toStore.add(s[bytesSent .. ^1])
+        len = bytesSent
+
+      ftp.job.progress.inc(len)
+      ftp.job.oneSecond.inc(len)
+
+proc store*[T](ftp: FtpBase[T], file, dest: string, async = false) =
+  ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
+  ## function asynchronously is recommended to view the progress of
+  ## the download.
+  ## The ``EvStore`` event is passed to the specified ``handleEvent`` function
+  ## when the upload is finished, and the ``filename`` field will be
+  ## equal to ``file``.
+  ftp.createJob(doUpload[T], JStore)
+  ftp.job.file = open(file)
+  ftp.job.total = ftp.job.file.getFileSize()
+  ftp.job.lastProgressReport = epochTime()
+  ftp.job.filename = file
+  ftp.pasv()
+
+  assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
+
+  if not async:
+    while not ftp.job.prc(ftp, false): discard
+    ftp.deleteJob()
+
+proc close*[T](ftp: FtpBase[T]) =
+  ## Terminates the connection to the server.
+  assertReply ftp.send("QUIT"), "221"
+  if ftp.jobInProgress: ftp.deleteJob()
+  ftp.csock.close()
+  ftp.dsock.close()
+
+proc csockHandleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
+  if ftp.jobInProgress:
+    assertReply ftp.expectReply(), "226" # Make sure the transfer completed.
+    var r: FTPEvent
+    case ftp.job.typ
+    of JRetrText:
+      r.typ = EvLines
+      r.lines = ftp.job.lines
+    of JRetr:
+      r.typ = EvRetr
+      r.filename = ftp.job.filename
+      if ftp.job.progress != ftp.job.total:
+        raise newException(FTPError, "Didn't download full file.")
+    of JStore:
+      r.typ = EvStore
+      r.filename = ftp.job.filename
+      if ftp.job.progress != ftp.job.total:
+        raise newException(FTPError, "Didn't upload full file.")
+    ftp.deleteJob()
+
+    ftp.handleEvent(ftp, r)
+
+proc asyncFTPClient*(address: string, port = Port(21),
+                     user, pass = "",
+    handleEvent: proc (ftp: AsyncFTPClient, ev: FTPEvent) {.closure,gcsafe.} =
+      (proc (ftp: AsyncFTPClient, ev: FTPEvent) = discard)): AsyncFTPClient =
+  ## Create a ``AsyncFTPClient`` object.
+  ##
+  ## Use this if you want to use asyncio's dispatcher.
+  var dres: AsyncFtpClient
+  new(dres)
+  dres.user = user
+  dres.pass = pass
+  dres.address = address
+  dres.port = port
+  dres.dsockConnected = false
+  dres.handleEvent = handleEvent
+  dres.csock = asyncSocket()
+  dres.csock.handleRead =
+    proc (s: AsyncSocket) =
+      csockHandleRead(s, dres)
+  result = dres
+
+proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} =
+  ## Registers ``ftp`` with dispatcher ``d``.
+  ftp.disp = d
+  return ftp.disp.register(ftp.csock)
+
+when not defined(testing) and isMainModule:
+  proc main =
+    var d = newDispatcher()
+    let hev =
+      proc (ftp: AsyncFTPClient, event: FTPEvent) =
+        case event.typ
+        of EvStore:
+          echo("Upload finished!")
+          ftp.retrFile("payload.jpg", "payload2.jpg", async = true)
+        of EvTransferProgress:
+          var time: int64 = -1
+          if event.speed != 0:
+            time = (event.bytesTotal - event.bytesFinished) div event.speed
+          echo(event.currentJob)
+          echo(event.speed div 1000, " kb/s. - ",
+               event.bytesFinished, "/", event.bytesTotal,
+               " - ", time, " seconds")
+          echo(d.len)
+        of EvRetr:
+          echo("Download finished!")
+          ftp.close()
+          echo d.len
+        else: assert(false)
+    var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev)
+
+    d.register(ftp)
+    d.len.echo()
+    ftp.connect()
+    echo "connected"
+    ftp.store("payload.jpg", "payload.jpg", async = true)
+    d.len.echo()
+    echo "uploading..."
+    while true:
+      if not d.poll(): break
+  main()
+
+when not defined(testing) and isMainModule:
+  var ftp = ftpClient("example.com", user = "foo", pass = "bar")
+  ftp.connect()
+  echo ftp.pwd()
+  echo ftp.list()
+  echo("uploading")
+  ftp.store("payload.jpg", "payload.jpg", async = false)
+
+  echo("Upload complete")
+  ftp.retrFile("payload.jpg", "payload2.jpg", async = false)
+
+  echo("Download complete")
+  sleep(5000)
+  ftp.close()
+  sleep(200)
diff --git a/lib/deprecated/pure/parseopt.nim b/lib/deprecated/pure/parseopt.nim
new file mode 100644
index 000000000..218f5ab81
--- /dev/null
+++ b/lib/deprecated/pure/parseopt.nim
@@ -0,0 +1,178 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides the standard Nim command line parser.
+## It supports one convenience iterator over all command line options and some
+## lower-level features.
+##
+## Supported syntax:
+##
+## 1. short options - ``-abcd``, where a, b, c, d are names
+## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
+## 3. argument - everything else
+
+{.push debugger: off.}
+
+include "system/inclrtl"
+
+import
+  os, strutils
+
+type
+  CmdLineKind* = enum         ## the detected command line token
+    cmdEnd,                   ## end of command line reached
+    cmdArgument,              ## argument detected
+    cmdLongOption,            ## a long option ``--option`` detected
+    cmdShortOption            ## a short option ``-c`` detected
+  OptParser* =
+      object of RootObj ## this object implements the command line parser
+    cmd: string
+    pos: int
+    inShortState: bool
+    kind*: CmdLineKind        ## the dected command line token
+    key*, val*: TaintedString ## key and value pair; ``key`` is the option
+                              ## or the argument, ``value`` is not "" if
+                              ## the option was given a value
+
+{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
+
+proc parseWord(s: string, i: int, w: var string,
+               delim: set[char] = {'\x09', ' ', '\0'}): int =
+  result = i
+  if s[result] == '\"':
+    inc(result)
+    while not (s[result] in {'\0', '\"'}):
+      add(w, s[result])
+      inc(result)
+    if s[result] == '\"': inc(result)
+  else:
+    while not (s[result] in delim):
+      add(w, s[result])
+      inc(result)
+
+when declared(os.paramCount):
+  proc quote(s: string): string =
+    if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
+      if s[0] == '-':
+        result = newStringOfCap(s.len)
+        var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='})
+        if s[i] in {':','='}:
+          result.add s[i]
+          inc i
+        result.add '"'
+        while i < s.len:
+          result.add s[i]
+          inc i
+        result.add '"'
+      else:
+        result = '"' & s & '"'
+    else:
+      result = s
+
+  # we cannot provide this for NimRtl creation on Posix, because we can't
+  # access the command line arguments then!
+
+  proc initOptParser*(cmdline = ""): OptParser =
+    ## inits the option parser. If ``cmdline == ""``, the real command line
+    ## (as provided by the ``OS`` module) is taken.
+    result.pos = 0
+    result.inShortState = false
+    if cmdline != "":
+      result.cmd = cmdline
+    else:
+      result.cmd = ""
+      for i in countup(1, paramCount()):
+        result.cmd.add quote(paramStr(i).string)
+        result.cmd.add ' '
+    result.kind = cmdEnd
+    result.key = TaintedString""
+    result.val = TaintedString""
+
+proc handleShortOption(p: var OptParser) =
+  var i = p.pos
+  p.kind = cmdShortOption
+  add(p.key.string, p.cmd[i])
+  inc(i)
+  p.inShortState = true
+  while p.cmd[i] in {'\x09', ' '}:
+    inc(i)
+    p.inShortState = false
+  if p.cmd[i] in {':', '='}:
+    inc(i)
+    p.inShortState = false
+    while p.cmd[i] in {'\x09', ' '}: inc(i)
+    i = parseWord(p.cmd, i, p.val.string)
+  if p.cmd[i] == '\0': p.inShortState = false
+  p.pos = i
+
+proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
+  ## parses the first or next option; ``p.kind`` describes what token has been
+  ## parsed. ``p.key`` and ``p.val`` are set accordingly.
+  var i = p.pos
+  while p.cmd[i] in {'\x09', ' '}: inc(i)
+  p.pos = i
+  setLen(p.key.string, 0)
+  setLen(p.val.string, 0)
+  if p.inShortState:
+    handleShortOption(p)
+    return
+  case p.cmd[i]
+  of '\0':
+    p.kind = cmdEnd
+  of '-':
+    inc(i)
+    if p.cmd[i] == '-':
+      p.kind = cmdLongoption
+      inc(i)
+      i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
+      while p.cmd[i] in {'\x09', ' '}: inc(i)
+      if p.cmd[i] in {':', '='}:
+        inc(i)
+        while p.cmd[i] in {'\x09', ' '}: inc(i)
+        p.pos = parseWord(p.cmd, i, p.val.string)
+      else:
+        p.pos = i
+    else:
+      p.pos = i
+      handleShortOption(p)
+  else:
+    p.kind = cmdArgument
+    p.pos = parseWord(p.cmd, i, p.key.string)
+
+proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
+  ## retrieves the rest of the command line that has not been parsed yet.
+  result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
+
+when declared(initOptParser):
+  iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
+    ## This is an convenience iterator for iterating over the command line.
+    ## This uses the OptParser object. Example:
+    ##
+    ## .. code-block:: nim
+    ##   var
+    ##     filename = ""
+    ##   for kind, key, val in getopt():
+    ##     case kind
+    ##     of cmdArgument:
+    ##       filename = key
+    ##     of cmdLongOption, cmdShortOption:
+    ##       case key
+    ##       of "help", "h": writeHelp()
+    ##       of "version", "v": writeVersion()
+    ##     of cmdEnd: assert(false) # cannot happen
+    ##   if filename == "":
+    ##     # no filename has been given, so we show the help:
+    ##     writeHelp()
+    var p = initOptParser()
+    while true:
+      next(p)
+      if p.kind == cmdEnd: break
+      yield (p.kind, p.key, p.val)
+
+{.pop.}
diff --git a/lib/deprecated/pure/parseurl.nim b/lib/deprecated/pure/parseurl.nim
new file mode 100644
index 000000000..6d58e8a73
--- /dev/null
+++ b/lib/deprecated/pure/parseurl.nim
@@ -0,0 +1,114 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## **Warnings:** This module is deprecated since version 0.10.2.
+## Use the `uri <uri.html>`_ module instead.
+##
+## Parses & constructs URLs.
+
+{.deprecated.}
+
+import strutils
+
+type
+  Url* = tuple[      ## represents a *Uniform Resource Locator* (URL)
+                     ## any optional component is "" if it does not exist
+    scheme, username, password,
+    hostname, port, path, query, anchor: string]
+
+{.deprecated: [TUrl: Url].}
+
+proc parseUrl*(url: string): Url {.deprecated.} =
+  var i = 0
+
+  var scheme, username, password: string = ""
+  var hostname, port, path, query, anchor: string = ""
+
+  var temp = ""
+
+  if url[i] != '/': # url isn't a relative path
+    while true:
+      # Scheme
+      if url[i] == ':':
+        if url[i+1] == '/' and url[i+2] == '/':
+          scheme = temp
+          temp.setLen(0)
+          inc(i, 3) # Skip the //
+      # Authority(username, password)
+      if url[i] == '@':
+        username = temp
+        let colon = username.find(':')
+        if colon >= 0:
+          password = username.substr(colon+1)
+          username = username.substr(0, colon-1)
+        temp.setLen(0)
+        inc(i) #Skip the @
+      # hostname(subdomain, domain, port)
+      if url[i] == '/' or url[i] == '\0':
+        hostname = temp
+        let colon = hostname.find(':')
+        if colon >= 0:
+          port = hostname.substr(colon+1)
+          hostname = hostname.substr(0, colon-1)
+
+        temp.setLen(0)
+        break
+
+      temp.add(url[i])
+      inc(i)
+
+  if url[i] == '/': inc(i) # Skip the '/'
+  # Path
+  while true:
+    if url[i] == '?':
+      path = temp
+      temp.setLen(0)
+    if url[i] == '#':
+      if temp[0] == '?':
+        query = temp
+      else:
+        path = temp
+      temp.setLen(0)
+
+    if url[i] == '\0':
+      if temp[0] == '?':
+        query = temp
+      elif temp[0] == '#':
+        anchor = temp
+      else:
+        path = temp
+      break
+
+    temp.add(url[i])
+    inc(i)
+
+  return (scheme, username, password, hostname, port, path, query, anchor)
+
+proc `$`*(u: Url): string {.deprecated.} =
+  ## turns the URL `u` into its string representation.
+  result = ""
+  if u.scheme.len > 0:
+    result.add(u.scheme)
+    result.add("://")
+  if u.username.len > 0:
+    result.add(u.username)
+    if u.password.len > 0:
+      result.add(":")
+      result.add(u.password)
+    result.add("@")
+  result.add(u.hostname)
+  if u.port.len > 0:
+    result.add(":")
+    result.add(u.port)
+  if u.path.len > 0:
+    result.add("/")
+    result.add(u.path)
+  result.add(u.query)
+  result.add(u.anchor)
+
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
new file mode 100644
index 000000000..8fa69256b
--- /dev/null
+++ b/lib/deprecated/pure/sockets.nim
@@ -0,0 +1,1738 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## **Warning:** Since version 0.10.2 this module is deprecated.
+## Use the `net <net.html>`_ or the
+## `rawsockets <rawsockets.html>`_ module instead.
+##
+## This module implements portable sockets, it supports a mix of different types
+## of sockets. Sockets are buffered by default meaning that data will be
+## received in ``BufferSize`` (4000) sized chunks, buffering
+## behaviour can be disabled by setting the ``buffered`` parameter when calling
+## the ``socket`` function to `false`. Be aware that some functions may not yet
+## support buffered sockets (mainly the recvFrom function).
+##
+## Most procedures raise OSError on error, but some may return ``-1`` or a
+## boolean ``false``.
+##
+## SSL is supported through the OpenSSL library. This support can be activated
+## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
+## raise ESSL exceptions when SSL errors occur.
+##
+## Asynchronous sockets are supported, however a better alternative is to use
+## the `asyncio <asyncio.html>`_ module.
+
+{.deprecated.}
+
+include "system/inclrtl"
+
+{.deadCodeElim: on.}
+
+when hostOS == "solaris":
+  {.passl: "-lsocket -lnsl".}
+
+import os, parseutils
+from times import epochTime
+import unsigned
+
+when defined(ssl):
+  import openssl
+
+when defined(Windows):
+  import winlean
+else:
+  import posix
+
+# Note: The enumerations are mapped to Window's constants.
+
+when defined(ssl):
+
+  type
+    SSLError* = object of Exception
+
+    SSLCVerifyMode* = enum
+      CVerifyNone, CVerifyPeer
+
+    SSLProtVersion* = enum
+      protSSLv2, protSSLv3, protTLSv1, protSSLv23
+
+    SSLContext* = distinct SSLCTX
+
+    SSLAcceptResult* = enum
+      AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
+
+  {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
+     TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
+     TSSLAcceptResult: SSLAcceptResult].}
+
+const
+  BufferSize*: int = 4000 ## size of a buffered socket's buffer
+
+type
+  SocketImpl = object ## socket type
+    fd: SocketHandle
+    case isBuffered: bool # determines whether this socket is buffered.
+    of true:
+      buffer: array[0..BufferSize, char]
+      currPos: int # current index in buffer
+      bufLen: int # current length of buffer
+    of false: nil
+    when defined(ssl):
+      case isSsl: bool
+      of true:
+        sslHandle: SSLPtr
+        sslContext: SSLContext
+        sslNoHandshake: bool # True if needs handshake.
+        sslHasPeekChar: bool
+        sslPeekChar: char
+      of false: nil
+    nonblocking: bool
+
+  Socket* = ref SocketImpl
+
+  Port* = distinct uint16  ## port type
+
+  Domain* = enum    ## domain, which specifies the protocol family of the
+                    ## created socket. Other domains than those that are listed
+                    ## here are unsupported.
+    AF_UNIX,        ## for local socket (using a file). Unsupported on Windows.
+    AF_INET = 2,    ## for network protocol IPv4 or
+    AF_INET6 = 23   ## for network protocol IPv6.
+
+  SockType* = enum     ## second argument to `socket` proc
+    SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
+    SOCK_DGRAM = 2,    ## datagram service or Datagram Sockets
+    SOCK_RAW = 3,      ## raw protocols atop the network layer.
+    SOCK_SEQPACKET = 5 ## reliable sequenced packet service
+
+  Protocol* = enum      ## third argument to `socket` proc
+    IPPROTO_TCP = 6,    ## Transmission control protocol.
+    IPPROTO_UDP = 17,   ## User datagram protocol.
+    IPPROTO_IP,         ## Internet protocol. Unsupported on Windows.
+    IPPROTO_IPV6,       ## Internet Protocol Version 6. Unsupported on Windows.
+    IPPROTO_RAW,        ## Raw IP Packets Protocol. Unsupported on Windows.
+    IPPROTO_ICMP        ## Control message protocol. Unsupported on Windows.
+
+  Servent* = object ## information about a service
+    name*: string
+    aliases*: seq[string]
+    port*: Port
+    proto*: string
+
+  Hostent* = object ## information about a given host
+    name*: string
+    aliases*: seq[string]
+    addrtype*: Domain
+    length*: int
+    addrList*: seq[string]
+
+  SOBool* = enum ## Boolean socket options.
+    OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
+    OptOOBInline, OptReuseAddr
+
+  RecvLineResult* = enum ## result for recvLineAsync
+    RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
+
+  ReadLineResult* = enum ## result for readLineAsync
+    ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
+
+  TimeoutError* = object of Exception
+
+{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
+    TProtocol: Protocol, TServent: Servent, THostent: Hostent,
+    TSOBool: SOBool, TRecvLineResult: RecvLineResult,
+    TReadLineResult: ReadLineResult, ETimeout: TimeoutError,
+    TSocketImpl: SocketImpl].}
+
+when defined(booting):
+  let invalidSocket*: Socket = nil ## invalid socket
+else:
+  const invalidSocket*: Socket = nil ## invalid socket
+
+when defined(windows):
+  let
+    osInvalidSocket = winlean.INVALID_SOCKET
+else:
+  let
+    osInvalidSocket = posix.INVALID_SOCKET
+
+proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
+  if fd == osInvalidSocket:
+    return nil
+  new(result)
+  result.fd = fd
+  result.isBuffered = isBuff
+  if isBuff:
+    result.currPos = 0
+  result.nonblocking = false
+
+proc `==`*(a, b: Port): bool {.borrow.}
+  ## ``==`` for ports.
+
+proc `$`*(p: Port): string {.borrow.}
+  ## returns the port number as a string
+
+proc ntohl*(x: int32): int32 =
+  ## Converts 32-bit integers from network to host byte order.
+  ## On machines where the host byte order is the same as network byte order,
+  ## this is a no-op; otherwise, it performs a 4-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 24'i32) or
+                 (x shr 8'i32 and 0xff00'i32) or
+                 (x shl 8'i32 and 0xff0000'i32) or
+                 (x shl 24'i32)
+
+proc ntohs*(x: int16): int16 =
+  ## Converts 16-bit integers from network to host byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 2-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 8'i16) or (x shl 8'i16)
+
+proc htonl*(x: int32): int32 =
+  ## Converts 32-bit integers from host to network byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 4-byte swap operation.
+  result = sockets.ntohl(x)
+
+proc htons*(x: int16): int16 =
+  ## Converts 16-bit positive integers from host to network byte order.
+  ## On machines where the host byte order is the same as network byte
+  ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
+  result = sockets.ntohs(x)
+
+when defined(Posix):
+  proc toInt(domain: Domain): cint =
+    case domain
+    of AF_UNIX:        result = posix.AF_UNIX
+    of AF_INET:        result = posix.AF_INET
+    of AF_INET6:       result = posix.AF_INET6
+    else: discard
+
+  proc toInt(typ: SockType): cint =
+    case typ
+    of SOCK_STREAM:    result = posix.SOCK_STREAM
+    of SOCK_DGRAM:     result = posix.SOCK_DGRAM
+    of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
+    of SOCK_RAW:       result = posix.SOCK_RAW
+    else: discard
+
+  proc toInt(p: Protocol): cint =
+    case p
+    of IPPROTO_TCP:    result = posix.IPPROTO_TCP
+    of IPPROTO_UDP:    result = posix.IPPROTO_UDP
+    of IPPROTO_IP:     result = posix.IPPROTO_IP
+    of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
+    of IPPROTO_RAW:    result = posix.IPPROTO_RAW
+    of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
+    else: discard
+
+else:
+  proc toInt(domain: Domain): cint =
+    result = toU16(ord(domain))
+
+  proc toInt(typ: SockType): cint =
+    result = cint(ord(typ))
+
+  proc toInt(p: Protocol): cint =
+    result = cint(ord(p))
+
+proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
+             protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
+  ## Creates a new socket; returns `InvalidSocket` if an error occurs.
+
+  # TODO: Perhaps this should just raise EOS when an error occurs.
+  when defined(Windows):
+    result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
+  else:
+    result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)
+
+when defined(ssl):
+  CRYPTO_malloc_init()
+  SslLibraryInit()
+  SslLoadErrorStrings()
+  ErrLoadBioStrings()
+  OpenSSL_add_all_algorithms()
+
+  proc raiseSSLError(s = "") =
+    if s != "":
+      raise newException(SSLError, s)
+    let err = ErrPeekLastError()
+    if err == 0:
+      raise newException(SSLError, "No error reported.")
+    if err == -1:
+      raiseOSError(osLastError())
+    var errStr = ErrErrorString(err, nil)
+    raise newException(SSLError, $errStr)
+
+  # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
+  proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) =
+    if certFile != "" and not existsFile(certFile):
+      raise newException(system.IOError, "Certificate file could not be found: " & certFile)
+    if keyFile != "" and not existsFile(keyFile):
+      raise newException(system.IOError, "Key file could not be found: " & keyFile)
+
+    if certFile != "":
+      var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
+      if ret != 1:
+        raiseSslError()
+
+    # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
+    if keyFile != "":
+      if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
+                                     SSL_FILETYPE_PEM) != 1:
+        raiseSslError()
+
+      if SSL_CTX_check_private_key(ctx) != 1:
+        raiseSslError("Verification of private key file failed.")
+
+  proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
+                   certFile = "", keyFile = ""): SSLContext =
+    ## Creates an SSL context.
+    ##
+    ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
+    ## are available with the addition of ``ProtSSLv23`` which allows for
+    ## compatibility with all of them.
+    ##
+    ## There are currently only two options for verify mode;
+    ## one is ``CVerifyNone`` and with it certificates will not be verified
+    ## the other is ``CVerifyPeer`` and certificates will be verified for
+    ## it, ``CVerifyPeer`` is the safest choice.
+    ##
+    ## The last two parameters specify the certificate file path and the key file
+    ## path, a server socket will most likely not work without these.
+    ## Certificates can be generated using the following command:
+    ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``.
+    var newCTX: SSL_CTX
+    case protVersion
+    of protSSLv23:
+      newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
+    of protSSLv2:
+      raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv3")
+    of protSSLv3:
+      newCTX = SSL_CTX_new(SSLv3_method())
+    of protTLSv1:
+      newCTX = SSL_CTX_new(TLSv1_method())
+
+    if newCTX.SSLCTXSetCipherList("ALL") != 1:
+      raiseSslError()
+    case verifyMode
+    of CVerifyPeer:
+      newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil)
+    of CVerifyNone:
+      newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
+    if newCTX == nil:
+      raiseSslError()
+
+    discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
+    newCTX.loadCertificates(certFile, keyFile)
+    return SSLContext(newCTX)
+
+  proc wrapSocket*(ctx: SSLContext, socket: Socket) =
+    ## Wraps a socket in an SSL context. This function effectively turns
+    ## ``socket`` into an SSL socket.
+    ##
+    ## **Disclaimer**: This code is not well tested, may be very unsafe and
+    ## prone to security vulnerabilities.
+
+    socket.isSSL = true
+    socket.sslContext = ctx
+    socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
+    socket.sslNoHandshake = false
+    socket.sslHasPeekChar = false
+    if socket.sslHandle == nil:
+      raiseSslError()
+
+    if SSLSetFd(socket.sslHandle, socket.fd) != 1:
+      raiseSslError()
+
+proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
+  ## Raises proper errors based on return values of ``recv`` functions.
+  ##
+  ## If ``async`` is ``True`` no error will be thrown in the case when the
+  ## error was caused by no data being available to be read.
+  ##
+  ## If ``err`` is not lower than 0 no exception will be raised.
+  when defined(ssl):
+    if socket.isSSL:
+      if err <= 0:
+        var ret = SSLGetError(socket.sslHandle, err.cint)
+        case ret
+        of SSL_ERROR_ZERO_RETURN:
+          raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+        of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
+          if async:
+            return
+          else: raiseSslError("Not enough data on socket.")
+        of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
+          if async:
+            return
+          else: raiseSslError("Not enough data on socket.")
+        of SSL_ERROR_WANT_X509_LOOKUP:
+          raiseSslError("Function for x509 lookup has been called.")
+        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+          raiseSslError()
+        else: raiseSslError("Unknown Error")
+
+  if err == -1 and not (when defined(ssl): socket.isSSL else: false):
+    let lastError = osLastError()
+    if async:
+      when defined(windows):
+        if lastError.int32 == WSAEWOULDBLOCK:
+          return
+        else: raiseOSError(lastError)
+      else:
+        if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
+          return
+        else: raiseOSError(lastError)
+    else: raiseOSError(lastError)
+
+proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
+  ## Marks ``socket`` as accepting connections.
+  ## ``Backlog`` specifies the maximum length of the
+  ## queue of pending connections.
+  if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
+
+proc invalidIp4(s: string) {.noreturn, noinline.} =
+  raise newException(ValueError, "invalid ip4 address: " & s)
+
+proc parseIp4*(s: string): BiggestInt =
+  ## parses an IP version 4 in dotted decimal form like "a.b.c.d".
+  ##
+  ## This is equivalent to `inet_ntoa`:idx:.
+  ##
+  ## Raises EInvalidValue in case of an error.
+  var a, b, c, d: int
+  var i = 0
+  var j = parseInt(s, a, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, b, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, c, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, d, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] != '\0': invalidIp4(s)
+  result = BiggestInt(a shl 24 or b shl 16 or c shl 8 or d)
+
+template gaiNim(a, p, h, list: expr): stmt =
+  block:
+    var gaiResult = getaddrinfo(a, $p, addr(h), list)
+    if gaiResult != 0'i32:
+      when defined(windows):
+        raiseOSError(osLastError())
+      else:
+        raiseOSError(osLastError(), $gai_strerror(gaiResult))
+
+proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
+  tags: [ReadIOEffect].} =
+  ## binds an address/port number to a socket.
+  ## Use address string in dotted decimal form like "a.b.c.d"
+  ## or leave "" for any address.
+
+  if address == "":
+    var name: Sockaddr_in
+    when defined(Windows):
+      name.sin_family = int16(ord(AF_INET))
+    else:
+      name.sin_family = posix.AF_INET
+    name.sin_port = sockets.htons(int16(port))
+    name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
+    if bindSocket(socket.fd, cast[ptr SockAddr](addr(name)),
+                  sizeof(name).SockLen) < 0'i32:
+      raiseOSError(osLastError())
+  else:
+    var hints: AddrInfo
+    var aiList: ptr AddrInfo = nil
+    hints.ai_family = toInt(AF_INET)
+    hints.ai_socktype = toInt(SOCK_STREAM)
+    hints.ai_protocol = toInt(IPPROTO_TCP)
+    gaiNim(address, port, hints, aiList)
+    if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
+      raiseOSError(osLastError())
+
+proc getSockName*(socket: Socket): Port =
+  ## returns the socket's associated port number.
+  var name: Sockaddr_in
+  when defined(Windows):
+    name.sin_family = int16(ord(AF_INET))
+  else:
+    name.sin_family = posix.AF_INET
+  #name.sin_port = htons(cint16(port))
+  #name.sin_addr.s_addr = htonl(INADDR_ANY)
+  var namelen = sizeof(name).SockLen
+  if getsockname(socket.fd, cast[ptr SockAddr](addr(name)),
+                 addr(namelen)) == -1'i32:
+    raiseOSError(osLastError())
+  result = Port(sockets.ntohs(name.sin_port))
+
+template acceptAddrPlain(noClientRet, successRet: expr,
+                         sslImplementation: stmt): stmt {.immediate.} =
+  assert(client != nil)
+  var sockAddress: Sockaddr_in
+  var addrLen = sizeof(sockAddress).SockLen
+  var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
+                    addr(addrLen))
+
+  if sock == osInvalidSocket:
+    let err = osLastError()
+    when defined(windows):
+      if err.int32 == WSAEINPROGRESS:
+        client = invalidSocket
+        address = ""
+        when noClientRet.int == -1:
+          return
+        else:
+          return noClientRet
+      else: raiseOSError(err)
+    else:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
+        client = invalidSocket
+        address = ""
+        when noClientRet.int == -1:
+          return
+        else:
+          return noClientRet
+      else: raiseOSError(err)
+  else:
+    client.fd = sock
+    client.isBuffered = server.isBuffered
+    sslImplementation
+    # Client socket is set above.
+    address = $inet_ntoa(sockAddress.sin_addr)
+    when successRet.int == -1:
+      return
+    else:
+      return successRet
+
+proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
+  tags: [ReadIOEffect].} =
+  ## 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.
+  ## If ``server`` is non-blocking then this function returns immediately, and
+  ## if there are no connections queued the returned socket will be
+  ## ``InvalidSocket``.
+  ## This function will raise EOS if an error occurs.
+  ##
+  ## The resulting client will inherit any properties of the server socket. For
+  ## example: whether the socket is buffered or not.
+  ##
+  ## **Note**: ``client`` must be initialised (with ``new``), this function
+  ## makes no effort to initialise the ``client`` variable.
+  ##
+  ## **Warning:** When using SSL with non-blocking sockets, it is best to use
+  ## the acceptAddrSSL procedure as this procedure will most likely block.
+  acceptAddrPlain(-1, -1):
+    when defined(ssl):
+      if server.isSSL:
+        # We must wrap the client sock in a ssl context.
+
+        server.sslContext.wrapSocket(client)
+        let ret = SSLAccept(client.sslHandle)
+        while ret <= 0:
+          let err = SSLGetError(client.sslHandle, ret)
+          if err != SSL_ERROR_WANT_ACCEPT:
+            case err
+            of SSL_ERROR_ZERO_RETURN:
+              raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+            of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
+               SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
+              raiseSslError("acceptAddrSSL should be used for non-blocking SSL sockets.")
+            of SSL_ERROR_WANT_X509_LOOKUP:
+              raiseSslError("Function for x509 lookup has been called.")
+            of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+              raiseSslError()
+            else:
+              raiseSslError("Unknown error")
+
+proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.}
+  ## Sets blocking mode on socket
+
+when defined(ssl):
+  proc acceptAddrSSL*(server: Socket, client: var Socket,
+                      address: var string): SSLAcceptResult {.
+                      tags: [ReadIOEffect].} =
+    ## This procedure should only be used for non-blocking **SSL** sockets.
+    ## It will immediately return with one of the following values:
+    ##
+    ## ``AcceptSuccess`` will be returned when a client has been successfully
+    ## accepted and the handshake has been successfully performed between
+    ## ``server`` and the newly connected client.
+    ##
+    ## ``AcceptNoHandshake`` will be returned when a client has been accepted
+    ## but no handshake could be performed. This can happen when the client
+    ## connects but does not yet initiate a handshake. In this case
+    ## ``acceptAddrSSL`` should be called again with the same parameters.
+    ##
+    ## ``AcceptNoClient`` will be returned when no client is currently attempting
+    ## to connect.
+    template doHandshake(): stmt =
+      when defined(ssl):
+        if server.isSSL:
+          client.setBlocking(false)
+          # We must wrap the client sock in a ssl context.
+
+          if not client.isSSL or client.sslHandle == nil:
+            server.sslContext.wrapSocket(client)
+          let ret = SSLAccept(client.sslHandle)
+          while ret <= 0:
+            let err = SSLGetError(client.sslHandle, ret)
+            if err != SSL_ERROR_WANT_ACCEPT:
+              case err
+              of SSL_ERROR_ZERO_RETURN:
+                raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+              of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
+                 SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
+                client.sslNoHandshake = true
+                return AcceptNoHandshake
+              of SSL_ERROR_WANT_X509_LOOKUP:
+                raiseSslError("Function for x509 lookup has been called.")
+              of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+                raiseSslError()
+              else:
+                raiseSslError("Unknown error")
+          client.sslNoHandshake = false
+
+    if client.isSSL and client.sslNoHandshake:
+      doHandshake()
+      return AcceptSuccess
+    else:
+      acceptAddrPlain(AcceptNoClient, AcceptSuccess):
+        doHandshake()
+
+proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} =
+  ## 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.
+
+  var addrDummy = ""
+  acceptAddr(server, client, addrDummy)
+
+proc acceptAddr*(server: Socket): tuple[client: Socket, address: string] {.
+  deprecated, tags: [ReadIOEffect].} =
+  ## Slightly different version of ``acceptAddr``.
+  ##
+  ## **Deprecated since version 0.9.0:** Please use the function above.
+  var client: Socket
+  new(client)
+  var address = ""
+  acceptAddr(server, client, address)
+  return (client, address)
+
+proc accept*(server: Socket): Socket {.deprecated, tags: [ReadIOEffect].} =
+  ## **Deprecated since version 0.9.0:** Please use the function above.
+  new(result)
+  var address = ""
+  acceptAddr(server, result, address)
+
+proc close*(socket: Socket) =
+  ## closes a socket.
+  when defined(windows):
+    discard winlean.closesocket(socket.fd)
+  else:
+    discard posix.close(socket.fd)
+  # TODO: These values should not be discarded. An EOS should be raised.
+  # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
+  when defined(ssl):
+    if socket.isSSL:
+      discard SSLShutdown(socket.sslHandle)
+      SSLFree(socket.sslHandle)
+      socket.sslHandle = nil
+
+proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
+  ## Searches the database from the beginning and finds the first entry for
+  ## which the service name specified by ``name`` matches the s_name member
+  ## and the protocol name specified by ``proto`` matches the s_proto member.
+  ##
+  ## On posix this will search through the ``/etc/services`` file.
+  when defined(Windows):
+    var s = winlean.getservbyname(name, proto)
+  else:
+    var s = posix.getservbyname(name, proto)
+  if s == nil: raiseOSError(osLastError(), "Service not found.")
+  result.name = $s.s_name
+  result.aliases = cstringArrayToSeq(s.s_aliases)
+  result.port = Port(s.s_port)
+  result.proto = $s.s_proto
+
+proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
+  ## Searches the database from the beginning and finds the first entry for
+  ## which the port specified by ``port`` matches the s_port member and the
+  ## protocol name specified by ``proto`` matches the s_proto member.
+  ##
+  ## On posix this will search through the ``/etc/services`` file.
+  when defined(Windows):
+    var s = winlean.getservbyport(ze(int16(port)).cint, proto)
+  else:
+    var s = posix.getservbyport(ze(int16(port)).cint, proto)
+  if s == nil: raiseOSError(osLastError(), "Service not found.")
+  result.name = $s.s_name
+  result.aliases = cstringArrayToSeq(s.s_aliases)
+  result.port = Port(s.s_port)
+  result.proto = $s.s_proto
+
+proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
+  ## This function will lookup the hostname of an IP Address.
+  var myaddr: InAddr
+  myaddr.s_addr = inet_addr(ip)
+
+  when defined(windows):
+    var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
+                                  cint(sockets.AF_INET))
+    if s == nil: raiseOSError(osLastError())
+  else:
+    var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
+                                cint(posix.AF_INET))
+    if s == nil:
+      raiseOSError(osLastError(), $hstrerror(h_errno))
+
+  result.name = $s.h_name
+  result.aliases = cstringArrayToSeq(s.h_aliases)
+  when defined(windows):
+    result.addrtype = Domain(s.h_addrtype)
+  else:
+    if s.h_addrtype == posix.AF_INET:
+      result.addrtype = AF_INET
+    elif s.h_addrtype == posix.AF_INET6:
+      result.addrtype = AF_INET6
+    else:
+      raiseOSError(osLastError(), "unknown h_addrtype")
+  result.addrList = cstringArrayToSeq(s.h_addr_list)
+  result.length = int(s.h_length)
+
+proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
+  ## This function will lookup the IP address of a hostname.
+  when defined(Windows):
+    var s = winlean.gethostbyname(name)
+  else:
+    var s = posix.gethostbyname(name)
+  if s == nil: raiseOSError(osLastError())
+  result.name = $s.h_name
+  result.aliases = cstringArrayToSeq(s.h_aliases)
+  when defined(windows):
+    result.addrtype = Domain(s.h_addrtype)
+  else:
+    if s.h_addrtype == posix.AF_INET:
+      result.addrtype = AF_INET
+    elif s.h_addrtype == posix.AF_INET6:
+      result.addrtype = AF_INET6
+    else:
+      raiseOSError(osLastError(), "unknown h_addrtype")
+  result.addrList = cstringArrayToSeq(s.h_addr_list)
+  result.length = int(s.h_length)
+
+proc getSockOptInt*(socket: Socket, level, optname: int): int {.
+  tags: [ReadIOEffect].} =
+  ## getsockopt for integer options.
+  var res: cint
+  var size = sizeof(res).SockLen
+  if getsockopt(socket.fd, cint(level), cint(optname),
+                addr(res), addr(size)) < 0'i32:
+    raiseOSError(osLastError())
+  result = int(res)
+
+proc setSockOptInt*(socket: Socket, level, optname, optval: int) {.
+  tags: [WriteIOEffect].} =
+  ## setsockopt for integer options.
+  var value = cint(optval)
+  if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
+                sizeof(value).SockLen) < 0'i32:
+    raiseOSError(osLastError())
+
+proc toCInt(opt: SOBool): cint =
+  case opt
+  of OptAcceptConn: SO_ACCEPTCONN
+  of OptBroadcast: SO_BROADCAST
+  of OptDebug: SO_DEBUG
+  of OptDontRoute: SO_DONTROUTE
+  of OptKeepAlive: SO_KEEPALIVE
+  of OptOOBInline: SO_OOBINLINE
+  of OptReuseAddr: SO_REUSEADDR
+
+proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
+  tags: [ReadIOEffect].} =
+  ## Retrieves option ``opt`` as a boolean value.
+  var res: cint
+  var size = sizeof(res).SockLen
+  if getsockopt(socket.fd, cint(level), toCInt(opt),
+                addr(res), addr(size)) < 0'i32:
+    raiseOSError(osLastError())
+  result = res != 0
+
+proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
+  tags: [WriteIOEffect].} =
+  ## Sets option ``opt`` to a boolean value specified by ``value``.
+  var valuei = cint(if value: 1 else: 0)
+  if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
+                sizeof(valuei).SockLen) < 0'i32:
+    raiseOSError(osLastError())
+
+proc connect*(socket: Socket, address: string, port = Port(0),
+              af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
+  ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
+  ## host name. If ``address`` is a host name, this function will try each IP
+  ## of that host name. ``htons`` is already performed on ``port`` so you must
+  ## not do it.
+  ##
+  ## If ``socket`` is an SSL socket a handshake will be automatically performed.
+  var hints: AddrInfo
+  var aiList: ptr AddrInfo = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(SOCK_STREAM)
+  hints.ai_protocol = toInt(IPPROTO_TCP)
+  gaiNim(address, port, hints, aiList)
+  # try all possibilities:
+  var success = false
+  var lastError: OSErrorCode
+  var it = aiList
+  while it != nil:
+    if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
+      success = true
+      break
+    else: lastError = osLastError()
+    it = it.ai_next
+
+  freeaddrinfo(aiList)
+  if not success: raiseOSError(lastError)
+
+  when defined(ssl):
+    if socket.isSSL:
+      let ret = SSLConnect(socket.sslHandle)
+      if ret <= 0:
+        let err = SSLGetError(socket.sslHandle, ret)
+        case err
+        of SSL_ERROR_ZERO_RETURN:
+          raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+        of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
+           SSL_ERROR_WANT_ACCEPT:
+          raiseSslError("The operation did not complete. Perhaps you should use connectAsync?")
+        of SSL_ERROR_WANT_X509_LOOKUP:
+          raiseSslError("Function for x509 lookup has been called.")
+        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+          raiseSslError()
+        else:
+          raiseSslError("Unknown error")
+
+  when false:
+    var s: TSockAddrIn
+    s.sin_addr.s_addr = inet_addr(address)
+    s.sin_port = sockets.htons(int16(port))
+    when defined(windows):
+      s.sin_family = toU16(ord(af))
+    else:
+      case af
+      of AF_UNIX: s.sin_family = posix.AF_UNIX
+      of AF_INET: s.sin_family = posix.AF_INET
+      of AF_INET6: s.sin_family = posix.AF_INET6
+      else: nil
+    if connect(socket.fd, cast[ptr TSockAddr](addr(s)), sizeof(s).cint) < 0'i32:
+      OSError()
+
+proc connectAsync*(socket: Socket, name: string, port = Port(0),
+                     af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
+  ## A variant of ``connect`` for non-blocking sockets.
+  ##
+  ## This procedure will immediately return, it will not block until a connection
+  ## is made. It is up to the caller to make sure the connection has been established
+  ## by checking (using ``select``) whether the socket is writeable.
+  ##
+  ## **Note**: For SSL sockets, the ``handshake`` procedure must be called
+  ## whenever the socket successfully connects to a server.
+  var hints: AddrInfo
+  var aiList: ptr AddrInfo = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(SOCK_STREAM)
+  hints.ai_protocol = toInt(IPPROTO_TCP)
+  gaiNim(name, port, hints, aiList)
+  # try all possibilities:
+  var success = false
+  var lastError: OSErrorCode
+  var it = aiList
+  while it != nil:
+    var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen)
+    if ret == 0'i32:
+      success = true
+      break
+    else:
+      lastError = osLastError()
+      when defined(windows):
+        # Windows EINTR doesn't behave same as POSIX.
+        if lastError.int32 == WSAEWOULDBLOCK:
+          success = true
+          break
+      else:
+        if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
+          success = true
+          break
+
+    it = it.ai_next
+
+  freeaddrinfo(aiList)
+  if not success: raiseOSError(lastError)
+  when defined(ssl):
+    if socket.isSSL:
+      socket.sslNoHandshake = true
+
+when defined(ssl):
+  proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
+    ## This proc needs to be called on a socket after it connects. This is
+    ## only applicable when using ``connectAsync``.
+    ## This proc performs the SSL handshake.
+    ##
+    ## Returns ``False`` whenever the socket is not yet ready for a handshake,
+    ## ``True`` whenever handshake completed successfully.
+    ##
+    ## A ESSL error is raised on any other errors.
+    result = true
+    if socket.isSSL:
+      var ret = SSLConnect(socket.sslHandle)
+      if ret <= 0:
+        var errret = SSLGetError(socket.sslHandle, ret)
+        case errret
+        of SSL_ERROR_ZERO_RETURN:
+          raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+        of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
+          SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE:
+          return false
+        of SSL_ERROR_WANT_X509_LOOKUP:
+          raiseSslError("Function for x509 lookup has been called.")
+        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+          raiseSslError()
+        else:
+          raiseSslError("Unknown Error")
+      socket.sslNoHandshake = false
+    else:
+      raiseSslError("Socket is not an SSL socket.")
+
+  proc gotHandshake*(socket: Socket): bool =
+    ## Determines whether a handshake has occurred between a client (``socket``)
+    ## and the server that ``socket`` is connected to.
+    ##
+    ## Throws ESSL if ``socket`` is not an SSL socket.
+    if socket.isSSL:
+      return not socket.sslNoHandshake
+    else:
+      raiseSslError("Socket is not an SSL socket.")
+
+proc timeValFromMilliseconds(timeout = 500): Timeval =
+  if timeout != -1:
+    var seconds = timeout div 1000
+    result.tv_sec = seconds.int32
+    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+
+proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
+  FD_ZERO(fd)
+  for i in items(s):
+    m = max(m, int(i.fd))
+    FD_SET(i.fd, fd)
+
+proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) =
+  var i = 0
+  var L = s.len
+  while i < L:
+    if FD_ISSET(s[i].fd, fd) == 0'i32:
+      # not set.
+      s[i] = s[L-1]
+      dec(L)
+    else:
+      inc(i)
+  setLen(s, L)
+
+proc hasDataBuffered*(s: Socket): bool =
+  ## Determines whether a socket has data buffered.
+  result = false
+  if s.isBuffered:
+    result = s.bufLen > 0 and s.currPos != s.bufLen
+
+  when defined(ssl):
+    if s.isSSL and not result:
+      result = s.sslHasPeekChar
+
+proc checkBuffer(readfds: var seq[Socket]): int =
+  ## Checks the buffer of each socket in ``readfds`` to see whether there is data.
+  ## Removes the sockets from ``readfds`` and returns the count of removed sockets.
+  var res: seq[Socket] = @[]
+  result = 0
+  for s in readfds:
+    if hasDataBuffered(s):
+      inc(result)
+      res.add(s)
+  if result > 0:
+    readfds = res
+
+proc select*(readfds, writefds, exceptfds: var seq[Socket],
+             timeout = 500): int {.tags: [ReadIOEffect].} =
+  ## Traditional select function. This function will return the number of
+  ## sockets that are ready to be read from, written to, or which have errors.
+  ## If there are none; 0 is returned.
+  ## ``Timeout`` is in milliseconds and -1 can be specified for no timeout.
+  ##
+  ## Sockets which are **not** ready for reading, writing or which don't have
+  ## errors waiting on them are removed from the ``readfds``, ``writefds``,
+  ## ``exceptfds`` sequences respectively.
+  let buffersFilled = checkBuffer(readfds)
+  if buffersFilled > 0:
+    return buffersFilled
+
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+
+  var rd, wr, ex: TFdSet
+  var m = 0
+  createFdSet((rd), readfds, m)
+  createFdSet((wr), writefds, m)
+  createFdSet((ex), exceptfds, m)
+
+  if timeout != -1:
+    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
+  else:
+    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
+
+  pruneSocketSet(readfds, (rd))
+  pruneSocketSet(writefds, (wr))
+  pruneSocketSet(exceptfds, (ex))
+
+proc select*(readfds, writefds: var seq[Socket],
+             timeout = 500): int {.tags: [ReadIOEffect].} =
+  ## Variant of select with only a read and write list.
+  let buffersFilled = checkBuffer(readfds)
+  if buffersFilled > 0:
+    return buffersFilled
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+
+  var rd, wr: TFdSet
+  var m = 0
+  createFdSet((rd), readfds, m)
+  createFdSet((wr), writefds, m)
+
+  if timeout != -1:
+    result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
+  else:
+    result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
+
+  pruneSocketSet(readfds, (rd))
+  pruneSocketSet(writefds, (wr))
+
+proc selectWrite*(writefds: var seq[Socket],
+                  timeout = 500): int {.tags: [ReadIOEffect].} =
+  ## When a socket in ``writefds`` is ready to be written to then a non-zero
+  ## value will be returned specifying the count of the sockets which can be
+  ## written to. The sockets which **cannot** be written to will also be removed
+  ## from ``writefds``.
+  ##
+  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
+  ## an unlimited time.
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+
+  var wr: TFdSet
+  var m = 0
+  createFdSet((wr), writefds, m)
+
+  if timeout != -1:
+    result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
+  else:
+    result = int(select(cint(m+1), nil, addr(wr), nil, nil))
+
+  pruneSocketSet(writefds, (wr))
+
+proc select*(readfds: var seq[Socket], timeout = 500): int =
+  ## variant of select with a read list only
+  let buffersFilled = checkBuffer(readfds)
+  if buffersFilled > 0:
+    return buffersFilled
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+
+  var rd: TFdSet
+  var m = 0
+  createFdSet((rd), readfds, m)
+
+  if timeout != -1:
+    result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
+  else:
+    result = int(select(cint(m+1), addr(rd), nil, nil, nil))
+
+  pruneSocketSet(readfds, (rd))
+
+proc readIntoBuf(socket: Socket, flags: int32): int =
+  result = 0
+  when defined(ssl):
+    if socket.isSSL:
+      result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high))
+    else:
+      result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
+  else:
+    result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
+  if result <= 0:
+    socket.bufLen = 0
+    socket.currPos = 0
+    return result
+  socket.bufLen = result
+  socket.currPos = 0
+
+template retRead(flags, readBytes: int) {.dirty.} =
+  let res = socket.readIntoBuf(flags.int32)
+  if res <= 0:
+    if readBytes > 0:
+      return readBytes
+    else:
+      return res
+
+proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect].} =
+  ## Receives data from a socket.
+  ##
+  ## **Note**: This is a low-level function, you may be interested in the higher
+  ## level versions of this function which are also named ``recv``.
+  if size == 0: return
+  if socket.isBuffered:
+    if socket.bufLen == 0:
+      retRead(0'i32, 0)
+
+    var read = 0
+    while read < size:
+      if socket.currPos >= socket.bufLen:
+        retRead(0'i32, read)
+
+      let chunk = min(socket.bufLen-socket.currPos, size-read)
+      var d = cast[cstring](data)
+      copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
+      read.inc(chunk)
+      socket.currPos.inc(chunk)
+
+    result = read
+  else:
+    when defined(ssl):
+      if socket.isSSL:
+        if socket.sslHasPeekChar:
+          copyMem(data, addr(socket.sslPeekChar), 1)
+          socket.sslHasPeekChar = false
+          if size-1 > 0:
+            var d = cast[cstring](data)
+            result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1
+          else:
+            result = 1
+        else:
+          result = SSLRead(socket.sslHandle, data, size)
+      else:
+        result = recv(socket.fd, data, size.cint, 0'i32)
+    else:
+      result = recv(socket.fd, data, size.cint, 0'i32)
+
+proc waitFor(socket: Socket, waited: var float, timeout, size: int,
+             funcName: string): int {.tags: [TimeEffect].} =
+  ## determines the amount of characters that can be read. Result will never
+  ## be larger than ``size``. For unbuffered sockets this will be ``1``.
+  ## For buffered sockets it can be as big as ``BufferSize``.
+  ##
+  ## If this function does not determine that there is data on the socket
+  ## within ``timeout`` ms, an ETimeout error will be raised.
+  result = 1
+  if size <= 0: assert false
+  if timeout == -1: return size
+  if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos:
+    result = socket.bufLen - socket.currPos
+    result = min(result, size)
+  else:
+    if timeout - int(waited * 1000.0) < 1:
+      raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
+
+    when defined(ssl):
+      if socket.isSSL:
+        if socket.hasDataBuffered:
+          # sslPeekChar is present.
+          return 1
+        let sslPending = SSLPending(socket.sslHandle)
+        if sslPending != 0:
+          return sslPending
+
+    var s = @[socket]
+    var startTime = epochTime()
+    let selRet = select(s, timeout - int(waited * 1000.0))
+    if selRet < 0: raiseOSError(osLastError())
+    if selRet != 1:
+      raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
+    waited += (epochTime() - startTime)
+
+proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
+  tags: [ReadIOEffect, TimeEffect].} =
+  ## overload with a ``timeout`` parameter in milliseconds.
+  var waited = 0.0 # number of seconds already waited
+
+  var read = 0
+  while read < size:
+    let avail = waitFor(socket, waited, timeout, size-read, "recv")
+    var d = cast[cstring](data)
+    result = recv(socket, addr(d[read]), avail)
+    if result == 0: break
+    if result < 0:
+      return result
+    inc(read, result)
+
+  result = read
+
+proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int =
+  ## Higher-level version of ``recv``.
+  ##
+  ## When 0 is returned the socket's connection has been closed.
+  ##
+  ## This function will throw an EOS exception when an error occurs. A value
+  ## lower than 0 is never returned.
+  ##
+  ## A timeout may be specified in milliseconds, if enough data is not received
+  ## within the time specified an ETimeout exception will be raised.
+  ##
+  ## **Note**: ``data`` must be initialised.
+  data.setLen(size)
+  result = recv(socket, cstring(data), size, timeout)
+  if result < 0:
+    data.setLen(0)
+    socket.raiseSocketError(result)
+  data.setLen(result)
+
+proc recvAsync*(socket: Socket, data: var string, size: int): int =
+  ## Async version of ``recv``.
+  ##
+  ## When socket is non-blocking and no data is available on the socket,
+  ## ``-1`` will be returned and ``data`` will be ``""``.
+  ##
+  ## **Note**: ``data`` must be initialised.
+  data.setLen(size)
+  result = recv(socket, cstring(data), size)
+  if result < 0:
+    data.setLen(0)
+    socket.raiseSocketError(async = true)
+    result = -1
+  data.setLen(result)
+
+proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
+  if socket.isBuffered:
+    result = 1
+    if socket.bufLen == 0 or socket.currPos > socket.bufLen-1:
+      var res = socket.readIntoBuf(0'i32)
+      if res <= 0:
+        result = res
+
+    c = socket.buffer[socket.currPos]
+  else:
+    when defined(ssl):
+      if socket.isSSL:
+        if not socket.sslHasPeekChar:
+          result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
+          socket.sslHasPeekChar = true
+
+        c = socket.sslPeekChar
+        return
+    result = recv(socket.fd, addr(c), 1, MSG_PEEK)
+
+proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
+  tags: [ReadIOEffect, TimeEffect], deprecated.} =
+  ## Receive a line of data from ``socket``.
+  ##
+  ## If a full line is received ``\r\L`` is not
+  ## added to ``line``, however if solely ``\r\L`` is received then ``line``
+  ## will be set to it.
+  ##
+  ## ``True`` is returned if data is available. ``False`` suggests an
+  ## error, EOS exceptions are not raised and ``False`` is simply returned
+  ## instead.
+  ##
+  ## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
+  ## will be returned.
+  ##
+  ## A timeout can be specified in milliseconds, if data is not received within
+  ## the specified time an ETimeout exception will be raised.
+  ##
+  ## **Deprecated since version 0.9.2**: This function has been deprecated in
+  ## favour of readLine.
+
+  template addNLIfEmpty(): stmt =
+    if line.len == 0:
+      line.add("\c\L")
+
+  var waited = 0.0
+
+  setLen(line.string, 0)
+  while true:
+    var c: char
+    discard waitFor(socket, waited, timeout, 1, "recvLine")
+    var n = recv(socket, addr(c), 1)
+    if n < 0: return
+    elif n == 0: return true
+    if c == '\r':
+      discard waitFor(socket, waited, timeout, 1, "recvLine")
+      n = peekChar(socket, c)
+      if n > 0 and c == '\L':
+        discard recv(socket, addr(c), 1)
+      elif n <= 0: return false
+      addNLIfEmpty()
+      return true
+    elif c == '\L':
+      addNLIfEmpty()
+      return true
+    add(line.string, c)
+
+proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
+  tags: [ReadIOEffect, TimeEffect].} =
+  ## Reads a line of data from ``socket``.
+  ##
+  ## If a full line is read ``\r\L`` is not
+  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
+  ## will be set to it.
+  ##
+  ## If the socket is disconnected, ``line`` will be set to ``""``.
+  ##
+  ## An EOS exception will be raised in the case of a socket error.
+  ##
+  ## A timeout can be specified in milliseconds, if data is not received within
+  ## the specified time an ETimeout exception will be raised.
+
+  template addNLIfEmpty(): stmt =
+    if line.len == 0:
+      line.add("\c\L")
+
+  var waited = 0.0
+
+  setLen(line.string, 0)
+  while true:
+    var c: char
+    discard waitFor(socket, waited, timeout, 1, "readLine")
+    var n = recv(socket, addr(c), 1)
+    if n < 0: socket.raiseSocketError()
+    elif n == 0: return
+    if c == '\r':
+      discard waitFor(socket, waited, timeout, 1, "readLine")
+      n = peekChar(socket, c)
+      if n > 0 and c == '\L':
+        discard recv(socket, addr(c), 1)
+      elif n <= 0: socket.raiseSocketError()
+      addNLIfEmpty()
+      return
+    elif c == '\L':
+      addNLIfEmpty()
+      return
+    add(line.string, c)
+
+proc recvLineAsync*(socket: Socket,
+  line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} =
+  ## Similar to ``recvLine`` but designed for non-blocking sockets.
+  ##
+  ## The values of the returned enum should be pretty self explanatory:
+  ##
+  ##   * If a full line has been retrieved; ``RecvFullLine`` is returned.
+  ##   * If some data has been retrieved; ``RecvPartialLine`` is returned.
+  ##   * If the socket has been disconnected; ``RecvDisconnected`` is returned.
+  ##   * If call to ``recv`` failed; ``RecvFail`` is returned.
+  ##
+  ## **Deprecated since version 0.9.2**: This function has been deprecated in
+  ## favour of readLineAsync.
+
+  setLen(line.string, 0)
+  while true:
+    var c: char
+    var n = recv(socket, addr(c), 1)
+    if n < 0:
+      return (if line.len == 0: RecvFail else: RecvPartialLine)
+    elif n == 0:
+      return (if line.len == 0: RecvDisconnected else: RecvPartialLine)
+    if c == '\r':
+      n = peekChar(socket, c)
+      if n > 0 and c == '\L':
+        discard recv(socket, addr(c), 1)
+      elif n <= 0:
+        return (if line.len == 0: RecvFail else: RecvPartialLine)
+      return RecvFullLine
+    elif c == '\L': return RecvFullLine
+    add(line.string, c)
+
+proc readLineAsync*(socket: Socket,
+  line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} =
+  ## Similar to ``recvLine`` but designed for non-blocking sockets.
+  ##
+  ## The values of the returned enum should be pretty self explanatory:
+  ##
+  ##   * If a full line has been retrieved; ``ReadFullLine`` is returned.
+  ##   * If some data has been retrieved; ``ReadPartialLine`` is returned.
+  ##   * If the socket has been disconnected; ``ReadDisconnected`` is returned.
+  ##   * If no data could be retrieved; ``ReadNone`` is returned.
+  ##   * If call to ``recv`` failed; **an EOS exception is raised.**
+  setLen(line.string, 0)
+
+  template errorOrNone =
+    socket.raiseSocketError(async = true)
+    return ReadNone
+
+  while true:
+    var c: char
+    var n = recv(socket, addr(c), 1)
+    #echo(n)
+    if n < 0:
+      if line.len == 0: errorOrNone else: return ReadPartialLine
+    elif n == 0:
+      return (if line.len == 0: ReadDisconnected else: ReadPartialLine)
+    if c == '\r':
+      n = peekChar(socket, c)
+      if n > 0 and c == '\L':
+        discard recv(socket, addr(c), 1)
+      elif n <= 0:
+        if line.len == 0: errorOrNone else: return ReadPartialLine
+      return ReadFullLine
+    elif c == '\L': return ReadFullLine
+    add(line.string, c)
+
+proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
+  ## receives all the available data from the socket.
+  ## Socket errors will result in an ``EOS`` error.
+  ## If socket is not a connectionless socket and socket is not connected
+  ## ``""`` will be returned.
+  ##
+  ## **Deprecated since version 0.9.2**: This function is not safe for use.
+  const bufSize = 4000
+  result = newStringOfCap(bufSize).TaintedString
+  var pos = 0
+  while true:
+    var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
+    if bytesRead == -1: raiseOSError(osLastError())
+    setLen(result.string, pos + bytesRead)
+    if bytesRead != bufSize-1: break
+    # increase capacity:
+    setLen(result.string, result.string.len + bufSize)
+    inc(pos, bytesRead)
+  when false:
+    var buf = newString(bufSize)
+    result = TaintedString""
+    while true:
+      var bytesRead = recv(socket, cstring(buf), bufSize-1)
+      # Error
+      if bytesRead == -1: OSError(osLastError())
+
+      buf[bytesRead] = '\0' # might not be necessary
+      setLen(buf, bytesRead)
+      add(result.string, buf)
+      if bytesRead != bufSize-1: break
+
+{.push warning[deprecated]: off.}
+proc recvTimeout*(socket: Socket, timeout: int): TaintedString {.
+  tags: [ReadIOEffect], deprecated.} =
+  ## overloaded variant to support a ``timeout`` parameter, the ``timeout``
+  ## parameter specifies the amount of milliseconds to wait for data on the
+  ## socket.
+  ##
+  ## **Deprecated since version 0.9.2**: This function is not safe for use.
+  if socket.bufLen == 0:
+    var s = @[socket]
+    if s.select(timeout) != 1:
+      raise newException(TimeoutError, "Call to recv() timed out.")
+
+  return socket.recv
+{.pop.}
+
+proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
+  tags: [ReadIOEffect], deprecated.} =
+  ## receives all the data from a non-blocking socket. If socket is non-blocking
+  ## and there are no messages available, `False` will be returned.
+  ## Other socket errors will result in an ``EOS`` error.
+  ## If socket is not a connectionless socket and socket is not connected
+  ## ``s`` will be set to ``""``.
+  ##
+  ## **Deprecated since version 0.9.2**: This function is not safe for use.
+  const bufSize = 1000
+  # ensure bufSize capacity:
+  setLen(s.string, bufSize)
+  setLen(s.string, 0)
+  var pos = 0
+  while true:
+    var bytesRead = recv(socket, addr(string(s)[pos]), bufSize-1)
+    when defined(ssl):
+      if socket.isSSL:
+        if bytesRead <= 0:
+          var ret = SSLGetError(socket.sslHandle, bytesRead.cint)
+          case ret
+          of SSL_ERROR_ZERO_RETURN:
+            raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+          of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
+            raiseSslError("Unexpected error occurred.") # This should just not happen.
+          of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
+            return false
+          of SSL_ERROR_WANT_X509_LOOKUP:
+            raiseSslError("Function for x509 lookup has been called.")
+          of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+            raiseSslError()
+          else: raiseSslError("Unknown Error")
+
+    if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
+      let err = osLastError()
+      when defined(windows):
+        if err.int32 == WSAEWOULDBLOCK:
+          return false
+        else: raiseOSError(err)
+      else:
+        if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
+          return false
+        else: raiseOSError(err)
+
+    setLen(s.string, pos + bytesRead)
+    if bytesRead != bufSize-1: break
+    # increase capacity:
+    setLen(s.string, s.string.len + bufSize)
+    inc(pos, bytesRead)
+  result = true
+
+proc recvFrom*(socket: Socket, data: var string, length: int,
+               address: var string, port: var Port, flags = 0'i32): int {.
+               tags: [ReadIOEffect].} =
+  ## Receives data from ``socket``. This function should normally be used with
+  ## connection-less sockets (UDP sockets).
+  ##
+  ## If an error occurs the return value will be ``-1``. Otherwise the return
+  ## value will be the length of data received.
+  ##
+  ## **Warning:** This function does not yet have a buffered implementation,
+  ## so when ``socket`` is buffered the non-buffered implementation will be
+  ## used. Therefore if ``socket`` contains something in its buffer this
+  ## function will make no effort to return it.
+
+  # TODO: Buffered sockets
+  data.setLen(length)
+  var sockAddress: Sockaddr_in
+  var addrLen = sizeof(sockAddress).SockLen
+  result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
+                    cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
+
+  if result != -1:
+    data.setLen(result)
+    address = $inet_ntoa(sockAddress.sin_addr)
+    port = ntohs(sockAddress.sin_port).Port
+
+proc recvFromAsync*(socket: Socket, data: var string, length: int,
+                    address: var string, port: var Port,
+                    flags = 0'i32): bool {.tags: [ReadIOEffect].} =
+  ## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
+  ## this function will raise an EOS error whenever a socket error occurs.
+  ##
+  ## If there is no data to be read from the socket ``False`` will be returned.
+  result = true
+  var callRes = recvFrom(socket, data, length, address, port, flags)
+  if callRes < 0:
+    let err = osLastError()
+    when defined(windows):
+      if err.int32 == WSAEWOULDBLOCK:
+        return false
+      else: raiseOSError(err)
+    else:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
+        return false
+      else: raiseOSError(err)
+
+proc skip*(socket: Socket) {.tags: [ReadIOEffect], deprecated.} =
+  ## skips all the data that is pending for the socket
+  ##
+  ## **Deprecated since version 0.9.2**: This function is not safe for use.
+  const bufSize = 1000
+  var buf = alloc(bufSize)
+  while recv(socket, buf, bufSize) == bufSize: discard
+  dealloc(buf)
+
+proc skip*(socket: Socket, size: int, timeout = -1) =
+  ## Skips ``size`` amount of bytes.
+  ##
+  ## An optional timeout can be specified in milliseconds, if skipping the
+  ## bytes takes longer than specified an ETimeout exception will be raised.
+  ##
+  ## Returns the number of skipped bytes.
+  var waited = 0.0
+  var dummy = alloc(size)
+  var bytesSkipped = 0
+  while bytesSkipped != size:
+    let avail = waitFor(socket, waited, timeout, size-bytesSkipped, "skip")
+    bytesSkipped += recv(socket, dummy, avail)
+  dealloc(dummy)
+
+proc send*(socket: Socket, data: pointer, size: int): int {.
+  tags: [WriteIOEffect].} =
+  ## sends data to a socket.
+  when defined(ssl):
+    if socket.isSSL:
+      return SSLWrite(socket.sslHandle, cast[cstring](data), size)
+
+  when defined(windows) or defined(macosx):
+    result = send(socket.fd, data, size.cint, 0'i32)
+  else:
+    when defined(solaris):
+      const MSG_NOSIGNAL = 0
+    result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
+
+proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} =
+  ## sends data to a socket.
+  if socket.nonblocking:
+    raise newException(ValueError, "This function cannot be used on non-blocking sockets.")
+  let sent = send(socket, cstring(data), data.len)
+  if sent < 0:
+    when defined(ssl):
+      if socket.isSSL:
+        raiseSslError()
+
+    raiseOSError(osLastError())
+
+  if sent != data.len:
+    raiseOSError(osLastError(), "Could not send all data.")
+
+proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
+  ## sends data to a non-blocking socket.
+  ## Returns ``0`` if no data could be sent, if data has been sent
+  ## returns the amount of bytes of ``data`` that was successfully sent. This
+  ## number may not always be the length of ``data`` but typically is.
+  ##
+  ## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error
+  ## occurs.
+  result = send(socket, cstring(data), data.len)
+  when defined(ssl):
+    if socket.isSSL:
+      if result <= 0:
+          let ret = SSLGetError(socket.sslHandle, result.cint)
+          case ret
+          of SSL_ERROR_ZERO_RETURN:
+            raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
+          of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
+            raiseSslError("Unexpected error occurred.") # This should just not happen.
+          of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
+            return 0
+          of SSL_ERROR_WANT_X509_LOOKUP:
+            raiseSslError("Function for x509 lookup has been called.")
+          of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+            raiseSslError()
+          else: raiseSslError("Unknown Error")
+      else:
+        return
+  if result == -1:
+    let err = osLastError()
+    when defined(windows):
+      if err.int32 == WSAEINPROGRESS:
+        return 0
+      else: raiseOSError(err)
+    else:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
+        return 0
+      else: raiseOSError(err)
+
+
+proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
+  ## safe alternative to ``send``. Does not raise an EOS when an error occurs,
+  ## and instead returns ``false`` on failure.
+  result = send(socket, cstring(data), data.len) == data.len
+
+proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
+             size: int, af: Domain = AF_INET, flags = 0'i32): int {.
+             tags: [WriteIOEffect].} =
+  ## low-level sendTo proc. This proc sends ``data`` to the specified ``address``,
+  ## which may be an IP address or a hostname, if a hostname is specified
+  ## this function will try each IP of that hostname.
+  ##
+  ## **Note:** This proc is not available for SSL sockets.
+  var hints: AddrInfo
+  var aiList: ptr AddrInfo = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(SOCK_STREAM)
+  hints.ai_protocol = toInt(IPPROTO_TCP)
+  gaiNim(address, port, hints, aiList)
+
+  # try all possibilities:
+  var success = false
+  var it = aiList
+  while it != nil:
+    result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
+                    it.ai_addrlen.SockLen)
+    if result != -1'i32:
+      success = true
+      break
+    it = it.ai_next
+
+  freeaddrinfo(aiList)
+
+proc sendTo*(socket: Socket, address: string, port: Port,
+             data: string): int {.tags: [WriteIOEffect].} =
+  ## Friendlier version of the low-level ``sendTo``.
+  result = socket.sendTo(address, port, cstring(data), data.len)
+
+when defined(Windows):
+  const
+    IOCPARM_MASK = 127
+    IOC_IN = int(-2147483648)
+    FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
+                             (102 shl 8) or 126
+
+  proc ioctlsocket(s: SocketHandle, cmd: clong,
+                   argptr: ptr clong): cint {.
+                   stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
+
+proc setBlocking(s: Socket, blocking: bool) =
+  when defined(Windows):
+    var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
+    if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
+      raiseOSError(osLastError())
+  else: # BSD sockets
+    var x: int = fcntl(s.fd, F_GETFL, 0)
+    if x == -1:
+      raiseOSError(osLastError())
+    else:
+      var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
+      if fcntl(s.fd, F_SETFL, mode) == -1:
+        raiseOSError(osLastError())
+  s.nonblocking = not blocking
+
+discard """ proc setReuseAddr*(s: Socket) =
+  var blah: int = 1
+  var mode = SO_REUSEADDR
+  if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1:
+    raiseOSError(osLastError()) """
+
+proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
+             af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} =
+  ## Connects to server as specified by ``address`` on port specified by ``port``.
+  ##
+  ## The ``timeout`` paremeter specifies the time in milliseconds to allow for
+  ## the connection to the server to be made.
+  let originalStatus = not socket.nonblocking
+  socket.setBlocking(false)
+
+  socket.connectAsync(address, port, af)
+  var s: seq[Socket] = @[socket]
+  if selectWrite(s, timeout) != 1:
+    raise newException(TimeoutError, "Call to 'connect' timed out.")
+  else:
+    when defined(ssl):
+      if socket.isSSL:
+        socket.setBlocking(true)
+        doAssert socket.handshake()
+  socket.setBlocking(originalStatus)
+
+proc isSSL*(socket: Socket): bool = return socket.isSSL
+  ## Determines whether ``socket`` is a SSL socket.
+
+proc getFD*(socket: Socket): SocketHandle = return socket.fd
+  ## Returns the socket's file descriptor
+
+proc isBlocking*(socket: Socket): bool = not socket.nonblocking
+  ## Determines whether ``socket`` is blocking.
+
+when defined(Windows):
+  var wsa: WSAData
+  if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
+
+