summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/asyncio.nim3
-rw-r--r--lib/pure/scgi.nim124
-rw-r--r--lib/pure/sockets.nim3
-rw-r--r--web/news.txt31
4 files changed, 124 insertions, 37 deletions
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index 88e3e848f..403401ff1 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -420,6 +420,9 @@ proc isListening*(s: PAsyncSocket): bool =
 proc isConnecting*(s: PAsyncSocket): bool =
   ## Determines whether ``s`` is connecting.  
   return s.info == SockConnecting
+proc isClosed*(s: PAsyncSocket): bool =
+  ## Determines whether ``s`` has been closed.
+  return s.info == SockClosed
 
 proc setHandleWrite*(s: PAsyncSocket,
     handleWrite: proc (s: PAsyncSocket) {.closure.}) =
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
index 0f3b44e00..57fa0b144 100644
--- a/lib/pure/scgi.nim
+++ b/lib/pure/scgi.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -67,10 +67,24 @@ type
     headers*: PStringTable ## the parsed headers
     input*: string  ## the input buffer
   
-  TAsyncScgiState* = object of TScgiState
-    handleRequest: proc (server: var TAsyncScgiState, client: TSocket, 
+  
+  # Async
+  
+  TClientMode = enum
+    ClientReadChar, ClientReadHeaders, ClientReadContent
+  
+  PAsyncClient = ref object
+    c: PAsyncSocket
+    mode: TClientMode
+    dataLen: int
+    headers: PStringTable ## the parsed headers
+    input: string  ## the input buffer
+  
+  TAsyncScgiState = object
+    handleRequest: proc (client: PAsyncSocket, 
                          input: string, headers: PStringTable) {.closure.}
     asyncServer: PAsyncSocket
+    disp: PDispatcher
   PAsyncScgiState* = ref TAsyncScgiState
     
 proc recvBuffer(s: var TScgiState, L: int) =
@@ -145,37 +159,88 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
   s.close()
 
 # -- AsyncIO start
+
+proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
+  result = ReadPartialLine
+  var data = ""
+  if L < 1:
+    scgiError("Cannot read negative or zero length: " & $L)
+  let ret = recvAsync(client.c, data, L)
+  if ret == 0 and data == "":
+    client.c.close()
+    return ReadDisconnected
+  if ret == -1:
+    return ReadNone # No more data available
+  client.input.add(data)
+  if ret == L:
+    return ReadFullLine
+
+proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
+  case client.mode
+  of ClientReadChar:
+    while true:
+      var d = ""
+      let ret = client.c.recvAsync(d, 1)
+      if d == "" and ret == 0:
+        # Disconnected
+        client.c.close()
+        return
+      if ret == -1:
+        return # No more data available
+      if d[0] notin strutils.digits:
+        if d[0] != ':': scgiError("':' after length expected")
+        break
+      client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
+    client.mode = ClientReadHeaders
+    handleClientRead(client, s) # Allow progression
+  of ClientReadHeaders:
+    let ret = recvBufferAsync(client, (client.dataLen+1)-client.input.len)
+    case ret
+    of ReadFullLine:
+      client.headers = parseHeaders(client.input, client.input.len-1)
+      if client.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
+      client.input = "" # For next part
+      
+      let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
+      if contentLen > 0:
+        client.mode = ClientReadContent
+      else:
+        s.handleRequest(client.c, client.input, client.headers)
+        if not client.c.isClosed: client.c.close()
+    of ReadPartialLine, ReadDisconnected, ReadNone: return
+  of ClientReadContent:
+    let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len
+    if L > 0:
+      let ret = recvBufferAsync(client, L)
+      case ret
+      of ReadFullLine:
+        s.handleRequest(client.c, client.input, client.headers)
+        if not client.c.isClosed: client.c.close()
+      of ReadPartialLine, ReadDisconnected, ReadNone: return
+    else:
+      s.handleRequest(client.c, client.input, client.headers)
+      if not client.c.isClosed: client.c.close()
+
 proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
-  new(s.client)
-  accept(getSocket(s.asyncServer), s.client)
-  var L = 0
-  while true:
-    var d = s.client.recvChar()
-    if d == '\0':
-      # Disconnected
-      s.client.close()
-      return
-    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)
-
-  s.handleRequest(s[], s.client, s.input, s.headers)
-
-proc open*(handleRequest: proc (server: var TAsyncScgiState, client: TSocket, 
+  var client: PAsyncSocket
+  new(client)
+  accept(s.asyncServer, client)
+  var asyncClient = PAsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
+                                 headers: newStringTable(), input: "")
+  client.handleRead = 
+    proc (sock: PAsyncSocket) =
+      handleClientRead(asyncClient, s)
+  s.disp.register(client)
+
+proc open*(handleRequest: proc (client: PAsyncSocket, 
                                 input: string, headers: PStringTable) {.closure.},
            port = TPort(4000), address = "127.0.0.1"): PAsyncScgiState =
-  ## Alternative of ``open`` for asyncio compatible SCGI.
+  ## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
+  ##
+  ## After the execution of ``handleRequest`` the client socket will be closed
+  ## automatically unless it has already been closed.
   var cres: PAsyncScgiState
   new(cres)
-  cres.bufLen = 4000
-  cres.input = newString(cres.buflen) # will be reused
-
   cres.asyncServer = AsyncSocket()
   cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres)
   bindAddr(cres.asyncServer, port, address)
@@ -186,6 +251,7 @@ proc open*(handleRequest: proc (server: var TAsyncScgiState, client: TSocket,
 proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} =
   ## Registers ``s`` with dispatcher ``d``.
   result = d.register(s.asyncServer)
+  s.disp = d
 
 proc close*(s: PAsyncScgiState) =
   ## Closes the ``PAsyncScgiState``.
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index 17e4d3118..cde2d4795 100644
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -629,7 +629,8 @@ proc close*(socket: TSocket) =
     discard winlean.closeSocket(socket.fd)
   else:
     discard posix.close(socket.fd)
-
+  # TODO: These values should not be discarded. An EOS should be raised.
+  # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
   when defined(ssl):
     if socket.isSSL:
       discard SSLShutdown(socket.sslHandle)
diff --git a/web/news.txt b/web/news.txt
index e948e762b..f66c06667 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -2,6 +2,15 @@
 News
 ====
 
+New website design!
+===================
+
+A brand new website is now live. All thanks go to Philip Witte and 
+Dominik Picheta, Philip Witte for the design of the website (together with
+the logo) as well as the HTML and CSS code for his template, and Dominik Picheta
+for integrating Philip's design with the ``nimweb`` utility. We're sure you will
+agree that Philip's design is beautiful.
+
 2013-XX-XX Version 0.9.2 released
 =================================
 
@@ -19,7 +28,10 @@ Bugfixes
 - ``cast`` for floating point types now does the bitcast as specified in the 
   manual. This breaks code that erroneously uses ``cast`` to convert different
   floating point values.
+- SCGI module's performance has been improved greatly, it will no longer block
+  on many concurrent requests.
 
+- In total fixed over 70 github issues and merged over 60 pull requests.
 
 Library Additions
 -----------------
@@ -46,9 +58,7 @@ Changes affecting backwards compatibility
   this affects very little (if any) real world code.
 - The expression/statement unification has been implemented. Again this
   only affects edge cases and no known real world code.
-- The scope rules of ``if`` statements will change in 0.9.4. This affects the 
-  ``=~`` pegs/re templates.
-
+- Changed the async interface of the ``scgi`` module.
 
 Compiler Additions
 ------------------
@@ -59,9 +69,8 @@ Compiler Additions
   to be turned on explicitly via ``--warning[ShadowIdent]:on``.
 - The compiler now supports almost every pragma in a ``push`` pragma.
 - Generic converters have been implemented.
-- Added a ``noforward`` pragma enabling a special compilation mode that largely
-  eliminates the need for forward declarations.
-
+- Added a **highly experimental** ``noforward`` pragma enabling a special 
+  compilation mode that largely eliminates the need for forward declarations.
 
 Language Additions
 ------------------
@@ -75,7 +84,7 @@ Language Additions
   exceptions for you.
 - User defined effects ("tags") tracking has been added and the ``doc2`` 
   command annotates possible tags for you.
-- Types can be annotated with the new syntax ``not nil`` to explictly state
+- Types can be annotated with the new syntax ``not nil`` to explicitly state
   that ``nil`` is not allowed. However currently the compiler performs no
   advanced static checking for this; for now it's merely for documentation
   purposes.
@@ -88,6 +97,14 @@ Language Additions
 - There is a new syntactic construct ``(;)`` unifying expressions and 
   statements.
 
+Notes for the future
+--------------------
+
+- The scope rules of ``if`` statements will change in 0.9.4. This affects the 
+  ``=~`` pegs/re templates.
+- The ``sockets`` module will become a low-level wrapper of OS-specific socket
+  functions. All the high-level features of the current ``sockets`` module
+  will be moved to a ``network`` module.
 
 2012-09-23 Version 0.9.0 released
 =================================