summary refs log tree commit diff stats
path: root/lib/pure/sockets.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/sockets.nim')
-rwxr-xr-xlib/pure/sockets.nim91
1 files changed, 53 insertions, 38 deletions
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index c00dcc80b..564b76343 100755
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -142,9 +142,11 @@ proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
   else:
     result = TSocket(posix.socket(ToInt(domain), ToInt(typ), ToInt(protocol)))
 
-proc listen*(socket: TSocket, attempts = 5) =
-  ## listens to socket.
-  if listen(cint(socket), cint(attempts)) < 0'i32: OSError()
+proc listen*(socket: TSocket, backlog = SOMAXCONN) =
+  ## Marks ``socket`` as accepting connections. 
+  ## ``Backlog`` specifies the maximum length of the 
+  ## queue of pending connections.
+  if listen(cint(socket), cint(backlog)) < 0'i32: OSError()
 
 proc invalidIp4(s: string) {.noreturn, noinline.} =
   raise newException(EInvalidValue, "invalid ip4 address: " & s)
@@ -239,22 +241,35 @@ proc getSockName*(socket: TSocket): TPort =
     OSError()
   result = TPort(sockets.ntohs(name.sin_port))
 
-proc accept*(server: TSocket): TSocket =
-  ## 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
+  ## Blocks until a connection is being made from a client. When a connection
+  ## is made returns the client socket and address of the connecting client.
+  ## If ``server`` is non-blocking then this function returns immediately, and
+  ## if there are no connections queued the returned socket will be
+  ## ``InvalidSocket``.
+  ## This function will raise EOS if an error occurs.
   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))
+  var sock = accept(cint(server), cast[ptr TSockAddr](addr(address)),
+                    addr(addrLen))
+  if sock < 0:
+    # TODO: Test on Windows.
+    when defined(windows):
+      var err = WSAGetLastError()
+      if err == WSAEINPROGRESS:
+        return (InvalidSocket, "")
+      else: OSError()
+    else:
+      if errno == EAGAIN or errno == EWOULDBLOCK:
+        return (InvalidSocket, "")
+      else: OSError()
+  else: return (TSocket(sock), $inet_ntoa(address.sin_addr))
+
+proc accept*(server: TSocket): TSocket =
+  ## Equivalent to ``acceptAddr`` but doesn't return the address, only the
+  ## socket.
+  var (client, a) = acceptAddr(server)
+  return client
 
 proc close*(socket: TSocket) =
   ## closes a socket.
@@ -428,6 +443,11 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
   if not success: OSError()
 
 
+proc timeValFromMilliseconds(timeout = 500): TTimeVal =
+  if timeout != -1:
+    var seconds = timeout div 1000
+    result.tv_sec = seconds
+    result.tv_usec = (timeout - seconds * 1000) * 1000
 #proc recvfrom*(s: TWinSocket, buf: cstring, len, flags: cint, 
 #               fromm: ptr TSockAddr, fromlen: ptr cint): cint 
 
@@ -460,9 +480,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
   ## You can determine whether a socket is ready by checking if it's still
   ## in one of the TSocket sequences.
 
-  var tv: TTimeVal
-  tv.tv_sec = 0
-  tv.tv_usec = timeout * 1000
+  var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
   
   var rd, wr, ex: TFdSet
   var m = 0
@@ -480,10 +498,8 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
   pruneSocketSet(exceptfds, (ex))
 
 proc select*(readfds, writefds: var seq[TSocket], 
-             timeout = 500): int = 
-  var tv: TTimeVal
-  tv.tv_sec = 0
-  tv.tv_usec = timeout * 1000
+             timeout = 500): int =
+  var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
   
   var rd, wr: TFdSet
   var m = 0
@@ -499,10 +515,8 @@ proc select*(readfds, writefds: var seq[TSocket],
   pruneSocketSet(writefds, (wr))
 
 proc selectWrite*(writefds: var seq[TSocket], 
-                  timeout = 500): int = 
-  var tv: TTimeVal
-  tv.tv_sec = 0
-  tv.tv_usec = timeout * 1000
+                  timeout = 500): int =
+  var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
   
   var wr: TFdSet
   var m = 0
@@ -515,11 +529,8 @@ proc selectWrite*(writefds: var seq[TSocket],
   
   pruneSocketSet(writefds, (wr))
 
-
-proc select*(readfds: var seq[TSocket], timeout = 500): int = 
-  var tv: TTimeVal
-  tv.tv_sec = 0
-  tv.tv_usec = timeout * 1000
+proc select*(readfds: var seq[TSocket], timeout = 500): int =
+  var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
   
   var rd: TFdSet
   var m = 0
@@ -537,11 +548,14 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool =
   ## 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.
+  ## If ``socket`` is disconnected, ``true`` will be returned and line will be
+  ## set to ``""``.
   setLen(line.string, 0)
   while true:
     var c: char
     var n = recv(cint(socket), addr(c), 1, 0'i32)
-    if n <= 0: return
+    if n < 0: return
+    elif n == 0: return true
     if c == '\r':
       n = recv(cint(socket), addr(c), 1, MSG_PEEK)
       if n > 0 and c == '\L':
@@ -625,7 +639,7 @@ proc skip*(socket: TSocket) =
 
 proc send*(socket: TSocket, data: pointer, size: int): int =
   ## sends data to a socket.
-  when defined(windows):
+  when defined(windows) or defined(macosx):
     result = send(cint(socket), data, size, 0'i32)
   else:
     result = send(cint(socket), data, size, int32(MSG_NOSIGNAL))
@@ -634,19 +648,20 @@ 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.
+proc sendAsync*(socket: TSocket, data: string): bool =
+  ## sends data to a non-blocking socket. Returns whether ``data`` was sent.
+  result = true
   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
+        return false
       else: OSError()
     else:
       if errno == EAGAIN or errno == EWOULDBLOCK:
-        return
+        return false
       else: OSError()
 
 when defined(Windows):