Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Araq 2014-12-26 22:43:40 +01:00
commit 010b8f85c7
16 changed files with 265 additions and 118 deletions

View file

@ -18,7 +18,7 @@ low-level interface to a C library.
Read this `document <apis.html>`_ for a quick overview of the API design. Read this `document <apis.html>`_ for a quick overview of the API design.
The `bottom <#babel>`_ of this page includes a list of 3rd party packages The `bottom <#nimble>`_ of this page includes a list of 3rd party packages
created by the Nim community. These packages are a useful addition to the created by the Nim community. These packages are a useful addition to the
modules in the standard library. modules in the standard library.
@ -581,12 +581,12 @@ Scientific computing
* `libsvm <libsvm.html>`_ * `libsvm <libsvm.html>`_
Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_. Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_.
Babel Nimble
==================== ====================
Babel is a package manager for the Nim programming language. Nimble is a package manager for the Nim programming language.
For instructions on how to install Babel packages see For instructions on how to install Nimble packages see
`its README <https://github.com/nim-code/babel#readme>`_. `its README <https://github.com/nim-lang/babel#readme>`_.
Official packages Official packages
----------------- -----------------
@ -598,7 +598,7 @@ compiler.
.. raw:: html .. raw:: html
<div id="officialPkgList"><b>If you are reading this you are missing <div id="officialPkgList"><b>If you are reading this you are missing
babelpkglist.js or have javascript disabled in your browser.</b></div> nimblepkglist.js or have javascript disabled in your browser.</b></div>
Unofficial packages Unofficial packages
------------------- -------------------
@ -610,7 +610,7 @@ Nim programming language.
.. raw:: html .. raw:: html
<div id="unofficialPkgList"><b>If you are reading this you are missing <div id="unofficialPkgList"><b>If you are reading this you are missing
babelpkglist.js or have javascript disabled in your browser.</b></div> nimblepkglist.js or have javascript disabled in your browser.</b></div>
<script type="text/javascript" src="babelpkglist.js"></script> <script type="text/javascript" src="nimblepkglist.js"></script>
<script type="text/javascript" src="http://build.nim-lang.org/packages?callback=gotPackageList"></script> <script type="text/javascript" src="http://irclogs.nim-lang.org/packages?callback=gotPackageList"></script>

View file

@ -436,12 +436,12 @@ when defined(windows) or defined(nimdoc):
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
raiseOSError(osLastError()) raiseOSError(osLastError())
proc connectEx(s: SocketHandle, name: ptr TSockAddr, namelen: cint, proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: Dword, lpSendBuffer: pointer, dwSendDataLength: Dword,
lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool = lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool =
if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().") if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().")
let fun = let fun =
cast[proc (s: SocketHandle, name: ptr TSockAddr, namelen: cint, cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: Dword, lpSendBuffer: pointer, dwSendDataLength: Dword,
lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr) lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr)
@ -464,16 +464,16 @@ when defined(windows) or defined(nimdoc):
proc getAcceptExSockaddrs(lpOutputBuffer: pointer, proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: Dword, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: Dword,
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: LPInt, LocalSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt,
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: LPInt) = RemoteSockaddr: ptr ptr SockAddr, RemoteSockaddrLength: LPInt) =
if getAcceptExSockAddrsPtr.isNil: if getAcceptExSockAddrsPtr.isNil:
raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().") raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().")
let fun = let fun =
cast[proc (lpOutputBuffer: pointer, cast[proc (lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr TSockAddr, dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr,
LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr,
RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr) RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
@ -489,12 +489,12 @@ when defined(windows) or defined(nimdoc):
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
# Apparently ``ConnectEx`` expects the socket to be initially bound: # 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_family = int16(toInt(af))
saddr.sin_port = 0 saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(socket.SocketHandle, cast[ptr TSockAddr](addr(saddr)), if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)),
sizeof(saddr).TSockLen) < 0'i32: sizeof(saddr).SockLen) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
var aiList = getAddrInfo(address, port, af) var aiList = getAddrInfo(address, port, af)
@ -516,7 +516,7 @@ when defined(windows) or defined(nimdoc):
) )
var ret = connectEx(socket.SocketHandle, it.ai_addr, 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)) cast[POVERLAPPED](ol))
if ret: if ret:
# Request to connect completed immediately. # Request to connect completed immediately.
@ -700,17 +700,17 @@ when defined(windows) or defined(nimdoc):
var lpOutputBuf = newString(lpOutputLen) var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: Dword var dwBytesReceived: Dword
let dwReceiveDataLength = 0.Dword # We don't want any data to be read. let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
let dwLocalAddressLength = Dword(sizeof (Tsockaddr_in) + 16) let dwLocalAddressLength = Dword(sizeof (Sockaddr_in) + 16)
let dwRemoteAddressLength = Dword(sizeof(Tsockaddr_in) + 16) let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
template completeAccept(): stmt {.immediate, dirty.} = template completeAccept(): stmt {.immediate, dirty.} =
var listenSock = socket var listenSock = socket
let setoptRet = setsockopt(clientSock, SOL_SOCKET, let setoptRet = setsockopt(clientSock, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
sizeof(listenSock).TSockLen) sizeof(listenSock).SockLen)
if setoptRet != 0: raiseOSError(osLastError()) if setoptRet != 0: raiseOSError(osLastError())
var localSockaddr, remoteSockaddr: ptr TSockAddr var localSockaddr, remoteSockaddr: ptr SockAddr
var localLen, remoteLen: int32 var localLen, remoteLen: int32
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength, getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength, dwLocalAddressLength, dwRemoteAddressLength,
@ -719,7 +719,7 @@ when defined(windows) or defined(nimdoc):
register(clientSock.TAsyncFD) register(clientSock.TAsyncFD)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete( 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) client: clientSock.TAsyncFD)
) )

View file

@ -18,7 +18,7 @@
## ##
## .. code-block::nim ## .. code-block::nim
## var server = newAsyncHttpServer() ## var server = newAsyncHttpServer()
## proc cb(req: TRequest) {.async.} = ## proc cb(req: Request) {.async.} =
## await req.respond(Http200, "Hello World") ## await req.respond(Http200, "Hello World")
## ##
## asyncCheck server.serve(Port(8080), cb) ## asyncCheck server.serve(Port(8080), cb)
@ -27,25 +27,52 @@
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
type type
Request* = object Request* = object
client*: PAsyncSocket # TODO: Separate this into a Response object? client*: AsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string reqMethod*: string
headers*: PStringTable headers*: StringTableRef
protocol*: tuple[orig: string, major, minor: int] protocol*: tuple[orig: string, major, minor: int]
url*: TUri url*: Uri
hostname*: string ## The hostname of the client that made the request. hostname*: string ## The hostname of the client that made the request.
body*: string body*: string
AsyncHttpServer* = ref object AsyncHttpServer* = ref object
socket: PAsyncSocket socket: AsyncSocket
reuseAddr: bool reuseAddr: bool
HttpCode* = enum HttpCode* = enum
Http100 = "100 Continue",
Http101 = "101 Switching Protocols",
Http200 = "200 OK", 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", Http400 = "400 Bad Request",
Http401 = "401 Unauthorized",
Http403 = "403 Forbidden",
Http404 = "404 Not Found", 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", 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 HttpVersion* = enum
HttpVer11, HttpVer11,
@ -55,7 +82,7 @@ type
THttpCode: HttpCode, THttpVersion: HttpVersion].} THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc `==`*(protocol: tuple[orig: string, major, minor: int], proc `==`*(protocol: tuple[orig: string, major, minor: int],
ver: THttpVersion): bool = ver: HttpVersion): bool =
let major = let major =
case ver case ver
of HttpVer11, HttpVer10: 1 of HttpVer11, HttpVer10: 1
@ -65,23 +92,23 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
of HttpVer10: 0 of HttpVer10: 0
result = protocol.major == major and protocol.minor == minor result = protocol.major == major and protocol.minor == minor
proc newAsyncHttpServer*(reuseAddr = true): PAsyncHttpServer = proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance. ## Creates a new ``AsyncHttpServer`` instance.
new result new result
result.reuseAddr = reuseAddr result.reuseAddr = reuseAddr
proc addHeaders(msg: var string, headers: PStringTable) = proc addHeaders(msg: var string, headers: StringTableRef) =
for k, v in headers: for k, v in headers:
msg.add(k & ": " & v & "\c\L") 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. ## Sends the specified headers to the requesting client.
var msg = "" var msg = ""
addHeaders(msg, headers) addHeaders(msg, headers)
return req.client.send(msg) return req.client.send(msg)
proc respond*(req: TRequest, code: THttpCode, proc respond*(req: Request, code: HttpCode,
content: string, headers: PStringTable = newStringTable()) {.async.} = content: string, headers = newStringTable()) {.async.} =
## Responds to the request with the specified ``HttpCode``, headers and ## Responds to the request with the specified ``HttpCode``, headers and
## content. ## content.
## ##
@ -92,7 +119,7 @@ proc respond*(req: TRequest, code: THttpCode,
msg.addHeaders(customHeaders) msg.addHeaders(customHeaders)
await req.client.send(msg & "\c\L" & content) await req.client.send(msg & "\c\L" & content)
proc newRequest(): TRequest = proc newRequest(): Request =
result.headers = newStringTable(modeCaseInsensitive) result.headers = newStringTable(modeCaseInsensitive)
result.hostname = "" result.hostname = ""
result.body = "" result.body = ""
@ -107,20 +134,20 @@ proc parseHeader(line: string): tuple[key, value: string] =
proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
var i = protocol.skipIgnoreCase("HTTP/") var i = protocol.skipIgnoreCase("HTTP/")
if i != 5: if i != 5:
raise newException(EInvalidValue, "Invalid request protocol. Got: " & raise newException(ValueError, "Invalid request protocol. Got: " &
protocol) protocol)
result.orig = protocol result.orig = protocol
i.inc protocol.parseInt(result.major, i) i.inc protocol.parseInt(result.major, i)
i.inc # Skip . i.inc # Skip .
i.inc protocol.parseInt(result.minor, i) 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") client.send("HTTP/1.1 " & status & "\c\L")
proc processClient(client: PAsyncSocket, address: string, proc processClient(client: AsyncSocket, address: string,
callback: proc (request: TRequest): callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} = Future[void] {.closure, gcsafe.}) {.async.} =
while not client.closed: while not client.isClosed:
# GET /path HTTP/1.1 # GET /path HTTP/1.1
# Header: val # Header: val
# \n # \n
@ -160,7 +187,7 @@ proc processClient(client: PAsyncSocket, address: string,
request.url = parseUri(path) request.url = parseUri(path)
try: try:
request.protocol = protocol.parseProtocol() request.protocol = protocol.parseProtocol()
except EInvalidValue: except ValueError:
asyncCheck request.respond(Http400, "Invalid request protocol. Got: " & asyncCheck request.respond(Http400, "Invalid request protocol. Got: " &
protocol) protocol)
continue continue
@ -206,8 +233,8 @@ proc processClient(client: PAsyncSocket, address: string,
request.client.close() request.client.close()
break break
proc serve*(server: PAsyncHttpServer, port: Port, proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: TRequest): Future[void] {.closure,gcsafe.}, callback: proc (request: Request): Future[void] {.closure,gcsafe.},
address = "") {.async.} = address = "") {.async.} =
## Starts the process of listening for incoming HTTP connections on the ## Starts the process of listening for incoming HTTP connections on the
## specified address and port. ## specified address and port.
@ -227,14 +254,14 @@ proc serve*(server: PAsyncHttpServer, port: Port,
#echo(f.isNil) #echo(f.isNil)
#echo(f.repr) #echo(f.repr)
proc close*(server: PAsyncHttpServer) = proc close*(server: AsyncHttpServer) =
## Terminates the async http server instance. ## Terminates the async http server instance.
server.socket.close() server.socket.close()
when isMainModule: when isMainModule:
proc main = proc main =
var server = newAsyncHttpServer() var server = newAsyncHttpServer()
proc cb(req: TRequest) {.async.} = proc cb(req: Request) {.async.} =
#echo(req.reqMethod, " ", req.url) #echo(req.reqMethod, " ", req.url)
#echo(req.headers) #echo(req.headers)
let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT",

View file

@ -10,6 +10,11 @@ include "system/inclrtl"
import sockets, os 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 ## This module implements an asynchronous event loop together with asynchronous
## sockets which use this event loop. ## sockets which use this event loop.
## It is akin to Python's asyncore module. Many modules that use sockets ## It is akin to Python's asyncore module. Many modules that use sockets
@ -90,6 +95,8 @@ import sockets, os
## var client: Socket ## var client: Socket
## getSocket(s).accept(client) ## getSocket(s).accept(client)
{.deprecated.}
when defined(windows): when defined(windows):
from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select FD_ISSET, select

View file

@ -69,13 +69,13 @@ type
# TODO: I would prefer to just do: # TODO: I would prefer to just do:
# AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work. # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work.
AsyncSocketDesc = object AsyncSocketDesc = object
fd*: SocketHandle fd: SocketHandle
closed*: bool ## determines whether this socket has been closed closed: bool ## determines whether this socket has been closed
case isBuffered*: bool ## determines whether this socket is buffered. case isBuffered: bool ## determines whether this socket is buffered.
of true: of true:
buffer*: array[0..BufferSize, char] buffer: array[0..BufferSize, char]
currPos*: int # current index in buffer currPos: int # current index in buffer
bufLen*: int # current length of buffer bufLen: int # current length of buffer
of false: nil of false: nil
case isSsl: bool case isSsl: bool
of true: of true:
@ -91,7 +91,8 @@ type
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect. # 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 assert fd != osInvalidSocket.TAsyncFD
new(result) new(result)
result.fd = fd.SocketHandle 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, proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket = protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
## Creates a new asynchronous socket. ## 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 = proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): AsyncSocket =
## Creates a new asynchronous socket. ## 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): when defined(ssl):
proc getSslError(handle: SslPtr, err: cint): cint = proc getSslError(handle: SslPtr, err: cint): cint =
@ -275,7 +282,7 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
retFuture.fail(future.readError) retFuture.fail(future.readError)
else: else:
let resultTup = (future.read.address, let resultTup = (future.read.address,
newSocket(future.read.client, socket.isBuffered)) newAsyncSocket(future.read.client, socket.isBuffered))
retFuture.complete(resultTup) retFuture.complete(resultTup)
return retFuture return retFuture
@ -439,6 +446,18 @@ proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool,
var valuei = cint(if value: 1 else: 0) var valuei = cint(if value: 1 else: 0)
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) 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: when isMainModule:
type type
TestCases = enum TestCases = enum

View file

@ -44,21 +44,21 @@ const
type type
SocketImpl* = object ## socket type SocketImpl* = object ## socket type
fd*: SocketHandle fd: SocketHandle
case isBuffered*: bool # determines whether this socket is buffered. case isBuffered: bool # determines whether this socket is buffered.
of true: of true:
buffer*: array[0..BufferSize, char] buffer: array[0..BufferSize, char]
currPos*: int # current index in buffer currPos: int # current index in buffer
bufLen*: int # current length of buffer bufLen: int # current length of buffer
of false: nil of false: nil
when defined(ssl): when defined(ssl):
case isSsl*: bool case isSsl: bool
of true: of true:
sslHandle*: SSLPtr sslHandle: SSLPtr
sslContext*: SSLContext sslContext: SSLContext
sslNoHandshake*: bool # True if needs handshake. sslNoHandshake: bool # True if needs handshake.
sslHasPeekChar*: bool sslHasPeekChar: bool
sslPeekChar*: char sslPeekChar: char
of false: nil of false: nil
Socket* = ref SocketImpl Socket* = ref SocketImpl
@ -100,7 +100,8 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
result = result or MSG_PEEK result = result or MSG_PEEK
of SocketFlag.SafeDisconn: continue 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 assert fd != osInvalidSocket
new(result) new(result)
result.fd = fd result.fd = fd
@ -115,7 +116,7 @@ proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
let fd = newRawSocket(domain, typ, protocol) let fd = newRawSocket(domain, typ, protocol)
if fd == osInvalidSocket: if fd == osInvalidSocket:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = createSocket(fd, buffered) result = newSocket(fd, buffered)
proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket = 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) let fd = newRawSocket(domain, typ, protocol)
if fd == osInvalidSocket: if fd == osInvalidSocket:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = createSocket(fd, buffered) result = newSocket(fd, buffered)
when defined(ssl): when defined(ssl):
CRYPTO_malloc_init() CRYPTO_malloc_init()
@ -937,10 +938,10 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
doAssert socket.handshake() doAssert socket.handshake()
socket.fd.setBlocking(true) 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. ## 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 ## Returns the socket's file descriptor
type type

View file

@ -7,10 +7,12 @@
# distribution, for details about the copyright. # 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 ## Parses & constructs URLs.
## new ``url`` module.
{.deprecated.}
import strutils import strutils

View file

@ -25,6 +25,10 @@
## ##
## **Warning:** The API of this module is unstable, and therefore is subject ## **Warning:** The API of this module is unstable, and therefore is subject
## to change. ## 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" include "system/inclrtl"

View file

@ -7,6 +7,10 @@
# distribution, for details about the copyright. # 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 ## This module implements portable sockets, it supports a mix of different types
## of sockets. Sockets are buffered by default meaning that data will be ## of sockets. Sockets are buffered by default meaning that data will be
## received in ``BufferSize`` (4000) sized chunks, buffering ## received in ``BufferSize`` (4000) sized chunks, buffering
@ -23,9 +27,6 @@
## ##
## Asynchronous sockets are supported, however a better alternative is to use ## Asynchronous sockets are supported, however a better alternative is to use
## the `asyncio <asyncio.html>`_ module. ## 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.} {.deprecated.}

View file

@ -19,14 +19,14 @@ type
{.deprecated: [TUrl: Url, TUri: Uri].} {.deprecated: [TUrl: Url, TUri: Uri].}
proc `$`*(url: TUrl): string {.deprecated.} = proc `$`*(url: Url): string {.deprecated.} =
## **Deprecated since 0.9.6**: Use ``TUri`` instead. ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
return string(url) return string(url)
proc `/`*(a, b: TUrl): TUrl {.deprecated.} = proc `/`*(a, b: Url): Url {.deprecated.} =
## Joins two URLs together, separating them with / if needed. ## 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 urlS = $a
var bS = $b var bS = $b
if urlS == "": return b if urlS == "": return b
@ -36,15 +36,15 @@ proc `/`*(a, b: TUrl): TUrl {.deprecated.} =
urlS.add(bS.substr(1)) urlS.add(bS.substr(1))
else: else:
urlS.add(bs) 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. ## Appends url to url.
## ##
## **Deprecated since 0.9.6**: Use ``TUri`` instead. ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
url = url / a url = url / a
proc parseAuthority(authority: string, result: var TUri) = proc parseAuthority(authority: string, result: var Uri) =
var i = 0 var i = 0
var inPort = false var inPort = false
while true: while true:
@ -65,7 +65,7 @@ proc parseAuthority(authority: string, result: var TUri) =
result.hostname.add(authority[i]) result.hostname.add(authority[i])
i.inc 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) i.inc parseUntil(uri, result.path, {'?', '#'}, i)
@ -82,11 +82,11 @@ proc parsePath(uri: string, i: var int, result: var TUri) =
i.inc # Skip '#' i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i) i.inc parseUntil(uri, result.anchor, {}, i)
proc initUri(): TUri = proc initUri(): Uri =
result = TUri(scheme: "", username: "", password: "", hostname: "", port: "", result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
path: "", query: "", anchor: "") path: "", query: "", anchor: "")
proc parseUri*(uri: string): TUri = proc parseUri*(uri: string): Uri =
## Parses a URI. ## Parses a URI.
result = initUri() result = initUri()
@ -113,7 +113,7 @@ proc parseUri*(uri: string): TUri =
var authority = "" var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
if authority == "": if authority == "":
raise newException(EInvalidValue, "Expected authority got nothing.") raise newException(ValueError, "Expected authority got nothing.")
parseAuthority(authority, result) parseAuthority(authority, result)
# Path # Path
@ -150,7 +150,7 @@ proc removeDotSegments(path: string): string =
result = collection.join("/") result = collection.join("/")
if endsWithSlash: result.add '/' 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 # http://tools.ietf.org/html/rfc3986#section-5.2.3
if base.hostname != "" and base.path == "": if base.hostname != "" and base.path == "":
'/' & reference.path '/' & reference.path
@ -161,7 +161,7 @@ proc merge(base, reference: TUri): string =
else: else:
base.path[0 .. lastSegment] & reference.path 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. ## Combines a base URI with a reference URI.
## ##
## This uses the algorithm specified in ## This uses the algorithm specified in
@ -216,13 +216,13 @@ proc combine*(base: TUri, reference: TUri): TUri =
result.scheme = base.scheme result.scheme = base.scheme
result.anchor = reference.anchor result.anchor = reference.anchor
proc combine*(uris: varargs[TUri]): TUri = proc combine*(uris: varargs[Uri]): Uri =
## Combines multiple URIs together. ## Combines multiple URIs together.
result = uris[0] result = uris[0]
for i in 1 .. <uris.len: for i in 1 .. <uris.len:
result = combine(result, uris[i]) 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. ## Concatenates the path specified to the specified URI's path.
## ##
## Contrary to the ``combine`` procedure you do not have to worry about ## Contrary to the ``combine`` procedure you do not have to worry about
@ -251,7 +251,7 @@ proc `/`*(x: TUri, path: string): TUri =
result.path.add '/' result.path.add '/'
result.path.add(path) result.path.add(path)
proc `$`*(u: TUri): string = proc `$`*(u: Uri): string =
## Returns the string representation of the specified URI object. ## Returns the string representation of the specified URI object.
result = "" result = ""
if u.scheme.len > 0: if u.scheme.len > 0:

View file

@ -53,9 +53,7 @@ proc createServer(port: TPort) {.async.} =
discard server.SocketHandle.listen() discard server.SocketHandle.listen()
while true: while true:
var client = await accept(server) asyncCheck readMessages(await accept(server))
asyncCheck readMessages(client)
# TODO: Test: readMessages(disp, await disp.accept(server))
asyncCheck createServer(TPort(10335)) asyncCheck createServer(TPort(10335))
asyncCheck launchSwarm(TPort(10335)) asyncCheck launchSwarm(TPort(10335))

View file

@ -47,7 +47,7 @@ proc serverAccept(s: PAsyncSocket) =
proc launchSwarm(disp: var PDispatcher, port: TPort, count: int, proc launchSwarm(disp: var PDispatcher, port: TPort, count: int,
buffered = true, useSSL = false) = buffered = true, useSSL = false) =
for i in 1..count: for i in 1..count:
var client = AsyncSocket() var client = asyncSocket()
when defined(ssl): when defined(ssl):
if useSSL: if useSSL:
ctx1.wrapSocket(client) ctx1.wrapSocket(client)
@ -56,7 +56,7 @@ proc launchSwarm(disp: var PDispatcher, port: TPort, count: int,
client.connect("localhost", port) client.connect("localhost", port)
proc createSwarm(port: TPort, buffered = true, useSSL = false) = proc createSwarm(port: TPort, buffered = true, useSSL = false) =
var server = AsyncSocket() var server = asyncSocket()
when defined(ssl): when defined(ssl):
if useSSL: if useSSL:
ctx.wrapSocket(server) ctx.wrapSocket(server)

View file

@ -42,14 +42,14 @@ proc swarmConnect(s: PAsyncSocket) =
proc createClient(disp: var PDispatcher, port: TPort, proc createClient(disp: var PDispatcher, port: TPort,
buffered = true) = buffered = true) =
currentClient.inc() currentClient.inc()
var client = AsyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, var client = asyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP,
buffered = buffered) buffered = buffered)
client.handleConnect = swarmConnect client.handleConnect = swarmConnect
disp.register(client) disp.register(client)
client.connect("localhost", port) client.connect("localhost", port)
proc createServer(port: TPort, buffered = true) = proc createServer(port: TPort, buffered = true) =
var server = AsyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, var server = asyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP,
buffered = buffered) buffered = buffered)
server.handleRead = serverRead server.handleRead = serverRead
disp.register(server) disp.register(server)
@ -75,4 +75,4 @@ while true:
break break
assert msgCount == messagesToSend * serverCount * swarmSize assert msgCount == messagesToSend * serverCount * swarmSize
echo(msgCount) echo(msgCount)

View file

@ -396,8 +396,8 @@ proc buildNewsRss(c: var TConfigData, destPath: string) =
generateRss(destFilename, parseNewsTitles(srcFilename)) generateRss(destFilename, parseNewsTitles(srcFilename))
proc buildJS(destPath: string) = proc buildJS(destPath: string) =
exec("nim js -d:release --out:$1 web/babelpkglist.nim" % exec("nim js -d:release --out:$1 web/nimblepkglist.nim" %
[destPath / "babelpkglist.js"]) [destPath / "nimblepkglist.js"])
proc buildWebsite(c: var TConfigData) = proc buildWebsite(c: var TConfigData) =
const const

View file

@ -41,6 +41,7 @@ proc processContent(content: string) =
dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "." dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "."
listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot) listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot)
if pkg["url"].str.startsWith("git://github.com/nimrod-code") or if pkg["url"].str.startsWith("git://github.com/nimrod-code") or
pkg["url"].str.startsWith("git://github.com/nim-lang") or
"official" in pkg["tags"].elems: "official" in pkg["tags"].elems:
officialCount.inc officialCount.inc
officialList.add listItem & "\n" officialList.add listItem & "\n"
@ -52,14 +53,14 @@ proc processContent(content: string) =
officialPkgListDiv.innerHTML = officialPkgListDiv.innerHTML =
p("There are currently " & $officialCount & p("There are currently " & $officialCount &
" official packages in the Babel package repository.") & " official packages in the Nimble package repository.") &
ul(officialList) ul(officialList)
var unofficialPkgListDiv = document.getElementById("unofficialPkgList") var unofficialPkgListDiv = document.getElementById("unofficialPkgList")
unofficialPkgListDiv.innerHTML = unofficialPkgListDiv.innerHTML =
p("There are currently " & $unofficialCount & p("There are currently " & $unofficialCount &
" unofficial packages in the Babel package repository.") & " unofficial packages in the Nimble package repository.") &
ul(unofficialList) ul(unofficialList)
proc gotPackageList(apiReply: TData) {.exportc.} = proc gotPackageList(apiReply: TData) {.exportc.} =

View file

@ -6,14 +6,67 @@ News
2014-10-21 Version 0.10.2 released 2014-10-21 Version 0.10.2 released
================================== ==================================
This release is the latest release before the release canditates for version This release marks the completion of a very important change to the project:
1.0 roll in. Starting with version 0.10.2 the rename of the language to Nim the official renaming from Nimrod to Nim. Version 0.10.2 contains many language
is officially complete. As the list of language changes is quite long it's changes, some of which may break your existing code. For your convenience, we
much more work to update the average Nim project than used to be the case. added a new tool called `nimfix <nimfix.html>`_ that will help you convert your
However there is a new tool, `nimfix <nimfix.html>`_ to help you existing projects so that it works with the latest version of the compiler.
in updating your code from Nimrod to Nim. This tool is unfortunately not
perfect but has been used to update thousands of lines of code successfully.
Progress towards version 1.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Although Nim is still pre-1.0, we were able to keep the number of breaking
changes to a minimum so far. Starting with version 1.0, we will not introduce
any breaking changes between major release versions.
One of Nim's goals is to ensure that the compiler is as efficient as possible.
Take a look at the
`latest benchmarks <https://github.com/logicchains/LPATHBench/blob/master/writeup.md>`_,
which show that Nim is consistently near
the top and already nearly as fast as C and C++. Recent developments, such as
the new ``asyncdispatch`` module will allow you to write efficient web server
applications using non-blocking code. Nim now also has a built-in thread pool
for lightweight threading through the use of ``spawn``.
The unpopular "T" and "P" prefixes on types have been deprecated. Nim also
became more expressive by weakening the distinction between statements and
epxressions. We also added new and searchable forums, a new website, and our
documentation generator ``docgen`` has seen major improvements.
What's left to be done
~~~~~~~~~~~~~~~~~~~~~~
The 1.0 release is actually very close. There are only a couple of last
things that need to be done:
* Implementing static[T] properly
* Support for the overloading of the assignment operator
Of course, the 1.0 release is not an end to the development of Nim.
It is very much the beginning and we will be fleshing out the then
stable language.
Nimble and other Nim tools
~~~~~~~~~~~~~~~~~~~~~~~~~~
Outside of the language and the compiler itself many Nim tools have seen
considerable improvements.
Babel the Nim package manager has been renamed to Nimble. Nimble's purpose
is the installation of packages containing libraries and/or applications
written in Nim.
Even though Nimble is still very young it already is very
functional. It can install packages by name, it does so by accessing a
packages repository which is hosted on a Github repo. Packages can also be
installed via a Git repo URL or Mercurial repo URL. The package repository
is searchable through Nimble. Anyone is free to add their own packages to
the package repository by forking the
`nim-lang/packages <https://github.com/nim-lang/packages>`_ repo and creating
a pull request. Nimble is fully cross-platform and should be fully functional
on all major operating systems.
It is of course completely written in Nim.
Changelog
~~~~~~~~~
Changes affecting backwards compatibility Changes affecting backwards compatibility
----------------------------------------- -----------------------------------------
@ -93,7 +146,7 @@ News
statement. statement.
- There is a new tool, `nimfix <nimfix.html>`_ to help you in updating your - There is a new tool, `nimfix <nimfix.html>`_ to help you in updating your
code from Nimrod to Nim. code from Nimrod to Nim.
- The compiler's output has been prettified.
Library Additions Library Additions
----------------- -----------------
@ -103,7 +156,41 @@ News
- ``system.setupForeignThreadGc`` can be used for better interaction with - ``system.setupForeignThreadGc`` can be used for better interaction with
foreign libraries that create threads and run a Nim callback from these foreign libraries that create threads and run a Nim callback from these
foreign threads. foreign threads.
- List comprehensions have been implemented as a macro in the ``future``
module.
- The new Async module (``asyncnet``) now supports SSL.
- The ``smtp`` module now has an async implementation.
- Added module ``asyncfile`` which implements asynchronous file reading
and writing.
- ``osproc.kill`` has been added.
- ``asyncnet`` and ``asynchttpserver`` now support ``SO_REUSEADDR``.
Bugfixes
--------
- ``nil`` and ``NULL`` are now preserved between Nim and databases in the
``db_*`` modules.
- Fixed issue with OS module in non-unicode mode on Windows.
- Fixed issue with ``x.low``
(`#1366 <https://github.com/Araq/Nim/issues/1366>`_).
- Fixed tuple unpacking issue inside closure iterators
(`#1067 <https://github.com/Araq/Nim/issues/1067>`_).
- Fixed ENDB compilation issues.
- Many ``asynchttpserver`` fixes.
- Macros can now keep global state across macro calls
(`#903 <https://github.com/Araq/Nim/issues/903>`_).
- ``osproc`` fixes on Windows.
- ``osproc.terminate`` fixed.
- Improvements to exception handling in async procedures.
(`#1487 <https://github.com/Araq/Nim/issues/1487>`_).
- ``try`` now works at compile-time.
- Fixes ``T = ref T`` to be an illegal recursive type.
- Self imports are now disallowed.
- Improved effect inference.
- Fixes for the ``math`` module on Windows.
- User defined pragmas will now work for generics that have
been instantiated in different modules.
- Fixed queue exhaustion bug.
2014-12-09 New website design! 2014-12-09 New website design!
============================== ==============================