summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorMichał Zieliński <michal@zielinscy.org.pl>2014-02-10 14:40:18 +0100
committerMichał Zieliński <michal@zielinscy.org.pl>2014-02-10 14:40:18 +0100
commit87abc22cc36d094ece8900d069cdc2dbca094e8b (patch)
tree0371f952a61ddb5df32f833a5540483b6de61715 /lib
parent820fdd634870686082ad01896327f67cf45cd96f (diff)
parent5d939570488706553bdb3dc58d7902e454cd6de4 (diff)
downloadNim-87abc22cc36d094ece8900d069cdc2dbca094e8b.tar.gz
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
Diffstat (limited to 'lib')
-rw-r--r--lib/posix/epoll.nim88
-rw-r--r--lib/pure/asyncio2.nim485
-rw-r--r--lib/pure/net.nim40
-rw-r--r--lib/pure/selectors.nim249
-rw-r--r--lib/pure/sockets2.nim202
-rw-r--r--lib/windows/winlean.nim77
6 files changed, 1141 insertions, 0 deletions
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
new file mode 100644
index 000000000..d50394f60
--- /dev/null
+++ b/lib/posix/epoll.nim
@@ -0,0 +1,88 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2013 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+const
+  EPOLLIN* = 0x00000001
+  EPOLLPRI* = 0x00000002
+  EPOLLOUT* = 0x00000004
+  EPOLLERR* = 0x00000008
+  EPOLLHUP* = 0x00000010
+  EPOLLRDNORM* = 0x00000040
+  EPOLLRDBAND* = 0x00000080
+  EPOLLWRNORM* = 0x00000100
+  EPOLLWRBAND* = 0x00000200
+  EPOLLMSG* = 0x00000400
+  EPOLLRDHUP* = 0x00002000
+  EPOLLWAKEUP* = 1 shl 29
+  EPOLLONESHOT* = 1 shl 30
+  EPOLLET* = 1 shl 31
+
+# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
+
+const 
+  EPOLL_CTL_ADD* = 1          # Add a file descriptor to the interface.  
+  EPOLL_CTL_DEL* = 2          # Remove a file descriptor from the interface.  
+  EPOLL_CTL_MOD* = 3          # Change file descriptor epoll_event structure.  
+
+type 
+  epoll_data* {.importc: "union epoll_data", 
+      header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
+    thePtr* {.importc: "ptr".}: pointer # \
+    #fd*: cint
+    #u32*: uint32
+    #u64*: uint64
+
+  epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object 
+    events*: uint32 # Epoll events 
+    data*: epoll_data # User data variable 
+
+proc epoll_create*(size: cint): cint {.importc: "epoll_create", 
+    header: "<sys/epoll.h>".}
+  ## Creates an epoll instance.  Returns an fd for the new instance.
+  ##   The "size" parameter is a hint specifying the number of file
+  ##   descriptors to be associated with the new instance.  The fd
+  ##   returned by epoll_create() should be closed with close().  
+
+proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", 
+    header: "<sys/epoll.h>".}
+  ## Same as epoll_create but with an FLAGS parameter.  The unused SIZE
+  ##   parameter has been dropped.  
+
+proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
+    importc: "epoll_ctl", header: "<sys/epoll.h>".}
+  ## Manipulate an epoll instance "epfd". Returns 0 in case of success,
+  ##   -1 in case of error ( the "errno" variable will contain the
+  ##   specific error code ) The "op" parameter is one of the EPOLL_CTL_*
+  ##   constants defined above. The "fd" parameter is the target of the
+  ##   operation. The "event" parameter describes which events the caller
+  ##   is interested in and any associated user data.  
+
+proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint; 
+                 timeout: cint): cint {.importc: "epoll_wait", 
+    header: "<sys/epoll.h>".}
+  ## Wait for events on an epoll instance "epfd". Returns the number of
+  ##   triggered events returned in "events" buffer. Or -1 in case of
+  ##   error with the "errno" variable set to the specific error code. The
+  ##   "events" parameter is a buffer that will contain triggered
+  ##   events. The "maxevents" is the maximum number of events to be
+  ##   returned ( usually size of "events" ). The "timeout" parameter
+  ##   specifies the maximum wait time in milliseconds (-1 == infinite).
+  ##
+  ##   This function is a cancellation point and therefore not marked with
+  ##   __THROW.
+
+
+#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint; 
+#                  timeout: cint; ss: ptr sigset_t): cint {.
+#    importc: "epoll_pwait", header: "<sys/epoll.h>".}
+# Same as epoll_wait, but the thread's signal mask is temporarily
+#   and atomically replaced with the one provided as parameter.
+#
+#   This function is a cancellation point and therefore not marked with
+#   __THROW.  
diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim
new file mode 100644
index 000000000..cdb4a6f49
--- /dev/null
+++ b/lib/pure/asyncio2.nim
@@ -0,0 +1,485 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import os, oids, tables, strutils
+
+import winlean
+
+import sockets2, net
+
+## Asyncio2 
+## --------
+##
+## This module implements a brand new asyncio module based on Futures.
+## IOCP is used under the hood on Windows and the selectors module is used for
+## other operating systems.
+
+# -- Futures
+
+type
+  PFutureVoid* = ref object of PObject
+    cbVoid: proc () {.closure.}
+    finished: bool
+
+  PFuture*[T] = ref object of PFutureVoid
+    value: T
+    error: ref EBase
+    cb: proc (future: PFuture[T]) {.closure.}
+
+proc newFuture*[T](): PFuture[T] =
+  ## Creates a new future.
+  new(result)
+  result.finished = false
+
+proc complete*[T](future: PFuture[T], val: T) =
+  ## Completes ``future`` with value ``val``.
+  assert(not future.finished)
+  assert(future.error == nil)
+  future.value = val
+  future.finished = true
+  if future.cb != nil:
+    future.cb(future)
+  if future.cbVoid != nil:
+    future.cbVoid()
+
+proc fail*[T](future: PFuture[T], error: ref EBase) =
+  ## Completes ``future`` with ``error``.
+  assert(not future.finished)
+  future.finished = true
+  future.error = error
+  if future.cb != nil:
+    future.cb(future)
+
+proc `callback=`*[T](future: PFuture[T],
+    cb: proc (future: PFuture[T]) {.closure.}) =
+  ## Sets the callback proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  future.cb = cb
+  if future.finished:
+    future.cb(future)
+
+proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
+  ## Sets the **void** callback proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  ##
+  ## **Note**: This is used for the ``await`` functionality, you most likely
+  ## want to use ``callback``.
+  future.cbVoid = cb
+  if future.finished:
+    future.cbVoid()
+
+proc read*[T](future: PFuture[T]): T =
+  ## Retrieves the value of ``future``. Future must be finished otherwise
+  ## this function will fail with a ``EInvalidValue`` exception.
+  ##
+  ## If the result of the future is an error then that error will be raised.
+  if future.finished:
+    if future.error != nil: raise future.error
+    return future.value
+  else:
+    # TODO: Make a custom exception type for this?
+    raise newException(EInvalidValue, "Future still in progress.")
+
+proc finished*[T](future: PFuture[T]): bool =
+  ## Determines whether ``future`` has completed.
+  ##
+  ## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
+  future.finished
+
+proc failed*[T](future: PFuture[T]): bool =
+  ## Determines whether ``future`` completed with an error.
+  future.error != nil
+
+when defined(windows):
+  type
+    TCompletionKey = dword
+
+    TCompletionData* = object
+      sock: TSocketHandle
+      cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
+
+    PDispatcher* = ref object
+      ioPort: THandle
+
+    TCustomOverlapped = object
+      Internal*: DWORD
+      InternalHigh*: DWORD
+      Offset*: DWORD
+      OffsetHigh*: DWORD
+      hEvent*: THANDLE
+      data*: TCompletionData
+
+    PCustomOverlapped = ptr TCustomOverlapped
+
+  proc newDispatcher*(): PDispatcher =
+    ## Creates a new Dispatcher instance.
+    new result
+    result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
+
+  proc register*(p: PDispatcher, sock: TSocketHandle) =
+    ## Registers ``sock`` with the dispatcher ``p``.
+    if CreateIOCompletionPort(sock.THandle, p.ioPort,
+                              cast[TCompletionKey](sock), 1) == 0:
+      OSError(OSLastError())
+
+  proc poll*(p: PDispatcher, timeout = 500) =
+    ## Waits for completion events and processes them.
+    let llTimeout =
+      if timeout ==  -1: winlean.INFINITE
+      else: timeout.int32
+    var lpNumberOfBytesTransferred: DWORD
+    var lpCompletionKey: ULONG
+    var lpOverlapped: POverlapped
+    let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
+        addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
+
+    # http://stackoverflow.com/a/12277264/492186
+    # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
+    var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
+    if res:
+      assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
+
+      customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
+      dealloc(customOverlapped)
+    else:
+      let errCode = OSLastError()
+      if lpOverlapped != nil:
+        assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
+        dealloc(customOverlapped)
+        customOverlapped.data.cb(customOverlapped.data.sock, errCode)
+      else:
+        if errCode.int32 == WAIT_TIMEOUT:
+          # Timed out
+          discard
+        else: OSError(errCode)
+
+  var connectExPtr: pointer = nil
+  var acceptExPtr: pointer = nil
+  var getAcceptExSockAddrsPtr: pointer = nil
+
+  proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
+    # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
+    var bytesRet: DWord
+    func = nil
+    result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
+                      sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
+                      addr bytesRet, nil, nil) == 0
+
+  proc initAll() =
+    let dummySock = socket()
+    if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
+      OSError(OSLastError())
+    if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
+      OSError(OSLastError())
+    if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
+      OSError(OSLastError())
+
+  proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, 
+                  lpSendBuffer: pointer, dwSendDataLength: dword,
+                  lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
+    if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
+    let func =
+      cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, 
+         lpSendBuffer: pointer, dwSendDataLength: dword,
+         lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
+
+    result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
+         lpOverlapped)
+
+  proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
+                 lpOverlapped: POverlapped): bool =
+    if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
+    let func =
+      cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
+                 lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
+    result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
+        dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
+        lpOverlapped)
+
+  proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
+      dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
+      LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
+      RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
+    if getAcceptExSockAddrsPtr.isNil:
+      raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
+
+    let func =
+      cast[proc (lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
+                 LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
+                RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
+    
+    func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
+                  dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
+                  RemoteSockaddr, RemoteSockaddrLength)
+
+  proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
+    af = AF_INET): PFuture[int] =
+    ## Connects ``socket`` to server at ``address:port``.
+    ##
+    ## Returns a ``PFuture`` which will complete when the connection succeeds
+    ## or an error occurs.
+
+    var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
+    # Apparently ``ConnectEx`` expects the socket to be initially bound:
+    var saddr: Tsockaddr_in
+    saddr.sin_family = int16(toInt(af))
+    saddr.sin_port = 0
+    saddr.sin_addr.s_addr = INADDR_ANY
+    if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
+                  sizeof(saddr).TSockLen) < 0'i32:
+      OSError(OSLastError())
+
+    var aiList = getAddrInfo(address, port, af)
+    var success = false
+    var lastError: TOSErrorCode
+    var it = aiList
+    while it != nil:
+      # "the OVERLAPPED structure must remain valid until the I/O completes"
+      # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
+      var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+      ol.data = TCompletionData(sock: socket, cb:
+        proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+          if errcode == TOSErrorCode(-1):
+            retFuture.complete(0)
+          else:
+            retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+      )
+      
+      var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
+                          nil, 0, nil, cast[POverlapped](ol))
+      if ret:
+        # Request to connect completed immediately.
+        success = true
+        retFuture.complete(0)
+        dealloc(ol)
+        break
+      else:
+        lastError = OSLastError()
+        if lastError.int32 == ERROR_IO_PENDING:
+          # In this case ``ol`` will be deallocated in ``poll``.
+          success = true
+          break
+        else:
+          dealloc(ol)
+          success = false
+      it = it.ai_next
+
+    dealloc(aiList)
+    if not success:
+      retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+    return retFuture
+
+  proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
+    ## Reads ``size`` bytes from ``socket``. Returned future will complete once
+    ## all of the requested data is read.
+
+    var retFuture = newFuture[string]()
+    
+    var dataBuf: TWSABuf
+    dataBuf.buf = newString(size)
+    dataBuf.len = size
+    
+    var bytesReceived, flags: DWord
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          var data = newString(size)
+          copyMem(addr data[0], addr dataBuf.buf[0], size)
+          retFuture.complete($data)
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+    
+    let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
+                      addr flags, cast[POverlapped](ol), nil)
+    if ret == -1:
+      let err = OSLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      # Request to read completed immediately.
+      var data = newString(size)
+      copyMem(addr data[0], addr dataBuf.buf[0], size)
+      retFuture.complete($data)
+      dealloc(ol)
+    return retFuture
+
+  proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
+    ## Sends ``data`` to ``socket``. The returned future will complete once all
+    ## data has been sent.
+    var retFuture = newFuture[int]()
+
+    var dataBuf: TWSABuf
+    dataBuf.buf = data
+    dataBuf.len = data.len
+
+    var bytesReceived, flags: DWord
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          retFuture.complete(0)
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+
+    let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
+                      flags, cast[POverlapped](ol), nil)
+    if ret == -1:
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      retFuture.complete(0)
+      dealloc(ol)
+    return retFuture
+
+  proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): 
+      PFuture[tuple[address: string, client: TSocketHandle]] =
+    ## Accepts a new connection. Returns a future containing the client socket
+    ## corresponding to that connection and the remote address of the client.
+    ## The future will complete when the connection is successfully accepted.
+    
+    var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
+
+    var clientSock = socket()
+    if clientSock == OSInvalidSocket: osError(osLastError())
+
+    const lpOutputLen = 1024
+    var lpOutputBuf = newString(lpOutputLen)
+    var dwBytesReceived: DWORD
+    let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
+    let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
+    let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
+
+    template completeAccept(): stmt {.immediate, dirty.} =
+      var listenSock = socket
+      let setoptRet = setsockopt(clientSock, SOL_SOCKET,
+          SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
+          sizeof(listenSock).TSockLen)
+      if setoptRet != 0: osError(osLastError())
+
+      var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
+      var localLen, remoteLen: int32
+      getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
+                           dwLocalAddressLength, dwRemoteAddressLength,
+                           addr LocalSockaddr, addr localLen,
+                           addr RemoteSockaddr, addr remoteLen)
+      # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
+      retFuture.complete(
+        (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
+         client: clientSock)
+      )
+
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          completeAccept()
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+
+    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
+    let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
+                       dwReceiveDataLength, 
+                       dwLocalAddressLength,
+                       dwRemoteAddressLength,
+                       addr dwBytesReceived, cast[POverlapped](ol))
+
+    if not ret:
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      completeAccept()
+      dealloc(ol)
+
+    return retFuture
+
+  proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
+    ## Accepts a new connection. Returns a future containing the client socket
+    ## corresponding to that connection.
+    ## The future will complete when the connection is successfully accepted.
+    var retFut = newFuture[TSocketHandle]()
+    var fut = p.acceptAddr(socket)
+    fut.callback =
+      proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
+        assert future.finished
+        if future.failed:
+          retFut.fail(future.error)
+        else:
+          retFut.complete(future.read.client)
+    return retFut
+
+  initAll()
+else:
+  # TODO: Selectors.
+
+
+when isMainModule:
+  
+  var p = newDispatcher()
+  var sock = socket()
+  #sock.setBlocking false
+  p.register(sock)
+
+  when true:
+
+    var f = p.connect(sock, "irc.freenode.org", TPort(6667))
+    f.callback =
+      proc (future: PFuture[int]) =
+        echo("Connected in future!")
+        echo(future.read)
+        for i in 0 .. 50:
+          var recvF = p.recv(sock, 10)
+          recvF.callback =
+            proc (future: PFuture[string]) =
+              echo("Read: ", future.read)
+
+  else:
+
+    sock.bindAddr(TPort(6667))
+    sock.listen()
+    proc onAccept(future: PFuture[TSocketHandle]) =
+      echo "Accepted"
+      var t = p.send(future.read, "test\c\L")
+      t.callback =
+        proc (future: PFuture[int]) =
+          echo(future.read)
+      
+      var f = p.accept(sock)
+      f.callback = onAccept
+      
+    var f = p.accept(sock)
+    f.callback = onAccept
+  
+  while true:
+    p.poll()
+    echo "polled"
+
+
+
+
+
+  
+
+  
\ No newline at end of file
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
new file mode 100644
index 000000000..bdcae677e
--- /dev/null
+++ b/lib/pure/net.nim
@@ -0,0 +1,40 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a high-level cross-platform sockets interface.
+
+import sockets2, os
+
+type
+  TSocket* = TSocketHandle
+
+proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
+  tags: [FReadIO].} =
+
+  ## 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: TSockaddr_in
+    when defined(windows):
+      name.sin_family = toInt(AF_INET).int16
+    else:
+      name.sin_family = toInt(AF_INET)
+    name.sin_port = htons(int16(port))
+    name.sin_addr.s_addr = htonl(INADDR_ANY)
+    if bindAddr(socket, cast[ptr TSockAddr](addr(name)),
+                  sizeof(name).TSocklen) < 0'i32:
+      osError(osLastError())
+  else:
+    var aiList = getAddrInfo(address, port, AF_INET)
+    if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
+      dealloc(aiList)
+      osError(osLastError())
+    dealloc(aiList)
\ No newline at end of file
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
new file mode 100644
index 000000000..83c158da1
--- /dev/null
+++ b/lib/pure/selectors.nim
@@ -0,0 +1,249 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2013 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# TODO: Docs.
+
+import tables, os, unsigned
+when defined(windows):
+  import winlean
+else:
+  import posix
+
+type
+  TEvent* = enum
+    EvRead, EvWrite
+
+  TSelectorKey* = object
+    fd: cint
+    events: set[TEvent]
+    data: PObject
+
+  TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
+
+  PSelector* = ref object of PObject ## Selector interface.
+    fds*: TTable[cint, TSelectorKey]
+    registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
+                    data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
+    unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
+    selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
+    closeImpl*: proc (s: PSelector) {.nimcall.}
+
+template initSelector(r: expr) =
+  new r
+  r.fds = initTable[cint, TSelectorKey]()
+
+proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
+    TSelectorKey =
+  if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
+
+proc unregister*(s: PSelector, fd: cint): TSelectorKey =
+  ##
+  ## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
+  ## will only have the ``fd`` field set. This is an optimisation and may
+  ## change in the future if a viable use case is presented. 
+  if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
+
+proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
+  ##
+  ## The ``events`` field of the returned ``key`` contains the original events
+  ## for which the ``fd`` was bound. This is contrary to the ``events`` field
+  ## of the ``TReadyInfo`` tuple which determines which events are ready
+  ## on the ``fd``.
+
+  if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
+
+proc close*(s: PSelector) =
+  if not s.closeImpl.isNil: s.closeImpl(s)
+
+# ---- Select() ----------------------------------------------------------------
+
+type
+  PSelectSelector* = ref object of PSelector ## Implementation of select()
+
+proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
+    data: PObject): TSelectorKey =
+  if s.fds.hasKey(fd):
+    raise newException(EInvalidValue, "FD already exists in selector.")
+  var sk = TSelectorKey(fd: fd, events: events, data: data)
+  s.fds[fd] = sk
+  result = sk
+
+proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
+  result = s.fds[fd]
+  s.fds.del(fd)
+
+proc ssClose(s: PSelector) = nil
+
+proc timeValFromMilliseconds(timeout: int): TTimeVal =
+  if timeout != -1:
+    var seconds = timeout div 1000
+    result.tv_sec = seconds.int32
+    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+
+proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
+    m: var int) =
+  FD_ZERO(rd); FD_ZERO(wr)
+  for k, v in pairs(fds):
+    if EvRead in v.events: 
+      m = max(m, int(k))
+      FD_SET(k, rd)
+    if EvWrite in v.events:
+      m = max(m, int(k))
+      FD_SET(k, wr)
+   
+proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
+    seq[TReadyInfo] =
+  result = @[]
+  for k, v in pairs(fds):
+    var events: set[TEvent] = {}
+    if FD_ISSET(k, rd) != 0'i32:
+      events = events + {EvRead}
+    if FD_ISSET(k, wr) != 0'i32:
+      events = events + {EvWrite}
+    result.add((v, events))
+
+proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
+  seq[TReadyInfo] =
+  var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
+  
+  var rd, wr: TFdSet
+  var m = 0
+  createFdSet(rd, wr, fds, m)
+  
+  var retCode = 0
+  if timeout != -1:
+    retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
+  else:
+    retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
+  
+  if retCode < 0:
+    OSError(OSLastError())
+  elif retCode == 0:
+    return @[]
+  else:
+    return getReadyFDs(rd, wr, fds)
+
+proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
+  result = select(s.fds, timeout)
+
+proc newSelectSelector*(): PSelectSelector =
+  initSelector(result)
+  result.registerImpl = ssRegister
+  result.unregisterImpl = ssUnregister
+  result.selectImpl = ssSelect
+  result.closeImpl = ssClose
+
+# ---- Epoll -------------------------------------------------------------------
+
+when defined(linux):
+  import epoll
+  type
+    PEpollSelector* = ref object of PSelector
+      epollFD: cint
+      events: array[64, ptr epoll_event]
+  
+    TDataWrapper = object
+      fd: cint
+      boundEvents: set[TEvent] ## The events which ``fd`` listens for.
+      data: PObject ## User object.
+  
+  proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
+      data: PObject): TSelectorKey =
+    var es = PEpollSelector(s)
+    var event: epoll_event
+    if EvRead in events:
+      event.events = EPOLLIN
+    if EvWrite in events:
+      event.events = event.events or EPOLLOUT
+    
+    var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
+    dw.fd = fd
+    dw.boundEvents = events
+    dw.data = data
+    event.data.thePtr = dw
+    
+    if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
+      OSError(OSLastError())
+    
+    result = TSelectorKey(fd: fd, events: events, data: data)
+  
+  proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
+    # We cannot find out the information about this ``fd`` from the epoll
+    # context. As such I will simply return an almost empty TSelectorKey.
+    var es = PEpollSelector(s)
+    if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
+      OSError(OSLastError())
+    # We could fill in the ``fds`` TTable to get the info, but that wouldn't
+    # be nice for our memory.
+    result = TSelectorKey(fd: fd, events: {}, data: nil)
+
+  proc esClose(s: PSelector) =
+    var es = PEpollSelector(s)
+    if es.epollFD.close() != 0: OSError(OSLastError())
+    dealloc(addr es.events) # TODO: Test this
+  
+  proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
+    result = @[]
+    var es = PEpollSelector(s)
+    
+    let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
+    if evNum < 0: OSError(OSLastError())
+    if evNum == 0: return @[]
+    for i in 0 .. <evNum:
+      var evSet: set[TEvent] = {}
+      if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
+      if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
+      let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
+      
+      let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents, 
+          data: dw.data)
+      result.add((selectorKey, evSet))
+  
+  proc newEpollSelector*(): PEpollSelector =
+    new result
+    result.epollFD = epoll_create(64)
+    result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
+    if result.epollFD < 0:
+      OSError(OSLastError())
+    result.registerImpl = esRegister
+    result.unregisterImpl = esUnregister
+    result.closeImpl = esClose
+    result.selectImpl = esSelect
+
+when isMainModule:
+  # Select()
+  import sockets
+  type
+    PSockWrapper = ref object of PObject
+      sock: TSocket
+  
+  var sock = socket()
+  sock.connect("irc.freenode.net", TPort(6667))
+  
+  var selector = newEpollSelector()
+  var data = PSockWrapper(sock: sock)
+  let key = selector.register(sock.getFD.cint, {EvRead}, data)
+  var i = 0
+  while true:
+    let ready = selector.select(1000)
+    echo ready.len
+    if ready.len > 0: echo ready[0].events
+    i.inc
+    if i == 6:
+      selector.close()
+      break
+  
+  
+  
+  
+  
+  
+  
+  
+  
\ No newline at end of file
diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim
new file mode 100644
index 000000000..22624bbad
--- /dev/null
+++ b/lib/pure/sockets2.nim
@@ -0,0 +1,202 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a low-level cross-platform sockets interface. Look
+## at the ``net`` module for the higher-level version.
+
+import unsigned, os
+
+when hostos == "solaris":
+  {.passl: "-lsocket -lnsl".}
+
+when defined(Windows):
+  import winlean
+else:
+  import posix
+
+export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
+  inet_ntoa
+
+type
+  
+  TPort* = distinct uint16  ## port type
+  
+  TDomain* = 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.
+
+  TType* = 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
+
+  TProtocol* = 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.
+
+  TServent* {.pure, final.} = object ## information about a service
+    name*: string
+    aliases*: seq[string]
+    port*: TPort
+    proto*: string
+
+  Thostent* {.pure, final.} = object ## information about a given host
+    name*: string
+    aliases*: seq[string]
+    addrtype*: TDomain
+    length*: int
+    addrList*: seq[string]
+
+when defined(windows):
+  let
+    OSInvalidSocket* = winlean.INVALID_SOCKET
+else:
+  let
+    OSInvalidSocket* = posix.INVALID_SOCKET
+
+proc `==`*(a, b: TPort): bool {.borrow.}
+  ## ``==`` for ports.
+
+proc `$`*(p: TPort): string {.borrow.}
+  ## returns the port number as a string
+
+proc toInt*(domain: TDomain): cint
+  ## Converts the TDomain enum to a platform-dependent ``cint``.
+
+proc toInt*(typ: TType): cint
+  ## Converts the TType enum to a platform-dependent ``cint``.
+
+proc toInt*(p: TProtocol): cint
+  ## Converts the TProtocol enum to a platform-dependent ``cint``.
+
+when defined(posix):
+  proc toInt(domain: TDomain): 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: nil
+
+  proc toInt(typ: TType): 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: nil
+
+  proc toInt(p: TProtocol): 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: nil
+
+else:
+  proc toInt(domain: TDomain): cint = 
+    result = toU16(ord(domain))
+
+  proc toInt(typ: TType): cint =
+    result = cint(ord(typ))
+  
+  proc toInt(p: TProtocol): cint =
+    result = cint(ord(p))
+
+
+proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
+             protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
+  ## Creates a new socket; returns `InvalidSocket` if an error occurs.
+  
+  # TODO: The function which will use this will raise EOS.
+  socket(toInt(domain), toInt(typ), toInt(protocol))
+
+proc close*(socket: TSocketHandle) =
+  ## closes a socket.
+  when defined(windows):
+    discard winlean.closeSocket(socket)
+  else:
+    discard posix.close(socket)
+  # 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
+
+proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
+  result = bindSocket(socket, name, namelen)
+
+proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
+  ## Marks ``socket`` as accepting connections. 
+  ## ``Backlog`` specifies the maximum length of the 
+  ## queue of pending connections.
+  when defined(windows):
+    if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
+  else:
+    if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
+
+proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
+                 prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
+  ##
+  ##
+  ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
+  var hints: TAddrInfo
+  result = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(typ)
+  hints.ai_protocol = toInt(prot)
+  var gaiResult = getAddrInfo(address, $port, addr(hints), result)
+  if gaiResult != 0'i32:
+    when defined(windows):
+      OSError(OSLastError())
+    else:
+      raise newException(EOS, $gai_strerror(gaiResult))
+
+proc dealloc*(ai: ptr TAddrInfo) =
+  freeaddrinfo(ai)
+
+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 = sockets2.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 = sockets2.ntohs(x)
+
+when defined(Windows):
+  var wsa: TWSADATA
+  if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
\ No newline at end of file
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 5b641185e..6c8fa4882 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -16,8 +16,12 @@ const
 type
   THandle* = int
   LONG* = int32
+  ULONG* = int
+  PULONG* = ptr int
   WINBOOL* = int32
   DWORD* = int32
+  PDWORD* = ptr DWORD
+  LPINT* = ptr int32
   HDC* = THandle
   HGLRC* = THandle
 
@@ -632,3 +636,76 @@ when not useWinUnicode:
 proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "UnmapViewOfFile".}
 
+type
+  TOVERLAPPED* {.final, pure.} = object
+    Internal*: DWORD
+    InternalHigh*: DWORD
+    Offset*: DWORD
+    OffsetHigh*: DWORD
+    hEvent*: THANDLE
+
+  POVERLAPPED* = ptr TOVERLAPPED
+
+  POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
+      para3: POVERLAPPED){.stdcall.}
+
+  TGUID* {.final, pure.} = object
+    D1*: int32
+    D2*: int16
+    D3*: int16
+    D4*: array [0..7, int8]
+
+const
+  ERROR_IO_PENDING* = 997
+
+proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE,
+                             CompletionKey: DWORD,
+                             NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall,
+    dynlib: "kernel32", importc: "CreateIoCompletionPort".}
+
+proc GetQueuedCompletionStatus*(CompletionPort: THandle,
+    lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
+                                lpOverlapped: ptr POverlapped,
+                                dwMilliseconds: DWORD): WINBOOL{.stdcall,
+    dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
+
+const 
+ IOC_OUT* = 0x40000000
+ IOC_IN*  = 0x80000000
+ IOC_WS2* = 0x08000000
+ IOC_INOUT* = IOC_IN or IOC_OUT
+
+template WSAIORW*(x,y): expr = (IOC_INOUT or x or y)
+
+const
+  SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
+  SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
+
+var
+  WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
+    0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
+  WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
+    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
+  WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
+    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
+
+proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
+  cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
+  lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED,
+  lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint 
+  {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
+
+type
+  TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
+    len*: ULONG
+    buf*: cstring
+
+proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
+  bytesReceived, flags: PDWORD, lpOverlapped: POverlapped,
+  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
+  stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
+
+proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
+  bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
+  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
+  stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}