summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/asyncdispatch.nim34
-rw-r--r--lib/pure/asynchttpserver.nim77
-rw-r--r--lib/pure/asyncio.nim7
-rw-r--r--lib/pure/asyncnet.nim45
-rw-r--r--lib/pure/complex.nim5
-rw-r--r--lib/pure/htmlgen.nim2
-rw-r--r--lib/pure/httpclient.nim185
-rw-r--r--lib/pure/md5.nim270
-rw-r--r--lib/pure/net.nim33
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/parseurl.nim8
-rw-r--r--lib/pure/pegs.nim2
-rw-r--r--lib/pure/rawsockets.nim4
-rw-r--r--lib/pure/rawsockets.pretty.nim426
-rw-r--r--lib/pure/scgi.nim4
-rw-r--r--lib/pure/selectors.nim11
-rw-r--r--lib/pure/sockets.nim7
-rw-r--r--lib/pure/unittest.nim4
-rw-r--r--lib/pure/uri.nim85
19 files changed, 923 insertions, 288 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index a9a0b0fbe..c4abdf9f3 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -436,12 +436,12 @@ when defined(windows) or defined(nimdoc):
     if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
       raiseOSError(osLastError())
 
-  proc connectEx(s: SocketHandle, name: ptr TSockAddr, namelen: cint, 
+  proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint, 
                   lpSendBuffer: pointer, dwSendDataLength: Dword,
                   lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool =
     if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().")
     let fun =
-      cast[proc (s: SocketHandle, name: ptr TSockAddr, namelen: cint, 
+      cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint, 
          lpSendBuffer: pointer, dwSendDataLength: Dword,
          lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr)
 
@@ -464,16 +464,16 @@ when defined(windows) or defined(nimdoc):
 
   proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
       dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: Dword,
-      LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: LPInt,
-      RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: LPInt) =
+      LocalSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt,
+      RemoteSockaddr: ptr ptr SockAddr, RemoteSockaddrLength: LPInt) =
     if getAcceptExSockAddrsPtr.isNil:
       raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().")
 
     let fun =
       cast[proc (lpOutputBuffer: pointer,
                  dwReceiveDataLength, dwLocalAddressLength,
-                 dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr TSockAddr,
-                 LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr TSockAddr,
+                 dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr,
+                 LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr,
                 RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
     
     fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
@@ -489,12 +489,12 @@ when defined(windows) or defined(nimdoc):
     verifyPresence(socket)
     var retFuture = newFuture[void]("connect")
     # Apparently ``ConnectEx`` expects the socket to be initially bound:
-    var saddr: Tsockaddr_in
+    var saddr: Sockaddr_in
     saddr.sin_family = int16(toInt(af))
     saddr.sin_port = 0
     saddr.sin_addr.s_addr = INADDR_ANY
-    if bindAddr(socket.SocketHandle, cast[ptr TSockAddr](addr(saddr)),
-                  sizeof(saddr).TSockLen) < 0'i32:
+    if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)),
+                  sizeof(saddr).SockLen) < 0'i32:
       raiseOSError(osLastError())
 
     var aiList = getAddrInfo(address, port, af)
@@ -516,7 +516,7 @@ when defined(windows) or defined(nimdoc):
       )
       
       var ret = connectEx(socket.SocketHandle, it.ai_addr,
-                          sizeof(Tsockaddr_in).cint, nil, 0, nil,
+                          sizeof(Sockaddr_in).cint, nil, 0, nil,
                           cast[POVERLAPPED](ol))
       if ret:
         # Request to connect completed immediately.
@@ -700,17 +700,17 @@ when defined(windows) or defined(nimdoc):
     var lpOutputBuf = newString(lpOutputLen)
     var dwBytesReceived: Dword
     let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
-    let dwLocalAddressLength = Dword(sizeof (Tsockaddr_in) + 16)
-    let dwRemoteAddressLength = Dword(sizeof(Tsockaddr_in) + 16)
+    let dwLocalAddressLength = Dword(sizeof (Sockaddr_in) + 16)
+    let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
 
     template completeAccept(): stmt {.immediate, dirty.} =
       var listenSock = socket
       let setoptRet = setsockopt(clientSock, SOL_SOCKET,
           SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
-          sizeof(listenSock).TSockLen)
+          sizeof(listenSock).SockLen)
       if setoptRet != 0: raiseOSError(osLastError())
 
-      var localSockaddr, remoteSockaddr: ptr TSockAddr
+      var localSockaddr, remoteSockaddr: ptr SockAddr
       var localLen, remoteLen: int32
       getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
                            dwLocalAddressLength, dwRemoteAddressLength,
@@ -719,7 +719,7 @@ when defined(windows) or defined(nimdoc):
       register(clientSock.TAsyncFD)
       # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
       retFuture.complete(
-        (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
+        (address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
          client: clientSock.TAsyncFD)
       )
 
@@ -878,6 +878,10 @@ else:
       let data = PData(info.key.data)
       assert data.fd == info.key.fd.TAsyncFD
       #echo("In poll ", data.fd.cint)
+      if EvError in info.events:
+        closeSocket(data.fd)
+        continue
+
       if EvRead in info.events:
         # Callback may add items to ``data.readCBs`` which causes issues if
         # we are iterating over ``data.readCBs`` at the same time. We therefore
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index e0f3b6fc2..4c48350aa 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -17,8 +17,10 @@
 ## as the response body.
 ##
 ## .. code-block::nim
+##    import asynchttpserver, asyncdispatch
+##
 ##    var server = newAsyncHttpServer()
-##    proc cb(req: TRequest) {.async.} =
+##    proc cb(req: Request) {.async.} =
 ##      await req.respond(Http200, "Hello World")
 ##
 ##    asyncCheck server.serve(Port(8080), cb)
@@ -27,25 +29,52 @@
 import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
 type
   Request* = object
-    client*: PAsyncSocket # TODO: Separate this into a Response object?
+    client*: AsyncSocket # TODO: Separate this into a Response object?
     reqMethod*: string
-    headers*: PStringTable
+    headers*: StringTableRef
     protocol*: tuple[orig: string, major, minor: int]
-    url*: TUri
+    url*: Uri
     hostname*: string ## The hostname of the client that made the request.
     body*: string
 
   AsyncHttpServer* = ref object
-    socket: PAsyncSocket
+    socket: AsyncSocket
     reuseAddr: bool
 
   HttpCode* = enum
+    Http100 = "100 Continue",
+    Http101 = "101 Switching Protocols",
     Http200 = "200 OK",
-    Http303 = "303 Moved",
+    Http201 = "201 Created",
+    Http202 = "202 Accepted",
+    Http204 = "204 No Content",
+    Http205 = "205 Reset Content",
+    Http206 = "206 Partial Content",
+    Http300 = "300 Multiple Choices",
+    Http301 = "301 Moved Permanently",
+    Http302 = "302 Found",
+    Http303 = "303 See Other",
+    Http304 = "304 Not Modified",
+    Http305 = "305 Use Proxy",
+    Http307 = "307 Temporary Redirect",
     Http400 = "400 Bad Request",
+    Http401 = "401 Unauthorized",
+    Http403 = "403 Forbidden",
     Http404 = "404 Not Found",
+    Http405 = "405 Method Not Allowed",
+    Http406 = "406 Not Acceptable",
+    Http407 = "407 Proxy Authentication Required",
+    Http408 = "408 Request Timeout",
+    Http409 = "409 Conflict",
+    Http410 = "410 Gone",
+    Http411 = "411 Length Required",
+    Http418 = "418 I'm a teapot",
     Http500 = "500 Internal Server Error",
-    Http502 = "502 Bad Gateway"
+    Http501 = "501 Not Implemented",
+    Http502 = "502 Bad Gateway",
+    Http503 = "503 Service Unavailable",
+    Http504 = "504 Gateway Timeout",
+    Http505 = "505 HTTP Version Not Supported"
 
   HttpVersion* = enum
     HttpVer11,
@@ -55,7 +84,7 @@ type
   THttpCode: HttpCode, THttpVersion: HttpVersion].}
 
 proc `==`*(protocol: tuple[orig: string, major, minor: int],
-           ver: THttpVersion): bool =
+           ver: HttpVersion): bool =
   let major =
     case ver
     of HttpVer11, HttpVer10: 1
@@ -65,23 +94,23 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
     of HttpVer10: 0
   result = protocol.major == major and protocol.minor == minor
 
-proc newAsyncHttpServer*(reuseAddr = true): PAsyncHttpServer =
+proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer =
   ## Creates a new ``AsyncHttpServer`` instance.
   new result
   result.reuseAddr = reuseAddr
 
-proc addHeaders(msg: var string, headers: PStringTable) =
+proc addHeaders(msg: var string, headers: StringTableRef) =
   for k, v in headers:
     msg.add(k & ": " & v & "\c\L")
 
-proc sendHeaders*(req: TRequest, headers: PStringTable): Future[void] =
+proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] =
   ## Sends the specified headers to the requesting client.
   var msg = ""
   addHeaders(msg, headers)
   return req.client.send(msg)
 
-proc respond*(req: TRequest, code: THttpCode,
-        content: string, headers: PStringTable = newStringTable()) {.async.} =
+proc respond*(req: Request, code: HttpCode,
+        content: string, headers = newStringTable()) {.async.} =
   ## Responds to the request with the specified ``HttpCode``, headers and
   ## content.
   ##
@@ -92,7 +121,7 @@ proc respond*(req: TRequest, code: THttpCode,
   msg.addHeaders(customHeaders)
   await req.client.send(msg & "\c\L" & content)
 
-proc newRequest(): TRequest =
+proc newRequest(): Request =
   result.headers = newStringTable(modeCaseInsensitive)
   result.hostname = ""
   result.body = ""
@@ -107,20 +136,20 @@ proc parseHeader(line: string): tuple[key, value: string] =
 proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
   var i = protocol.skipIgnoreCase("HTTP/")
   if i != 5:
-    raise newException(EInvalidValue, "Invalid request protocol. Got: " &
+    raise newException(ValueError, "Invalid request protocol. Got: " &
         protocol)
   result.orig = protocol
   i.inc protocol.parseInt(result.major, i)
   i.inc # Skip .
   i.inc protocol.parseInt(result.minor, i)
 
-proc sendStatus(client: PAsyncSocket, status: string): Future[void] =
+proc sendStatus(client: AsyncSocket, status: string): Future[void] =
   client.send("HTTP/1.1 " & status & "\c\L")
 
-proc processClient(client: PAsyncSocket, address: string,
-                   callback: proc (request: TRequest):
+proc processClient(client: AsyncSocket, address: string,
+                   callback: proc (request: Request):
                       Future[void] {.closure, gcsafe.}) {.async.} =
-  while not client.closed:
+  while not client.isClosed:
     # GET /path HTTP/1.1
     # Header: val
     # \n
@@ -160,7 +189,7 @@ proc processClient(client: PAsyncSocket, address: string,
     request.url = parseUri(path)
     try:
       request.protocol = protocol.parseProtocol()
-    except EInvalidValue:
+    except ValueError:
       asyncCheck request.respond(Http400, "Invalid request protocol. Got: " &
           protocol)
       continue
@@ -206,8 +235,8 @@ proc processClient(client: PAsyncSocket, address: string,
       request.client.close()
       break
 
-proc serve*(server: PAsyncHttpServer, port: Port,
-            callback: proc (request: TRequest): Future[void] {.closure,gcsafe.},
+proc serve*(server: AsyncHttpServer, port: Port,
+            callback: proc (request: Request): Future[void] {.closure,gcsafe.},
             address = "") {.async.} =
   ## Starts the process of listening for incoming HTTP connections on the
   ## specified address and port.
@@ -227,14 +256,14 @@ proc serve*(server: PAsyncHttpServer, port: Port,
     #echo(f.isNil)
     #echo(f.repr)
 
-proc close*(server: PAsyncHttpServer) =
+proc close*(server: AsyncHttpServer) =
   ## Terminates the async http server instance.
   server.socket.close()
 
 when isMainModule:
   proc main =
     var server = newAsyncHttpServer()
-    proc cb(req: TRequest) {.async.} =
+    proc cb(req: Request) {.async.} =
       #echo(req.reqMethod, " ", req.url)
       #echo(req.headers)
       let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT",
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index 4c25952a8..d40c3849e 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -10,6 +10,11 @@ include "system/inclrtl"
 
 import sockets, os
 
+##
+## **Warning:** This module is deprecated since version 0.10.2.
+## Use the brand new `asyncdispatch <asyncdispatch.html>`_ module together
+## with the `asyncnet <asyncnet.html>`_ module.
+
 ## This module implements an asynchronous event loop together with asynchronous
 ## sockets which use this event loop.
 ## It is akin to Python's asyncore module. Many modules that use sockets
@@ -90,6 +95,8 @@ import sockets, os
 ##      var client: Socket
 ##      getSocket(s).accept(client)
 
+{.deprecated.}
+
 when defined(windows):
   from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
     FD_ISSET, select
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index fd29e0a22..76b2bc46c 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -69,13 +69,13 @@ type
   # TODO: I would prefer to just do:
   # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work.
   AsyncSocketDesc  = object
-    fd*: SocketHandle
-    closed*: bool ## determines whether this socket has been closed
-    case isBuffered*: bool ## determines whether this socket is buffered.
+    fd: SocketHandle
+    closed: bool ## determines whether this socket has been closed
+    case isBuffered: bool ## determines whether this socket is buffered.
     of true:
-      buffer*: array[0..BufferSize, char]
-      currPos*: int # current index in buffer
-      bufLen*: int # current length of buffer
+      buffer: array[0..BufferSize, char]
+      currPos: int # current index in buffer
+      bufLen: int # current length of buffer
     of false: nil
     case isSsl: bool
     of true:
@@ -91,7 +91,8 @@ type
 
 # TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
 
-proc newSocket(fd: TAsyncFD, isBuff: bool): AsyncSocket =
+proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket =
+  ## Creates a new ``AsyncSocket`` based on the supplied params.
   assert fd != osInvalidSocket.TAsyncFD
   new(result)
   result.fd = fd.SocketHandle
@@ -102,11 +103,17 @@ proc newSocket(fd: TAsyncFD, isBuff: bool): AsyncSocket =
 proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
     protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
   ## Creates a new asynchronous socket.
-  result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
+  ##
+  ## This procedure will also create a brand new file descriptor for
+  ## this socket.
+  result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
 
 proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): AsyncSocket =
   ## Creates a new asynchronous socket.
-  result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
+  ##
+  ## This procedure will also create a brand new file descriptor for
+  ## this socket.
+  result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
 
 when defined(ssl):
   proc getSslError(handle: SslPtr, err: cint): cint =
@@ -275,7 +282,7 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
         retFuture.fail(future.readError)
       else:
         let resultTup = (future.read.address,
-                         newSocket(future.read.client, socket.isBuffered))
+                         newAsyncSocket(future.read.client, socket.isBuffered))
         retFuture.complete(resultTup)
   return retFuture
 
@@ -399,13 +406,13 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
 
 proc close*(socket: AsyncSocket) =
   ## Closes the socket.
-  socket.fd.TAsyncFD.closeSocket()
+  defer:
+    socket.fd.TAsyncFD.closeSocket()
   when defined(ssl):
     if socket.isSSL:
       let res = SslShutdown(socket.sslHandle)
       if res == 0:
-        if SslShutdown(socket.sslHandle) != 1:
-          raiseSslError()
+        discard
       elif res != 1:
         raiseSslError()
   socket.closed = true # TODO: Add extra debugging checks for this.
@@ -439,6 +446,18 @@ proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool,
   var valuei = cint(if value: 1 else: 0)
   setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
 
+proc isSsl*(socket: AsyncSocket): bool =
+  ## Determines whether ``socket`` is a SSL socket.
+  socket.isSsl
+
+proc getFd*(socket: AsyncSocket): SocketHandle =
+  ## Returns the socket's file descriptor.
+  return socket.fd
+
+proc isClosed*(socket: AsyncSocket): bool =
+  ## Determines whether the socket has been closed.
+  return socket.closed
+
 when isMainModule:
   type
     TestCases = enum
diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim
index a8709e098..0c5704f69 100644
--- a/lib/pure/complex.nim
+++ b/lib/pure/complex.nim
@@ -19,10 +19,7 @@ import
   math
  
 const
-  EPS = 5.0e-6 ## Epsilon used for float comparisons (should be smaller
-               ## if float is really float64, but w/ the current version
-               ## it seems to be float32?)
-
+  EPS = 1.0e-7 ## Epsilon used for float comparisons.
 
 type
   Complex* = tuple[re, im: float]
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index a1440b6f4..4852ed50d 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -264,7 +264,7 @@ macro html*(e: expr): expr {.immediate.} =
   let e = callsite()
   result = xmlCheckedTag(e, "html", "xmlns", "")
 
-macro hr*(e: expr): expr {.immediate.} = 
+macro hr*(): expr {.immediate.} = 
   ## generates the HTML ``hr`` element.
   let e = callsite()
   result = xmlCheckedTag(e, "hr", commonAttr, "", true)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 3afb625ee..bfdfed72c 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -32,21 +32,12 @@
 ## the server.
 ##
 ## .. code-block:: Nim
-##   var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
-##   var body: string = "--xyz\c\L"
-##   # soap 1.2 output
-##   body.add("Content-Disposition: form-data; name=\"output\"\c\L")
-##   body.add("\c\Lsoap12\c\L")
+##   var data = newMultipartData()
+##   data["output"] = "soap12"
+##   data["uploaded_file"] = ("test.html", "text/html",
+##     "<html><head></head><body><p>test</p></body></html>")
 ##
-##   # html
-##   body.add("--xyz\c\L")
-##   body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
-##            " filename=\"test.html\"\c\L")
-##   body.add("Content-Type: text/html\c\L")
-##   body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
-##   body.add("--xyz--")
-##
-##   echo(postContent("http://validator.w3.org/check", headers, body))
+##   echo postContent("http://validator.w3.org/check", multipart=data)
 ##
 ## Asynchronous HTTP requests
 ## ==========================
@@ -88,7 +79,7 @@
 ## constructor should be used for this purpose. However,
 ## currently only basic authentication is supported.
 
-import net, strutils, uri, parseutils, strtabs, base64, os
+import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math
 import asyncnet, asyncdispatch
 import rawsockets
 
@@ -103,6 +94,10 @@ type
     url*: Uri
     auth*: string
 
+  MultipartEntries* = openarray[tuple[name, content: string]]
+  MultipartData* = ref object
+    content: seq[string]
+
   ProtocolError* = object of IOError   ## exception that is raised when server
                                        ## does not conform to the implemented
                                        ## protocol
@@ -282,6 +277,109 @@ proc newProxy*(url: string, auth = ""): Proxy =
   ## Constructs a new ``TProxy`` object.
   result = Proxy(url: parseUri(url), auth: auth)
 
+proc newMultipartData*: MultipartData =
+  ## Constructs a new ``MultipartData`` object.
+  MultipartData(content: @[])
+
+proc add*(p: var MultipartData, name, content: string, filename: string = nil,
+          contentType: string = nil) =
+  ## Add a value to the multipart data. Raises a `ValueError` exception if
+  ## `name`, `filename` or `contentType` contain newline characters.
+
+  if {'\c','\L'} in name:
+    raise newException(ValueError, "name contains a newline character")
+  if filename != nil and {'\c','\L'} in filename:
+    raise newException(ValueError, "filename contains a newline character")
+  if contentType != nil and {'\c','\L'} in contentType:
+    raise newException(ValueError, "contentType contains a newline character")
+
+  var str = "Content-Disposition: form-data; name=\"" & name & "\""
+  if filename != nil:
+    str.add("; filename=\"" & filename & "\"")
+  str.add("\c\L")
+  if contentType != nil:
+    str.add("Content-Type: " & contentType & "\c\L")
+  str.add("\c\L" & content & "\c\L")
+
+  p.content.add(str)
+
+proc add*(p: var MultipartData, xs: MultipartEntries): MultipartData
+         {.discardable.} =
+  ## Add a list of multipart entries to the multipart data `p`. All values are
+  ## added without a filename and without a content type.
+  ##
+  ## .. code-block:: Nim
+  ##   data.add({"action": "login", "format": "json"})
+  for name, content in xs.items:
+    p.add(name, content)
+  result = p
+
+proc newMultipartData*(xs: MultipartEntries): MultipartData =
+  ## Create a new multipart data object and fill it with the entries `xs`
+  ## directly.
+  ##
+  ## .. code-block:: Nim
+  ##   var data = newMultipartData({"action": "login", "format": "json"})
+  result = MultipartData(content: @[])
+  result.add(xs)
+
+proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
+              MultipartData {.discardable.} =
+  ## Add files to a multipart data object. The file will be opened from your
+  ## disk, read and sent with the automatically determined MIME type. Raises an
+  ## `IOError` if the file cannot be opened or reading fails. To manually
+  ## specify file content, filename and MIME type, use `[]=` instead.
+  ##
+  ## .. code-block:: Nim
+  ##   data.addFiles({"uploaded_file": "public/test.html"})
+  var m = newMimetypes()
+  for name, file in xs.items:
+    var contentType: string
+    let (dir, fName, ext) = splitFile(file)
+    if ext.len > 0:
+      contentType = m.getMimetype(ext[1..ext.high], nil)
+    p.add(name, readFile(file), fName & ext, contentType)
+  result = p
+
+proc `[]=`*(p: var MultipartData, name, content: string) =
+  ## Add a multipart entry to the multipart data `p`. The value is added
+  ## without a filename and without a content type.
+  ##
+  ## .. code-block:: Nim
+  ##   data["username"] = "NimUser"
+  p.add(name, content)
+
+proc `[]=`*(p: var MultipartData, name: string,
+            file: tuple[name, contentType, content: string]) =
+  ## Add a file to the multipart data `p`, specifying filename, contentType and
+  ## content manually.
+  ##
+  ## .. code-block:: Nim
+  ##   data["uploaded_file"] = ("test.html", "text/html",
+  ##     "<html><head></head><body><p>test</p></body></html>")
+  p.add(name, file.content, file.name, file.contentType)
+
+proc format(p: MultipartData): tuple[header, body: string] =
+  if p == nil or p.content == nil or p.content.len == 0:
+    return ("", "")
+
+  # Create boundary that is not in the data to be formatted
+  var bound: string
+  while true:
+    bound = $random(int.high)
+    var found = false
+    for s in p.content:
+      if bound in s:
+        found = true
+    if not found:
+      break
+
+  result.header = "Content-Type: multipart/form-data; boundary=" & bound & "\c\L"
+  result.body = ""
+  for s in p.content:
+    result.body.add("--" & bound & "\c\L" & s)
+  result.body.add("--" & bound & "--\c\L")
+
 proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
               body = "",
               sslContext: SSLContext = defaultSSLContext,
@@ -294,7 +392,9 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   var r = if proxy == nil: parseUri(url) else: proxy.url
   var headers = substr($httpMethod, len("http"))
   if proxy == nil:
-    headers.add(" /" & r.path & r.query)
+    headers.add(" " & r.path)
+    if r.query.len > 0:
+      headers.add("?" & r.query)
   else:
     headers.add(" " & url)
 
@@ -387,15 +487,30 @@ proc post*(url: string, extraHeaders = "", body = "",
            maxRedirects = 5,
            sslContext: SSLContext = defaultSSLContext,
            timeout = -1, userAgent = defUserAgent,
-           proxy: Proxy = nil): Response =
+           proxy: Proxy = nil,
+           multipart: MultipartData = nil): Response =
   ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
   ## | This proc adds the necessary Content-Length header.
   ## | This proc also handles redirection.
   ## | Extra headers can be specified and must be separated by ``\c\L``.
   ## | An optional timeout can be specified in miliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
-  var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
-  result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent,
+  ## | The optional ``multipart`` parameter can be used to create
+  ## ``multipart/form-data`` POSTs comfortably.
+  let (mpHeaders, mpBody) = format(multipart)
+
+  template withNewLine(x): expr =
+    if x.len > 0 and not x.endsWith("\c\L"):
+      x & "\c\L"
+    else:
+      x
+
+  var xb = mpBody.withNewLine() & body.withNewLine()
+
+  var xh = extraHeaders.withNewLine() & mpHeaders.withNewLine() &
+    withNewLine("Content-Length: " & $len(xb))
+
+  result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
                    proxy)
   var lastUrl = ""
   for i in 1..maxRedirects:
@@ -410,14 +525,17 @@ proc postContent*(url: string, extraHeaders = "", body = "",
                   maxRedirects = 5,
                   sslContext: SSLContext = defaultSSLContext,
                   timeout = -1, userAgent = defUserAgent,
-                  proxy: Proxy = nil): string =
+                  proxy: Proxy = nil,
+                  multipart: MultipartData = nil): string =
   ## | 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``.
   ## | An optional timeout can be specified in miliseconds, if reading from the
   ## 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.
   var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
-               userAgent, proxy)
+               userAgent, proxy, multipart)
   if r.status[0] in {'4','5'}:
     raise newException(HttpRequestError, r.status)
   else:
@@ -442,7 +560,9 @@ proc generateHeaders(r: Uri, httpMethod: HttpMethod,
                      headers: StringTableRef): string =
   result = substr($httpMethod, len("http"))
   # TODO: Proxies
-  result.add(" /" & r.path & r.query)
+  result.add(" " & r.path)
+  if r.query.len > 0:
+    result.add("?" & r.query)
   result.add(" HTTP/1.1\c\L")
 
   add(result, "Host: " & r.hostname & "\c\L")
@@ -706,18 +826,9 @@ when isMainModule:
     #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com&
     #  charset=%28detect+automatically%29&doctype=Inline&group=0")
 
-    var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
-    var body: string = "--xyz\c\L"
-    # soap 1.2 output
-    body.add("Content-Disposition: form-data; name=\"output\"\c\L")
-    body.add("\c\Lsoap12\c\L")
-
-    # html
-    body.add("--xyz\c\L")
-    body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
-             " filename=\"test.html\"\c\L")
-    body.add("Content-Type: text/html\c\L")
-    body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
-    body.add("--xyz--")
-
-    echo(postContent("http://validator.w3.org/check", headers, body))
+    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)
diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim
index 3b5453957..c6228af50 100644
--- a/lib/pure/md5.nim
+++ b/lib/pure/md5.nim
@@ -9,18 +9,20 @@
 
 ## Module for computing MD5 checksums.
 
-type 
-  MD5State = array[0..3, int32]
-  MD5Block = array[0..15, int32]
-  MD5CBits = array[0..7, int8]
-  MD5Digest* = array[0..15, int8]
-  MD5Buffer = array[0..63, int8]
-  MD5Context* {.final.} = object 
+import unsigned
+
+type
+  MD5State = array[0..3, uint32]
+  MD5Block = array[0..15, uint32]
+  MD5CBits = array[0..7, uint8]
+  MD5Digest* = array[0..15, uint8]
+  MD5Buffer = array[0..63, uint8]
+  MD5Context* {.final.} = object
     state: MD5State
-    count: array[0..1, int32]
+    count: array[0..1, uint32]
     buffer: MD5Buffer
 
-const 
+const
   padding: cstring = "\x80\0\0\0" &
                      "\0\0\0\0\0\0\0\0" &
                      "\0\0\0\0\0\0\0\0" &
@@ -31,60 +33,60 @@ const
                      "\0\0\0\0\0\0\0\0" &
                      "\0\0\0\0"
 
-proc F(x, y, z: int32): int32 {.inline.} = 
+proc F(x, y, z: uint32): uint32 {.inline.} =
   result = (x and y) or ((not x) and z)
 
-proc G(x, y, z: int32): int32 {.inline.} = 
+proc G(x, y, z: uint32): uint32 {.inline.} =
   result = (x and z) or (y and (not z))
 
-proc H(x, y, z: int32): int32 {.inline.} = 
+proc H(x, y, z: uint32): uint32 {.inline.} =
   result = x xor y xor z
 
-proc I(x, y, z: int32): int32 {.inline.} = 
+proc I(x, y, z: uint32): uint32 {.inline.} =
   result = y xor (x or (not z))
 
-proc rot(x: var int32, n: int8) {.inline.} = 
-  x = toU32(x shl ze(n)) or (x shr toU32(32 -% ze(n)))
+proc rot(x: var uint32, n: uint8) {.inline.} =
+  x = (x shl n) or (x shr (32'u32 - n))
 
-proc FF(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% F(b, c, d) +% x +% ac
+proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + F(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc GG(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% G(b, c, d) +% x +% ac
+proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + G(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc HH(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% H(b, c, d) +% x +% ac
+proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + H(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc II(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% I(b, c, d) +% x +% ac
+proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + I(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc encode(dest: var MD5Block, src: cstring) = 
+proc encode(dest: var MD5Block, src: cstring) =
   var j = 0
   for i in 0..high(dest):
-    dest[i] = toU32(ord(src[j]) or 
-                ord(src[j+1]) shl 8 or
-                ord(src[j+2]) shl 16 or
-                ord(src[j+3]) shl 24)
+    dest[i] = uint32(ord(src[j])) or
+              uint32(ord(src[j+1])) shl 8 or
+              uint32(ord(src[j+2])) shl 16 or
+              uint32(ord(src[j+3])) shl 24
     inc(j, 4)
 
-proc decode(dest: var openArray[int8], src: openArray[int32]) = 
+proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
   var i = 0
   for j in 0..high(src):
-    dest[i] = toU8(src[j] and 0xff'i32)
-    dest[i+1] = toU8(src[j] shr 8'i32 and 0xff'i32)
-    dest[i+2] = toU8(src[j] shr 16'i32 and 0xff'i32)
-    dest[i+3] = toU8(src[j] shr 24'i32 and 0xff'i32)
+    dest[i] = src[j] and 0xff'u32
+    dest[i+1] = src[j] shr 8 and 0xff'u32
+    dest[i+2] = src[j] shr 16 and 0xff'u32
+    dest[i+3] = src[j] shr 24 and 0xff'u32
     inc(i, 4)
 
-proc transform(buffer: pointer, state: var MD5State) = 
+proc transform(buffer: pointer, state: var MD5State) =
   var
     myBlock: MD5Block
   encode(myBlock, cast[cstring](buffer))
@@ -92,111 +94,111 @@ proc transform(buffer: pointer, state: var MD5State) =
   var b = state[1]
   var c = state[2]
   var d = state[3]
-  FF(a, b, c, d, myBlock[0], 7'i8, 0xD76AA478'i32)
-  FF(d, a, b, c, myBlock[1], 12'i8, 0xE8C7B756'i32)
-  FF(c, d, a, b, myBlock[2], 17'i8, 0x242070DB'i32)
-  FF(b, c, d, a, myBlock[3], 22'i8, 0xC1BDCEEE'i32)
-  FF(a, b, c, d, myBlock[4], 7'i8, 0xF57C0FAF'i32)
-  FF(d, a, b, c, myBlock[5], 12'i8, 0x4787C62A'i32)
-  FF(c, d, a, b, myBlock[6], 17'i8, 0xA8304613'i32)
-  FF(b, c, d, a, myBlock[7], 22'i8, 0xFD469501'i32)
-  FF(a, b, c, d, myBlock[8], 7'i8, 0x698098D8'i32)
-  FF(d, a, b, c, myBlock[9], 12'i8, 0x8B44F7AF'i32)
-  FF(c, d, a, b, myBlock[10], 17'i8, 0xFFFF5BB1'i32)
-  FF(b, c, d, a, myBlock[11], 22'i8, 0x895CD7BE'i32)
-  FF(a, b, c, d, myBlock[12], 7'i8, 0x6B901122'i32)
-  FF(d, a, b, c, myBlock[13], 12'i8, 0xFD987193'i32)
-  FF(c, d, a, b, myBlock[14], 17'i8, 0xA679438E'i32)
-  FF(b, c, d, a, myBlock[15], 22'i8, 0x49B40821'i32)
-  GG(a, b, c, d, myBlock[1], 5'i8, 0xF61E2562'i32)
-  GG(d, a, b, c, myBlock[6], 9'i8, 0xC040B340'i32)
-  GG(c, d, a, b, myBlock[11], 14'i8, 0x265E5A51'i32)
-  GG(b, c, d, a, myBlock[0], 20'i8, 0xE9B6C7AA'i32)
-  GG(a, b, c, d, myBlock[5], 5'i8, 0xD62F105D'i32)
-  GG(d, a, b, c, myBlock[10], 9'i8, 0x02441453'i32)
-  GG(c, d, a, b, myBlock[15], 14'i8, 0xD8A1E681'i32)
-  GG(b, c, d, a, myBlock[4], 20'i8, 0xE7D3FBC8'i32)
-  GG(a, b, c, d, myBlock[9], 5'i8, 0x21E1CDE6'i32)
-  GG(d, a, b, c, myBlock[14], 9'i8, 0xC33707D6'i32)
-  GG(c, d, a, b, myBlock[3], 14'i8, 0xF4D50D87'i32)
-  GG(b, c, d, a, myBlock[8], 20'i8, 0x455A14ED'i32)
-  GG(a, b, c, d, myBlock[13], 5'i8, 0xA9E3E905'i32)
-  GG(d, a, b, c, myBlock[2], 9'i8, 0xFCEFA3F8'i32)
-  GG(c, d, a, b, myBlock[7], 14'i8, 0x676F02D9'i32)
-  GG(b, c, d, a, myBlock[12], 20'i8, 0x8D2A4C8A'i32)
-  HH(a, b, c, d, myBlock[5], 4'i8, 0xFFFA3942'i32)
-  HH(d, a, b, c, myBlock[8], 11'i8, 0x8771F681'i32)
-  HH(c, d, a, b, myBlock[11], 16'i8, 0x6D9D6122'i32)
-  HH(b, c, d, a, myBlock[14], 23'i8, 0xFDE5380C'i32)
-  HH(a, b, c, d, myBlock[1], 4'i8, 0xA4BEEA44'i32)
-  HH(d, a, b, c, myBlock[4], 11'i8, 0x4BDECFA9'i32)
-  HH(c, d, a, b, myBlock[7], 16'i8, 0xF6BB4B60'i32)
-  HH(b, c, d, a, myBlock[10], 23'i8, 0xBEBFBC70'i32)
-  HH(a, b, c, d, myBlock[13], 4'i8, 0x289B7EC6'i32)
-  HH(d, a, b, c, myBlock[0], 11'i8, 0xEAA127FA'i32)
-  HH(c, d, a, b, myBlock[3], 16'i8, 0xD4EF3085'i32)
-  HH(b, c, d, a, myBlock[6], 23'i8, 0x04881D05'i32)
-  HH(a, b, c, d, myBlock[9], 4'i8, 0xD9D4D039'i32)
-  HH(d, a, b, c, myBlock[12], 11'i8, 0xE6DB99E5'i32)
-  HH(c, d, a, b, myBlock[15], 16'i8, 0x1FA27CF8'i32)
-  HH(b, c, d, a, myBlock[2], 23'i8, 0xC4AC5665'i32)
-  II(a, b, c, d, myBlock[0], 6'i8, 0xF4292244'i32)
-  II(d, a, b, c, myBlock[7], 10'i8, 0x432AFF97'i32)
-  II(c, d, a, b, myBlock[14], 15'i8, 0xAB9423A7'i32)
-  II(b, c, d, a, myBlock[5], 21'i8, 0xFC93A039'i32)
-  II(a, b, c, d, myBlock[12], 6'i8, 0x655B59C3'i32)
-  II(d, a, b, c, myBlock[3], 10'i8, 0x8F0CCC92'i32)
-  II(c, d, a, b, myBlock[10], 15'i8, 0xFFEFF47D'i32)
-  II(b, c, d, a, myBlock[1], 21'i8, 0x85845DD1'i32)
-  II(a, b, c, d, myBlock[8], 6'i8, 0x6FA87E4F'i32)
-  II(d, a, b, c, myBlock[15], 10'i8, 0xFE2CE6E0'i32)
-  II(c, d, a, b, myBlock[6], 15'i8, 0xA3014314'i32)
-  II(b, c, d, a, myBlock[13], 21'i8, 0x4E0811A1'i32)
-  II(a, b, c, d, myBlock[4], 6'i8, 0xF7537E82'i32)
-  II(d, a, b, c, myBlock[11], 10'i8, 0xBD3AF235'i32)
-  II(c, d, a, b, myBlock[2], 15'i8, 0x2AD7D2BB'i32)
-  II(b, c, d, a, myBlock[9], 21'i8, 0xEB86D391'i32)
-  state[0] = state[0] +% a
-  state[1] = state[1] +% b
-  state[2] = state[2] +% c
-  state[3] = state[3] +% d
-  
-proc md5Init*(c: var MD5Context) = 
-  ## initializes a MD5Context  
-  c.state[0] = 0x67452301'i32
-  c.state[1] = 0xEFCDAB89'i32
-  c.state[2] = 0x98BADCFE'i32
-  c.state[3] = 0x10325476'i32
-  c.count[0] = 0'i32
-  c.count[1] = 0'i32
+  FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32)
+  FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32)
+  FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32)
+  FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32)
+  FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32)
+  FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32)
+  FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32)
+  FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32)
+  FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32)
+  FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32)
+  FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32)
+  FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32)
+  FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32)
+  FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32)
+  FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32)
+  FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32)
+  GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32)
+  GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32)
+  GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32)
+  GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32)
+  GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32)
+  GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32)
+  GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32)
+  GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32)
+  GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32)
+  GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32)
+  GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32)
+  GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32)
+  GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32)
+  GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32)
+  GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32)
+  GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32)
+  HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32)
+  HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32)
+  HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32)
+  HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32)
+  HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32)
+  HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32)
+  HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32)
+  HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32)
+  HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32)
+  HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32)
+  HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32)
+  HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32)
+  HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32)
+  HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32)
+  HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32)
+  HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32)
+  II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32)
+  II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32)
+  II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32)
+  II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32)
+  II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32)
+  II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32)
+  II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32)
+  II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32)
+  II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32)
+  II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32)
+  II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32)
+  II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32)
+  II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32)
+  II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32)
+  II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32)
+  II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32)
+  state[0] = state[0] + a
+  state[1] = state[1] + b
+  state[2] = state[2] + c
+  state[3] = state[3] + d
+
+proc md5Init*(c: var MD5Context) =
+  ## initializes a MD5Context
+  c.state[0] = 0x67452301'u32
+  c.state[1] = 0xEFCDAB89'u32
+  c.state[2] = 0x98BADCFE'u32
+  c.state[3] = 0x10325476'u32
+  c.count[0] = 0'u32
+  c.count[1] = 0'u32
   zeroMem(addr(c.buffer), sizeof(MD5buffer))
 
-proc md5Update*(c: var MD5Context, input: cstring, len: int) = 
+proc md5Update*(c: var MD5Context, input: cstring, len: int) =
   ## updates the MD5Context with the `input` data of length `len`
   var input = input
-  var Index = (c.count[0] shr 3) and 0x3F
-  c.count[0] = c.count[0] +% toU32(len shl 3)
-  if c.count[0] < (len shl 3): c.count[1] = c.count[1] +% 1'i32
-  c.count[1] = c.count[1] +% toU32(len shr 29)
+  var Index = int((c.count[0] shr 3) and 0x3F)
+  c.count[0] = c.count[0] + (uint32(len) shl 3)
+  if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32
+  c.count[1] = c.count[1] + (uint32(len) shr 29)
   var PartLen = 64 - Index
-  if len >= PartLen: 
+  if len >= PartLen:
     copyMem(addr(c.buffer[Index]), input, PartLen)
     transform(addr(c.buffer), c.state)
     var i = PartLen
-    while i + 63 < len: 
+    while i + 63 < len:
       transform(addr(input[i]), c.state)
       inc(i, 64)
     copyMem(addr(c.buffer[0]), addr(input[i]), len-i)
   else:
     copyMem(addr(c.buffer[Index]), addr(input[0]), len)
 
-proc md5Final*(c: var MD5Context, digest: var MD5Digest) = 
+proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
   ## finishes the MD5Context and stores the result in `digest`
   var
     Bits: MD5CBits
     PadLen: int
   decode(Bits, c.count)
-  var Index = (c.count[0] shr 3) and 0x3F
+  var Index = int((c.count[0] shr 3) and 0x3F)
   if Index < 56: PadLen = 56 - Index
   else: PadLen = 120 - Index
   md5Update(c, padding, PadLen)
@@ -204,34 +206,34 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
   decode(digest, c.state)
   zeroMem(addr(c), sizeof(MD5Context))
 
-proc toMD5*(s: string): MD5Digest = 
+proc toMD5*(s: string): MD5Digest =
   ## computes the MD5Digest value for a string `s`
   var c: MD5Context
   md5Init(c)
   md5Update(c, cstring(s), len(s))
   md5Final(c, result)
-  
-proc `$`*(D: MD5Digest): string = 
+
+proc `$`*(D: MD5Digest): string =
   ## converts a MD5Digest value into its string representation
   const digits = "0123456789abcdef"
   result = ""
-  for i in 0..15: 
+  for i in 0..15:
     add(result, digits[(D[i] shr 4) and 0xF])
     add(result, digits[D[i] and 0xF])
 
-proc getMD5*(s: string): string =  
+proc getMD5*(s: string): string =
   ## computes an MD5 value of `s` and returns its string representation
-  var 
+  var
     c: MD5Context
     d: MD5Digest
   md5Init(c)
   md5Update(c, cstring(s), len(s))
   md5Final(c, d)
   result = $d
-  
-proc `==`*(D1, D2: MD5Digest): bool =  
+
+proc `==`*(D1, D2: MD5Digest): bool =
   ## checks if two MD5Digest values are identical
-  for i in 0..15: 
+  for i in 0..15:
     if D1[i] != D2[i]: return false
   return true
 
@@ -241,5 +243,3 @@ when isMainModule:
   assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
     "7e716d0e702df0505fc72e2b89467910")
   assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e")
-
-
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 28b84eb39..e6fe79740 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -44,21 +44,21 @@ const
 
 type
   SocketImpl* = object ## socket type
-    fd*: SocketHandle
-    case isBuffered*: bool # determines whether this socket is buffered.
+    fd: SocketHandle
+    case isBuffered: bool # determines whether this socket is buffered.
     of true:
-      buffer*: array[0..BufferSize, char]
-      currPos*: int # current index in buffer
-      bufLen*: int # current length of buffer
+      buffer: array[0..BufferSize, char]
+      currPos: int # current index in buffer
+      bufLen: int # current length of buffer
     of false: nil
     when defined(ssl):
-      case isSsl*: bool
+      case isSsl: bool
       of true:
-        sslHandle*: SSLPtr
-        sslContext*: SSLContext
-        sslNoHandshake*: bool # True if needs handshake.
-        sslHasPeekChar*: bool
-        sslPeekChar*: char
+        sslHandle: SSLPtr
+        sslContext: SSLContext
+        sslNoHandshake: bool # True if needs handshake.
+        sslHasPeekChar: bool
+        sslPeekChar: char
       of false: nil
   
   Socket* = ref SocketImpl
@@ -100,7 +100,8 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
       result = result or MSG_PEEK
     of SocketFlag.SafeDisconn: continue
 
-proc createSocket(fd: SocketHandle, isBuff: bool): Socket =
+proc newSocket(fd: SocketHandle, isBuff: bool): Socket =
+  ## Creates a new socket as specified by the params.
   assert fd != osInvalidSocket
   new(result)
   result.fd = fd
@@ -115,7 +116,7 @@ proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
   let fd = newRawSocket(domain, typ, protocol)
   if fd == osInvalidSocket:
     raiseOSError(osLastError())
-  result = createSocket(fd, buffered)
+  result = newSocket(fd, buffered)
 
 proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
              protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
@@ -125,7 +126,7 @@ proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
   let fd = newRawSocket(domain, typ, protocol)
   if fd == osInvalidSocket:
     raiseOSError(osLastError())
-  result = createSocket(fd, buffered)
+  result = newSocket(fd, buffered)
 
 when defined(ssl):
   CRYPTO_malloc_init()
@@ -937,10 +938,10 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
         doAssert socket.handshake()
   socket.fd.setBlocking(true)
 
-proc isSSL*(socket: Socket): bool = return socket.isSSL
+proc isSsl*(socket: Socket): bool = return socket.isSSL
   ## Determines whether ``socket`` is a SSL socket.
 
-proc getFD*(socket: Socket): SocketHandle = return socket.fd
+proc getFd*(socket: Socket): SocketHandle = return socket.fd
   ## Returns the socket's file descriptor
 
 type
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 004287d39..ad82dc682 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -575,7 +575,7 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
 proc splitPath*(path: string): tuple[head, tail: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Splits a directory into (head, tail), so that
-  ## ``joinPath(head, tail) == path``.
+  ## ``head / tail == path`` (except for edge cases like "/usr").
   ##
   ## Examples:
   ##
diff --git a/lib/pure/parseurl.nim b/lib/pure/parseurl.nim
index 32e69b89a..f27cd8c12 100644
--- a/lib/pure/parseurl.nim
+++ b/lib/pure/parseurl.nim
@@ -7,10 +7,12 @@
 #    distribution, for details about the copyright.
 #
 
-## Parses & constructs URLs.
+## **Warnings:** This module is deprecated since version 0.10.2.
+## Use the `uri <uri.html>`_ module instead.
 ##
-## **Note**: This module will be deprecated in the future and merged into a
-## new ``url`` module.
+## Parses & constructs URLs.
+
+{.deprecated.}
 
 import strutils
 
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 8b7554661..7cef0a00d 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -1433,7 +1433,7 @@ proc eat(p: var PegParser, kind: TTokKind) =
   if p.tok.kind == kind: getTok(p)
   else: pegError(p, tokKindToStr[kind] & " expected")
 
-proc parseExpr(p: var PegParser): Peg
+proc parseExpr(p: var PegParser): Peg {.gcsafe.}
 
 proc getNonTerminal(p: var PegParser, name: string): NonTerminal =
   for i in 0..high(p.nonterms):
diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim
index 62a011999..e23deea5b 100644
--- a/lib/pure/rawsockets.nim
+++ b/lib/pure/rawsockets.nim
@@ -428,6 +428,10 @@ proc selectWrite*(writefds: var seq[SocketHandle],
   
   pruneSocketSet(writefds, (wr))
 
+# We ignore signal SIGPIPE on Darwin
+when defined(macosx):
+  signal(SIGPIPE, SIG_IGN)
+
 when defined(Windows):
   var wsa: WSAData
   if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
diff --git a/lib/pure/rawsockets.pretty.nim b/lib/pure/rawsockets.pretty.nim
new file mode 100644
index 000000000..46bfba9e7
--- /dev/null
+++ b/lib/pure/rawsockets.pretty.nim
@@ -0,0 +1,426 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a low-level cross-platform sockets interface. Look
+## at the ``net`` module for the higher-level version.
+
+# TODO: Clean up the exports a bit and everything else in general.
+
+import unsigned, os
+
+when hostOS == "solaris":
+  {.passl: "-lsocket -lnsl".}
+
+const useWinVersion = defined(Windows) or defined(nimdoc)
+
+when useWinVersion:
+  import winlean
+  export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
+         WSAEDISCON, ERROR_NETNAME_DELETED
+else:
+  import posix
+  export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
+    EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
+
+export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
+  inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
+
+export
+  SO_ERROR,
+  SOL_SOCKET,
+  SOMAXCONN,
+  SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE,
+  SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR,
+  MSG_PEEK
+
+type
+  Port* = distinct uint16  ## port type
+  
+  Domain* = enum    ## domain, which specifies the protocol family of the
+                    ## created socket. Other domains than those that are listed
+                    ## here are unsupported.
+    AF_UNIX,        ## for local socket (using a file). Unsupported on Windows.
+    AF_INET = 2,    ## for network protocol IPv4 or
+    AF_INET6 = 23   ## for network protocol IPv6.
+
+  SockType* = enum     ## second argument to `socket` proc
+    SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
+    SOCK_DGRAM = 2,    ## datagram service or Datagram Sockets
+    SOCK_RAW = 3,      ## raw protocols atop the network layer.
+    SOCK_SEQPACKET = 5 ## reliable sequenced packet service
+
+  Protocol* = enum      ## third argument to `socket` proc
+    IPPROTO_TCP = 6,    ## Transmission control protocol. 
+    IPPROTO_UDP = 17,   ## User datagram protocol.
+    IPPROTO_IP,         ## Internet protocol. Unsupported on Windows.
+    IPPROTO_IPV6,       ## Internet Protocol Version 6. Unsupported on Windows.
+    IPPROTO_RAW,        ## Raw IP Packets Protocol. Unsupported on Windows.
+    IPPROTO_ICMP        ## Control message protocol. Unsupported on Windows.
+
+  Servent* = object ## information about a service
+    name*: string
+    aliases*: seq[string]
+    port*: Port
+    proto*: string
+
+  Hostent* = object ## information about a given host
+    name*: string
+    aliases*: seq[string]
+    addrtype*: Domain
+    length*: int
+    addrList*: seq[string]
+
+{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
+    TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
+
+when useWinVersion:
+  let
+    osInvalidSocket* = winlean.INVALID_SOCKET
+
+  const
+    IOCPARM_MASK* = 127
+    IOC_IN* = int(-2147483648)
+    FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or 
+                             (102 shl 8) or 126
+
+  proc ioctlsocket*(s: SocketHandle, cmd: clong, 
+                   argptr: ptr clong): cint {.
+                   stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
+else:
+  let
+    osInvalidSocket* = posix.INVALID_SOCKET
+
+proc `==`*(a, b: Port): bool {.borrow.}
+  ## ``==`` for ports.
+
+proc `$`*(p: Port): string {.borrow.}
+  ## returns the port number as a string
+
+proc toInt*(domain: Domain): cint
+  ## Converts the TDomain enum to a platform-dependent ``cint``.
+
+proc toInt*(typ: SockType): cint
+  ## Converts the TType enum to a platform-dependent ``cint``.
+
+proc toInt*(p: Protocol): cint
+  ## Converts the TProtocol enum to a platform-dependent ``cint``.
+
+when not useWinVersion:
+  proc toInt(domain: Domain): cint =
+    case domain
+    of AF_UNIX:        result = posix.AF_UNIX
+    of AF_INET:        result = posix.AF_INET
+    of AF_INET6:       result = posix.AF_INET6
+    else: discard
+
+  proc toInt(typ: SockType): cint =
+    case typ
+    of SOCK_STREAM:    result = posix.SOCK_STREAM
+    of SOCK_DGRAM:     result = posix.SOCK_DGRAM
+    of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
+    of SOCK_RAW:       result = posix.SOCK_RAW
+    else: discard
+
+  proc toInt(p: Protocol): cint =
+    case p
+    of IPPROTO_TCP:    result = posix.IPPROTO_TCP
+    of IPPROTO_UDP:    result = posix.IPPROTO_UDP
+    of IPPROTO_IP:     result = posix.IPPROTO_IP
+    of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
+    of IPPROTO_RAW:    result = posix.IPPROTO_RAW
+    of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
+    else: discard
+
+else:
+  proc toInt(domain: Domain): cint = 
+    result = toU16(ord(domain))
+
+  proc toInt(typ: SockType): cint =
+    result = cint(ord(typ))
+  
+  proc toInt(p: Protocol): cint =
+    result = cint(ord(p))
+
+
+proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
+             protocol: Protocol = IPPROTO_TCP): SocketHandle =
+  ## Creates a new socket; returns `InvalidSocket` if an error occurs.
+  socket(toInt(domain), toInt(typ), toInt(protocol))
+
+proc close*(socket: SocketHandle) =
+  ## closes a socket.
+  when useWinVersion:
+    discard winlean.closesocket(socket)
+  else:
+    discard posix.close(socket)
+  # TODO: These values should not be discarded. An EOS should be raised.
+  # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
+
+proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
+  result = bindSocket(socket, name, namelen)
+
+proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} =
+  ## Marks ``socket`` as accepting connections. 
+  ## ``Backlog`` specifies the maximum length of the 
+  ## queue of pending connections.
+  when useWinVersion:
+    result = winlean.listen(socket, cint(backlog))
+  else:
+    result = posix.listen(socket, cint(backlog))
+
+proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
+                 prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
+  ##
+  ##
+  ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
+  var hints: AddrInfo
+  result = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(typ)
+  hints.ai_protocol = toInt(prot)
+  var gaiResult = getaddrinfo(address, $port, addr(hints), result)
+  if gaiResult != 0'i32:
+    when useWinVersion:
+      raiseOSError(osLastError())
+    else:
+      raise newException(OSError, $gai_strerror(gaiResult))
+
+proc dealloc*(ai: ptr AddrInfo) =
+  freeaddrinfo(ai)
+
+proc ntohl*(x: int32): int32 = 
+  ## Converts 32-bit integers from network to host byte order.
+  ## On machines where the host byte order is the same as network byte order,
+  ## this is a no-op; otherwise, it performs a 4-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 24'i32) or
+                 (x shr 8'i32 and 0xff00'i32) or
+                 (x shl 8'i32 and 0xff0000'i32) or
+                 (x shl 24'i32)
+
+proc ntohs*(x: int16): int16 =
+  ## Converts 16-bit integers from network to host byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 2-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 8'i16) or (x shl 8'i16)
+
+proc htonl*(x: int32): int32 =
+  ## Converts 32-bit integers from host to network byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 4-byte swap operation.
+  result = rawsockets.ntohl(x)
+
+proc htons*(x: int16): int16 =
+  ## Converts 16-bit positive integers from host to network byte order.
+  ## On machines where the host byte order is the same as network byte
+  ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
+  result = rawsockets.ntohs(x)
+
+proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
+  ## Searches the database from the beginning and finds the first entry for 
+  ## which the service name specified by ``name`` matches the s_name member
+  ## and the protocol name specified by ``proto`` matches the s_proto member.
+  ##
+  ## On posix this will search through the ``/etc/services`` file.
+  when useWinVersion:
+    var s = winlean.getservbyname(name, proto)
+  else:
+    var s = posix.getservbyname(name, proto)
+  if s == nil: raise newException(OSError, "Service not found.")
+  result.name = $s.s_name
+  result.aliases = cstringArrayToSeq(s.s_aliases)
+  result.port = Port(s.s_port)
+  result.proto = $s.s_proto
+  
+proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = 
+  ## Searches the database from the beginning and finds the first entry for 
+  ## which the port specified by ``port`` matches the s_port member and the 
+  ## protocol name specified by ``proto`` matches the s_proto member.
+  ##
+  ## On posix this will search through the ``/etc/services`` file.
+  when useWinVersion:
+    var s = winlean.getservbyport(ze(int16(port)).cint, proto)
+  else:
+    var s = posix.getservbyport(ze(int16(port)).cint, proto)
+  if s == nil: raise newException(OSError, "Service not found.")
+  result.name = $s.s_name
+  result.aliases = cstringArrayToSeq(s.s_aliases)
+  result.port = Port(s.s_port)
+  result.proto = $s.s_proto
+
+proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
+  ## This function will lookup the hostname of an IP Address.
+  var myaddr: InAddr
+  myaddr.s_addr = inet_addr(ip)
+  
+  when useWinVersion:
+    var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
+                                  cint(rawsockets.AF_INET))
+    if s == nil: raiseOSError(osLastError())
+  else:
+    var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, 
+                                cint(posix.AF_INET))
+    if s == nil:
+      raise newException(OSError, $hstrerror(h_errno))
+  
+  result.name = $s.h_name
+  result.aliases = cstringArrayToSeq(s.h_aliases)
+  when useWinVersion:
+    result.addrtype = Domain(s.h_addrtype)
+  else:
+    if s.h_addrtype == posix.AF_INET:
+      result.addrtype = AF_INET
+    elif s.h_addrtype == posix.AF_INET6:
+      result.addrtype = AF_INET6
+    else:
+      raise newException(OSError, "unknown h_addrtype")
+  result.addrList = cstringArrayToSeq(s.h_addr_list)
+  result.length = int(s.h_length)
+
+proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = 
+  ## This function will lookup the IP address of a hostname.
+  when useWinVersion:
+    var s = winlean.gethostbyname(name)
+  else:
+    var s = posix.gethostbyname(name)
+  if s == nil: raiseOSError(osLastError())
+  result.name = $s.h_name
+  result.aliases = cstringArrayToSeq(s.h_aliases)
+  when useWinVersion:
+    result.addrtype = Domain(s.h_addrtype)
+  else:
+    if s.h_addrtype == posix.AF_INET:
+      result.addrtype = AF_INET
+    elif s.h_addrtype == posix.AF_INET6:
+      result.addrtype = AF_INET6
+    else:
+      raise newException(OSError, "unknown h_addrtype")
+  result.addrList = cstringArrayToSeq(s.h_addr_list)
+  result.length = int(s.h_length)
+
+proc getSockName*(socket: SocketHandle): Port = 
+  ## returns the socket's associated port number.
+  var name: Sockaddr_in
+  when useWinVersion:
+    name.sin_family = int16(ord(AF_INET))
+  else:
+    name.sin_family = posix.AF_INET
+  #name.sin_port = htons(cint16(port))
+  #name.sin_addr.s_addr = htonl(INADDR_ANY)
+  var namelen = sizeof(name).SockLen
+  if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                 addr(namelen)) == -1'i32:
+    raiseOSError(osLastError())
+  result = Port(rawsockets.ntohs(name.sin_port))
+
+proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
+  tags: [ReadIOEffect].} = 
+  ## getsockopt for integer options.
+  var res: cint
+  var size = sizeof(res).SockLen
+  if getsockopt(socket, cint(level), cint(optname), 
+                addr(res), addr(size)) < 0'i32:
+    raiseOSError(osLastError())
+  result = int(res)
+
+proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
+  tags: [WriteIOEffect].} =
+  ## setsockopt for integer options.
+  var value = cint(optval)
+  if setsockopt(socket, cint(level), cint(optname), addr(value),  
+                sizeof(value).SockLen) < 0'i32:
+    raiseOSError(osLastError())
+
+proc setBlocking*(s: SocketHandle, blocking: bool) =
+  ## Sets blocking mode on socket.
+  ##
+  ## Raises EOS on error.
+  when useWinVersion:
+    var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
+    if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
+      raiseOSError(osLastError())
+  else: # BSD sockets
+    var x: int = fcntl(s, F_GETFL, 0)
+    if x == -1:
+      raiseOSError(osLastError())
+    else:
+      var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
+      if fcntl(s, F_SETFL, mode) == -1:
+        raiseOSError(osLastError())
+
+proc timeValFromMilliseconds(timeout = 500): Timeval =
+  if timeout != -1:
+    var seconds = timeout div 1000
+    result.tv_sec = seconds.int32
+    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+
+proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) = 
+  FD_ZERO(fd)
+  for i in items(s): 
+    m = max(m, int(i))
+    fdSet(i, fd)
+   
+proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) = 
+  var i = 0
+  var L = s.len
+  while i < L:
+    if FD_ISSET(s[i], fd) == 0'i32:
+      s[i] = s[L-1]
+      dec(L)
+    else:
+      inc(i)
+  setLen(s, L)
+
+proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
+  ## Traditional select function. This function will return the number of
+  ## sockets that are ready to be read from, written to, or which have errors.
+  ## If there are none; 0 is returned. 
+  ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout.
+  ## 
+  ## A socket is removed from the specific ``seq`` when it has data waiting to
+  ## be read/written to or has errors (``exceptfds``).
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+  
+  var rd: FdSet
+  var m = 0
+  createFdSet((rd), readfds, m)
+  
+  if timeout != -1:
+    result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
+  else:
+    result = int(select(cint(m+1), addr(rd), nil, nil, nil))
+  
+  pruneSocketSet(readfds, (rd))
+
+proc selectWrite*(writefds: var seq[SocketHandle],
+                  timeout = 500): int {.tags: [ReadIOEffect].} =
+  ## When a socket in ``writefds`` is ready to be written to then a non-zero
+  ## value will be returned specifying the count of the sockets which can be
+  ## written to. The sockets which can be written to will also be removed
+  ## from ``writefds``.
+  ##
+  ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
+  ## an unlimited time.
+  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+  
+  var wr: FdSet
+  var m = 0
+  createFdSet((wr), writefds, m)
+  
+  if timeout != -1:
+    result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
+  else:
+    result = int(select(cint(m+1), nil, addr(wr), nil, nil))
+  
+  pruneSocketSet(writefds, (wr))
+
+when defined(Windows):
+  var wsa: WSAData
+  if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
index 58b37833a..f3e2b583c 100644
--- a/lib/pure/scgi.nim
+++ b/lib/pure/scgi.nim
@@ -25,6 +25,10 @@
 ##
 ## **Warning:** The API of this module is unstable, and therefore is subject
 ## to change.
+##
+## **Warning:** This module only supports the old asynchronous interface.
+## You may wish to use the `asynchttpserver <asynchttpserver.html>`_
+## instead for web applications.
 
 include "system/inclrtl"
 
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 1c988c609..f17c6d317 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -23,7 +23,7 @@ proc `$`*(x: SocketHandle): string {.borrow.}
 
 type
   Event* = enum
-    EvRead, EvWrite
+    EvRead, EvWrite, EvError
 
   SelectorKey* = ref object
     fd*: SocketHandle
@@ -146,12 +146,19 @@ elif defined(linux):
     ## on the ``fd``.
     result = @[]
     let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
-    if evNum < 0: raiseOSError(osLastError())
+    if evNum < 0:
+      let err = osLastError()
+      if err.cint == EINTR:
+        return @[]
+      raiseOSError(osLastError())
     if evNum == 0: return @[]
     for i in 0 .. <evNum:
       let fd = s.events[i].data.fd.SocketHandle
     
       var evSet: set[Event] = {}
+      if (s.events[i].events and EPOLLERR) != 0: evSet = evSet + {EvError}
+      if (s.events[i].events and EPOLLHUP) != 0: evSet = evSet + {EvError}
+      if (s.events[i].events and EPOLLRDHUP) != 0: evSet = evSet + {EvError}
       if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
       if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
       let selectorKey = s.fds[fd]
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index 79f409179..11eeefcb9 100644
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -7,6 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
+## **Warning:** Since version 0.10.2 this module is deprecated.
+## Use the `net <net.html>`_ or the
+## `rawsockets <rawsockets.html>`_ module instead.
+##
 ## This module implements portable sockets, it supports a mix of different types
 ## of sockets. Sockets are buffered by default meaning that data will be
 ## received in ``BufferSize`` (4000) sized chunks, buffering
@@ -23,9 +27,6 @@
 ##
 ## Asynchronous sockets are supported, however a better alternative is to use
 ## the `asyncio <asyncio.html>`_ module.
-##
-## Since version 0.10.2 this module is deprecated. Use the `net <net.html>`_
-## or the `rawsockets <rawsockets.html>`_ module instead.
 
 {.deprecated.}
 
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 6c0246f8b..21efea3bc 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -97,7 +97,9 @@ proc checkpoint*(msg: string) =
 template fail* =
   bind checkpoints
   for msg in items(checkpoints):
-    echo msg
+    # this used to be 'echo' which now breaks due to a bug. XXX will revisit
+    # this issue later.
+    stdout.writeln msg
 
   when not defined(ECMAScript):
     if abortOnError: quit(1)
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index 2c65d071e..edc690aec 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -16,17 +16,18 @@ type
   Uri* = object
     scheme*, username*, password*: string 
     hostname*, port*, path*, query*, anchor*: string
+    opaque*: bool
 
 {.deprecated: [TUrl: Url, TUri: Uri].}
 
-proc `$`*(url: TUrl): string {.deprecated.} =
-  ## **Deprecated since 0.9.6**: Use ``TUri`` instead.
+proc `$`*(url: Url): string {.deprecated.} =
+  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
   return string(url)
 
-proc `/`*(a, b: TUrl): TUrl {.deprecated.} =
+proc `/`*(a, b: Url): Url {.deprecated.} =
   ## Joins two URLs together, separating them with / if needed.
   ##
-  ## **Deprecated since 0.9.6**: Use ``TUri`` instead.
+  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
   var urlS = $a
   var bS = $b
   if urlS == "": return b
@@ -36,15 +37,15 @@ proc `/`*(a, b: TUrl): TUrl {.deprecated.} =
     urlS.add(bS.substr(1))
   else:
     urlS.add(bs)
-  result = TUrl(urlS)
+  result = Url(urlS)
 
-proc add*(url: var TUrl, a: TUrl) {.deprecated.} =
+proc add*(url: var Url, a: Url) {.deprecated.} =
   ## Appends url to url.
   ##
-  ## **Deprecated since 0.9.6**: Use ``TUri`` instead.
+  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
   url = url / a
 
-proc parseAuthority(authority: string, result: var TUri) =
+proc parseAuthority(authority: string, result: var Uri) =
   var i = 0
   var inPort = false
   while true:
@@ -65,7 +66,7 @@ proc parseAuthority(authority: string, result: var TUri) =
         result.hostname.add(authority[i])
     i.inc
 
-proc parsePath(uri: string, i: var int, result: var TUri) =
+proc parsePath(uri: string, i: var int, result: var Uri) =
   
   i.inc parseUntil(uri, result.path, {'?', '#'}, i)
 
@@ -82,11 +83,11 @@ proc parsePath(uri: string, i: var int, result: var TUri) =
     i.inc # Skip '#'
     i.inc parseUntil(uri, result.anchor, {}, i)
 
-proc initUri(): TUri =
-  result = TUri(scheme: "", username: "", password: "", hostname: "", port: "",
+proc initUri(): Uri =
+  result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
                 path: "", query: "", anchor: "")
 
-proc parseUri*(uri: string): TUri =
+proc parseUri*(uri: string): Uri =
   ## Parses a URI.
   result = initUri()
 
@@ -113,8 +114,10 @@ proc parseUri*(uri: string): TUri =
     var authority = ""
     i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
     if authority == "":
-      raise newException(EInvalidValue, "Expected authority got nothing.")
+      raise newException(ValueError, "Expected authority got nothing.")
     parseAuthority(authority, result)
+  else:
+    result.opaque = true
 
   # Path
   parsePath(uri, i, result)
@@ -150,7 +153,7 @@ proc removeDotSegments(path: string): string =
   result = collection.join("/")
   if endsWithSlash: result.add '/'
 
-proc merge(base, reference: TUri): string =
+proc merge(base, reference: Uri): string =
   # http://tools.ietf.org/html/rfc3986#section-5.2.3
   if base.hostname != "" and base.path == "":
     '/' & reference.path
@@ -161,7 +164,7 @@ proc merge(base, reference: TUri): string =
     else:
       base.path[0 .. lastSegment] & reference.path
 
-proc combine*(base: TUri, reference: TUri): TUri =
+proc combine*(base: Uri, reference: Uri): Uri =
   ## Combines a base URI with a reference URI.
   ##
   ## This uses the algorithm specified in
@@ -216,13 +219,13 @@ proc combine*(base: TUri, reference: TUri): TUri =
     result.scheme = base.scheme
   result.anchor = reference.anchor
 
-proc combine*(uris: varargs[TUri]): TUri =
+proc combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
   result = uris[0]
   for i in 1 .. <uris.len:
     result = combine(result, uris[i])
 
-proc `/`*(x: TUri, path: string): TUri =
+proc `/`*(x: Uri, path: string): Uri =
   ## Concatenates the path specified to the specified URI's path.
   ##
   ## Contrary to the ``combine`` procedure you do not have to worry about
@@ -251,12 +254,15 @@ proc `/`*(x: TUri, path: string): TUri =
       result.path.add '/'
     result.path.add(path)
 
-proc `$`*(u: TUri): string =
+proc `$`*(u: Uri): string =
   ## Returns the string representation of the specified URI object.
   result = ""
   if u.scheme.len > 0:
     result.add(u.scheme)
-    result.add("://")
+    if u.opaque:
+      result.add(":")
+    else:
+      result.add("://")
   if u.username.len > 0:
     result.add(u.username)
     if u.password.len > 0:
@@ -268,22 +274,28 @@ proc `$`*(u: TUri): string =
     result.add(":")
     result.add(u.port)
   if u.path.len > 0:
-    if u.path[0] != '/': result.add("/")
     result.add(u.path)
-  result.add(u.query)
-  result.add(u.anchor)
+  if u.query.len > 0:
+    result.add("?")
+    result.add(u.query)
+  if u.anchor.len > 0:
+    result.add("#")
+    result.add(u.anchor)
 
 when isMainModule:
   block:
-    let test = parseUri("http://localhost:8080/test")
+    let str = "http://localhost:8080/test"
+    let test = parseUri(str)
     doAssert test.scheme == "http"
     doAssert test.port == "8080"
     doAssert test.path == "/test"
     doAssert test.hostname == "localhost"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("foo://username:password@example.com:8042/over/there" &
-                        "/index.dtb?type=animal&name=narwhal#nose")
+    let str = "foo://username:password@example.com:8042/over/there" &
+              "/index.dtb?type=animal&name=narwhal#nose"
+    let test = parseUri(str)
     doAssert test.scheme == "foo"
     doAssert test.username == "username"
     doAssert test.password == "password"
@@ -292,34 +304,45 @@ when isMainModule:
     doAssert test.path == "/over/there/index.dtb"
     doAssert test.query == "type=animal&name=narwhal"
     doAssert test.anchor == "nose"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("urn:example:animal:ferret:nose")
+    let str = "urn:example:animal:ferret:nose"
+    let test = parseUri(str)
     doAssert test.scheme == "urn"
     doAssert test.path == "example:animal:ferret:nose"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("mailto:username@example.com?subject=Topic")
+    let str = "mailto:username@example.com?subject=Topic"
+    let test = parseUri(str)
     doAssert test.scheme == "mailto"
     doAssert test.username == "username"
     doAssert test.hostname == "example.com"
     doAssert test.query == "subject=Topic"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar")
+    let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+    let test = parseUri(str)
     doAssert test.scheme == "magnet"
     doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("/test/foo/bar?q=2#asdf")
+    let str = "/test/foo/bar?q=2#asdf"
+    let test = parseUri(str)
     doAssert test.scheme == ""
     doAssert test.path == "/test/foo/bar"
     doAssert test.query == "q=2"
     doAssert test.anchor == "asdf"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("test/no/slash")
+    let str = "test/no/slash"
+    let test = parseUri(str)
     doAssert test.path == "test/no/slash"
+    doAssert($test == str)
 
   # Remove dot segments tests
   block:
@@ -371,5 +394,3 @@ when isMainModule:
   block:
     let test = parseUri("http://example.com/foo/") / "/bar/asd"
     doAssert test.path == "/foo/bar/asd"
-
-