parent
9500780037
commit
6f155af802
7 changed files with 183 additions and 26 deletions
|
|
@ -18,42 +18,137 @@ export Port, SocketFlag
|
||||||
#{.injectStmt: newGcInvariant().}
|
#{.injectStmt: newGcInvariant().}
|
||||||
|
|
||||||
## AsyncDispatch
|
## AsyncDispatch
|
||||||
## -------------
|
## *************
|
||||||
##
|
##
|
||||||
## This module implements a brand new dispatcher based on Futures.
|
## This module implements asynchronous IO. This includes a dispatcher,
|
||||||
## On Windows IOCP is used and on other operating systems the ``selectors``
|
## a ``Future`` type implementation, and an ``async`` macro which allows
|
||||||
## module is used instead.
|
## asynchronous code to be written in a synchronous style with the ``await``
|
||||||
|
## keyword.
|
||||||
##
|
##
|
||||||
## **Note:** This module is still largely experimental.
|
## The dispatcher acts as a kind of event loop. You must call ``poll`` on it
|
||||||
|
## (or a function which does so for you such as ``waitFor`` or ``runForever``)
|
||||||
|
## in order to poll for any outstanding events. The underlying implementation
|
||||||
|
## is based on epoll on Linux, IO Completion Ports on Windows and select on
|
||||||
|
## other operating systems.
|
||||||
|
##
|
||||||
|
## The ``poll`` function will not, on its own, return any events. Instead
|
||||||
|
## an appropriate ``Future`` object will be completed. A ``Future`` is a
|
||||||
|
## type which holds a value which is not yet available, but which *may* be
|
||||||
|
## available in the future. You can check whether a future is finished
|
||||||
|
## by using the ``finished`` function. When a future is finished it means that
|
||||||
|
## either the value that it holds is now available or it holds an error instead.
|
||||||
|
## The latter situation occurs when the operation to complete a future fails
|
||||||
|
## with an exception. You can distinguish between the two situations with the
|
||||||
|
## ``failed`` function.
|
||||||
|
##
|
||||||
|
## Future objects can also store a callback procedure which will be called
|
||||||
|
## automatically once the future completes.
|
||||||
|
##
|
||||||
|
## Futures therefore can be thought of as an implementation of the proactor
|
||||||
|
## pattern. In this
|
||||||
|
## pattern you make a request for an action, and once that action is fulfilled
|
||||||
|
## a future is completed with the result of that action. Requests can be
|
||||||
|
## made by calling the appropriate functions. For example: calling the ``recv``
|
||||||
|
## function will create a request for some data to be read from a socket. The
|
||||||
|
## future which the ``recv`` function returns will then complete once the
|
||||||
|
## requested amount of data is read **or** an exception occurs.
|
||||||
|
##
|
||||||
|
## Code to read some data from a socket may look something like this:
|
||||||
|
##
|
||||||
|
## .. code-block::nim
|
||||||
|
## var future = socket.recv(100)
|
||||||
|
## future.callback =
|
||||||
|
## proc () =
|
||||||
|
## echo(future.read)
|
||||||
|
##
|
||||||
|
## All asynchronous functions returning a ``Future`` will not block. They
|
||||||
|
## will not however return immediately. An asynchronous function will have
|
||||||
|
## code which will be executed before an asynchronous request is made, in most
|
||||||
|
## cases this code sets up the request.
|
||||||
|
##
|
||||||
|
## In the above example, the ``recv`` function will return a brand new
|
||||||
|
## ``Future`` instance once the request for data to be read from the socket
|
||||||
|
## is made. This ``Future`` instance will complete once the requested amount
|
||||||
|
## of data is read, in this case it is 100 bytes. The second line sets a
|
||||||
|
## callback on this future which will be called once the future completes.
|
||||||
|
## All the callback does is write the data stored in the future to ``stdout``.
|
||||||
|
## The ``read`` function is used for this and it checks whether the future
|
||||||
|
## completes with an error for you (if it did it will simply raise the
|
||||||
|
## error), if there is no error however it returns the value of the future.
|
||||||
|
##
|
||||||
|
## Asynchronous procedures
|
||||||
|
## -----------------------
|
||||||
|
##
|
||||||
|
## Asynchronous procedures remove the pain of working with callbacks. They do
|
||||||
|
## this by allowing you to write asynchronous code the same way as you would
|
||||||
|
## write synchronous code.
|
||||||
|
##
|
||||||
|
## An asynchronous procedure is marked using the ``{.async.}`` pragma.
|
||||||
|
## When marking a procedure with the ``{.async.}`` pragma it must have a
|
||||||
|
## ``Future[T]`` return type or no return type at all. If you do not specify
|
||||||
|
## a return type then ``Future[void]`` is assumed.
|
||||||
|
##
|
||||||
|
## Inside asynchronous procedures ``await`` can be used to call any
|
||||||
|
## procedures which return a
|
||||||
|
## ``Future``; this includes asynchronous procedures. When a procedure is
|
||||||
|
## "awaited", the asynchronous procedure it is awaited in will
|
||||||
|
## suspend its execution
|
||||||
|
## until the awaited procedure's Future completes. At which point the
|
||||||
|
## asynchronous procedure will resume its execution. During the period
|
||||||
|
## when an asynchronous procedure is suspended other asynchronous procedures
|
||||||
|
## will be run by the dispatcher.
|
||||||
|
##
|
||||||
|
## The ``await`` call may be used in many contexts. It can be used on the right
|
||||||
|
## hand side of a variable declaration: ``var data = await socket.recv(100)``,
|
||||||
|
## in which case the variable will be set to the value of the future
|
||||||
|
## automatically. It can be used to await a ``Future`` object, and it can
|
||||||
|
## be used to await a procedure returning a ``Future[void]``:
|
||||||
|
## ``await socket.send("foobar")``.
|
||||||
|
##
|
||||||
|
## Discarding futures
|
||||||
|
## ------------------
|
||||||
|
##
|
||||||
|
## Futures should **never** be discarded. This is because they may contain
|
||||||
|
## errors. If you do not care for the result of a Future then you should
|
||||||
|
## use the ``asyncCheck`` procedure instead of the ``discard`` keyword.
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## For examples take a look at the documentation for the modules implementing
|
||||||
|
## asynchronous IO. A good place to start is the
|
||||||
|
## `asyncnet module <asyncnet.html>`_.
|
||||||
|
##
|
||||||
|
## Limitations/Bugs
|
||||||
|
## ----------------
|
||||||
|
##
|
||||||
|
## * ``except`` statement (without `try`) does not work inside async procedures.
|
||||||
|
## * The effect system (``raises: []``) does not work with async procedures.
|
||||||
|
## * Can't await in a ``except`` body
|
||||||
|
|
||||||
|
|
||||||
# TODO: ``except`` statement (without `try`) does not work.
|
|
||||||
# TODO: Multiple exception names in a ``except`` don't work.
|
|
||||||
# TODO: The effect system (raises: []) has trouble with my try transformation.
|
|
||||||
# TODO: Can't await in a 'except' body
|
|
||||||
# TODO: getCurrentException(Msg) don't work
|
|
||||||
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
||||||
|
|
||||||
# -- Futures
|
# -- Futures
|
||||||
|
|
||||||
type
|
type
|
||||||
FutureBase* = ref object of RootObj
|
FutureBase* = ref object of RootObj ## Untyped future.
|
||||||
cb: proc () {.closure,gcsafe.}
|
cb: proc () {.closure,gcsafe.}
|
||||||
finished: bool
|
finished: bool
|
||||||
error*: ref Exception
|
error*: ref Exception ## Stored exception
|
||||||
errorStackTrace*: string
|
errorStackTrace*: string
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
stackTrace: string ## For debugging purposes only.
|
stackTrace: string ## For debugging purposes only.
|
||||||
id: int
|
id: int
|
||||||
fromProc: string
|
fromProc: string
|
||||||
|
|
||||||
Future*[T] = ref object of FutureBase
|
Future*[T] = ref object of FutureBase ## Typed future.
|
||||||
value: T
|
value: T ## Stored value
|
||||||
|
|
||||||
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
||||||
|
|
||||||
|
|
||||||
var currentID* = 0
|
var currentID = 0
|
||||||
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
||||||
## Creates a new future.
|
## Creates a new future.
|
||||||
##
|
##
|
||||||
|
|
@ -161,6 +256,10 @@ proc read*[T](future: Future[T]): T =
|
||||||
raise newException(ValueError, "Future still in progress.")
|
raise newException(ValueError, "Future still in progress.")
|
||||||
|
|
||||||
proc readError*[T](future: Future[T]): ref Exception =
|
proc readError*[T](future: Future[T]): ref Exception =
|
||||||
|
## Retrieves the exception stored in ``future``.
|
||||||
|
##
|
||||||
|
## An ``ValueError`` exception will be thrown if no exception exists
|
||||||
|
## in the specified Future.
|
||||||
if future.error != nil: return future.error
|
if future.error != nil: return future.error
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "No error in future.")
|
raise newException(ValueError, "No error in future.")
|
||||||
|
|
@ -173,7 +272,7 @@ proc finished*[T](future: Future[T]): bool =
|
||||||
|
|
||||||
proc failed*(future: FutureBase): bool =
|
proc failed*(future: FutureBase): bool =
|
||||||
## Determines whether ``future`` completed with an error.
|
## Determines whether ``future`` completed with an error.
|
||||||
future.error != nil
|
return future.error != nil
|
||||||
|
|
||||||
proc asyncCheck*[T](future: Future[T]) =
|
proc asyncCheck*[T](future: Future[T]) =
|
||||||
## Sets a callback on ``future`` which raises an exception if the future
|
## Sets a callback on ``future`` which raises an exception if the future
|
||||||
|
|
@ -950,7 +1049,7 @@ proc accept*(socket: TAsyncFD,
|
||||||
|
|
||||||
# -- Await Macro
|
# -- Await Macro
|
||||||
|
|
||||||
template createCb*(retFutureSym, iteratorNameSym,
|
template createCb(retFutureSym, iteratorNameSym,
|
||||||
name: expr): stmt {.immediate.} =
|
name: expr): stmt {.immediate.} =
|
||||||
var nameIterVar = iteratorNameSym
|
var nameIterVar = iteratorNameSym
|
||||||
#{.push stackTrace: off.}
|
#{.push stackTrace: off.}
|
||||||
|
|
@ -1193,7 +1292,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
# -> createCb(retFuture)
|
# -> createCb(retFuture)
|
||||||
var cbName = newIdentNode("cb")
|
var cbName = newIdentNode("cb")
|
||||||
var procCb = newCall("createCb", retFutureSym, iteratorNameSym,
|
var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym,
|
||||||
newStrLitNode(prc[0].getName))
|
newStrLitNode(prc[0].getName))
|
||||||
outerProcBody.add procCb
|
outerProcBody.add procCb
|
||||||
|
|
||||||
|
|
@ -1232,7 +1331,10 @@ proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
|
||||||
## is read) then line will be set to ``""``.
|
## is read) then line will be set to ``""``.
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
## **Warning**: This assumes that lines are delimited by ``\r\l``.
|
## **Warning**: This assumes that lines are delimited by ``\r\L``.
|
||||||
|
##
|
||||||
|
## **Note**: This procedure is mostly used for testing. You likely want to
|
||||||
|
## use ``asyncnet.recvLine`` instead.
|
||||||
|
|
||||||
template addNLIfEmpty(): stmt =
|
template addNLIfEmpty(): stmt =
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,24 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
## This module implement an asynchronous FTP client.
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## .. code-block::nim
|
||||||
|
##
|
||||||
|
## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
||||||
|
## proc main(ftp: AsyncFtpClient) {.async.} =
|
||||||
|
## await ftp.connect()
|
||||||
|
## echo await ftp.pwd()
|
||||||
|
## echo await ftp.listDirs()
|
||||||
|
## await ftp.store("payload.jpg", "payload.jpg")
|
||||||
|
## await ftp.retrFile("payload.jpg", "payload2.jpg")
|
||||||
|
## echo("Finished")
|
||||||
|
##
|
||||||
|
## waitFor main(ftp)
|
||||||
|
|
||||||
import asyncdispatch, asyncnet, strutils, parseutils, os, times
|
import asyncdispatch, asyncnet, strutils, parseutils, os, times
|
||||||
|
|
||||||
from ftpclient import FtpBaseObj, ReplyError, FtpEvent
|
from ftpclient import FtpBaseObj, ReplyError, FtpEvent
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,20 @@
|
||||||
|
|
||||||
## This module implements a high performance asynchronous HTTP server.
|
## This module implements a high performance asynchronous HTTP server.
|
||||||
##
|
##
|
||||||
## **Note:** This module is still largely experimental.
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## This example will create an HTTP server on port 8080. The server will
|
||||||
|
## respond to all requests with a ``200 OK`` response code and "Hello World"
|
||||||
|
## as the response body.
|
||||||
|
##
|
||||||
|
## .. code-block::nim
|
||||||
|
## var server = newAsyncHttpServer()
|
||||||
|
## proc cb(req: TRequest) {.async.} =
|
||||||
|
## await req.respond(Http200, "Hello World")
|
||||||
|
##
|
||||||
|
## asyncCheck server.serve(Port(8080), cb)
|
||||||
|
## runForever()
|
||||||
|
|
||||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||||
type
|
type
|
||||||
|
|
@ -52,6 +65,7 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
|
||||||
result = protocol.major == major and protocol.minor == minor
|
result = protocol.major == major and protocol.minor == minor
|
||||||
|
|
||||||
proc newAsyncHttpServer*(): PAsyncHttpServer =
|
proc newAsyncHttpServer*(): PAsyncHttpServer =
|
||||||
|
## Creates a new ``AsyncHttpServer`` instance.
|
||||||
new result
|
new result
|
||||||
|
|
||||||
proc addHeaders(msg: var string, headers: PStringTable) =
|
proc addHeaders(msg: var string, headers: PStringTable) =
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,20 @@
|
||||||
## This module implements a high-level asynchronous sockets API based on the
|
## This module implements a high-level asynchronous sockets API based on the
|
||||||
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
||||||
##
|
##
|
||||||
## Example
|
## SSL
|
||||||
## =======
|
## ---
|
||||||
|
##
|
||||||
|
## SSL can be enabled by compiling with the ``-d:ssl`` flag.
|
||||||
|
##
|
||||||
|
## You must create a new SSL context with the ``newContext`` function defined
|
||||||
|
## in the ``net`` module. You may then call ``wrapSocket`` on your socket using
|
||||||
|
## the newly created SSL context to get an SSL socket.
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## Chat server
|
||||||
|
## ^^^^^^^^^^^
|
||||||
##
|
##
|
||||||
## The following example demonstrates a simple chat server.
|
## The following example demonstrates a simple chat server.
|
||||||
##
|
##
|
||||||
|
|
@ -41,8 +53,6 @@
|
||||||
## asyncCheck serve()
|
## asyncCheck serve()
|
||||||
## runForever()
|
## runForever()
|
||||||
##
|
##
|
||||||
##
|
|
||||||
## **Note:** This module is still largely experimental.
|
|
||||||
|
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
import rawsockets
|
import rawsockets
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nim's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,19 @@
|
||||||
##
|
##
|
||||||
## echo(postContent("http://validator.w3.org/check", headers, body))
|
## echo(postContent("http://validator.w3.org/check", headers, body))
|
||||||
##
|
##
|
||||||
|
## Asynchronous HTTP requests
|
||||||
|
## ==========================
|
||||||
|
##
|
||||||
|
## You simply have to create a new instance of the ``AsyncHttpClient`` object.
|
||||||
|
## You may then use ``await`` on the functions defined for that object.
|
||||||
|
## Keep in mind that the following code needs to be inside an asynchronous
|
||||||
|
## procedure.
|
||||||
|
##
|
||||||
|
## .. code-block::nim
|
||||||
|
##
|
||||||
|
## var client = newAsyncHttpClient()
|
||||||
|
## var resp = await client.request("http://google.com")
|
||||||
|
##
|
||||||
## SSL/TLS support
|
## SSL/TLS support
|
||||||
## ===============
|
## ===============
|
||||||
## This requires the OpenSSL library, fortunately it's widely used and installed
|
## This requires the OpenSSL library, fortunately it's widely used and installed
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ when defined(Nimdoc): # only for proper documentation:
|
||||||
## search patch components (as in PATH), such as ':' for POSIX or ';' for
|
## search patch components (as in PATH), such as ':' for POSIX or ';' for
|
||||||
## Windows.
|
## Windows.
|
||||||
|
|
||||||
FileSystemCaseSensitive* = True
|
FileSystemCaseSensitive* = true
|
||||||
## True if the file system is case sensitive, false otherwise. Used by
|
## True if the file system is case sensitive, false otherwise. Used by
|
||||||
## `cmpPaths` to compare filenames properly.
|
## `cmpPaths` to compare filenames properly.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue