diff options
author | Araq <rumpf_a@web.de> | 2011-05-01 20:12:14 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-05-01 20:12:14 +0200 |
commit | fcabc0f9f46cbec1b9755110f29312c81d3a451a (patch) | |
tree | cc0566ed8e6f990e1d0c59d7359d42ae444f5813 | |
parent | 6ff8752be53b7c0ad2c01615fdf1ab1bb619fb83 (diff) | |
parent | 6b4101f82e822afd18ed5c757186dfa0c11ca9fb (diff) | |
download | Nim-fcabc0f9f46cbec1b9755110f29312c81d3a451a.tar.gz |
Merge branch 'master' of github.com:Araq/Nimrod
-rwxr-xr-x | lib/posix/posix.nim | 13 | ||||
-rwxr-xr-x | lib/pure/json.nim | 2 | ||||
-rwxr-xr-x | lib/pure/scgi.nim | 47 | ||||
-rwxr-xr-x | lib/pure/sockets.nim | 183 | ||||
-rwxr-xr-x | lib/windows/winlean.nim | 13 | ||||
-rwxr-xr-x | lib/wrappers/gtk/gtk2.nim | 3 |
6 files changed, 223 insertions, 38 deletions
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index fa8a56dba..966093c81 100755 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -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 {. diff --git a/lib/pure/json.nim b/lib/pure/json.nim index c90c071b6..75958a55f 100755 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -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 diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 4893cf603..48f5438c0 100755 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -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: diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index add41afd6..45e4da118 100755 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -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 diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 9ebd4504b..e8c93d8b1 100755 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -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.} diff --git a/lib/wrappers/gtk/gtk2.nim b/lib/wrappers/gtk/gtk2.nim index 747ab4840..86419dd7e 100755 --- a/lib/wrappers/gtk/gtk2.nim +++ b/lib/wrappers/gtk/gtk2.nim @@ -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, |