summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/arch/arch.nim5
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/impure/re.nim2
-rw-r--r--lib/packages/docutils/highlite.nim15
-rw-r--r--lib/posix/kqueue.nim2
-rw-r--r--lib/pure/asyncdispatch.nim292
-rw-r--r--lib/pure/asyncftpclient.nim19
-rw-r--r--lib/pure/asynchttpserver.nim30
-rw-r--r--lib/pure/asyncmacro.nim51
-rw-r--r--lib/pure/asyncnet.nim51
-rw-r--r--lib/pure/basic2d.nim4
-rw-r--r--lib/pure/basic3d.nim6
-rw-r--r--lib/pure/collections/sequtils.nim4
-rw-r--r--lib/pure/collections/tables.nim47
-rw-r--r--lib/pure/httpclient.nim253
-rw-r--r--lib/pure/httpcore.nim8
-rw-r--r--lib/pure/includes/asyncfutures.nim295
-rw-r--r--lib/pure/ioselectors.nim38
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim51
-rw-r--r--lib/pure/json.nim2
-rw-r--r--lib/pure/net.nim22
-rw-r--r--lib/pure/os.nim161
-rw-r--r--lib/pure/ospaths.nim4
-rw-r--r--lib/pure/osproc.nim72
-rw-r--r--lib/pure/parsexml.nim32
-rw-r--r--lib/pure/random.nim4
-rw-r--r--lib/pure/rationals.nim2
-rw-r--r--lib/pure/selectors.nim3
-rw-r--r--lib/pure/stats.nim26
-rw-r--r--lib/pure/times.nim34
-rw-r--r--lib/pure/unittest.nim24
-rw-r--r--lib/pure/xmlparser.nim5
-rw-r--r--lib/stdlib.nimble2
-rw-r--r--lib/system.nim14
-rw-r--r--lib/system/alloc.nim4
-rw-r--r--lib/system/avltree.nim8
-rw-r--r--lib/system/channels.nim54
-rw-r--r--lib/system/deepcopy.nim19
-rw-r--r--lib/system/dyncalls.nim5
-rw-r--r--lib/system/gc2.nim12
-rw-r--r--lib/system/gc_common.nim1
-rw-r--r--lib/system/gc_stack.nim7
-rw-r--r--lib/system/osalloc.nim2
-rw-r--r--lib/system/sysio.nim27
-rw-r--r--lib/system/sysstr.nim5
-rw-r--r--lib/system/threads.nim2
-rw-r--r--lib/upcoming/asyncdispatch.nim368
-rw-r--r--lib/windows/registry.nim77
-rw-r--r--lib/wrappers/linenoise/clinenoise.c4
-rw-r--r--lib/wrappers/linenoise/linenoise.nim3
-rw-r--r--lib/wrappers/mysql.nim4
-rw-r--r--lib/wrappers/openssl.nim2
52 files changed, 1261 insertions, 925 deletions
diff --git a/lib/arch/arch.nim b/lib/arch/arch.nim
index a11bfb21f..0b3df3d3c 100644
--- a/lib/arch/arch.nim
+++ b/lib/arch/arch.nim
@@ -6,6 +6,9 @@
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
+# Architecture-specific optimizations and features.
+# arch.nim can be imported by only a subset of the
+# architectures supported by Nim.
 
 when defined(windows):
   const
@@ -31,7 +34,7 @@ when defined(amd64):
     Reg* {.pure.} = enum
       AX, BX, CX, DX, SI, DI, BP, SP, IP, R8, R9, R10, R11, R12, R13, R14, R15, TOTAL
 
-elif defined(i386):
+elif defined(i386) or defined(nimdoc):
     # identical fastcall calling convention on all x86 OS
     type
       JmpBufReg* {.pure.} = enum
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 19452b4a8..292e8dd3c 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -92,7 +92,7 @@ type
     ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64,
     ntyBigNum,
     ntyConst, ntyMutable, ntyVarargs,
-    ntyIter,
+    ntyUnused,
     ntyError,
     ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite,
     ntyAnd, ntyOr, ntyNot
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index bf397550a..bd86bcdcf 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -359,7 +359,7 @@ proc parallelReplace*(s: string, subs: openArray[
 proc transformFile*(infile, outfile: string,
                     subs: openArray[tuple[pattern: Regex, repl: string]]) =
   ## reads in the file `infile`, performs a parallel replacement (calls
-  ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
+  ## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
   ## error occurs. This is supposed to be used for quick scripting.
   var x = readFile(infile).string
   writeFile(outfile, x.parallelReplace(subs))
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 9de25f82b..06b90768c 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -242,14 +242,17 @@ proc nimNextToken(g: var GeneralTokenizer) =
       inc(pos)
       case g.buf[pos]
       of 'b', 'B':
+        g.kind = gtBinNumber
         inc(pos)
         while g.buf[pos] in binChars: inc(pos)
         pos = nimNumberPostfix(g, pos)
       of 'x', 'X':
+        g.kind = gtHexNumber
         inc(pos)
         while g.buf[pos] in hexChars: inc(pos)
         pos = nimNumberPostfix(g, pos)
       of 'o', 'O':
+        g.kind = gtOctNumber
         inc(pos)
         while g.buf[pos] in octChars: inc(pos)
         pos = nimNumberPostfix(g, pos)
@@ -700,8 +703,8 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       g.state = gtLongStringLit
   elif g.state == gtLongStringLit:
     # beware, this is the only token where we actually have to parse
-    # indentation. 
-    
+    # indentation.
+
     g.kind = gtLongStringLit
     # first, we have to find the parent indentation of the block scalar, so that
     # we know when to stop
@@ -738,20 +741,20 @@ proc yamlNextToken(g: var GeneralTokenizer) =
     # because lookbehind was at newline char when calculating indentation, we're
     # off by one. fix that. top level's parent will have indentation of -1.
     let parentIndentation = indentation - 1
-    
+
     # find first content
     while g.buf[pos] in {' ', '\x0A', '\x0D'}:
       if g.buf[pos] == ' ': inc(indentation)
       else: indentation = 0
       inc(pos)
     var minIndentation = indentation
-    
+
     # for stupid edge cases, we must check whether an explicit indentation depth
     # is given at the header.
     while g.buf[headerStart] in {'>', '|', '+', '-'}: inc(headerStart)
     if g.buf[headerStart] in {'0'..'9'}:
       minIndentation = min(minIndentation, ord(g.buf[headerStart]) - ord('0'))
-    
+
     # process content lines
     while indentation > parentIndentation and g.buf[pos] != '\0':
       if (indentation < minIndentation and g.buf[pos] == '#') or
@@ -766,7 +769,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
         if g.buf[pos] == ' ': inc(indentation)
         else: indentation = 0
         inc(pos)
-    
+
     g.state = gtOther
   elif g.state == gtOther:
     # gtOther means 'inside YAML document'
diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim
index 5c67d621e..19d661490 100644
--- a/lib/posix/kqueue.nim
+++ b/lib/posix/kqueue.nim
@@ -123,7 +123,7 @@ when defined(macosx) or defined(freebsd):
     NOTE_USECONDS*   = 0x00000004'u32 ## data is microseconds
     NOTE_NSECONDS*   = 0x00000008'u32 ## data is nanoseconds
 else:
-  # NetBSD and OpenBSD doesnt support NOTE_{TIME} constants, but
+  # NetBSD and OpenBSD doesn't support NOTE_{TIME} constants, but
   # support EVFILT_TIMER with granularity of milliseconds.
   const
     NOTE_MSECONDS*   = 0x00000000'u32
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index f9085de55..8c4a0e41d 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -132,7 +132,7 @@ export Port, SocketFlag
 ##        # Handle exception
 ##
 ## Unfortunately the semantics of the try statement may not always be correct,
-## and occassionally the compilation may fail altogether.
+## and occasionally the compilation may fail altogether.
 ## As such it is better to use the former style when possible.
 ##
 ## Discarding futures
@@ -156,290 +156,10 @@ export Port, SocketFlag
 ## * Can't await in a ``except`` body
 ## * Forward declarations for async procs are broken,
 ##   link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
-## * FutureVar[T] needs to be completed manually.
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
-# -- Futures
-
-type
-  FutureBase* = ref object of RootObj ## Untyped future.
-    cb: proc () {.closure,gcsafe.}
-    finished: bool
-    error*: ref Exception ## Stored exception
-    errorStackTrace*: string
-    when not defined(release):
-      stackTrace: string ## For debugging purposes only.
-      id: int
-      fromProc: string
-
-  Future*[T] = ref object of FutureBase ## Typed future.
-    value: T ## Stored value
-
-  FutureVar*[T] = distinct Future[T]
-
-  FutureError* = object of Exception
-    cause*: FutureBase
-
-{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
-
-when not defined(release):
-  var currentID = 0
-
-proc callSoon*(cbproc: proc ()) {.gcsafe.}
-
-proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
-  ## Creates a new future.
-  ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
-  ## that this future belongs to, is a good habit as it helps with debugging.
-  new(result)
-  result.finished = false
-  when not defined(release):
-    result.stackTrace = getStackTrace()
-    result.id = currentID
-    result.fromProc = fromProc
-    currentID.inc()
-
-proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
-  ## Create a new ``FutureVar``. This Future type is ideally suited for
-  ## situations where you want to avoid unnecessary allocations of Futures.
-  ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
-  ## that this future belongs to, is a good habit as it helps with debugging.
-  result = FutureVar[T](newFuture[T](fromProc))
-
-proc clean*[T](future: FutureVar[T]) =
-  ## Resets the ``finished`` status of ``future``.
-  Future[T](future).finished = false
-  Future[T](future).error = nil
-
-proc checkFinished[T](future: Future[T]) =
-  ## Checks whether `future` is finished. If it is then raises a
-  ## ``FutureError``.
-  when not defined(release):
-    if future.finished:
-      var msg = ""
-      msg.add("An attempt was made to complete a Future more than once. ")
-      msg.add("Details:")
-      msg.add("\n  Future ID: " & $future.id)
-      msg.add("\n  Created in proc: " & future.fromProc)
-      msg.add("\n  Stack trace to moment of creation:")
-      msg.add("\n" & indent(future.stackTrace.strip(), 4))
-      when T is string:
-        msg.add("\n  Contents (string): ")
-        msg.add("\n" & indent(future.value.repr, 4))
-      msg.add("\n  Stack trace to moment of secondary completion:")
-      msg.add("\n" & indent(getStackTrace().strip(), 4))
-      var err = newException(FutureError, msg)
-      err.cause = future
-      raise err
-
-proc complete*[T](future: Future[T], val: T) =
-  ## Completes ``future`` with value ``val``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  assert(future.error == nil)
-  future.value = val
-  future.finished = true
-  if future.cb != nil:
-    future.cb()
-
-proc complete*(future: Future[void]) =
-  ## Completes a void ``future``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  assert(future.error == nil)
-  future.finished = true
-  if future.cb != nil:
-    future.cb()
-
-proc complete*[T](future: FutureVar[T]) =
-  ## Completes a ``FutureVar``.
-  template fut: expr = Future[T](future)
-  checkFinished(fut)
-  assert(fut.error == nil)
-  fut.finished = true
-  if fut.cb != nil:
-    fut.cb()
-
-proc fail*[T](future: Future[T], error: ref Exception) =
-  ## Completes ``future`` with ``error``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  future.finished = true
-  future.error = error
-  future.errorStackTrace =
-    if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
-  if future.cb != nil:
-    future.cb()
-  else:
-    # This is to prevent exceptions from being silently ignored when a future
-    # is discarded.
-    # TODO: This may turn out to be a bad idea.
-    # Turns out this is a bad idea.
-    #raise error
-    discard
-
-proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when the future completes.
-  ##
-  ## If future has already completed then ``cb`` will be called immediately.
-  ##
-  ## **Note**: You most likely want the other ``callback`` setter which
-  ## passes ``future`` as a param to the callback.
-  future.cb = cb
-  if future.finished:
-    callSoon(future.cb)
-
-proc `callback=`*[T](future: Future[T],
-    cb: proc (future: Future[T]) {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when the future completes.
-  ##
-  ## If future has already completed then ``cb`` will be called immediately.
-  future.callback = proc () = cb(future)
-
-proc injectStacktrace[T](future: Future[T]) =
-  # TODO: Come up with something better.
-  when not defined(release):
-    var msg = ""
-    msg.add("\n  " & future.fromProc & "'s lead up to read of failed Future:")
-
-    if not future.errorStackTrace.isNil and future.errorStackTrace != "":
-      msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
-    else:
-      msg.add("\n    Empty or nil stack trace.")
-    future.error.msg.add(msg)
-
-proc read*[T](future: Future[T]): T =
-  ## Retrieves the value of ``future``. Future must be finished otherwise
-  ## this function will fail with a ``ValueError`` exception.
-  ##
-  ## If the result of the future is an error then that error will be raised.
-  if future.finished:
-    if future.error != nil:
-      injectStacktrace(future)
-      raise future.error
-    when T isnot void:
-      return future.value
-  else:
-    # TODO: Make a custom exception type for this?
-    raise newException(ValueError, "Future still in progress.")
-
-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
-  else:
-    raise newException(ValueError, "No error in future.")
-
-proc mget*[T](future: FutureVar[T]): var T =
-  ## Returns a mutable value stored in ``future``.
-  ##
-  ## Unlike ``read``, this function will not raise an exception if the
-  ## Future has not been finished.
-  result = Future[T](future).value
-
-proc finished*[T](future: Future[T]): bool =
-  ## Determines whether ``future`` has completed.
-  ##
-  ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
-  future.finished
-
-proc failed*(future: FutureBase): bool =
-  ## Determines whether ``future`` completed with an error.
-  return future.error != nil
-
-proc asyncCheck*[T](future: Future[T]) =
-  ## Sets a callback on ``future`` which raises an exception if the future
-  ## finished with an error.
-  ##
-  ## This should be used instead of ``discard`` to discard void futures.
-  future.callback =
-    proc () =
-      if future.failed:
-        injectStacktrace(future)
-        raise future.error
-
-proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once both ``fut1`` and ``fut2``
-  ## complete.
-  var retFuture = newFuture[void]("asyncdispatch.`and`")
-  fut1.callback =
-    proc () =
-      if not retFuture.finished:
-        if fut1.failed: retFuture.fail(fut1.error)
-        elif fut2.finished: retFuture.complete()
-  fut2.callback =
-    proc () =
-      if not retFuture.finished:
-        if fut2.failed: retFuture.fail(fut2.error)
-        elif fut1.finished: retFuture.complete()
-  return retFuture
-
-proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once either ``fut1`` or ``fut2``
-  ## complete.
-  var retFuture = newFuture[void]("asyncdispatch.`or`")
-  proc cb[X](fut: Future[X]) =
-    if fut.failed: retFuture.fail(fut.error)
-    if not retFuture.finished: retFuture.complete()
-  fut1.callback = cb[T]
-  fut2.callback = cb[Y]
-  return retFuture
-
-proc all*[T](futs: varargs[Future[T]]): auto =
-  ## Returns a future which will complete once
-  ## all futures in ``futs`` complete.
-  ##
-  ## If the awaited futures are not ``Future[void]``, the returned future
-  ## will hold the values of all awaited futures in a sequence.
-  ##
-  ## If the awaited futures *are* ``Future[void]``,
-  ## this proc returns ``Future[void]``.
-
-  when T is void:
-    var
-      retFuture = newFuture[void]("asyncdispatch.all")
-      completedFutures = 0
-
-    let totalFutures = len(futs)
-
-    for fut in futs:
-      fut.callback = proc(f: Future[T]) =
-        if f.failed:
-          retFuture.fail(f.error)
-        elif not retFuture.finished:
-          inc(completedFutures)
-
-          if completedFutures == totalFutures:
-            retFuture.complete()
-
-    return retFuture
-
-  else:
-    var
-      retFuture = newFuture[seq[T]]("asyncdispatch.all")
-      retValues = newSeq[T](len(futs))
-      completedFutures = 0
-
-    for i, fut in futs:
-      proc setCallback(i: int) =
-        fut.callback = proc(f: Future[T]) =
-          if f.failed:
-            retFuture.fail(f.error)
-          elif not retFuture.finished:
-            retValues[i] = f.read()
-            inc(completedFutures)
-
-            if completedFutures == len(retValues):
-              retFuture.complete(retValues)
-
-      setCallback(i)
-
-    return retFuture
+include includes/asyncfutures
 
 type
   PDispatcherBase = ref object of RootRef
@@ -1335,7 +1055,7 @@ else:
         # so that exceptions can be raised from `send(...)` and
         # `recv(...)` routines.
 
-        if EvRead in info.events:
+        if EvRead in info.events or info.events == {EvError}:
           # Callback may add items to ``data.readCBs`` which causes issues if
           # we are iterating over ``data.readCBs`` at the same time. We therefore
           # make a copy to iterate over.
@@ -1346,7 +1066,7 @@ else:
               # Callback wants to be called again.
               data.readCBs.add(cb)
 
-        if EvWrite in info.events:
+        if EvWrite in info.events or info.events == {EvError}:
           let currentCBs = data.writeCBs
           data.writeCBs = @[]
           for cb in currentCBs:
@@ -1646,7 +1366,7 @@ proc send*(socket: AsyncFD, data: string,
 # -- Await Macro
 include asyncmacro
 
-proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
+proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
   ## Reads a line of data from ``socket``. Returned future will complete once
   ## a full line is read or an error occurs.
   ##
@@ -1664,6 +1384,8 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
   ##
   ## **Note**: This procedure is mostly used for testing. You likely want to
   ## use ``asyncnet.recvLine`` instead.
+  ##
+  ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
 
   template addNLIfEmpty(): stmt =
     if result.len == 0:
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index 037d55e1d..019a18f55 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -354,6 +354,20 @@ proc store*(ftp: AsyncFtpClient, file, dest: string,
 
   await doUpload(ftp, destFile, onProgressChanged)
 
+proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
+  ## Rename a file or directory on the remote FTP Server from current name
+  ## ``name_from`` to new name ``name_to``
+  assertReply(await ftp.send("RNFR " & name_from), "350")
+  assertReply(await ftp.send("RNTO " & name_to), "250")
+
+proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} =
+  ## Delete a file ``filename`` on the remote FTP server
+  assertReply(await ftp.send("DELE " & filename), "250")
+
+proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
+  ## Delete a directory ``dir`` on the remote FTP server
+  assertReply(await ftp.send("RMD " & dir), "250")
+
 proc newAsyncFtpClient*(address: string, port = Port(21),
     user, pass = ""): AsyncFtpClient =
   ## Creates a new ``AsyncFtpClient`` object.
@@ -373,6 +387,11 @@ when not defined(testing) and isMainModule:
     echo await ftp.listDirs()
     await ftp.store("payload.jpg", "payload.jpg")
     await ftp.retrFile("payload.jpg", "payload2.jpg")
+    await ftp.rename("payload.jpg", "payload_renamed.jpg")
+    await ftp.store("payload.jpg", "payload_remove.jpg")
+    await ftp.removeFile("payload_remove.jpg")
+    await ftp.createDir("deleteme")
+    await ftp.removeDir("deleteme")
     echo("Finished")
 
   waitFor main(ftp)
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 6a7326e83..a658097f9 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -9,6 +9,12 @@
 
 ## This module implements a high performance asynchronous HTTP server.
 ##
+## This HTTP server has not been designed to be used in production, but
+## for testing applications locally. Because of this, when deploying your
+## application you should use a reverse proxy (for example nginx) instead of
+## allowing users to connect directly to this server.
+##
+##
 ## Examples
 ## --------
 ##
@@ -38,7 +44,7 @@ export httpcore except parseHeader
 type
   Request* = object
     client*: AsyncSocket # TODO: Separate this into a Response object?
-    reqMethod*: string
+    reqMethod*: HttpMethod
     headers*: HttpHeaders
     protocol*: tuple[orig: string, major, minor: int]
     url*: Uri
@@ -127,7 +133,14 @@ proc processClient(client: AsyncSocket, address: string,
     var i = 0
     for linePart in lineFut.mget.split(' '):
       case i
-      of 0: request.reqMethod.shallowCopy(linePart.normalize)
+      of 0:
+        try:
+          # TODO: this is likely slow.
+          request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
+        except ValueError:
+          asyncCheck request.respond(Http400, "Invalid request method. Got: " &
+                                     linePart)
+          continue
       of 1: parseUri(linePart, request.url)
       of 2:
         try:
@@ -159,7 +172,7 @@ proc processClient(client: AsyncSocket, address: string,
         request.client.close()
         return
 
-    if request.reqMethod == "post":
+    if request.reqMethod == HttpPost:
       # Check for Expect header
       if request.headers.hasKey("Expect"):
         if "100-continue" in request.headers["Expect"]:
@@ -178,17 +191,12 @@ proc processClient(client: AsyncSocket, address: string,
       else:
         request.body = await client.recv(contentLength)
         assert request.body.len == contentLength
-    elif request.reqMethod == "post":
+    elif request.reqMethod == HttpPost:
       await request.respond(Http400, "Bad Request. No Content-Length.")
       continue
 
-    case request.reqMethod
-    of "get", "post", "head", "put", "delete", "trace", "options",
-       "connect", "patch":
-      await callback(request)
-    else:
-      await request.respond(Http400, "Invalid request method. Got: " &
-        request.reqMethod)
+    # Call the user's callback.
+    await callback(request)
 
     if "upgrade" in request.headers.getOrDefault("connection"):
       return
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index f70afaafa..3d004e84c 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -25,7 +25,7 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
     result = node[0]
 
 template createCb(retFutureSym, iteratorNameSym,
-                   name: untyped) =
+                  name, futureVarCompletions: untyped) =
   var nameIterVar = iteratorNameSym
   #{.push stackTrace: off.}
   proc cb {.closure,gcsafe.} =
@@ -44,6 +44,8 @@ template createCb(retFutureSym, iteratorNameSym,
         raise
       else:
         retFutureSym.fail(getCurrentException())
+
+      futureVarCompletions
   cb()
   #{.pop.}
 proc generateExceptionCheck(futSym,
@@ -119,8 +121,22 @@ template createVar(result: var NimNode, futSymName: string,
   result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
   useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
 
+proc createFutureVarCompletions(futureVarIdents: seq[NimNode]): NimNode
+                                {.compileTime.} =
+  result = newStmtList()
+  # Add calls to complete each FutureVar parameter.
+  for ident in futureVarIdents:
+    # Only complete them if they have not been completed already by the user.
+    result.add newIfStmt(
+      (
+        newCall(newIdentNode("not"),
+                newDotExpr(ident, newIdentNode("finished"))),
+        newCall(newIdentNode("complete"), ident)
+      )
+    )
+
 proc processBody(node, retFutureSym: NimNode,
-                 subTypeIsVoid: bool,
+                 subTypeIsVoid: bool, futureVarIdents: seq[NimNode],
                  tryStmt: NimNode): NimNode {.compileTime.} =
   #echo(node.treeRepr)
   result = node
@@ -134,11 +150,14 @@ proc processBody(node, retFutureSym: NimNode,
       else:
         result.add newCall(newIdentNode("complete"), retFutureSym)
     else:
-      let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
+      let x = node[0].processBody(retFutureSym, subTypeIsVoid,
+                                  futureVarIdents, tryStmt)
       if x.kind == nnkYieldStmt: result.add x
       else:
         result.add newCall(newIdentNode("complete"), retFutureSym, x)
 
+    result.add createFutureVarCompletions(futureVarIdents)
+
     result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
     return # Don't process the children of this return stmt
   of nnkCommand, nnkCall:
@@ -196,7 +215,8 @@ proc processBody(node, retFutureSym: NimNode,
       # Transform ``except`` body.
       # TODO: Could we perform some ``await`` transformation here to get it
       # working in ``except``?
-      tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
+      tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid,
+                               futureVarIdents, nil)
 
     proc processForTry(n: NimNode, i: var int,
                        res: NimNode): bool {.compileTime.} =
@@ -207,7 +227,7 @@ proc processBody(node, retFutureSym: NimNode,
       var skipped = n.skipStmtList()
       while i < skipped.len:
         var processed = processBody(skipped[i], retFutureSym,
-                                    subTypeIsVoid, n)
+                                    subTypeIsVoid, futureVarIdents, n)
 
         # Check if we transformed the node into an exception check.
         # This suggests skipped[i] contains ``await``.
@@ -239,7 +259,8 @@ proc processBody(node, retFutureSym: NimNode,
   else: discard
 
   for i in 0 .. <result.len:
-    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
+    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
+                            futureVarIdents, nil)
 
 proc getName(node: NimNode): string {.compileTime.} =
   case node.kind
@@ -252,6 +273,14 @@ proc getName(node: NimNode): string {.compileTime.} =
   else:
     error("Unknown name.")
 
+proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
+  result = @[]
+  for i in 1 .. <len(params):
+    expectKind(params[i], nnkIdentDefs)
+    if params[i][1].kind == nnkBracketExpr and
+       ($params[i][1][0].ident).normalize == "futurevar":
+      result.add(params[i][0])
+
 proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   ## This macro transforms a single procedure into a closure iterator.
   ## The ``async`` macro supports a stmtList holding multiple async procedures.
@@ -282,6 +311,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   let subtypeIsVoid = returnType.kind == nnkEmpty or
         (baseType.kind == nnkIdent and returnType[1].ident == !"void")
 
+  let futureVarIdents = getFutureVarIdents(prc[3])
+
   var outerProcBody = newNimNode(nnkStmtList, prc[6])
 
   # -> var retFuture = newFuture[T]()
@@ -304,7 +335,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   # ->   <proc_body>
   # ->   complete(retFuture, result)
   var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
-  var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
+  var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid,
+                                    futureVarIdents, nil)
   # don't do anything with forward bodies (empty)
   if procBody.kind != nnkEmpty:
     if not subtypeIsVoid:
@@ -326,6 +358,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
       # -> complete(retFuture)
       procBody.add(newCall(newIdentNode("complete"), retFutureSym))
 
+    procBody.add(createFutureVarCompletions(futureVarIdents))
+
     var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
                                   procBody, nnkIteratorDef)
     closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
@@ -334,7 +368,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
     # -> createCb(retFuture)
     #var cbName = newIdentNode("cb")
     var procCb = getAst createCb(retFutureSym, iteratorNameSym,
-                         newStrLitNode(prc[0].getName))
+                         newStrLitNode(prc[0].getName),
+                         createFutureVarCompletions(futureVarIdents))
     outerProcBody.add procCb
 
     # -> return retFuture
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 334f95baa..3b64c278f 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -159,7 +159,9 @@ when defineSsl:
       await socket.fd.AsyncFd.send(data, flags)
 
   proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
-                  sslError: cint) {.async.} =
+                  sslError: cint): Future[bool] {.async.} =
+    ## Returns ``true`` if ``socket`` is still connected, otherwise ``false``.
+    result = true
     case sslError
     of SSL_ERROR_WANT_WRITE:
       await sendPendingSslData(socket, flags)
@@ -173,6 +175,7 @@ when defineSsl:
       elif length == 0:
         # connection not properly closed by remote side or connection dropped
         SSL_set_shutdown(socket.sslHandle, SSL_RECEIVED_SHUTDOWN)
+        result = false
     else:
       raiseSSLError("Cannot appease SSL.")
 
@@ -180,13 +183,27 @@ when defineSsl:
                    op: expr) =
     var opResult {.inject.} = -1.cint
     while opResult < 0:
+      # Call the desired operation.
       opResult = op
       # Bit hackish here.
       # TODO: Introduce an async template transformation pragma?
+
+      # Send any remaining pending SSL data.
       yield sendPendingSslData(socket, flags)
+
+      # If the operation failed, try to see if SSL has some data to read
+      # or write.
       if opResult < 0:
         let err = getSslError(socket.sslHandle, opResult.cint)
-        yield appeaseSsl(socket, flags, err.cint)
+        let fut = appeaseSsl(socket, flags, err.cint)
+        yield fut
+        if not fut.read():
+          # Socket disconnected.
+          if SocketFlag.SafeDisconn in flags:
+            break
+          else:
+            raiseSSLError("Socket has been disconnected")
+
 
 proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
   ## Connects ``socket`` to server at ``address:port``.
@@ -388,7 +405,7 @@ proc accept*(socket: AsyncSocket,
   return retFut
 
 proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
-    flags = {SocketFlag.SafeDisconn}) {.async.} =
+    flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} =
   ## Reads a line of data from ``socket`` into ``resString``.
   ##
   ## If a full line is read ``\r\L`` is not
@@ -401,13 +418,14 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
   ## is read) then line will be set to ``""``.
   ## The partial line **will be lost**.
   ##
+  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## that can be read before a ``ValueError`` is raised. This prevents Denial
+  ## of Service (DOS) attacks.
+  ##
   ## **Warning**: The ``Peek`` flag is not yet implemented.
   ##
   ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
   ## protocol uses ``\r\L`` to delimit a new line.
-  ##
-  ## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
-  ## performance reasons. This will likely change soon to use FutureVars.
   assert SocketFlag.Peek notin flags ## TODO:
   assert(not resString.mget.isNil(),
          "String inside resString future needs to be initialised")
@@ -454,6 +472,12 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
         else:
           resString.mget.add socket.buffer[socket.currPos]
       socket.currPos.inc()
+
+      # Verify that this isn't a DOS attack: #3847.
+      if resString.mget.len > maxLength:
+        let msg = "recvLine received more than the specified `maxLength` " &
+                  "allowed."
+        raise newException(ValueError, msg)
   else:
     var c = ""
     while true:
@@ -475,10 +499,17 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
         resString.complete()
         return
       resString.mget.add c
+
+      # Verify that this isn't a DOS attack: #3847.
+      if resString.mget.len > maxLength:
+        let msg = "recvLine received more than the specified `maxLength` " &
+                  "allowed."
+        raise newException(ValueError, msg)
   resString.complete()
 
 proc recvLine*(socket: AsyncSocket,
-    flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
+    flags = {SocketFlag.SafeDisconn},
+    maxLength = MaxLineLength): Future[string] {.async.} =
   ## Reads a line of data from ``socket``. Returned future will complete once
   ## a full line is read or an error occurs.
   ##
@@ -492,6 +523,10 @@ proc recvLine*(socket: AsyncSocket,
   ## is read) then line will be set to ``""``.
   ## The partial line **will be lost**.
   ##
+  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## that can be read before a ``ValueError`` is raised. This prevents Denial
+  ## of Service (DOS) attacks.
+  ##
   ## **Warning**: The ``Peek`` flag is not yet implemented.
   ##
   ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
@@ -501,7 +536,7 @@ proc recvLine*(socket: AsyncSocket,
   # TODO: Optimise this
   var resString = newFutureVar[string]("asyncnet.recvLine")
   resString.mget() = ""
-  await socket.recvLineInto(resString, flags)
+  await socket.recvLineInto(resString, flags, maxLength)
   result = resString.mget()
 
 proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim
index 7d74424fa..e4696c6a8 100644
--- a/lib/pure/basic2d.nim
+++ b/lib/pure/basic2d.nim
@@ -117,13 +117,13 @@ proc safeArccos(v:float):float=
 
 
 template makeBinOpVector(s:expr)=
-  ## implements binary operators + , - , * and / for vectors
+  ## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors
   proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
   proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.}  = vector2d(s(a.x,b),s(a.y,b))
   proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.}  = vector2d(s(a,b.x),s(a,b.y))
 
 template makeBinOpAssignVector(s:expr)=
-  ## implements inplace binary operators += , -= , /= and *= for vectors
+  ## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors
   proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
   proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
 
diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim
index 424c191f8..f7a9c237c 100644
--- a/lib/pure/basic3d.nim
+++ b/lib/pure/basic3d.nim
@@ -117,7 +117,6 @@ proc safeArccos(v:float):float=
   return arccos(clamp(v,-1.0,1.0))
 
 template makeBinOpVector(s:expr)=
-  ## implements binary operators + , - , * and / for vectors
   proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} =
     vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
   proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.}  =
@@ -126,11 +125,10 @@ template makeBinOpVector(s:expr)=
     vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
 
 template makeBinOpAssignVector(s:expr)=
-  ## implements inplace binary operators += , -= , /= and *= for vectors
   proc s*(a:var Vector3d,b:Vector3d) {.inline.} =
-    s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
+    s(a.x,b.x); s(a.y,b.y); s(a.z,b.z)
   proc s*(a:var Vector3d,b:float) {.inline.} =
-    s(a.x,b) ; s(a.y,b) ; s(a.z,b)
+    s(a.x,b); s(a.y,b); s(a.z,b)
 
 
 
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index f458b7636..45a148fbf 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -228,7 +228,7 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
   ##   var a = @["1", "2", "3", "4"]
   ##   echo repr(a)
   ##   # --> ["1", "2", "3", "4"]
-  ##   map(a, proc(x: var string) = x &= "42")
+  ##   apply(a, proc(x: var string) = x &= "42")
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
@@ -247,7 +247,7 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
   ##   var a = @["1", "2", "3", "4"]
   ##   echo repr(a)
   ##   # --> ["1", "2", "3", "4"]
-  ##   map(a, proc(x: string): string = x & "42")
+  ##   apply(a, proc(x: string): string = x & "42")
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 9fa8f5263..fe75f9a58 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -118,7 +118,11 @@ template dataLen(t): untyped = len(t.data)
 
 include tableimpl
 
-proc clear*[A, B](t: var Table[A, B] | TableRef[A, B]) =
+proc clear*[A, B](t: var Table[A, B]) =
+  ## Resets the table so that it is empty.
+  clearImpl()
+
+proc clear*[A, B](t: TableRef[A, B]) =
   ## Resets the table so that it is empty.
   clearImpl()
 
@@ -334,7 +338,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
   ## returns true iff `key` is in the table `t`.
   result = t[].hasKey(key)
 
-template equalsImpl(t) =
+template equalsImpl(s, t: typed): typed =
   if s.counter == t.counter:
     # different insertion orders mean different 'data' seqs, so we have
     # to use the slow route here:
@@ -344,7 +348,9 @@ template equalsImpl(t) =
     return true
 
 proc `==`*[A, B](s, t: Table[A, B]): bool =
-  equalsImpl(t)
+  ## The `==` operator for hash tables. Returns ``true`` iff the content of both
+  ## tables contains the same key-value pairs. Insert order does not matter.
+  equalsImpl(s, t)
 
 proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
   ## Index the collection with the proc provided.
@@ -432,9 +438,12 @@ proc `$`*[A, B](t: TableRef[A, B]): string =
   dollarImpl()
 
 proc `==`*[A, B](s, t: TableRef[A, B]): bool =
+  ## The `==` operator for hash tables. Returns ``true`` iff either both tables 
+  ## are ``nil`` or none is ``nil`` and the content of both tables contains the
+  ## same key-value pairs. Insert order does not matter.
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
-  else: equalsImpl(t[])
+  else: equalsImpl(s[], t[])
 
 proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
   ## Index the collection with the proc provided.
@@ -460,12 +469,16 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
   ## returns the number of keys in `t`.
   result = t.counter
 
-proc clear*[A, B](t: var OrderedTable[A, B] | OrderedTableRef[A, B]) =
+proc clear*[A, B](t: var OrderedTable[A, B]) =
   ## Resets the table so that it is empty.
   clearImpl()
   t.first = -1
   t.last = -1
 
+proc clear*[A, B](t: var OrderedTableRef[A, B]) =
+  ## Resets the table so that is is empty.
+  clear(t[])
+
 template forAllOrderedPairs(yieldStmt: untyped) {.oldimmediate, dirty.} =
   var h = t.first
   while h >= 0:
@@ -602,6 +615,15 @@ proc `$`*[A, B](t: OrderedTable[A, B]): string =
   ## The `$` operator for ordered hash tables.
   dollarImpl()
 
+proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
+  ## The `==` operator for ordered hash tables. Both the content and the order
+  ## must be equal for this to return ``true``.
+  if s.counter == t.counter:
+    forAllOrderedPairs:
+      if s.data[h] != t.data[h]: return false
+    result = true
+  else: result = false
+
 proc sort*[A, B](t: var OrderedTable[A, B],
                  cmp: proc (x,y: (A, B)): int) =
   ## sorts `t` according to `cmp`. This modifies the internal list
@@ -745,6 +767,11 @@ proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
   ## The `$` operator for ordered hash tables.
   dollarImpl()
 
+proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
+  ## The `==` operator for ordered hash tables. Both the content and the order
+  ## must be equal for this to return ``true``.
+  result = s[] == t[]
+
 proc sort*[A, B](t: OrderedTableRef[A, B],
                  cmp: proc (x,y: (A, B)): int) =
   ## sorts `t` according to `cmp`. This modifies the internal list
@@ -912,6 +939,11 @@ proc `$`*[A](t: CountTable[A]): string =
   ## The `$` operator for count tables.
   dollarImpl()
 
+proc `==`*[A](s, t: CountTable[A]): bool =
+  ## The `==` operator for count tables. Returns ``true`` iff both tables
+  ## contain the same keys with the same count. Insert order does not matter.
+  equalsImpl(s, t)
+
 proc inc*[A](t: var CountTable[A], key: A, val = 1) =
   ## increments `t[key]` by `val`.
   var index = rawGet(t, key)
@@ -1036,6 +1068,11 @@ proc `$`*[A](t: CountTableRef[A]): string =
   ## The `$` operator for count tables.
   dollarImpl()
 
+proc `==`*[A](s, t: CountTableRef[A]): bool =
+  ## The `==` operator for count tables. Returns ``true`` iff both tables
+  ## contain the same keys with the same count. Insert order does not matter.
+  result = s[] == t[]
+
 proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
   ## increments `t[key]` by `val`.
   t[].inc(key, val)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 27b3b46be..c56d13b57 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -8,79 +8,116 @@
 #
 
 ## This module implements a simple HTTP client that can be used to retrieve
-## webpages/other data.
-##
-##
-## **Note**: This module is not ideal, connection is not kept alive so sites with
-## many redirects are expensive. As such in the future this module may change,
-## and the current procedures will be deprecated.
+## webpages and other data.
 ##
 ## Retrieving a website
 ## ====================
 ##
 ## This example uses HTTP GET to retrieve
-## ``http://google.com``
+## ``http://google.com``:
 ##
 ## .. code-block:: Nim
-##   echo(getContent("http://google.com"))
+##   var client = newHttpClient()
+##   echo client.getContent("http://google.com")
+##
+## The same action can also be performed asynchronously, simply use the
+## ``AsyncHttpClient``:
+##
+## .. code-block:: Nim
+##   var client = newAsyncHttpClient()
+##   echo await client.getContent("http://google.com")
+##
+## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient``
+## is the same, so you can use whichever one suits you best in the examples
+## shown here.
+##
+## **Note:** You will need to run asynchronous examples in an async proc
+## otherwise you will get an ``Undeclared identifier: 'await'`` error.
 ##
 ## Using HTTP POST
 ## ===============
 ##
 ## This example demonstrates the usage of the W3 HTML Validator, it
-## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
-## the server.
+## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to be
+## validated to the server.
 ##
 ## .. code-block:: Nim
+##   var client = newHttpClient()
 ##   var data = newMultipartData()
 ##   data["output"] = "soap12"
 ##   data["uploaded_file"] = ("test.html", "text/html",
 ##     "<html><head></head><body><p>test</p></body></html>")
 ##
-##   echo postContent("http://validator.w3.org/check", multipart=data)
+##   echo client.postContent("http://validator.w3.org/check", multipart=data)
 ##
-## Asynchronous HTTP requests
-## ==========================
+## You can also make post requests with custom headers. 
+## This example sets ``Content-Type`` to ``application/json``
+## and uses a json object for the body
 ##
-## 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
+##   import httpclient, json
+##   
+##   let client = newHttpClient()
+##   client.headers = newHttpHeaders({ "Content-Type": "application/json" })
+##   let body = %*{
+##       "data": "some text"
+##   }
+##   echo client.request("http://some.api", httpMethod = HttpPost, body = $body)
+##
+## Progress reporting
+## ==================
 ##
-## .. code-block::nim
+## You may specify a callback procedure to be called during an HTTP request.
+## This callback will be executed every second with information about the
+## progress of the HTTP request.
 ##
+## .. code-block:: Nim
 ##    var client = newAsyncHttpClient()
-##    var resp = await client.request("http://google.com")
+##    proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
+##      echo("Downloaded ", progress, " of ", total)
+##      echo("Current rate: ", speed div 1000, "kb/s")
+##    client.onProgressChanged = onProgressChanged
+##    discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##
+## If you would like to remove the callback simply set it to ``nil``.
+##
+## .. code-block:: Nim
+##   client.onProgressChanged = nil
 ##
 ## SSL/TLS support
 ## ===============
 ## This requires the OpenSSL library, fortunately it's widely used and installed
 ## on many operating systems. httpclient will use SSL automatically if you give
 ## any of the functions a url with the ``https`` schema, for example:
-## ``https://github.com/``, you also have to compile with ``ssl`` defined like so:
+## ``https://github.com/``.
+##
+## You will also have to compile with ``ssl`` defined like so:
 ## ``nim c -d:ssl ...``.
 ##
 ## Timeouts
 ## ========
-## Currently all functions support an optional timeout, by default the timeout is set to
-## `-1` which means that the function will never time out. The timeout is
+##
+## Currently only the synchronous functions support a timeout.
+## The timeout is
 ## measured in milliseconds, once it is set any call on a socket which may
-## block will be susceptible to this timeout, however please remember that the
+## block will be susceptible to this timeout.
+##
+## It may be surprising but the
 ## function as a whole can take longer than the specified timeout, only
 ## individual internal calls on the socket are affected. In practice this means
 ## that as long as the server is sending data an exception will not be raised,
-## if however data does not reach client within the specified timeout an ETimeout
-## exception will then be raised.
+## if however data does not reach the client within the specified timeout a
+## ``TimeoutError`` exception will be raised.
 ##
 ## Proxy
 ## =====
 ##
-## A proxy can be specified as a param to any of these procedures, the ``newProxy``
-## constructor should be used for this purpose. However,
-## currently only basic authentication is supported.
+## A proxy can be specified as a param to any of the procedures defined in
+## this module. To do this, use the ``newProxy`` constructor. Unfortunately,
+## only basic authentication is supported at the moment.
 
 import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
-  math, random, httpcore
+  math, random, httpcore, times
 import asyncnet, asyncdispatch
 import nativesockets
 
@@ -379,15 +416,18 @@ proc format(p: MultipartData): tuple[header, body: string] =
 
 proc request*(url: string, httpMethod: string, extraHeaders = "",
               body = "", sslContext = defaultSSLContext, timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response =
+              userAgent = defUserAgent, proxy: Proxy = nil): Response
+              {.deprecated.} =
   ## | Requests ``url`` with the custom method string specified by the
   ## | ``httpMethod`` parameter.
   ## | Extra headers can be specified and must be separated by ``\c\L``
   ## | An optional timeout can be specified in milliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
   var r = if proxy == nil: parseUri(url) else: proxy.url
   var hostUrl = if proxy == nil: r else: parseUri(url)
-  var headers = substr(httpMethod, len("http")).toUpper()
+  var headers = httpMethod.toUpper()
   # TODO: Use generateHeaders further down once it supports proxies.
 
   var s = newSocket()
@@ -406,7 +446,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
 
 
   # get the socket ready. If we are connecting through a proxy to SSL,
-  # send the appropiate CONNECT header. If not, simply connect to the proper
+  # send the appropriate CONNECT header. If not, simply connect to the proper
   # host (which may still be the proxy, for normal HTTP)
   if proxy != nil and hostUrl.scheme == "https":
     when defined(ssl):
@@ -471,15 +511,18 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
   if body != "":
     s.send(body)
 
-  result = parseResponse(s, httpMethod != "httpHEAD", timeout)
+  result = parseResponse(s, httpMethod != "HEAD", timeout)
 
 proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
               body = "", sslContext = defaultSSLContext, timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response =
+              userAgent = defUserAgent, proxy: Proxy = nil): Response
+              {.deprecated.} =
   ## | Requests ``url`` with the specified ``httpMethod``.
   ## | Extra headers can be specified and must be separated by ``\c\L``
   ## | An optional timeout can be specified in milliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
   result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout,
                    userAgent, proxy)
 
@@ -502,12 +545,14 @@ proc getNewLocation(lastURL: string, headers: HttpHeaders): string =
 proc get*(url: string, extraHeaders = "", maxRedirects = 5,
           sslContext: SSLContext = defaultSSLContext,
           timeout = -1, userAgent = defUserAgent,
-          proxy: Proxy = nil): Response =
+          proxy: Proxy = nil): Response {.deprecated.} =
   ## | GETs the ``url`` and returns a ``Response`` object
   ## | This proc also handles redirection
   ## | Extra headers can be specified and must be separated by ``\c\L``.
   ## | An optional timeout can be specified in milliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
+  ##
+  ## ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
   result = request(url, httpGET, extraHeaders, "", sslContext, timeout,
                    userAgent, proxy)
   var lastURL = url
@@ -521,12 +566,14 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5,
 proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
                  sslContext: SSLContext = defaultSSLContext,
                  timeout = -1, userAgent = defUserAgent,
-                 proxy: Proxy = nil): string =
+                 proxy: Proxy = nil): string {.deprecated.} =
   ## | GETs the body and returns it as a string.
   ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
   ## | Extra headers can be specified and must be separated by ``\c\L``.
   ## | An optional timeout can be specified in milliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``HttpClient.getContent`` instead.
   var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent,
               proxy)
   if r.status[0] in {'4','5'}:
@@ -539,7 +586,7 @@ proc post*(url: string, extraHeaders = "", body = "",
            sslContext: SSLContext = defaultSSLContext,
            timeout = -1, userAgent = defUserAgent,
            proxy: Proxy = nil,
-           multipart: MultipartData = nil): Response =
+           multipart: MultipartData = nil): Response {.deprecated.} =
   ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
   ## | This proc adds the necessary Content-Length header.
   ## | This proc also handles redirection.
@@ -548,6 +595,8 @@ proc post*(url: string, extraHeaders = "", body = "",
   ## server takes longer than specified an ETimeout exception will be raised.
   ## | The optional ``multipart`` parameter can be used to create
   ## ``multipart/form-data`` POSTs comfortably.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead.
   let (mpHeaders, mpBody) = format(multipart)
 
   template withNewLine(x): expr =
@@ -577,7 +626,8 @@ proc postContent*(url: string, extraHeaders = "", body = "",
                   sslContext: SSLContext = defaultSSLContext,
                   timeout = -1, userAgent = defUserAgent,
                   proxy: Proxy = nil,
-                  multipart: MultipartData = nil): string =
+                  multipart: MultipartData = nil): string
+                  {.deprecated.} =
   ## | POSTs ``body`` to ``url`` and returns the response's body as a string
   ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
   ## | Extra headers can be specified and must be separated by ``\c\L``.
@@ -585,6 +635,9 @@ proc postContent*(url: string, extraHeaders = "", body = "",
   ## server takes longer than specified an ETimeout exception will be raised.
   ## | The optional ``multipart`` parameter can be used to create
   ## ``multipart/form-data`` POSTs comfortably.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``HttpClient.postContent``
+  ## instead.
   var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
                userAgent, proxy, multipart)
   if r.status[0] in {'4','5'}:
@@ -610,7 +663,7 @@ proc downloadFile*(url: string, outputFilename: string,
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
-  result = substr(httpMethod, len("http")).toUpper()
+  result = httpMethod.toUpper()
   result.add ' '
 
   if proxy.isNil:
@@ -653,17 +706,30 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
   add(result, "\c\L")
 
 type
+  ProgressChangedProc*[ReturnType] =
+    proc (total, progress, speed: BiggestInt):
+      ReturnType {.closure, gcsafe.}
+
   HttpClientBase*[SocketType] = ref object
     socket: SocketType
     connected: bool
     currentURL: Uri ## Where we are currently connected.
-    headers*: HttpHeaders
+    headers*: HttpHeaders ## Headers to send in requests.
     maxRedirects: int
     userAgent: string
     timeout: int ## Only used for blocking HttpClient for now.
     proxy: Proxy
+    ## ``nil`` or the callback to call when request progress changes.
+    when SocketType is Socket:
+      onProgressChanged*: ProgressChangedProc[void]
+    else:
+      onProgressChanged*: ProgressChangedProc[Future[void]]
     when defined(ssl):
       sslContext: net.SslContext
+    contentTotal: BiggestInt
+    contentProgress: BiggestInt
+    oneSecondProgress: BiggestInt
+    lastProgressReport: float
 
 type
   HttpClient* = HttpClientBase[Socket]
@@ -684,7 +750,7 @@ proc newHttpClient*(userAgent = defUserAgent,
   ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's
   ## connections.
   ##
-  ## ``timeout`` specifies the number of miliseconds to allow before a
+  ## ``timeout`` specifies the number of milliseconds to allow before a
   ## ``TimeoutError`` is raised.
   new result
   result.headers = newHttpHeaders()
@@ -692,6 +758,7 @@ proc newHttpClient*(userAgent = defUserAgent,
   result.maxRedirects = maxRedirects
   result.proxy = proxy
   result.timeout = timeout
+  result.onProgressChanged = nil
   when defined(ssl):
     result.sslContext = sslContext
 
@@ -721,6 +788,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
   result.maxRedirects = maxRedirects
   result.proxy = proxy
   result.timeout = -1 # TODO
+  result.onProgressChanged = nil
   when defined(ssl):
     result.sslContext = sslContext
 
@@ -730,19 +798,37 @@ proc close*(client: HttpClient | AsyncHttpClient) =
     client.socket.close()
     client.connected = false
 
-proc recvFull(socket: Socket | AsyncSocket,
+proc reportProgress(client: HttpClient | AsyncHttpClient,
+                    progress: BiggestInt) {.multisync.} =
+  client.contentProgress += progress
+  client.oneSecondProgress += progress
+  if epochTime() - client.lastProgressReport >= 1.0:
+    if not client.onProgressChanged.isNil:
+      await client.onProgressChanged(client.contentTotal,
+                                     client.contentProgress,
+                                     client.oneSecondProgress)
+      client.oneSecondProgress = 0
+      client.lastProgressReport = epochTime()
+
+proc recvFull(client: HttpClient | AsyncHttpClient,
               size: int, timeout: int): Future[string] {.multisync.} =
   ## Ensures that all the data requested is read and returned.
   result = ""
   while true:
     if size == result.len: break
-    when socket is Socket:
-      let data = socket.recv(size - result.len, timeout)
+
+    let remainingSize = size - result.len
+    let sizeToRecv = min(remainingSize, net.BufferSize)
+
+    when client.socket is Socket:
+      let data = client.socket.recv(sizeToRecv, timeout)
     else:
-      let data = await socket.recv(size - result.len)
+      let data = await client.socket.recv(sizeToRecv)
     if data == "": break # We've been disconnected.
     result.add data
 
+    await reportProgress(client, data.len)
+
 proc parseChunks(client: HttpClient | AsyncHttpClient): Future[string]
                  {.multisync.} =
   result = ""
@@ -770,10 +856,10 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[string]
         httpError("Invalid chunk size: " & chunkSizeStr)
       inc(i)
     if chunkSize <= 0:
-      discard await recvFull(client.socket, 2, client.timeout) # Skip \c\L
+      discard await recvFull(client, 2, client.timeout) # Skip \c\L
       break
-    result.add await recvFull(client.socket, chunkSize, client.timeout)
-    discard await recvFull(client.socket, 2, client.timeout) # Skip \c\L
+    result.add await recvFull(client, chunkSize, client.timeout)
+    discard await recvFull(client, 2, client.timeout) # Skip \c\L
     # Trailer headers will only be sent if the request specifies that we want
     # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
 
@@ -781,6 +867,12 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
                headers: HttpHeaders,
                httpVersion: string): Future[string] {.multisync.} =
   result = ""
+  # Reset progress from previous requests.
+  client.contentTotal = 0
+  client.contentProgress = 0
+  client.oneSecondProgress = 0
+  client.lastProgressReport = 0
+
   if headers.getOrDefault"Transfer-Encoding" == "chunked":
     result = await parseChunks(client)
   else:
@@ -789,8 +881,9 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
     var contentLengthHeader = headers.getOrDefault"Content-Length"
     if contentLengthHeader != "":
       var length = contentLengthHeader.parseint()
+      client.contentTotal = length
       if length > 0:
-        result = await client.socket.recvFull(length, client.timeout)
+        result = await client.recvFull(length, client.timeout)
         if result == "":
           httpError("Got disconnected while trying to read body.")
         if result.len != length:
@@ -804,7 +897,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
       if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
         var buf = ""
         while true:
-          buf = await client.socket.recvFull(4000, client.timeout)
+          buf = await client.recvFull(4000, client.timeout)
           if buf == "": break
           result.add(buf)
 
@@ -865,8 +958,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
 proc newConnection(client: HttpClient | AsyncHttpClient,
                    url: Uri) {.multisync.} =
   if client.currentURL.hostname != url.hostname or
-      client.currentURL.scheme != url.scheme:
-    if client.connected: client.close()
+      client.currentURL.scheme != url.scheme or
+      client.currentURL.port != url.port:
+    if client.connected:
+      client.close()
 
     when client is HttpClient:
       client.socket = newSocket()
@@ -902,8 +997,6 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   ## Connection will kept alive. Further requests on the same ``client`` to
   ## the same hostname will not require a new connection to be made. The
   ## connection can be closed by using the ``close`` procedure.
-  ##
-  ## The returned future will complete once the request is completed.
   let connectionUrl =
     if client.proxy.isNil: parseUri(url) else: client.proxy.url
   let requestUrl = parseUri(url)
@@ -933,14 +1026,15 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   if not client.headers.hasKey("user-agent") and client.userAgent != "":
     client.headers["User-Agent"] = client.userAgent
 
-  var headers = generateHeaders(requestUrl, $httpMethod,
+  var headers = generateHeaders(requestUrl, httpMethod,
                                 client.headers, body, client.proxy)
 
   await client.socket.send(headers)
   if body != "":
     await client.socket.send(body)
 
-  result = await parseResponse(client, httpMethod notin {HttpHead, HttpConnect})
+  result = await parseResponse(client,
+                               httpMethod.toLower() notin ["head", "connect"])
 
   # Restore the clients proxy in case it was overwritten.
   client.proxy = savedProxy
@@ -950,11 +1044,12 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   ## Connects to the hostname specified by the URL and performs a request
   ## using the method specified.
   ##
-  ## Connection will kept alive. Further requests on the same ``client`` to
+  ## Connection will be kept alive. Further requests on the same ``client`` to
   ## the same hostname will not require a new connection to be made. The
   ## connection can be closed by using the ``close`` procedure.
   ##
-  ## The returned future will complete once the request is completed.
+  ## When a request is made to a different hostname, the current connection will
+  ## be closed.
   result = await request(client, url, $httpMethod, body)
 
 proc get*(client: HttpClient | AsyncHttpClient,
@@ -964,6 +1059,8 @@ proc get*(client: HttpClient | AsyncHttpClient,
   ## This procedure will follow redirects up to a maximum number of redirects
   ## specified in ``client.maxRedirects``.
   result = await client.request(url, HttpGET)
+
+  # Handle redirects.
   var lastURL = url
   for i in 1..client.maxRedirects:
     if result.status.redirection():
@@ -971,6 +1068,21 @@ proc get*(client: HttpClient | AsyncHttpClient,
       result = await client.request(redirectTo, HttpGET)
       lastURL = redirectTo
 
+proc getContent*(client: HttpClient | AsyncHttpClient,
+                 url: string): Future[string] {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a GET request.
+  ##
+  ## This procedure will follow redirects up to a maximum number of redirects
+  ## specified in ``client.maxRedirects``.
+  ##
+  ## A ``HttpRequestError`` will be raised if the server responds with a
+  ## client error (status code 4xx) or a server error (status code 5xx).
+  let resp = await get(client, url)
+  if resp.code.is4xx or resp.code.is5xx:
+    raise newException(HttpRequestError, resp.status)
+  else:
+    return resp.body
+
 proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
            multipart: MultipartData = nil): Future[Response] {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a POST request.
@@ -990,3 +1102,28 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
   client.headers["Content-Length"] = $len(xb)
 
   result = await client.request(url, HttpPOST, xb)
+  # Handle redirects.
+  var lastURL = url
+  for i in 1..client.maxRedirects:
+    if result.status.redirection():
+      let redirectTo = getNewLocation(lastURL, result.headers)
+      var meth = if result.status != "307": HttpGet else: HttpPost
+      result = await client.request(redirectTo, meth, xb)
+      lastURL = redirectTo
+
+proc postContent*(client: HttpClient | AsyncHttpClient, url: string,
+                  body = "",
+                  multipart: MultipartData = nil): Future[string]
+                  {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a POST request.
+  ##
+  ## This procedure will follow redirects up to a maximum number of redirects
+  ## specified in ``client.maxRedirects``.
+  ##
+  ## A ``HttpRequestError`` will be raised if the server responds with a
+  ## client error (status code 4xx) or a server error (status code 5xx).
+  let resp = await post(client, url, body, multipart)
+  if resp.code.is4xx or resp.code.is5xx:
+    raise newException(HttpRequestError, resp.status)
+  else:
+    return resp.body
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index ba69c5669..8147f1c50 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -41,8 +41,9 @@ type
                       ## changing in the request.
     HttpOptions,      ## Returns the HTTP methods that the server supports
                       ## for specified address.
-    HttpConnect       ## Converts the request connection to a transparent
+    HttpConnect,      ## Converts the request connection to a transparent
                       ## TCP/IP tunnel, usually used for proxies.
+    HttpPatch         ## Applies partial modifications to a resource.
 
 {.deprecated: [httpGet: HttpGet, httpHead: HttpHead, httpPost: HttpPost,
                httpPut: HttpPut, httpDelete: HttpDelete, httpTrace: HttpTrace,
@@ -224,7 +225,7 @@ proc `$`*(code: HttpCode): string =
   ## For example:
   ##
   ##   .. code-block:: nim
-  ##       doAssert(Http404.status == "404 Not Found")
+  ##       doAssert($Http404 == "404 Not Found")
   case code.int
   of 100: "100 Continue"
   of 101: "101 Switching Protocols"
@@ -296,6 +297,9 @@ proc is5xx*(code: HttpCode): bool =
   ## Determines whether ``code`` is a 5xx HTTP status code.
   return code.int in {500 .. 599}
 
+proc `$`*(httpMethod: HttpMethod): string =
+  return (system.`$`(httpMethod))[4 .. ^1].toUpper()
+
 when isMainModule:
   var test = newHttpHeaders()
   test["Connection"] = @["Upgrade", "Close"]
diff --git a/lib/pure/includes/asyncfutures.nim b/lib/pure/includes/asyncfutures.nim
new file mode 100644
index 000000000..d78464a91
--- /dev/null
+++ b/lib/pure/includes/asyncfutures.nim
@@ -0,0 +1,295 @@
+
+# TODO: This shouldn't need to be included, but should ideally be exported.
+type
+  FutureBase* = ref object of RootObj ## Untyped future.
+    cb: proc () {.closure,gcsafe.}
+    finished: bool
+    error*: ref Exception ## Stored exception
+    errorStackTrace*: string
+    when not defined(release):
+      stackTrace: string ## For debugging purposes only.
+      id: int
+      fromProc: string
+
+  Future*[T] = ref object of FutureBase ## Typed future.
+    value: T ## Stored value
+
+  FutureVar*[T] = distinct Future[T]
+
+  FutureError* = object of Exception
+    cause*: FutureBase
+
+{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
+
+when not defined(release):
+  var currentID = 0
+
+proc callSoon*(cbproc: proc ()) {.gcsafe.}
+
+proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
+  ## Creates a new future.
+  ##
+  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## that this future belongs to, is a good habit as it helps with debugging.
+  new(result)
+  result.finished = false
+  when not defined(release):
+    result.stackTrace = getStackTrace()
+    result.id = currentID
+    result.fromProc = fromProc
+    currentID.inc()
+
+proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
+  ## Create a new ``FutureVar``. This Future type is ideally suited for
+  ## situations where you want to avoid unnecessary allocations of Futures.
+  ##
+  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## that this future belongs to, is a good habit as it helps with debugging.
+  result = FutureVar[T](newFuture[T](fromProc))
+
+proc clean*[T](future: FutureVar[T]) =
+  ## Resets the ``finished`` status of ``future``.
+  Future[T](future).finished = false
+  Future[T](future).error = nil
+
+proc checkFinished[T](future: Future[T]) =
+  ## Checks whether `future` is finished. If it is then raises a
+  ## ``FutureError``.
+  when not defined(release):
+    if future.finished:
+      var msg = ""
+      msg.add("An attempt was made to complete a Future more than once. ")
+      msg.add("Details:")
+      msg.add("\n  Future ID: " & $future.id)
+      msg.add("\n  Created in proc: " & future.fromProc)
+      msg.add("\n  Stack trace to moment of creation:")
+      msg.add("\n" & indent(future.stackTrace.strip(), 4))
+      when T is string:
+        msg.add("\n  Contents (string): ")
+        msg.add("\n" & indent(future.value.repr, 4))
+      msg.add("\n  Stack trace to moment of secondary completion:")
+      msg.add("\n" & indent(getStackTrace().strip(), 4))
+      var err = newException(FutureError, msg)
+      err.cause = future
+      raise err
+
+proc complete*[T](future: Future[T], val: T) =
+  ## Completes ``future`` with value ``val``.
+  #assert(not future.finished, "Future already finished, cannot finish twice.")
+  checkFinished(future)
+  assert(future.error == nil)
+  future.value = val
+  future.finished = true
+  if future.cb != nil:
+    future.cb()
+
+proc complete*(future: Future[void]) =
+  ## Completes a void ``future``.
+  #assert(not future.finished, "Future already finished, cannot finish twice.")
+  checkFinished(future)
+  assert(future.error == nil)
+  future.finished = true
+  if future.cb != nil:
+    future.cb()
+
+proc complete*[T](future: FutureVar[T]) =
+  ## Completes a ``FutureVar``.
+  template fut: expr = Future[T](future)
+  checkFinished(fut)
+  assert(fut.error == nil)
+  fut.finished = true
+  if fut.cb != nil:
+    fut.cb()
+
+proc complete*[T](future: FutureVar[T], val: T) =
+  ## Completes a ``FutureVar`` with value ``val``.
+  ##
+  ## Any previously stored value will be overwritten.
+  template fut: expr = Future[T](future)
+  checkFinished(fut)
+  assert(fut.error == nil)
+  fut.finished = true
+  fut.value = val
+  if fut.cb != nil:
+    fut.cb()
+
+proc fail*[T](future: Future[T], error: ref Exception) =
+  ## Completes ``future`` with ``error``.
+  #assert(not future.finished, "Future already finished, cannot finish twice.")
+  checkFinished(future)
+  future.finished = true
+  future.error = error
+  future.errorStackTrace =
+    if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
+  if future.cb != nil:
+    future.cb()
+  else:
+    # This is to prevent exceptions from being silently ignored when a future
+    # is discarded.
+    # TODO: This may turn out to be a bad idea.
+    # Turns out this is a bad idea.
+    #raise error
+    discard
+
+proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
+  ## Sets the callback proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  ##
+  ## **Note**: You most likely want the other ``callback`` setter which
+  ## passes ``future`` as a param to the callback.
+  future.cb = cb
+  if future.finished:
+    callSoon(future.cb)
+
+proc `callback=`*[T](future: Future[T],
+    cb: proc (future: Future[T]) {.closure,gcsafe.}) =
+  ## Sets the callback proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  future.callback = proc () = cb(future)
+
+proc injectStacktrace[T](future: Future[T]) =
+  # TODO: Come up with something better.
+  when not defined(release):
+    var msg = ""
+    msg.add("\n  " & future.fromProc & "'s lead up to read of failed Future:")
+
+    if not future.errorStackTrace.isNil and future.errorStackTrace != "":
+      msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
+    else:
+      msg.add("\n    Empty or nil stack trace.")
+    future.error.msg.add(msg)
+
+proc read*[T](future: Future[T] | FutureVar[T]): T =
+  ## Retrieves the value of ``future``. Future must be finished otherwise
+  ## this function will fail with a ``ValueError`` exception.
+  ##
+  ## If the result of the future is an error then that error will be raised.
+  {.push hint[ConvFromXtoItselfNotNeeded]: off.}
+  let fut = Future[T](future)
+  {.pop.}
+  if fut.finished:
+    if fut.error != nil:
+      injectStacktrace(fut)
+      raise fut.error
+    when T isnot void:
+      return fut.value
+  else:
+    # TODO: Make a custom exception type for this?
+    raise newException(ValueError, "Future still in progress.")
+
+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
+  else:
+    raise newException(ValueError, "No error in future.")
+
+proc mget*[T](future: FutureVar[T]): var T =
+  ## Returns a mutable value stored in ``future``.
+  ##
+  ## Unlike ``read``, this function will not raise an exception if the
+  ## Future has not been finished.
+  result = Future[T](future).value
+
+proc finished*[T](future: Future[T] | FutureVar[T]): bool =
+  ## Determines whether ``future`` has completed.
+  ##
+  ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
+  (Future[T](future)).finished
+
+proc failed*(future: FutureBase): bool =
+  ## Determines whether ``future`` completed with an error.
+  return future.error != nil
+
+proc asyncCheck*[T](future: Future[T]) =
+  ## Sets a callback on ``future`` which raises an exception if the future
+  ## finished with an error.
+  ##
+  ## This should be used instead of ``discard`` to discard void futures.
+  future.callback =
+    proc () =
+      if future.failed:
+        injectStacktrace(future)
+        raise future.error
+
+proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
+  ## Returns a future which will complete once both ``fut1`` and ``fut2``
+  ## complete.
+  var retFuture = newFuture[void]("asyncdispatch.`and`")
+  fut1.callback =
+    proc () =
+      if not retFuture.finished:
+        if fut1.failed: retFuture.fail(fut1.error)
+        elif fut2.finished: retFuture.complete()
+  fut2.callback =
+    proc () =
+      if not retFuture.finished:
+        if fut2.failed: retFuture.fail(fut2.error)
+        elif fut1.finished: retFuture.complete()
+  return retFuture
+
+proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
+  ## Returns a future which will complete once either ``fut1`` or ``fut2``
+  ## complete.
+  var retFuture = newFuture[void]("asyncdispatch.`or`")
+  proc cb[X](fut: Future[X]) =
+    if fut.failed: retFuture.fail(fut.error)
+    if not retFuture.finished: retFuture.complete()
+  fut1.callback = cb[T]
+  fut2.callback = cb[Y]
+  return retFuture
+
+proc all*[T](futs: varargs[Future[T]]): auto =
+  ## Returns a future which will complete once
+  ## all futures in ``futs`` complete.
+  ##
+  ## If the awaited futures are not ``Future[void]``, the returned future
+  ## will hold the values of all awaited futures in a sequence.
+  ##
+  ## If the awaited futures *are* ``Future[void]``,
+  ## this proc returns ``Future[void]``.
+
+  when T is void:
+    var
+      retFuture = newFuture[void]("asyncdispatch.all")
+      completedFutures = 0
+
+    let totalFutures = len(futs)
+
+    for fut in futs:
+      fut.callback = proc(f: Future[T]) =
+        if f.failed:
+          retFuture.fail(f.error)
+        elif not retFuture.finished:
+          inc(completedFutures)
+
+          if completedFutures == totalFutures:
+            retFuture.complete()
+
+    return retFuture
+
+  else:
+    var
+      retFuture = newFuture[seq[T]]("asyncdispatch.all")
+      retValues = newSeq[T](len(futs))
+      completedFutures = 0
+
+    for i, fut in futs:
+      proc setCallback(i: int) =
+        fut.callback = proc(f: Future[T]) =
+          if f.failed:
+            retFuture.fail(f.error)
+          elif not retFuture.finished:
+            retValues[i] = f.read()
+            inc(completedFutures)
+
+            if completedFutures == len(retValues):
+              retFuture.complete(retValues)
+
+      setCallback(i)
+
+    return retFuture
diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim
index a5d5d2c01..744bdbaa1 100644
--- a/lib/pure/ioselectors.nim
+++ b/lib/pure/ioselectors.nim
@@ -44,14 +44,21 @@ when defined(nimdoc):
 
     Event* {.pure.} = enum
       ## An enum which hold event types
-      Read,    ## Descriptor is available for read
-      Write,   ## Descriptor is available for write
-      Timer,   ## Timer descriptor is completed
-      Signal,  ## Signal is raised
-      Process, ## Process is finished
-      Vnode,   ## Currently not supported
-      User,    ## User event is raised
-      Error    ## Error happens while waiting, for descriptor
+      Read,        ## Descriptor is available for read
+      Write,       ## Descriptor is available for write
+      Timer,       ## Timer descriptor is completed
+      Signal,      ## Signal is raised
+      Process,     ## Process is finished
+      Vnode,       ## BSD specific file change happens
+      User,        ## User event is raised
+      Error,       ## Error happens while waiting, for descriptor
+      VnodeWrite,  ## NOTE_WRITE (BSD specific, write to file occurred)
+      VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred)
+      VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended)
+      VnodeAttrib, ## NOTE_ATTRIB (BSD specific, file attributes changed)
+      VnodeLink,   ## NOTE_LINK (BSD specific, file link count changed)
+      VnodeRename, ## NOTE_RENAME (BSD specific, file renamed)
+      VnodeRevoke  ## NOTE_REVOKE (BSD specific, file revoke occurred)
 
     ReadyKey*[T] = object
       ## An object which holds result for descriptor
@@ -107,6 +114,15 @@ when defined(nimdoc):
     ## ``data`` application-defined data, which to be passed, when
     ## ``ev`` happens.
 
+  proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event],
+                         data: T) =
+    ## Registers selector BSD/MacOSX specific vnode events for file
+    ## descriptor ``fd`` and events ``events``.
+    ## ``data`` application-defined data, which to be passed, when
+    ## vnode event happens.
+    ##
+    ## This function is supported only by BSD and MacOSX.
+
   proc newSelectEvent*(): SelectEvent =
     ## Creates new event ``SelectEvent``.
 
@@ -124,7 +140,7 @@ when defined(nimdoc):
 
   proc flush*[T](s: Selector[T]) =
     ## Flushes all changes was made to kernel pool/queue.
-    ## This function is usefull only for BSD and MacOS, because
+    ## This function is useful only for BSD and MacOS, because
     ## kqueue supports bulk changes to be made.
     ## On Linux/Windows and other Posix compatible operation systems,
     ## ``flush`` is alias for `discard`.
@@ -194,7 +210,9 @@ else:
       deallocShared(cast[pointer](sa))
   type
     Event* {.pure.} = enum
-      Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot
+      Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot,
+      VnodeWrite, VnodeDelete, VnodeExtend, VnodeAttrib, VnodeLink,
+      VnodeRename, VnodeRevoke
 
     ReadyKey*[T] = object
       fd* : int
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 3e86f19aa..3c0cf4e90 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -26,8 +26,8 @@ when defined(macosx) or defined(freebsd):
     const MAX_DESCRIPTORS_ID = 29 # KERN_MAXFILESPERPROC (MacOS)
   else:
     const MAX_DESCRIPTORS_ID = 27 # KERN_MAXFILESPERPROC (FreeBSD)
-  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
-              newp: pointer, newplen: int): cint
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
+              newp: pointer, newplen: csize): cint
        {.importc: "sysctl",header: """#include <sys/types.h>
                                       #include <sys/sysctl.h>"""}
 elif defined(netbsd) or defined(openbsd):
@@ -35,8 +35,8 @@ elif defined(netbsd) or defined(openbsd):
   # KERN_MAXFILES, because KERN_MAXFILES is always bigger,
   # than KERN_MAXFILESPERPROC.
   const MAX_DESCRIPTORS_ID = 7 # KERN_MAXFILES
-  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
-              newp: pointer, newplen: int): cint
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
+              newp: pointer, newplen: csize): cint
        {.importc: "sysctl",header: """#include <sys/param.h>
                                       #include <sys/sysctl.h>"""}
 
@@ -72,7 +72,7 @@ type SelectEvent* = ptr SelectEventImpl
 
 proc newSelector*[T](): Selector[T] =
   var maxFD = 0.cint
-  var size = sizeof(cint)
+  var size = csize(sizeof(cint))
   var namearr = [1.cint, MAX_DESCRIPTORS_ID.cint]
   # Obtain maximum number of file descriptors for process
   if sysctl(addr(namearr[0]), 2, cast[pointer](addr maxFD), addr size,
@@ -262,6 +262,30 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
   inc(s.count)
 
+template processVnodeEvents(events: set[Event]): cuint =
+  var rfflags = 0.cuint
+  if events == {Event.VnodeWrite, Event.VnodeDelete, Event.VnodeExtend,
+                Event.VnodeAttrib, Event.VnodeLink, Event.VnodeRename,
+                Event.VnodeRevoke}:
+    rfflags = NOTE_DELETE or NOTE_WRITE or NOTE_EXTEND or NOTE_ATTRIB or
+              NOTE_LINK or NOTE_RENAME or NOTE_REVOKE
+  else:
+    if Event.VnodeDelete in events: rfflags = rfflags or NOTE_DELETE
+    if Event.VnodeWrite in events: rfflags = rfflags or NOTE_WRITE
+    if Event.VnodeExtend in events: rfflags = rfflags or NOTE_EXTEND
+    if Event.VnodeAttrib in events: rfflags = rfflags or NOTE_ATTRIB
+    if Event.VnodeLink in events: rfflags = rfflags or NOTE_LINK
+    if Event.VnodeRename in events: rfflags = rfflags or NOTE_RENAME
+    if Event.VnodeRevoke in events: rfflags = rfflags or NOTE_REVOKE
+  rfflags
+
+proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event], data: T) =
+  let fdi = fd.int
+  setKey(s, fdi, fdi, {Event.Vnode} + events, 0, data)
+  var fflags = processVnodeEvents(events)
+  modifyKQueue(s, fdi.uint, EVFILT_VNODE, EV_ADD or EV_CLEAR, fflags, 0, nil)
+  inc(s.count)
+
 proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
@@ -295,6 +319,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
       discard posix.close(cint(pkey.key.fd))
       modifyKQueue(s, fdi.uint, EVFILT_PROC, EV_DELETE, 0, 0, nil)
       dec(s.count)
+    elif Event.Vnode in pkey.events:
+      modifyKQueue(s, fdi.uint, EVFILT_VNODE, EV_DELETE, 0, 0, nil)
+      dec(s.count)
     elif Event.User in pkey.events:
       modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
       dec(s.count)
@@ -392,6 +419,20 @@ proc selectInto*[T](s: Selector[T], timeout: int,
         of EVFILT_VNODE:
           pkey = addr(s.fds[kevent.ident.int])
           pkey.key.events = {Event.Vnode}
+          if (kevent.fflags and NOTE_DELETE) != 0:
+            pkey.key.events.incl(Event.VnodeDelete)
+          if (kevent.fflags and NOTE_WRITE) != 0:
+            pkey.key.events.incl(Event.VnodeWrite)
+          if (kevent.fflags and NOTE_EXTEND) != 0:
+            pkey.key.events.incl(Event.VnodeExtend)
+          if (kevent.fflags and NOTE_ATTRIB) != 0:
+            pkey.key.events.incl(Event.VnodeAttrib)
+          if (kevent.fflags and NOTE_LINK) != 0:
+            pkey.key.events.incl(Event.VnodeLink)
+          if (kevent.fflags and NOTE_RENAME) != 0:
+            pkey.key.events.incl(Event.VnodeRename)
+          if (kevent.fflags and NOTE_REVOKE) != 0:
+            pkey.key.events.incl(Event.VnodeRevoke)
         of EVFILT_SIGNAL:
           pkey = addr(s.fds[cast[int](kevent.udata)])
           pkey.key.events = {Event.Signal}
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 7ad7efd23..0b7908c02 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -24,6 +24,8 @@
 ##  jobj["test"] = newJFloat(0.7)  # create or update
 ##  echo($jobj["test"].fnum)
 ##  echo($jobj["key2"].bval)
+##  echo jobj{"missing key"}.getFNum(0.1)  # read a float value using a default
+##  jobj{"a", "b", "c"} = newJFloat(3.3)  # created nested keys
 ##
 ## Results in:
 ##
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index a70f60a8e..d4f239c49 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -112,6 +112,7 @@ else:
 
 const
   BufferSize*: int = 4000 ## size of a buffered socket's buffer
+  MaxLineLength* = 1_000_000
 
 type
   SocketImpl* = object ## socket type
@@ -1006,7 +1007,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
     result = recv(socket.fd, addr(c), 1, MSG_PEEK)
 
 proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
-               flags = {SocketFlag.SafeDisconn}) {.
+               flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.
   tags: [ReadIOEffect, TimeEffect].} =
   ## Reads a line of data from ``socket``.
   ##
@@ -1021,6 +1022,10 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
   ## A timeout can be specified in milliseconds, if data is not received within
   ## the specified time an ETimeout exception will be raised.
   ##
+  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## that can be read before a ``ValueError`` is raised. This prevents Denial
+  ## of Service (DOS) attacks.
+  ##
   ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
 
   template addNLIfEmpty() =
@@ -1054,8 +1059,15 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
       return
     add(line.string, c)
 
+    # Verify that this isn't a DOS attack: #3847.
+    if line.string.len > maxLength:
+      let msg = "recvLine received more than the specified `maxLength` " &
+                "allowed."
+      raise newException(ValueError, msg)
+
 proc recvLine*(socket: Socket, timeout = -1,
-               flags = {SocketFlag.SafeDisconn}): TaintedString =
+               flags = {SocketFlag.SafeDisconn},
+               maxLength = MaxLineLength): TaintedString =
   ## Reads a line of data from ``socket``.
   ##
   ## If a full line is read ``\r\L`` is not
@@ -1069,9 +1081,13 @@ proc recvLine*(socket: Socket, timeout = -1,
   ## A timeout can be specified in milliseconds, if data is not received within
   ## the specified time an ETimeout exception will be raised.
   ##
+  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## that can be read before a ``ValueError`` is raised. This prevents Denial
+  ## of Service (DOS) attacks.
+  ##
   ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
   result = ""
-  readLine(socket, result, timeout, flags)
+  readLine(socket, result, timeout, flags, maxLength)
 
 proc recvFrom*(socket: Socket, data: var string, length: int,
                address: var string, port: var Port, flags = 0'i32): int {.
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index cdbe170cc..001d3d250 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -50,6 +50,8 @@ proc c_getenv(env: cstring): cstring {.
   importc: "getenv", header: "<stdlib.h>".}
 proc c_putenv(env: cstring): cint {.
   importc: "putenv", header: "<stdlib.h>".}
+proc c_free(p: pointer) {.
+  importc: "free", header: "<stdlib.h>".}
 
 var errno {.importc, header: "<errno.h>".}: cint
 
@@ -303,24 +305,47 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
 
 proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
   ## Returns the `current working directory`:idx:.
-  const bufsize = 512 # should be enough
   when defined(windows):
+    var bufsize = MAX_PATH.int32
     when useWinUnicode:
       var res = newWideCString("", bufsize)
-      var L = getCurrentDirectoryW(bufsize, res)
-      if L == 0'i32: raiseOSError(osLastError())
-      result = res$L
+      while true:
+        var L = getCurrentDirectoryW(bufsize, res)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString("", L)
+          bufsize = L
+        else:
+          result = res$L
+          break
     else:
       result = newString(bufsize)
-      var L = getCurrentDirectoryA(bufsize, result)
-      if L == 0'i32: raiseOSError(osLastError())
-      setLen(result, L)
+      while true:
+        var L = getCurrentDirectoryA(bufsize, result)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          result = newString(L)
+          bufsize = L
+        else:
+          setLen(result, L)
+          break
   else:
+    var bufsize = 1024 # should be enough
     result = newString(bufsize)
-    if getcwd(result, bufsize) != nil:
-      setLen(result, c_strlen(result))
-    else:
-      raiseOSError(osLastError())
+    while true:
+      if getcwd(result, bufsize) != nil:
+        setLen(result, c_strlen(result))
+        break
+      else:
+        let err = osLastError()
+        if err.int32 == ERANGE:
+          bufsize = bufsize shl 1
+          doAssert(bufsize >= 0)
+          result = newString(bufsize)
+        else:
+          raiseOSError(osLastError())
 
 proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
   ## Sets the `current working directory`:idx:; `OSError` is raised if
@@ -336,28 +361,45 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
 
 proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
   tags: [ReadDirEffect].} =
-  ## Returns the full (`absolute`:idx:) path of the file `filename`, raises OSError in case of an error.
+  ## Returns the full (`absolute`:idx:) path of the file `filename`,
+  ## raises OSError in case of an error.
   when defined(windows):
-    const bufsize = 3072'i32
+    var bufsize = MAX_PATH.int32
     when useWinUnicode:
-      var unused: WideCString
-      var res = newWideCString("", bufsize div 2)
-      var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
-      if L <= 0'i32 or L >= bufsize:
-        raiseOSError(osLastError())
-      result = res$L
+      var unused: WideCString = nil
+      var res = newWideCString("", bufsize)
+      while true:
+        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString("", L)
+          bufsize = L
+        else:
+          result = res$L
+          break
     else:
-      var unused: cstring
+      var unused: cstring = nil
       result = newString(bufsize)
-      var L = getFullPathNameA(filename, bufsize, result, unused)
-      if L <= 0'i32 or L >= bufsize: raiseOSError(osLastError())
-      setLen(result, L)
+      while true:
+        var L = getFullPathNameA(filename, bufsize, result, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          result = newString(L)
+          bufsize = L
+        else:
+          setLen(result, L)
+          break
   else:
-    # careful, realpath needs to take an allocated buffer according to Posix:
-    result = newString(pathMax)
-    var r = realpath(filename, result)
-    if r.isNil: raiseOSError(osLastError())
-    setLen(result, c_strlen(result))
+    # according to Posix we don't need to allocate space for result pathname.
+    # But we need to free return value with free(3).
+    var r = realpath(filename, nil)
+    if r.isNil:
+      raiseOSError(osLastError())
+    else:
+      result = $r
+      c_free(cast[pointer](r))
 
 when defined(Windows):
   proc openHandle(path: string, followSymlink=true): Handle =
@@ -1382,6 +1424,35 @@ when declared(paramCount) or defined(nimdoc):
     for i in 1..paramCount():
       result.add(paramStr(i))
 
+when defined(freebsd):
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
+              newp: pointer, newplen: csize): cint
+       {.importc: "sysctl",header: """#include <sys/types.h>
+                                      #include <sys/sysctl.h>"""}
+  const
+    CTL_KERN = 1
+    KERN_PROC = 14
+    KERN_PROC_PATHNAME = 12
+    MAX_PATH = 1024
+
+  proc getApplFreebsd(): string =
+    var pathLength = csize(MAX_PATH)
+    result = newString(pathLength)
+    var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint]
+    while true:
+      let res = sysctl(addr req[0], 4, cast[pointer](addr result[0]),
+                       addr pathLength, nil, 0)
+      if res < 0:
+        let err = osLastError()
+        if err.int32 == ENOMEM:
+          result = newString(pathLength)
+        else:
+          result.setLen(0) # error!
+          break
+      else:
+        result.setLen(pathLength)
+        break
+
 when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
   proc getApplAux(procPath: string): string =
     result = newString(256)
@@ -1426,16 +1497,34 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
   # Solaris:
   # /proc/<pid>/object/a.out (filename only)
   # /proc/<pid>/path/a.out (complete pathname)
-  # FreeBSD: /proc/<pid>/file
   when defined(windows):
+    var bufsize = int32(MAX_PATH)
     when useWinUnicode:
-      var buf = newWideCString("", 256)
-      var len = getModuleFileNameW(0, buf, 256)
-      result = buf$len
+      var buf = newWideCString("", bufsize)
+      while true:
+        var L = getModuleFileNameW(0, buf, bufsize)
+        if L == 0'i32:
+          result = "" # error!
+          break
+        elif L > bufsize:
+          buf = newWideCString("", L)
+          bufsize = L
+        else:
+          result = buf$L
+          break
     else:
-      result = newString(256)
-      var len = getModuleFileNameA(0, result, 256)
-      setlen(result, int(len))
+      result = newString(bufsize)
+      while true:
+        var L = getModuleFileNameA(0, result, bufsize)
+        if L == 0'i32:
+          result = "" # error!
+          break
+        elif L > bufsize:
+          result = newString(L)
+          bufsize = L
+        else:
+          setLen(result, L)
+          break
   elif defined(macosx):
     var size: cuint32
     getExecPath1(nil, size)
@@ -1450,7 +1539,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
     elif defined(solaris):
       result = getApplAux("/proc/" & $getpid() & "/path/a.out")
     elif defined(freebsd):
-      result = getApplAux("/proc/" & $getpid() & "/file")
+      result = getApplFreebsd()
     # little heuristic that may work on other POSIX-like systems:
     if result.len == 0:
       result = getApplHeuristic()
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index 56671ee62..3d3a105f0 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -434,8 +434,8 @@ when not declared(getEnv) or defined(nimscript):
     ## On Windows, network paths are considered absolute too.
     when doslike:
       var len = len(path)
-      result = (len > 1 and path[0] in {'/', '\\'}) or
-               (len > 2 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
+      result = (len > 0 and path[0] in {'/', '\\'}) or
+               (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
     elif defined(macos):
       result = path.len > 0 and path[0] != ':'
     elif defined(RISCOS):
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index abc21b2b2..44ec5b548 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -48,7 +48,7 @@ type
       inHandle, outHandle, errHandle: FileHandle
       inStream, outStream, errStream: Stream
       id: Pid
-    exitCode: cint
+    exitStatus: cint
     options: set[ProcessOption]
 
   Process* = ref ProcessObj ## represents an operating system process
@@ -731,7 +731,7 @@ elif not defined(useNimRtl):
       pStdin, pStdout, pStderr: array[0..1, cint]
     new(result)
     result.options = options
-    result.exitCode = -3 # for ``waitForExit``
+    result.exitStatus = -3 # for ``waitForExit``
     if poParentStreams notin options:
       if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
          pipe(pStderr) != 0'i32:
@@ -774,7 +774,10 @@ elif not defined(useNimRtl):
     data.workingDir = workingDir
 
     when useProcessAuxSpawn:
+      var currentDir = getCurrentDir()
       pid = startProcessAuxSpawn(data)
+      if workingDir.len > 0:
+        setCurrentDir(currentDir)
     else:
       pid = startProcessAuxFork(data)
 
@@ -835,7 +838,6 @@ elif not defined(useNimRtl):
           chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
 
       var res: cint
-      # FIXME: chdir is global to process
       if data.workingDir.len > 0:
         setCurrentDir($data.workingDir)
       var pid: Pid
@@ -957,13 +959,10 @@ elif not defined(useNimRtl):
 
   proc running(p: Process): bool =
     var ret : int
-    when not defined(freebsd):
-      ret = waitpid(p.id, p.exitCode, WNOHANG)
-    else:
-      var status : cint = 1
-      ret = waitpid(p.id, status, WNOHANG)
-      if WIFEXITED(status):
-        p.exitCode = status
+    var status : cint = 1
+    ret = waitpid(p.id, status, WNOHANG)
+    if WIFEXITED(status):
+      p.exitStatus = status
     if ret == 0: return true # Can't establish status. Assume running.
     result = ret == int(p.id)
 
@@ -980,11 +979,12 @@ elif not defined(useNimRtl):
     import kqueue, times
 
     proc waitForExit(p: Process, timeout: int = -1): int =
-      if p.exitCode != -3: return p.exitCode
+      if p.exitStatus != -3: return int(p.exitStatus) shr 8
       if timeout == -1:
-        if waitpid(p.id, p.exitCode, 0) < 0:
-          p.exitCode = -3
+        var status : cint = 1
+        if waitpid(p.id, status, 0) < 0:
           raiseOSError(osLastError())
+        p.exitStatus = status
       else:
         var kqFD = kqueue()
         if kqFD == -1:
@@ -1004,6 +1004,7 @@ elif not defined(useNimRtl):
 
         try:
           while true:
+            var status : cint = 1
             var count = kevent(kqFD, addr(kevIn), 1, addr(kevOut), 1,
                                addr(tmspec))
             if count < 0:
@@ -1014,22 +1015,22 @@ elif not defined(useNimRtl):
               # timeout expired, so we trying to kill process
               if posix.kill(p.id, SIGKILL) == -1:
                 raiseOSError(osLastError())
-              if waitpid(p.id, p.exitCode, 0) < 0:
-                p.exitCode = -3
+              if waitpid(p.id, status, 0) < 0:
                 raiseOSError(osLastError())
+              p.exitStatus = status
               break
             else:
               if kevOut.ident == p.id.uint and kevOut.filter == EVFILT_PROC:
-                if waitpid(p.id, p.exitCode, 0) < 0:
-                  p.exitCode = -3
+                if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitStatus = status
                 break
               else:
                 raiseOSError(osLastError())
         finally:
           discard posix.close(kqFD)
 
-      result = int(p.exitCode) shr 8
+      result = int(p.exitStatus) shr 8
   else:
     import times
 
@@ -1061,15 +1062,16 @@ elif not defined(useNimRtl):
         s.tv_sec = b.tv_sec
         s.tv_nsec = b.tv_nsec
 
-      #if waitPid(p.id, p.exitCode, 0) == int(p.id):
+      #if waitPid(p.id, p.exitStatus, 0) == int(p.id):
       # ``waitPid`` fails if the process is not running anymore. But then
-      # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
+      # ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is
       # initialized with -3, wrong success exit codes are prevented.
-      if p.exitCode != -3: return p.exitCode
+      if p.exitStatus != -3: return int(p.exitStatus) shr 8
       if timeout == -1:
-        if waitpid(p.id, p.exitCode, 0) < 0:
-          p.exitCode = -3
+        var status : cint = 1
+        if waitpid(p.id, status, 0) < 0:
           raiseOSError(osLastError())
+        p.exitStatus = status
       else:
         var nmask, omask: Sigset
         var sinfo: SigInfo
@@ -1100,9 +1102,10 @@ elif not defined(useNimRtl):
             let res = sigtimedwait(nmask, sinfo, tmspec)
             if res == SIGCHLD:
               if sinfo.si_pid == p.id:
-                if waitpid(p.id, p.exitCode, 0) < 0:
-                  p.exitCode = -3
+                var status : cint = 1
+                if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitStatus = status
                 break
               else:
                 # we have SIGCHLD, but not for process we are waiting,
@@ -1122,9 +1125,10 @@ elif not defined(useNimRtl):
                 # timeout expired, so we trying to kill process
                 if posix.kill(p.id, SIGKILL) == -1:
                   raiseOSError(osLastError())
-                if waitpid(p.id, p.exitCode, 0) < 0:
-                  p.exitCode = -3
+                var status : cint = 1
+                if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitStatus = status
                 break
               else:
                 raiseOSError(err)
@@ -1136,17 +1140,19 @@ elif not defined(useNimRtl):
             if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
               raiseOSError(osLastError())
 
-      result = int(p.exitCode) shr 8
+      result = int(p.exitStatus) shr 8
 
   proc peekExitCode(p: Process): int =
-    if p.exitCode != -3: return p.exitCode
-    var ret = waitpid(p.id, p.exitCode, WNOHANG)
+    var status : cint = 1
+    if p.exitStatus != -3: return int(p.exitStatus) shr 8
+    var ret = waitpid(p.id, status, WNOHANG)
     var b = ret == int(p.id)
     if b: result = -1
-    if not WIFEXITED(p.exitCode):
-      p.exitCode = -3
+    if WIFEXITED(status):
+      p.exitStatus = status
+      result = p.exitStatus.int shr 8
+    else:
       result = -1
-    else: result = p.exitCode.int shr 8
 
   proc createStream(stream: var Stream, handle: var FileHandle,
                     fileMode: FileMode) =
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index d16a55302..aa4a13ecf 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -142,9 +142,9 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} =
 template charData*(my: XmlParser): string =
   ## returns the character data for the events: ``xmlCharData``,
   ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
-  ## Raises an assertion in debug mode if ``my.kind`` is not one 
+  ## Raises an assertion in debug mode if ``my.kind`` is not one
   ## of those events. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
                      xmlSpecial})
   my.a
@@ -152,49 +152,49 @@ template charData*(my: XmlParser): string =
 template elementName*(my: XmlParser): string =
   ## returns the element name for the events: ``xmlElementStart``,
   ## ``xmlElementEnd``, ``xmlElementOpen``
-  ## Raises an assertion in debug mode if ``my.kind`` is not one 
+  ## Raises an assertion in debug mode if ``my.kind`` is not one
   ## of those events. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen})
   my.a
 
 template entityName*(my: XmlParser): string =
   ## returns the entity name for the event: ``xmlEntity``
-  ## Raises an assertion in debug mode if ``my.kind`` is not 
+  ## Raises an assertion in debug mode if ``my.kind`` is not
   ## ``xmlEntity``. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind == xmlEntity)
   my.a
 
 template attrKey*(my: XmlParser): string =
   ## returns the attribute key for the event ``xmlAttribute``
-  ## Raises an assertion in debug mode if ``my.kind`` is not 
+  ## Raises an assertion in debug mode if ``my.kind`` is not
   ## ``xmlAttribute``. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind == xmlAttribute)
   my.a
 
 template attrValue*(my: XmlParser): string =
   ## returns the attribute value for the event ``xmlAttribute``
-  ## Raises an assertion in debug mode if ``my.kind`` is not 
+  ## Raises an assertion in debug mode if ``my.kind`` is not
   ## ``xmlAttribute``. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind == xmlAttribute)
   my.b
 
 template piName*(my: XmlParser): string =
   ## returns the processing instruction name for the event ``xmlPI``
-  ## Raises an assertion in debug mode if ``my.kind`` is not 
+  ## Raises an assertion in debug mode if ``my.kind`` is not
   ## ``xmlPI``. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind == xmlPI)
   my.a
 
 template piRest*(my: XmlParser): string =
   ## returns the rest of the processing instruction for the event ``xmlPI``
-  ## Raises an assertion in debug mode if ``my.kind`` is not 
+  ## Raises an assertion in debug mode if ``my.kind`` is not
   ## ``xmlPI``. In release mode, this will not trigger an error
-  ## but the value returned will not be valid. 
+  ## but the value returned will not be valid.
   assert(my.kind == xmlPI)
   my.b
 
@@ -636,12 +636,14 @@ proc rawGetTok(my: var XmlParser) =
 
 proc getTok(my: var XmlParser) =
   while true:
+    let lastKind = my.kind
     rawGetTok(my)
     case my.kind
     of xmlComment:
       if my.options.contains(reportComments): break
     of xmlWhitespace:
-      if my.options.contains(reportWhitespace): break
+      if my.options.contains(reportWhitespace) or lastKind in {xmlCharData, xmlComment, xmlEntity}:
+        break
     else: break
 
 proc next*(my: var XmlParser) =
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 08da771dc..955a70143 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -122,7 +122,7 @@ when isMainModule:
       inc occur[x]
     for i, oc in occur:
       if oc < 69:
-        doAssert false, "too few occurances of " & $i
+        doAssert false, "too few occurrences of " & $i
       elif oc > 130:
-        doAssert false, "too many occurances of " & $i
+        doAssert false, "too many occurrences of " & $i
   main()
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index bf134f2ae..c2ba2b1f3 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -349,4 +349,4 @@ when isMainModule:
   assert toRational(0.98765432) == 12345679 // 12500000
   assert toRational(0.1, 1000000) == 1 // 10
   assert toRational(0.9, 1000000) == 9 // 10
-  assert toRational(PI) == 80143857 // 25510582
+  #assert toRational(PI) == 80143857 // 25510582
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index cba101fff..506b2cec0 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -377,7 +377,8 @@ proc contains*(s: Selector, key: SelectorKey): bool =
 
 proc len*(s: Selector): int =
   ## Retrieves the number of registered file descriptors in this Selector.
-  return s.fds.len
+  when not defined(nimdoc):
+    return s.fds.len
 
 {.deprecated: [TEvent: Event, PSelectorKey: SelectorKey,
    TReadyInfo: ReadyInfo, PSelector: Selector].}
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
index ec4cd182b..2004337df 100644
--- a/lib/pure/stats.nim
+++ b/lib/pure/stats.nim
@@ -334,15 +334,17 @@ when isMainModule:
   doAssert(rs1.sum == 9.5)
   doAssert(rs1.mean() == 2.375)
 
-  var rr: RunningRegress
-  rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
-  doAssert(rr.slope() == 0.9695585996955861)
-  doAssert(rr.intercept() == -0.03424657534246611)
-  doAssert(rr.correlation() == 0.9905100362239381)
-  var rr1, rr2: RunningRegress
-  rr1.push(@[0.0,1.0], @[0.0,1.0])
-  rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
-  let rr3 = rr1 + rr2
-  doAssert(rr3.correlation() == rr.correlation())
-  doAssert(clean(rr3.slope()) == clean(rr.slope()))
-  doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
+  when not defined(cpu32):
+    # XXX For some reason on 32bit CPUs these results differ
+    var rr: RunningRegress
+    rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
+    doAssert(rr.slope() == 0.9695585996955861)
+    doAssert(rr.intercept() == -0.03424657534246611)
+    doAssert(rr.correlation() == 0.9905100362239381)
+    var rr1, rr2: RunningRegress
+    rr1.push(@[0.0,1.0], @[0.0,1.0])
+    rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
+    let rr3 = rr1 + rr2
+    doAssert(rr3.correlation() == rr.correlation())
+    doAssert(clean(rr3.slope()) == clean(rr.slope()))
+    doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index b78a2b966..efc1dfa92 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -266,13 +266,13 @@ proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
   result.milliseconds = `mod`(milliseconds, 1000)
   carryO = `div`(milliseconds, 1000)
   result.seconds = `mod`(carryO + seconds, 60)
-  carryO = `div`(seconds, 60)
+  carryO = `div`(carryO + seconds, 60)
   result.minutes = `mod`(carryO + minutes, 60)
-  carryO = `div`(minutes, 60)
+  carryO = `div`(carryO + minutes, 60)
   result.hours = `mod`(carryO + hours, 24)
-  carryO = `div`(hours, 24)
+  carryO = `div`(carryO + hours, 24)
   result.days = carryO + days
-  carryO = 0
+
   result.months = `mod`(months, 12)
   carryO = `div`(months, 12)
   result.years = carryO + years
@@ -283,13 +283,13 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
   result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
   carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
   result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
-  carryO = `div`(ti1.seconds + ti2.seconds, 60)
+  carryO = `div`(carryO + ti1.seconds + ti2.seconds, 60)
   result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
-  carryO = `div`(ti1.minutes + ti2.minutes, 60)
+  carryO = `div`(carryO + ti1.minutes + ti2.minutes, 60)
   result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
-  carryO = `div`(ti1.hours + ti2.hours, 24)
+  carryO = `div`(carryO + ti1.hours + ti2.hours, 24)
   result.days = carryO + ti1.days + ti2.days
-  carryO = 0
+
   result.months = `mod`(ti1.months + ti2.months, 12)
   carryO = `div`(ti1.months + ti2.months, 12)
   result.years = carryO + ti1.years + ti2.years
@@ -1248,12 +1248,18 @@ proc parse*(value, layout: string): TimeInfo =
       else:
         parseToken(info, token, value, j)
         token = ""
-  # Reset weekday (might not have been provided and the default may be wrong)
-  # and yearday (is never provided directly and therefore probably wrong)
-  let processed = getLocalTime(toTime(info))
-  info.weekday = processed.weekday
-  info.yearday = processed.yearday
-  return info
+
+  # We are going to process the date to find out if we are in DST, because the
+  # default based on the current time may be wrong. Calling getLocalTime will
+  # set this correctly, but the actual time may be offset from when we called
+  # toTime with a possibly incorrect DST setting, so we are only going to take
+  # the isDST from this result.
+  let correctDST = getLocalTime(toTime(info))
+  info.isDST = correctDST.isDST
+
+  # Now we preocess it again with the correct isDST to correct things like
+  # weekday and yearday.
+  return getLocalTime(toTime(info))
 
 # Leap year calculations are adapted from:
 # http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 0fc2e441e..12553e3da 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -11,11 +11,21 @@
 ##
 ## This module implements boilerplate to make unit testing easy.
 ##
+## The test status and name is printed after any output or traceback.
+##
 ## Example:
 ##
 ## .. code:: nim
 ##
 ##   suite "description for this stuff":
+##     echo "suite setup: run once before the tests"
+##
+##     setup:
+##       echo "run before each test"
+##
+##     teardown:
+##       echo "run after each test":
+##
 ##     test "essential truths":
 ##       # give up and stop if this fails
 ##       require(true)
@@ -30,6 +40,13 @@
 ##       let v = @[1, 2, 3]  # you can do initialization here
 ##       expect(IndexError):
 ##         discard v[4]
+##
+##     echo "suite teardown: run once after the tests"
+##
+##
+## Tests can be nested, however failure of a nested test will not mark the
+## parent test as failed. Setup and teardown are inherited. Setup can be
+## overridden locally.
 
 import
   macros
@@ -226,10 +243,11 @@ template fail* =
   checkpoints = @[]
 
 template skip* =
-  ## Makes test to be skipped. Should be used directly
+  ## Mark the test as skipped. Should be used directly
   ## in case when it is not possible to perform test
   ## for reasons depending on outer environment,
   ## or certain application logic conditions or configurations.
+  ## The test code is still executed.
   ##
   ## .. code-block:: nim
   ##
@@ -250,12 +268,12 @@ macro check*(conditions: untyped): untyped =
   ##
   ##  import strutils
   ##
-  ##  check("AKB48".toLower() == "akb48")
+  ##  check("AKB48".toLowerAscii() == "akb48")
   ##
   ##  let teams = {'A', 'K', 'B', '4', '8'}
   ##
   ##  check:
-  ##    "AKB48".toLower() == "akb48"
+  ##    "AKB48".toLowerAscii() == "akb48"
   ##    'C' in teams
   let checked = callsite()[1]
   var
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim
index 2a2c3e1dd..22bd259b7 100644
--- a/lib/pure/xmlparser.nim
+++ b/lib/pure/xmlparser.nim
@@ -28,7 +28,7 @@ proc raiseInvalidXml(errors: seq[string]) =
 proc addNode(father, son: XmlNode) =
   if son != nil: add(father, son)
 
-proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
+proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.}
 
 proc untilElementEnd(x: var XmlParser, result: XmlNode,
                      errors: var seq[string]) =
@@ -164,3 +164,6 @@ when isMainModule:
       var xml = loadXml(filePath, errors)
       assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.")
 
+    block bug1518:
+      var err: seq[string] = @[]
+      assert $parsexml(newStringStream"<tag>One &amp; two</tag>", "temp.xml", err) == "<tag>One &amp; two</tag>"
diff --git a/lib/stdlib.nimble b/lib/stdlib.nimble
index 4b0066ee8..5238d900b 100644
--- a/lib/stdlib.nimble
+++ b/lib/stdlib.nimble
@@ -1,6 +1,6 @@
 [Package]
 name          = "stdlib"
-version       = "0.14.3"
+version       = "0.15.2"
 author        = "Dominik Picheta"
 description   = "Nim's standard library."
 license       = "MIT"
diff --git a/lib/system.nim b/lib/system.nim
index 31d14d4bf..919b4694c 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1824,10 +1824,10 @@ const
   NimMajor*: int = 0
     ## is the major number of Nim's version.
 
-  NimMinor*: int = 14
+  NimMinor*: int = 15
     ## is the minor number of Nim's version.
 
-  NimPatch*: int = 3
+  NimPatch*: int = 2
     ## is the patch number of Nim's version.
 
   NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
@@ -2581,10 +2581,11 @@ else:
 when not defined(JS): #and not defined(nimscript):
   {.push stack_trace: off, profiler:off.}
 
-  when not defined(nimscript) and not defined(nogc):
+  when hasAlloc:
     when not defined(gcStack):
       proc initGC()
-    when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc) and not defined(gcStack):
+    when not defined(boehmgc) and not defined(useMalloc) and
+        not defined(gogc) and not defined(gcStack):
       proc initAllocator() {.inline.}
 
     proc initStackBottom() {.inline, compilerproc.} =
@@ -2602,7 +2603,6 @@ when not defined(JS): #and not defined(nimscript):
       when declared(setStackBottom):
         setStackBottom(locals)
 
-  when hasAlloc:
     var
       strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
 
@@ -2731,7 +2731,7 @@ when not defined(JS): #and not defined(nimscript):
     proc setStdIoUnbuffered*() {.tags: [], benign.}
       ## Configures `stdin`, `stdout` and `stderr` to be unbuffered.
 
-    proc close*(f: File) {.tags: [].}
+    proc close*(f: File) {.tags: [], gcsafe.}
       ## Closes the file.
 
     proc endOfFile*(f: File): bool {.tags: [], benign.}
@@ -3676,7 +3676,7 @@ template closureScope*(body: untyped): untyped =
 when defined(nimconfig):
   include "system/nimscript"
 
-when defined(windows) and appType == "console" and not defined(nimconfig):
+when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage):
   proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32",
     importc: "SetConsoleOutputCP".}
   discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index bed9fd906..745bbbf62 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -101,8 +101,8 @@ type
 
 # shared:
 var
-  bottomData: AvlNode
-  bottom: PAvlNode
+  bottomData {.threadvar.}: AvlNode
+  bottom {.threadvar.}: PAvlNode
 
 {.push stack_trace: off.}
 proc initAllocator() =
diff --git a/lib/system/avltree.nim b/lib/system/avltree.nim
index d5c901542..50faada26 100644
--- a/lib/system/avltree.nim
+++ b/lib/system/avltree.nim
@@ -9,7 +9,7 @@
 
 # not really an AVL tree anymore, but still balanced ...
 
-template isBottom(n: PAvlNode): bool = n == bottom
+template isBottom(n: PAvlNode): bool = n.link[0] == n
 
 proc lowGauge(n: PAvlNode): int =
   var it = n
@@ -52,7 +52,7 @@ proc split(t: var PAvlNode) =
     inc t.level
 
 proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
-  if t == bottom:
+  if t.isBottom:
     t = allocAvlNode(a, key, upperBound)
   else:
     if key <% t.key:
@@ -65,14 +65,14 @@ proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
     split(t)
 
 proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} =
-  if t == bottom: return
+  if isBottom(t): return
   a.last = t
   if x <% t.key:
     del(a, t.link[0], x)
   else:
     a.deleted = t
     del(a, t.link[1], x)
-  if t == a.last and a.deleted != bottom and x == a.deleted.key:
+  if t == a.last and not isBottom(a.deleted) and x == a.deleted.key:
     a.deleted.key = t.key
     a.deleted.upperBound = t.upperBound
     a.deleted = bottom
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index caa709229..4b8b895a5 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -52,6 +52,7 @@ proc deinitRawChannel(p: pointer) =
 
 proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
               mode: LoadStoreMode) {.benign.}
+
 proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
               mode: LoadStoreMode) {.benign.} =
   var
@@ -71,6 +72,9 @@ proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
 
 proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
               mode: LoadStoreMode) =
+  template `+!`(p: pointer; x: int): pointer =
+    cast[pointer](cast[int](p) +% x)
+
   var
     d = cast[ByteAddress](dest)
     s = cast[ByteAddress](src)
@@ -93,7 +97,9 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
       if s2 == nil:
         unsureAsgnRef(x, s2)
       else:
-        unsureAsgnRef(x, copyString(cast[NimString](s2)))
+        let y = copyDeepString(cast[NimString](s2))
+        #echo "loaded ", cast[int](y), " ", cast[string](y)
+        unsureAsgnRef(x, y)
         dealloc(t.region, s2)
   of tySequence:
     var s2 = cast[PPointer](src)[]
@@ -107,26 +113,27 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
     else:
       sysAssert(dest != nil, "dest == nil")
       if mode == mStore:
-        x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize)
+        x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize)
       else:
         unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize))
       var dst = cast[ByteAddress](cast[PPointer](dest)[])
+      var dstseq = cast[PGenericSeq](dst)
+      dstseq.len = seq.len
+      dstseq.reserved = seq.len
       for i in 0..seq.len-1:
         storeAux(
           cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
           cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
                         GenericSeqSize),
           mt.base, t, mode)
-      var dstseq = cast[PGenericSeq](dst)
-      dstseq.len = seq.len
-      dstseq.reserved = seq.len
       if mode != mStore: dealloc(t.region, s2)
   of tyObject:
-    # copy type field:
-    var pint = cast[ptr PNimType](dest)
-    pint[] = cast[ptr PNimType](src)[]
     if mt.base != nil:
       storeAux(dest, src, mt.base, t, mode)
+    else:
+      # copy type field:
+      var pint = cast[ptr PNimType](dest)
+      pint[] = cast[ptr PNimType](src)[]
     storeAux(dest, src, mt.node, t, mode)
   of tyTuple:
     storeAux(dest, src, mt.node, t, mode)
@@ -143,15 +150,24 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
       else:
         unsureAsgnRef(x, nil)
     else:
-      let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size
-                 else: mt.base.size
+      #let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size
+      #           else: mt.base.size
       if mode == mStore:
-        x[] = alloc(t.region, size)
+        let dyntype = when declared(usrToCell): usrToCell(s).typ
+                      else: mt
+        let size = dyntype.base.size
+        # we store the real dynamic 'ref type' at offset 0, so that
+        # no information is lost
+        let a = alloc0(t.region, size+sizeof(pointer))
+        x[] = a
+        cast[PPointer](a)[] = dyntype
+        storeAux(a +! sizeof(pointer), s, dyntype.base, t, mode)
       else:
-        var obj = newObj(mt, size)
+        let dyntype = cast[ptr PNimType](s)[]
+        var obj = newObj(dyntype, dyntype.base.size)
         unsureAsgnRef(x, obj)
-      storeAux(x[], s, mt.base, t, mode)
-      if mode != mStore: dealloc(t.region, s)
+        storeAux(x[], s +! sizeof(pointer), dyntype.base, t, mode)
+        dealloc(t.region, s)
   else:
     copyMem(dest, src, mt.size) # copy raw bits
 
@@ -194,10 +210,8 @@ template sendImpl(q: expr) {.immediate.} =
   if q.mask == ChannelDeadMask:
     sysFatal(DeadThreadError, "cannot send message; thread died")
   acquireSys(q.lock)
-  var m: TMsg
-  shallowCopy(m, msg)
   var typ = cast[PNimType](getTypeInfo(msg))
-  rawSend(q, addr(m), typ)
+  rawSend(q, unsafeAddr(msg), typ)
   q.elemType = typ
   releaseSys(q.lock)
   signalSysCond(q.cond)
@@ -228,8 +242,10 @@ proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
 
 proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
                                                   msg: TMsg] =
-  ## try to receives a message from the channel `c` if available. Otherwise
-  ## it returns ``(false, default(msg))``.
+  ## Tries to receive a message from the channel `c`, but this can fail
+  ## for all sort of reasons, including contention. If it fails,
+  ## it returns ``(false, default(msg))`` otherwise it
+  ## returns ``(true, msg)``.
   var q = cast[PRawChannel](addr(c))
   if q.mask != ChannelDeadMask:
     if tryAcquireSys(q.lock):
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index 5445a067c..38cc8cbf3 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -32,12 +32,6 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
       genericDeepCopyAux(dest, src, m)
   of nkNone: sysAssert(false, "genericDeepCopyAux")
 
-proc copyDeepString(src: NimString): NimString {.inline.} =
-  if src != nil:
-    result = rawNewStringNoInit(src.len)
-    result.len = src.len
-    copyMem(addr(result.data), addr(src.data), src.len + 1)
-
 proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
   var
     d = cast[ByteAddress](dest)
@@ -70,10 +64,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
   of tyObject:
     # we need to copy m_type field for tyObject, as it could be empty for
     # sequence reallocations:
-    var pint = cast[ptr PNimType](dest)
-    pint[] = cast[ptr PNimType](src)[]
     if mt.base != nil:
       genericDeepCopyAux(dest, src, mt.base)
+    else:
+      var pint = cast[ptr PNimType](dest)
+      pint[] = cast[ptr PNimType](src)[]
     genericDeepCopyAux(dest, src, mt.node)
   of tyTuple:
     genericDeepCopyAux(dest, src, mt.node)
@@ -103,16 +98,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
         else:
           let realType = x.typ
           let z = newObj(realType, realType.base.size)
-
           unsureAsgnRef(cast[PPointer](dest), z)
           x.typ = cast[PNimType](cast[int](z) or 1)
           genericDeepCopyAux(z, s2, realType.base)
           x.typ = realType
       else:
-        let realType = mt
-        let z = newObj(realType, realType.base.size)
+        let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size
+                   else: mt.base.size
+        let z = newObj(mt, size)
         unsureAsgnRef(cast[PPointer](dest), z)
-        genericDeepCopyAux(z, s2, realType.base)
+        genericDeepCopyAux(z, s2, mt.base)
   of tyPtr:
     # no cycle check here, but also not really required
     let s2 = cast[PPointer](src)[]
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 0a994efac..fa997e982 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -26,6 +26,8 @@ proc nimLoadLibraryError(path: string) =
   stderr.rawWrite("could not load: ")
   stderr.rawWrite(path)
   stderr.rawWrite("\n")
+  when not(defined(nimDebugDlOpen)):
+    stderr.rawWrite("compile with -d:nimDebugDlOpen for more information\n")
   quit(1)
 
 proc procAddrError(name: cstring) {.noinline.} =
@@ -74,7 +76,8 @@ when defined(posix):
     when defined(nimDebugDlOpen):
       let error = dlerror()
       if error != nil:
-        c_fprintf(c_stderr, "%s\n", error)
+        stderr.write(error)
+        stderr.rawWrite("\n")
 
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     result = dlsym(lib, name)
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 089c9c915..ce2bfc2ae 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -33,6 +33,9 @@ when withRealTime and not declared(getTicks):
 when defined(memProfiler):
   proc nimProfile(requestedSize: int) {.benign.}
 
+when hasThreadSupport:
+  include sharedlist
+
 type
   ObjectSpaceIter = object
     state: range[-1..0]
@@ -96,7 +99,7 @@ type
     stat: GcStat
     additionalRoots: CellSeq # dummy roots for GC_ref/unref
     spaceIter: ObjectSpaceIter
-    dumpHeapFile: File # File that is used for GC_dumpHeap
+    pDumpHeapFile: pointer # File that is used for GC_dumpHeap
     when hasThreadSupport:
       toDispose: SharedList[pointer]
 
@@ -612,6 +615,9 @@ template checkTime {.dirty.} =
 
 # ---------------- dump heap ----------------
 
+template dumpHeapFile(gch: var GcHeap): File =
+  cast[File](gch.pDumpHeapFile)
+
 proc debugGraph(s: PCell) =
   c_fprintf(gch.dumpHeapFile, "child %p\n", s)
 
@@ -625,7 +631,7 @@ proc GC_dumpHeap*(file: File) =
   ## Dumps the GCed heap's content to a file. Can be useful for
   ## debugging. Produces an undocumented text file format that
   ## can be translated into "dot" syntax via the "heapdump2dot" tool.
-  gch.dumpHeapFile = file
+  gch.pDumpHeapFile = file
   var spaceIter: ObjectSpaceIter
   var d = gch.decStack.d
   for i in 0 .. < gch.decStack.len:
@@ -643,7 +649,7 @@ proc GC_dumpHeap*(file: File) =
       writeCell(file, "cell ", c)
       forAllChildren(c, waDebug)
       c_fprintf(file, "end\n")
-  gch.dumpHeapFile = nil
+  gch.pDumpHeapFile = nil
 
 proc GC_dumpHeap() =
   var f: File
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 7a1b88c84..513ede173 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -120,6 +120,7 @@ when allowForeignThreadGc:
     ## switches are used
     if not localGcInitialized:
       localGcInitialized = true
+      initAllocator()
       var stackTop {.volatile.}: pointer
       setStackBottom(addr(stackTop))
       initGC()
diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim
index 5f72b8959..3eda08df9 100644
--- a/lib/system/gc_stack.nim
+++ b/lib/system/gc_stack.nim
@@ -79,6 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) =
   try:
     body
   finally:
+    r = tlRegion
     tlRegion = oldRegion
 
 template inc(p: pointer, s: int) =
@@ -464,4 +465,10 @@ proc getFreeMem(): int = tlRegion.remaining
 proc getTotalMem(): int =
   result = tlRegion.totalSize
 
+proc getOccupiedMem*(r: MemRegion): int =
+  result = r.totalSize - r.remaining
+proc getFreeMem*(r: MemRegion): int = r.remaining
+proc getTotalMem*(r: MemRegion): int =
+  result = r.totalSize
+
 proc setStackBottom(theStackBottom: pointer) = discard
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index b07a362a0..316dd74d7 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -87,8 +87,6 @@ elif defined(posix):
     const MAP_ANONYMOUS = 0x1000
   elif defined(solaris):
     const MAP_ANONYMOUS = 0x100
-  elif defined(linux):
-    const MAP_ANONYMOUS = 0x20
   else:
     var
       MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 3e9657ce0..5c10392f1 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -104,21 +104,22 @@ proc getFileHandle*(f: File): FileHandle = c_fileno(f)
 
 proc readLine(f: File, line: var TaintedString): bool =
   var pos = 0
+  var sp: cint = 80
   # Use the currently reserved space for a first try
-  when defined(nimscript):
-    var space: cint = 80
+  if line.string.isNil:
+    line = TaintedString(newStringOfCap(80))
   else:
-    var space: cint = cint(cast[PGenericSeq](line.string).space)
-  line.string.setLen(space)
-
+    when not defined(nimscript):
+      sp = cint(cast[PGenericSeq](line.string).space)
+    line.string.setLen(sp)
   while true:
     # memset to \l so that we can tell how far fgets wrote, even on EOF, where
     # fgets doesn't append an \l
-    c_memset(addr line.string[pos], '\l'.ord, space)
-    if c_fgets(addr line.string[pos], space, f) == nil:
+    c_memset(addr line.string[pos], '\l'.ord, sp)
+    if c_fgets(addr line.string[pos], sp, f) == nil:
       line.string.setLen(0)
       return false
-    let m = c_memchr(addr line.string[pos], '\l'.ord, space)
+    let m = c_memchr(addr line.string[pos], '\l'.ord, sp)
     if m != nil:
       # \l found: Could be our own or the one by fgets, in any case, we're done
       var last = cast[ByteAddress](m) - cast[ByteAddress](addr line.string[0])
@@ -129,17 +130,17 @@ proc readLine(f: File, line: var TaintedString): bool =
         # \0\l\0 => line ending in a null character.
         # \0\l\l => last line without newline, null was put there by fgets.
       elif last > 0 and line.string[last-1] == '\0':
-        if last < pos + space - 1 and line.string[last+1] != '\0':
+        if last < pos + sp - 1 and line.string[last+1] != '\0':
           dec last
       line.string.setLen(last)
       return true
     else:
       # fgets will have inserted a null byte at the end of the string.
-      dec space
+      dec sp
     # No \l found: Increase buffer and read more
-    inc pos, space
-    space = 128 # read in 128 bytes at a time
-    line.string.setLen(pos+space)
+    inc pos, sp
+    sp = 128 # read in 128 bytes at a time
+    line.string.setLen(pos+sp)
 
 proc readLine(f: File): TaintedString =
   result = TaintedString(newStringOfCap(80))
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 3e170172b..3a93221e0 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -110,6 +110,11 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
     result.len = src.len
     copyMem(addr(result.data), addr(src.data), src.len + 1)
 
+proc copyDeepString(src: NimString): NimString {.inline.} =
+  if src != nil:
+    result = rawNewStringNoInit(src.len)
+    result.len = src.len
+    copyMem(addr(result.data), addr(src.data), src.len + 1)
 
 proc hashString(s: string): int {.compilerproc.} =
   # the compiler needs exactly the same hash function!
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 62829f62c..6f5bb38b1 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -356,6 +356,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
 
 template threadProcWrapperBody(closure: expr) {.immediate.} =
   when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
+  when declared(initAllocator):
+    initAllocator()
   var thrd = cast[ptr Thread[TArg]](closure)
   threadProcWrapStackFrame(thrd)
   # Since an unhandled exception terminates the whole process (!), there is
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 4465e961c..731ef52dc 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -130,286 +130,7 @@ export Port, SocketFlag
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
-# -- Futures
-
-type
-  FutureBase* = ref object of RootObj ## Untyped future.
-    cb: proc () {.closure,gcsafe.}
-    finished: bool
-    error*: ref Exception ## Stored exception
-    errorStackTrace*: string
-    when not defined(release):
-      stackTrace: string ## For debugging purposes only.
-      id: int
-      fromProc: string
-
-  Future*[T] = ref object of FutureBase ## Typed future.
-    value: T ## Stored value
-
-  FutureVar*[T] = distinct Future[T]
-
-  FutureError* = object of Exception
-    cause*: FutureBase
-
-{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
-
-when not defined(release):
-  var currentID = 0
-
-proc callSoon*(cbproc: proc ()) {.gcsafe.}
-
-proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
-  ## Creates a new future.
-  ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
-  ## that this future belongs to, is a good habit as it helps with debugging.
-  new(result)
-  result.finished = false
-  when not defined(release):
-    result.stackTrace = getStackTrace()
-    result.id = currentID
-    result.fromProc = fromProc
-    currentID.inc()
-
-proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
-  ## Create a new ``FutureVar``. This Future type is ideally suited for
-  ## situations where you want to avoid unnecessary allocations of Futures.
-  ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
-  ## that this future belongs to, is a good habit as it helps with debugging.
-  result = FutureVar[T](newFuture[T](fromProc))
-
-proc clean*[T](future: FutureVar[T]) =
-  ## Resets the ``finished`` status of ``future``.
-  Future[T](future).finished = false
-  Future[T](future).error = nil
-
-proc checkFinished[T](future: Future[T]) =
-  ## Checks whether `future` is finished. If it is then raises a
-  ## ``FutureError``.
-  when not defined(release):
-    if future.finished:
-      var msg = ""
-      msg.add("An attempt was made to complete a Future more than once. ")
-      msg.add("Details:")
-      msg.add("\n  Future ID: " & $future.id)
-      msg.add("\n  Created in proc: " & future.fromProc)
-      msg.add("\n  Stack trace to moment of creation:")
-      msg.add("\n" & indent(future.stackTrace.strip(), 4))
-      when T is string:
-        msg.add("\n  Contents (string): ")
-        msg.add("\n" & indent(future.value.repr, 4))
-      msg.add("\n  Stack trace to moment of secondary completion:")
-      msg.add("\n" & indent(getStackTrace().strip(), 4))
-      var err = newException(FutureError, msg)
-      err.cause = future
-      raise err
-
-proc complete*[T](future: Future[T], val: T) =
-  ## Completes ``future`` with value ``val``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  assert(future.error == nil)
-  future.value = val
-  future.finished = true
-  if future.cb != nil:
-    future.cb()
-
-proc complete*(future: Future[void]) =
-  ## Completes a void ``future``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  assert(future.error == nil)
-  future.finished = true
-  if future.cb != nil:
-    future.cb()
-
-proc complete*[T](future: FutureVar[T]) =
-  ## Completes a ``FutureVar``.
-  template fut: expr = Future[T](future)
-  checkFinished(fut)
-  assert(fut.error == nil)
-  fut.finished = true
-  if fut.cb != nil:
-    fut.cb()
-
-proc fail*[T](future: Future[T], error: ref Exception) =
-  ## Completes ``future`` with ``error``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  future.finished = true
-  future.error = error
-  future.errorStackTrace =
-    if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
-  if future.cb != nil:
-    future.cb()
-  else:
-    # This is to prevent exceptions from being silently ignored when a future
-    # is discarded.
-    # TODO: This may turn out to be a bad idea.
-    # Turns out this is a bad idea.
-    #raise error
-    discard
-
-proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when the future completes.
-  ##
-  ## If future has already completed then ``cb`` will be called immediately.
-  ##
-  ## **Note**: You most likely want the other ``callback`` setter which
-  ## passes ``future`` as a param to the callback.
-  future.cb = cb
-  if future.finished:
-    callSoon(future.cb)
-
-proc `callback=`*[T](future: Future[T],
-    cb: proc (future: Future[T]) {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when the future completes.
-  ##
-  ## If future has already completed then ``cb`` will be called immediately.
-  future.callback = proc () = cb(future)
-
-proc injectStacktrace[T](future: Future[T]) =
-  # TODO: Come up with something better.
-  when not defined(release):
-    var msg = ""
-    msg.add("\n  " & future.fromProc & "'s lead up to read of failed Future:")
-
-    if not future.errorStackTrace.isNil and future.errorStackTrace != "":
-      msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
-    else:
-      msg.add("\n    Empty or nil stack trace.")
-    future.error.msg.add(msg)
-
-proc read*[T](future: Future[T]): T =
-  ## Retrieves the value of ``future``. Future must be finished otherwise
-  ## this function will fail with a ``ValueError`` exception.
-  ##
-  ## If the result of the future is an error then that error will be raised.
-  if future.finished:
-    if future.error != nil:
-      injectStacktrace(future)
-      raise future.error
-    when T isnot void:
-      return future.value
-  else:
-    # TODO: Make a custom exception type for this?
-    raise newException(ValueError, "Future still in progress.")
-
-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
-  else:
-    raise newException(ValueError, "No error in future.")
-
-proc mget*[T](future: FutureVar[T]): var T =
-  ## Returns a mutable value stored in ``future``.
-  ##
-  ## Unlike ``read``, this function will not raise an exception if the
-  ## Future has not been finished.
-  result = Future[T](future).value
-
-proc finished*[T](future: Future[T]): bool =
-  ## Determines whether ``future`` has completed.
-  ##
-  ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
-  future.finished
-
-proc failed*(future: FutureBase): bool =
-  ## Determines whether ``future`` completed with an error.
-  return future.error != nil
-
-proc asyncCheck*[T](future: Future[T]) =
-  ## Sets a callback on ``future`` which raises an exception if the future
-  ## finished with an error.
-  ##
-  ## This should be used instead of ``discard`` to discard void futures.
-  future.callback =
-    proc () =
-      if future.failed:
-        injectStacktrace(future)
-        raise future.error
-
-proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once both ``fut1`` and ``fut2``
-  ## complete.
-  var retFuture = newFuture[void]("asyncdispatch.`and`")
-  fut1.callback =
-    proc () =
-      if not retFuture.finished:
-        if fut1.failed: retFuture.fail(fut1.error)
-        elif fut2.finished: retFuture.complete()
-  fut2.callback =
-    proc () =
-      if not retFuture.finished:
-        if fut2.failed: retFuture.fail(fut2.error)
-        elif fut1.finished: retFuture.complete()
-  return retFuture
-
-proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once either ``fut1`` or ``fut2``
-  ## complete.
-  var retFuture = newFuture[void]("asyncdispatch.`or`")
-  proc cb[X](fut: Future[X]) =
-    if fut.failed: retFuture.fail(fut.error)
-    if not retFuture.finished: retFuture.complete()
-  fut1.callback = cb[T]
-  fut2.callback = cb[Y]
-  return retFuture
-
-proc all*[T](futs: varargs[Future[T]]): auto =
-  ## Returns a future which will complete once
-  ## all futures in ``futs`` complete.
-  ##
-  ## If the awaited futures are not ``Future[void]``, the returned future
-  ## will hold the values of all awaited futures in a sequence.
-  ##
-  ## If the awaited futures *are* ``Future[void]``,
-  ## this proc returns ``Future[void]``.
-
-  when T is void:
-    var
-      retFuture = newFuture[void]("asyncdispatch.all")
-      completedFutures = 0
-
-    let totalFutures = len(futs)
-
-    for fut in futs:
-      fut.callback = proc(f: Future[T]) =
-        if f.failed:
-          retFuture.fail(f.error)
-        elif not retFuture.finished:
-          inc(completedFutures)
-
-          if completedFutures == totalFutures:
-            retFuture.complete()
-
-    return retFuture
-
-  else:
-    var
-      retFuture = newFuture[seq[T]]("asyncdispatch.all")
-      retValues = newSeq[T](len(futs))
-      completedFutures = 0
-
-    for i, fut in futs:
-      proc setCallback(i: int) =
-        fut.callback = proc(f: Future[T]) =
-          if f.failed:
-            retFuture.fail(f.error)
-          elif not retFuture.finished:
-            retValues[i] = f.read()
-            inc(completedFutures)
-
-            if completedFutures == len(retValues):
-              retFuture.complete(retValues)
-
-      setCallback(i)
-
-    return retFuture
+include "../includes/asyncfutures"
 
 type
   PDispatcherBase = ref object of RootRef
@@ -522,43 +243,44 @@ when defined(windows) or defined(nimdoc):
       if at == -1: winlean.INFINITE
       else: at.int32
 
-    var lpNumberOfBytesTransferred: Dword
-    var lpCompletionKey: ULONG_PTR
-    var customOverlapped: PCustomOverlapped
-    let res = getQueuedCompletionStatus(p.ioPort,
-        addr lpNumberOfBytesTransferred, addr lpCompletionKey,
-        cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
-
-    # http://stackoverflow.com/a/12277264/492186
-    # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
-    if res:
-      # This is useful for ensuring the reliability of the overlapped struct.
-      assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
-
-      customOverlapped.data.cb(customOverlapped.data.fd,
-          lpNumberOfBytesTransferred, OSErrorCode(-1))
-
-      # If cell.data != nil, then system.protect(rawEnv(cb)) was called,
-      # so we need to dispose our `cb` environment, because it is not needed
-      # anymore.
-      if customOverlapped.data.cell.data != nil:
-        system.dispose(customOverlapped.data.cell)
-
-      GC_unref(customOverlapped)
-    else:
-      let errCode = osLastError()
-      if customOverlapped != nil:
+    if p.handles.len != 0:
+      var lpNumberOfBytesTransferred: Dword
+      var lpCompletionKey: ULONG_PTR
+      var customOverlapped: PCustomOverlapped
+      let res = getQueuedCompletionStatus(p.ioPort,
+          addr lpNumberOfBytesTransferred, addr lpCompletionKey,
+          cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
+
+      # http://stackoverflow.com/a/12277264/492186
+      # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
+      if res:
+        # This is useful for ensuring the reliability of the overlapped struct.
         assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
+
         customOverlapped.data.cb(customOverlapped.data.fd,
-            lpNumberOfBytesTransferred, errCode)
+            lpNumberOfBytesTransferred, OSErrorCode(-1))
+
+        # If cell.data != nil, then system.protect(rawEnv(cb)) was called,
+        # so we need to dispose our `cb` environment, because it is not needed
+        # anymore.
         if customOverlapped.data.cell.data != nil:
           system.dispose(customOverlapped.data.cell)
+
         GC_unref(customOverlapped)
       else:
-        if errCode.int32 == WAIT_TIMEOUT:
-          # Timed out
-          discard
-        else: raiseOSError(errCode)
+        let errCode = osLastError()
+        if customOverlapped != nil:
+          assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
+          customOverlapped.data.cb(customOverlapped.data.fd,
+              lpNumberOfBytesTransferred, errCode)
+          if customOverlapped.data.cell.data != nil:
+            system.dispose(customOverlapped.data.cell)
+          GC_unref(customOverlapped)
+        else:
+          if errCode.int32 == WAIT_TIMEOUT:
+            # Timed out
+            discard
+          else: raiseOSError(errCode)
 
     # Timer processing.
     processTimers(p)
@@ -1472,21 +1194,21 @@ else:
         var fd = keys[i].fd.SocketHandle
         let events = keys[i].events
 
-        if Event.Read in events:
+        if Event.Read in events or events == {Event.Error}:
           let cb = keys[i].data.readCB
-          doAssert(cb != nil)
-          if cb(fd.AsyncFD):
-            p.selector.withData(fd, adata) do:
-              if adata.readCB == cb:
-                adata.readCB = nil
+          if cb != nil:
+            if cb(fd.AsyncFD):
+              p.selector.withData(fd, adata) do:
+                if adata.readCB == cb:
+                  adata.readCB = nil
 
-        if Event.Write in events:
+        if Event.Write in events or events == {Event.Error}:
           let cb = keys[i].data.writeCB
-          doAssert(cb != nil)
-          if cb(fd.AsyncFD):
-            p.selector.withData(fd, adata) do:
-              if adata.writeCB == cb:
-                adata.writeCB = nil
+          if cb != nil:
+            if cb(fd.AsyncFD):
+              p.selector.withData(fd, adata) do:
+                if adata.writeCB == cb:
+                  adata.writeCB = nil
 
         when supportedPlatform:
           if (customSet * events) != {}:
diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim
new file mode 100644
index 000000000..06f84c881
--- /dev/null
+++ b/lib/windows/registry.nim
@@ -0,0 +1,77 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module is experimental and its interface may change.
+
+import winlean, os
+
+type
+  HKEY* = uint
+
+const
+  HKEY_LOCAL_MACHINE* = HKEY(0x80000002u)
+  HKEY_CURRENT_USER* = HKEY(2147483649)
+
+  RRF_RT_ANY = 0x0000ffff
+  KEY_WOW64_64KEY = 0x0100
+  KEY_WOW64_32KEY = 0x0200
+  KEY_READ = 0x00020019
+  REG_SZ = 1
+
+proc regOpenKeyEx(hKey: HKEY, lpSubKey: WideCString, ulOptions: int32,
+                  samDesired: int32,
+                  phkResult: var HKEY): int32 {.
+  importc: "RegOpenKeyExW", dynlib: "Advapi32.dll", stdcall.}
+
+proc regCloseKey(hkey: HKEY): int32 {.
+  importc: "RegCloseKey", dynlib: "Advapi32.dll", stdcall.}
+
+proc regGetValue(key: HKEY, lpSubKey, lpValue: WideCString;
+                 dwFlags: int32 = RRF_RT_ANY, pdwType: ptr int32,
+                 pvData: pointer,
+                 pcbData: ptr int32): int32 {.
+  importc: "RegGetValueW", dynlib: "Advapi32.dll", stdcall.}
+
+template call(f) =
+  let err = f
+  if err != 0:
+    raiseOSError(err.OSErrorCode, astToStr(f))
+
+proc getUnicodeValue*(path, key: string; handle: HKEY): string =
+  let hh = newWideCString path
+  let kk = newWideCString key
+  var bufsize: int32
+  # try a couple of different flag settings:
+  var flags: int32 = RRF_RT_ANY
+  let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufsize)
+  if err != 0:
+    var newHandle: HKEY
+    call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle)
+    call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize)
+    var res = newWideCString("", bufsize)
+    call regGetValue(newHandle, nil, kk, flags, nil, cast[pointer](res),
+                   addr bufsize)
+    result = res $ bufsize
+    call regCloseKey(newHandle)
+  else:
+    var res = newWideCString("", bufsize)
+    call regGetValue(handle, hh, kk, flags, nil, cast[pointer](res),
+                   addr bufsize)
+    result = res $ bufsize
+
+proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString,
+                 dwType: int32; lpData: WideCString; cbData: int32): int32 {.
+  importc: "RegSetKeyValueW", dynlib: "Advapi32.dll", stdcall.}
+
+proc setUnicodeValue*(path, key, val: string; handle: HKey) =
+  let hh = newWideCString path
+  let kk = newWideCString key
+  let vv = newWideCString val
+  call regSetValue(handle, hh, kk, REG_SZ, vv, (vv.len.int32+1)*2)
+
diff --git a/lib/wrappers/linenoise/clinenoise.c b/lib/wrappers/linenoise/clinenoise.c
index a03e6f379..c76fc6586 100644
--- a/lib/wrappers/linenoise/clinenoise.c
+++ b/lib/wrappers/linenoise/clinenoise.c
@@ -116,7 +116,9 @@
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include "clinenoise.h"
+#ifndef __LINENOISE_H
+#  include "clinenoise.h"
+#endif
 
 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
 #define LINENOISE_MAX_LINE 4096
diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim
index b7004c6e2..4ac7bf4a8 100644
--- a/lib/wrappers/linenoise/linenoise.nim
+++ b/lib/wrappers/linenoise/linenoise.nim
@@ -14,7 +14,8 @@ type
 
   CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
 
-{.compile: "clinenoise.c".}
+{.emit: staticRead"clinenoise.h".}
+{.emit: staticRead"clinenoise.c".}
 
 proc setCompletionCallback*(a2: ptr CompletionCallback) {.
     importc: "linenoiseSetCompletionCallback".}
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
index af504864d..6dbed23b3 100644
--- a/lib/wrappers/mysql.nim
+++ b/lib/wrappers/mysql.nim
@@ -13,10 +13,10 @@
 when defined(Unix):
   when defined(macosx):
     const
-      lib = "libmysqlclient.(15|16|17|18).dylib"
+      lib = "libmysqlclient.(15|16|17|18|19|20).dylib"
   else:
     const
-      lib = "libmysqlclient.so.(15|16|17|18)"
+      lib = "libmysqlclient.so.(15|16|17|18|19|20)"
 when defined(Windows):
   const
     lib = "libmysql.dll"
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 9dad7e489..204e5bb40 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -26,7 +26,7 @@ when useWinVersion:
   from winlean import SocketHandle
 else:
   const
-    versions = "(|.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
+    versions = "(|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
   when defined(macosx):
     const
       DLLSSLName = "libssl" & versions & ".dylib"