# Module asyncio

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 TObject!

Note: If you want to provide async ability to your module please do not use the `TDelegate` object, instead use `PAsyncSocket`. 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 `TDelegate` type. The `PAsyncSocket` is what you are after. It's a reference to the `TAsyncSocket` 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:

    var disp: PDispatcher = newDispatcher()
    ...
    proc handleAccept(s: PAsyncSocket) =
      echo("Accepted client.")
      var client: PAsyncSocket
      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 a PAsyncSocket

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 TSocket object from the PAsyncSocket object, this can then be combined with `accept` like so:

    proc handleAccept(s: PAsyncSocket) =
      var client: TSocket
      getSocket(s).accept(client)

# Imports

sockets, os, posix

# Types

    TDelegate = object
      fd*: cint
      deleVal*: PObject
      handleRead*: proc (h: PObject) {.nimcall.}
      handleWrite*: proc (h: PObject) {.nimcall.}
      handleError*: proc (h: PObject) {.nimcall.}
      hasDataBuffered*: proc (h: PObject): bool {.nimcall.}
      open*: bool
      task*: proc (h: PObject) {.nimcall.}
      mode*: TFileMode

    PDelegate = ref TDelegate

    PDispatcher = ref TDispatcher

    PAsyncSocket = ref TAsyncSocket

    TAsyncSocket = object of TObject
      socket: TSocket
      info: TInfo
      handleRead*: proc (s: PAsyncSocket) {.closure.}
      handleWrite: proc (s: PAsyncSocket) {.closure.}
      handleConnect*: proc (s: PAsyncSocket) {.closure.}
      handleAccept*: proc (s: PAsyncSocket) {.closure.}
      handleTask*: proc (s: PAsyncSocket) {.closure.}
      lineBuffer: TaintedString
      sendBuffer: string          ## Temporary storage for ``send``
      sslNeedAccept: bool
      proto: TProtocol
      deleg: PDelegate

  Temporary storage for `readLine`

    TInfo = enum
      SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
      SockUDPBound

# Procs

    proc newDelegate(): PDelegate {.raises: [], tags: [].}

  Creates a new delegate.

    proc AsyncSocket(domain: TDomain = AF_INET; typ: TType = SOCK_STREAM;
                     protocol: TProtocol = IPPROTO_TCP; buffered = true): PAsyncSocket {.
        raises: [EOS], tags: [].}

  Initialises an AsyncSocket object. If a socket cannot be initialised EOS is raised.

    proc toAsyncSocket(sock: TSocket; state: TInfo = SockConnected): PAsyncSocket {.
        raises: [EOS], tags: [].}

  Wraps an already initialized `TSocket` into a PAsyncSocket. This is useful if you want to use an already connected TSocket as an asynchronous PAsyncSocket 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 `PAsyncSocket` object may not work properly.

  Note: This will set `sock` to be non-blocking.

    proc connect(sock: PAsyncSocket; name: string; port = TPort(0);
                 af: TDomain = AF_INET) {.raises: [EOS], tags: [FReadIO].}

  Begins connecting `sock` to `name`:`port`.

    proc close(sock: PAsyncSocket) {.raises: [], tags: [].}

  Closes `sock`. Terminates any current connections.

    proc bindAddr(sock: PAsyncSocket; port = TPort(0); address = "") {.
        raises: [EOS], tags: [FReadIO].}

  Equivalent to `sockets.bindAddr`.

    proc listen(sock: PAsyncSocket) {.raises: [EOS], tags: [FReadIO].}

  Equivalent to `sockets.listen`.

    proc acceptAddr(server: PAsyncSocket; client: var PAsyncSocket;
                    address: var string) {.raises: [EOS], tags: [FReadIO].}

  Equivalent to `sockets.acceptAddr`. This procedure should be called in a `handleAccept` event handler only once.

  Note: `client` needs to be initialised.

    proc accept(server: PAsyncSocket; client: var PAsyncSocket) {.raises: [EOS],
        tags: [FReadIO].}

  Equivalent to `sockets.accept`.

    proc acceptAddr(server: PAsyncSocket): tuple[sock: PAsyncSocket, address: string] {.
        deprecated, raises: [EOS], tags: [FReadIO].}

  Equivalent to `sockets.acceptAddr`.

  Deprecated since version 0.9.0: Please use the function above.

    proc accept(server: PAsyncSocket): PAsyncSocket {.deprecated, raises: [EOS],
        tags: [FReadIO].}

  Equivalent to `sockets.accept`.

  Deprecated since version 0.9.0: Please use the function above.

    proc newDispatcher(): PDispatcher {.raises: [], tags: [].}

    proc register(d: PDispatcher; deleg: PDelegate) {.raises: [], tags: [].}

  Registers delegate `deleg` with dispatcher `d`.

    proc register(d: PDispatcher; sock: PAsyncSocket): PDelegate {.discardable,
        raises: [], tags: [].}

  Registers async socket `sock` with dispatcher `d`.

    proc unregister(d: PDispatcher; deleg: PDelegate) {.raises: [EInvalidIndex],
        tags: [].}

  Unregisters deleg `deleg` from dispatcher `d`.

    proc isWriteable(s: PAsyncSocket): bool {.raises: [], tags: [FReadIO].}

  Determines whether socket `s` is ready to be written to.

    proc isConnected(s: PAsyncSocket): bool {.raises: [], tags: [].}

  Determines whether `s` is connected.

    proc isListening(s: PAsyncSocket): bool {.raises: [], tags: [].}

  Determines whether `s` is listening for incoming connections.

    proc isConnecting(s: PAsyncSocket): bool {.raises: [], tags: [].}

  Determines whether `s` is connecting.

    proc isClosed(s: PAsyncSocket): bool {.raises: [], tags: [].}

  Determines whether `s` has been closed.

    proc setHandleWrite(s: PAsyncSocket;
                        handleWrite: proc (s: PAsyncSocket) {.closure.}) {.
        raises: [], tags: [].}

  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: PAsyncSocket) = nil` as that would mean that that function would be called constantly.

    proc delHandleWrite(s: PAsyncSocket) {.raises: [], tags: [].}

  Removes the `handleWrite` event handler on `s`.

    proc recvLine(s: PAsyncSocket; line: var TaintedString): bool {.deprecated,
        raises: [EOS], tags: [FReadIO].}

  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.

    proc readLine(s: PAsyncSocket; line: var TaintedString): bool {.raises: [EOS],
        tags: [FReadIO].}

  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.

    proc send(sock: PAsyncSocket; data: string) {.raises: [EOS], tags: [FWriteIO].}

  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.

    proc poll(d: PDispatcher; timeout: int = 500): bool {.raises: [E_Base],
        tags: [TEffect].}

  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.

    proc len(disp: PDispatcher): int {.raises: [], tags: [].}

  Retrieves the amount of delegates in `disp`.

# Converters

    converter getSocket(s: PAsyncSocket): TSocket {.raises: [], tags: [].}

Generated: 2013-05-21 09:55:24 UTC