summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xlib/posix/posix.nim13
-rwxr-xr-xlib/pure/json.nim2
-rwxr-xr-xlib/pure/scgi.nim47
-rwxr-xr-xlib/pure/sockets.nim183
-rwxr-xr-xlib/windows/winlean.nim13
-rwxr-xr-xlib/wrappers/gtk/gtk2.nim3
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,