Merge branch 'master' of github.com:Araq/Nimrod

This commit is contained in:
Araq
2011-05-01 20:12:14 +02:00
6 changed files with 223 additions and 38 deletions

View File

@@ -454,7 +454,10 @@ type
TInPort* = int16 ## unsigned!
TInAddrScalar* = int32 ## unsigned!
TInAddrT* {.importc: "in_addr_t", pure, final,
header: "<netinet/in.h>".} = int32 ## unsigned!
TInAddr* {.importc: "struct in_addr", pure, final,
header: "<netinet/in.h>".} = object ## struct in_addr
s_addr*: TInAddrScalar
@@ -544,6 +547,7 @@ type
var
errno* {.importc, header: "<errno.h>".}: cint ## error variable
h_errno* {.importc, header: "<netdb.h>".}: cint
daylight* {.importc, header: "<time.h>".}: cint
timezone* {.importc, header: "<time.h>".}: int
@@ -1558,6 +1562,8 @@ var
## Terminates a record (if supported by the protocol).
MSG_OOB* {.importc, header: "<sys/socket.h>".}: cint
## Out-of-band data.
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
MSG_PEEK* {.importc, header: "<sys/socket.h>".}: cint
## Leave received data in queue.
MSG_TRUNC* {.importc, header: "<sys/socket.h>".}: cint
@@ -1733,8 +1739,8 @@ proc htons*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
proc ntohl*(a1: int32): int32 {.importc, header: "<arpa/inet.h>".}
proc ntohs*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
proc inet_addr*(a1: cstring): int32 {.importc, header: "<arpa/inet.h>".}
proc inet_ntoa*(a1: int32): cstring {.importc, header: "<arpa/inet.h>".}
proc inet_addr*(a1: cstring): TInAddrT {.importc, header: "<arpa/inet.h>".}
proc inet_ntoa*(a1: TInAddr): cstring {.importc, header: "<arpa/inet.h>".}
proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {.
importc, header: "<arpa/inet.h>".}
proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {.
@@ -2315,6 +2321,7 @@ proc sched_setscheduler*(a1: tpid, a2: cint, a3: var tsched_param): cint {.
proc sched_yield*(): cint {.importc, header: "<sched.h>".}
proc strerror*(errnum: cint): cstring {.importc, header: "<string.h>".}
proc hstrerror*(herrnum: cint): cstring {.importc, header: "<netdb.h>".}
proc FD_CLR*(a1: cint, a2: var Tfd_set) {.importc, header: "<sys/select.h>".}
proc FD_ISSET*(a1: cint, a2: var Tfd_set): cint {.

View File

@@ -495,7 +495,7 @@ type
JArray
PJsonNode* = ref TJsonNode ## JSON node
TJsonNode {.final, pure.} = object
TJsonNode* {.final, pure.} = object
case kind*: TJsonNodeKind
of JString:
str*: String

View File

@@ -88,30 +88,35 @@ proc close*(s: var TScgiState) =
## closes the connection.
s.server.close()
proc next*(s: var TScgistate) =
## proceed to the first/next request.
s.client = accept(s.server)
var L = 0
while true:
var d = s.client.recvChar()
if d notin strutils.digits:
if d != ':': scgiError("':' after length expected")
break
L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1)
s.headers = parseHeaders(s.input, L)
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
L = parseInt(s.headers["CONTENT_LENGTH"])
recvBuffer(s, L)
proc next*(s: var TScgistate, timeout: int = -1): bool =
## proceed to the first/next request. Waits ``timeout`` miliseconds for a
## request, if ``timeout`` is `-1` then this function will never time out.
## Returns `True` if a new request has been processed.
var rsocks = @[s.server]
if select(rsocks, timeout) == 1 and rsocks.len == 0:
s.client = accept(s.server)
var L = 0
while true:
var d = s.client.recvChar()
if d notin strutils.digits:
if d != ':': scgiError("':' after length expected")
break
L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1)
s.headers = parseHeaders(s.input, L)
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
L = parseInt(s.headers["CONTENT_LENGTH"])
recvBuffer(s, L)
return True
proc writeStatusOkTextContent*(c: TSocket) =
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
## sends the following string to the socket `c`::
##
## Status: 200 OK\r\LContent-Type: text/plain\r\L\r\L
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
##
## You should send this before sending your HTML page, for example.
c.send("Status: 200 OK\r\L" &
"Content-Type: text/plain\r\L\r\L")
"Content-Type: $1\r\L\r\L" % contentType)
proc run*(handleRequest: proc (client: TSocket, input: string,
headers: PStringTable): bool,
@@ -121,9 +126,9 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
s.open(port)
var stop = false
while not stop:
next(s)
stop = handleRequest(s.client, s.input, s.headers)
s.client.close()
if next(s):
stop = handleRequest(s.client, s.input, s.headers)
s.client.close()
s.close()
when isMainModule:

View File

@@ -223,12 +223,22 @@ proc getSockName*(socket: TSocket): TPort =
result = TPort(sockets.ntohs(name.sin_port))
proc accept*(server: TSocket): TSocket =
## waits for a client and returns its socket
## waits for a client and returns its socket. ``InvalidSocket`` is returned
## if an error occurs, or if ``server`` is non-blocking and there are no
## clients connecting.
var client: Tsockaddr_in
var clientLen: cint = sizeof(client)
result = TSocket(accept(cint(server), cast[ptr TSockAddr](addr(client)),
addr(clientLen)))
proc acceptAddr*(server: TSocket): tuple[sock: TSocket, address: string] =
## waits for a client and returns its socket and IP address
var address: Tsockaddr_in
var addrLen: cint = sizeof(address)
var sock = TSocket(accept(cint(server), cast[ptr TSockAddr](addr(address)),
addr(addrLen)))
return (sock, $inet_ntoa(address.sin_addr))
proc close*(socket: TSocket) =
## closes a socket.
when defined(windows):
@@ -260,6 +270,35 @@ proc getServByPort*(port: TPort, proto: string): TServent =
result.port = TPort(s.s_port)
result.proto = $s.s_proto
proc getHostByAddr*(ip: string): THostEnt =
## This function will lookup the hostname of an IP Address.
var myaddr: TInAddr
myaddr.s_addr = inet_addr(ip)
when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr),
cint(sockets.AF_INET))
if s == nil: OSError()
else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr),
cint(posix.AF_INET))
if s == nil:
raise newException(EOS, $hStrError(h_errno))
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows):
result.addrType = TDomain(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:
OSError("unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getHostByName*(name: string): THostEnt =
## well-known gethostbyname proc.
when defined(Windows):
@@ -299,9 +338,10 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) =
proc connect*(socket: TSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) =
## well-known connect operation. Already does ``htons`` on the port number,
## so you shouldn't do it. `name` can be an IP address or a host name of the
## form "force7.de".
## Connects socket to ``name``:``port``. ``Name`` can be an IP address or a
## host name. If ``name`` is a host name, this function will try each IP
## of that host name. ``htons`` is already performed on ``port`` so you must
## not do it.
var hints: TAddrInfo
var aiList: ptr TAddrInfo = nil
hints.ai_family = toInt(af)
@@ -316,6 +356,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
success = true
break
it = it.ai_next
freeaddrinfo(aiList)
if not success: OSError()
@@ -334,6 +375,40 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
if connect(cint(socket), cast[ptr TSockAddr](addr(s)), sizeof(s)) < 0'i32:
OSError()
proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) =
## A variant of ``connect`` for non-blocking sockets.
var hints: TAddrInfo
var aiList: ptr TAddrInfo = nil
hints.ai_family = toInt(af)
hints.ai_socktype = toInt(SOCK_STREAM)
hints.ai_protocol = toInt(IPPROTO_TCP)
if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
# try all possibilities:
var success = false
var it = aiList
while it != nil:
var ret = connect(cint(socket), it.ai_addr, it.ai_addrlen)
if ret == 0'i32:
success = true
break
else:
# TODO: Test on Windows.
when defined(windows):
var err = WSAGetLastError()
# Windows EINTR doesn't behave same as POSIX.
if err == WSAEWOULDBLOCK:
return
else:
if errno == EINTR or errno == EINPROGRESS:
return
it = it.ai_next
freeaddrinfo(aiList)
if not success: OSError()
#proc recvfrom*(s: TWinSocket, buf: cstring, len, flags: cint,
# fromm: ptr TSockAddr, fromlen: ptr cint): cint
@@ -360,6 +435,7 @@ proc pruneSocketSet(s: var seq[TSocket], fd: var TFdSet) =
proc select*(readfds, writefds, exceptfds: var seq[TSocket],
timeout = 500): int =
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
## Specify -1 for no timeout.
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
@@ -370,7 +446,10 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
createFdSet((wr), writefds, m)
createFdSet((ex), exceptfds, m)
result = int(select(cint(m), addr(rd), addr(wr), addr(ex), addr(tv)))
if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
pruneSocketSet(readfds, (rd))
pruneSocketSet(writefds, (wr))
@@ -379,6 +458,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
proc select*(readfds, writefds: var seq[TSocket],
timeout = 500): int =
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
## Specify -1 for no timeout.
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
@@ -388,14 +468,37 @@ proc select*(readfds, writefds: var seq[TSocket],
createFdSet((rd), readfds, m)
createFdSet((wr), writefds, m)
result = int(select(cint(m), addr(rd), addr(wr), nil, addr(tv)))
if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
pruneSocketSet(readfds, (rd))
pruneSocketSet(writefds, (wr))
proc selectWrite*(writefds: var seq[TSocket],
timeout = 500): int =
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
## Specify -1 for no timeout.
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
var wr: TFdSet
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))
proc select*(readfds: var seq[TSocket], timeout = 500): int =
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
## Specify -1 for no timeout.
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
@@ -404,14 +507,18 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int =
var m = 0
createFdSet((rd), readfds, m)
result = int(select(cint(m), addr(rd), nil, nil, addr(tv)))
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 recvLine*(socket: TSocket, line: var string): bool =
## returns false if no further data is available. `line` must be initalized
## and not nil!
## returns false if no further data is available. `Line` must be initialized
## and not nil! This does not throw an EOS exception, therefore
## it can be used in both blocking and non-blocking sockets.
setLen(line, 0)
while true:
var c: char
@@ -431,16 +538,52 @@ proc recv*(socket: TSocket, data: pointer, size: int): int =
result = recv(cint(socket), data, size, 0'i32)
proc recv*(socket: TSocket): string =
## receives all the data from the socket
## receives all the data from the socket.
## Socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
## ``""`` will be returned.
const bufSize = 200
var buf = newString(bufSize)
result = ""
while true:
var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error
if bytesRead == -1: OSError()
buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead)
add(result, buf)
if bytesRead != bufSize-1: break
proc recvAsync*(socket: TSocket, s: var string): bool =
## receives all the data from a non-blocking socket. If socket is non-blocking
## and there are no messages available, `False` will be returned.
## Other socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
## ``s`` will be set to ``""``.
const bufSize = 200
var buf = newString(bufSize)
s = ""
while true:
var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error
if bytesRead == -1:
when defined(windows):
# TODO: Test on Windows
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
return False
else: OSError()
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
return False
else: OSError()
buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead)
add(s, buf)
if bytesRead != bufSize-1: break
result = True
proc skip*(socket: TSocket) =
## skips all the data that is pending for the socket
@@ -451,12 +594,30 @@ proc skip*(socket: TSocket) =
proc send*(socket: TSocket, data: pointer, size: int): int =
## sends data to a socket.
result = send(cint(socket), data, size, 0'i32)
when defined(windows):
result = send(cint(socket), data, size, 0'i32)
else:
result = send(cint(socket), data, size, int32(MSG_NOSIGNAL))
proc send*(socket: TSocket, data: string) =
## sends data to a socket.
if send(socket, cstring(data), data.len) != data.len: OSError()
proc sendAsync*(socket: TSocket, data: string) =
## sends data to a non-blocking socket.
var bytesSent = send(socket, cstring(data), data.len)
if bytesSent == -1:
when defined(windows):
var err = WSAGetLastError()
# TODO: Test on windows.
if err == WSAEINPROGRESS:
return
else: OSError()
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
return
else: OSError()
when defined(Windows):
const
SOCKET_ERROR = -1

View File

@@ -215,7 +215,12 @@ const
INADDR_NONE* = -1
ws2dll = "Ws2_32.dll"
WSAEWOULDBLOCK* = 10035
WSAEINPROGRESS* = 10036
proc WSAGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.}
type
TWSAData* {.pure, final.} = object
wVersion, wHighVersion: int16
@@ -297,6 +302,9 @@ proc getservbyname*(name, proto: cstring): ptr TServent {.
proc getservbyport*(port: cint, proto: cstring): ptr TServent {.
stdcall, importc: "getservbyport", dynlib: ws2dll.}
proc gethostbyaddr*(ip: ptr TInAddr, len: cint, theType: cint): ptr THostEnt {.
stdcall, importc: "gethostbyaddr", dynlib: ws2dll.}
proc gethostbyname*(name: cstring): ptr THostEnt {.
stdcall, importc: "gethostbyname", dynlib: ws2dll.}
@@ -373,4 +381,5 @@ proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,
proc freeaddrinfo*(ai: ptr TAddrInfo) {.
stdcall, importc: "freeaddrinfo", dynlib: ws2dll.}
proc inet_ntoa*(i: TInAddr): cstring {.
stdcall, importc, dynlib: ws2dll.}

View File

@@ -7469,6 +7469,9 @@ proc iter_nth_child*(tree_model: PTreeModel, iter: PTreeIter,
proc iter_parent*(tree_model: PTreeModel, iter: PTreeIter,
child: PTreeIter): gboolean{.cdecl, dynlib: lib,
importc: "gtk_tree_model_iter_parent".}
proc get_string_from_iter*(tree_model: PTreeModel, iter: PTreeIter):
cstring{.cdecl, dynlib: lib,
importc: "gtk_tree_model_get_string_from_iter".}
proc ref_node*(tree_model: PTreeModel, iter: PTreeIter){.cdecl,
dynlib: lib, importc: "gtk_tree_model_ref_node".}
proc unref_node*(tree_model: PTreeModel, iter: PTreeIter){.cdecl,