Browse Source

Fix the problem with the more modern SDK only allowing one outstanding
connection:send at a time. Long and short of it, don't use coroutine.yield
any more when serving content.

Philip Gladstone 8 years ago
parent
commit
b4a2d02431
4 changed files with 36 additions and 16 deletions
  1. 0 5
      README.md
  2. 0 3
      http/file_list.lua
  3. 0 1
      httpserver-static.lua
  4. 36 7
      httpserver.lua

+ 0 - 5
README.md

@@ -78,11 +78,6 @@ A (very) simple web server written in Lua for the ESP8266 running the NodeMCU fi
    For example, if the client requests _http://2.2.2.2/foo.lua?color=red_ then the server will execute the function
    in your Lua script _foo.lua_ and pass in _connection_ and _args_, where _args.color = "red"_.
 
-   If you are going to be sending lots (as in over a KB) of data in your script, you should yield the thread/coroutine
-   every now and then in order to avoid overflowing the send buffer in the microcontroller. Use:
-
-      coroutine.yield()
-
    Look at the included example scripts for more ideas.
 
 ### Example: Garage door opener

+ 0 - 3
http/file_list.lua

@@ -2,7 +2,6 @@ return function (connection, req, args)
    connection:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nCache-Control: private, no-store\r\n\r\n")
    connection:send('<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Server File Listing</title></head>')
    connection:send('<body>')
-   coroutine.yield()
    connection:send('<h1>Server File Listing</h1>')
 
    local remaining, used, total=file.fsinfo()
@@ -20,8 +19,6 @@ return function (connection, req, args)
       if isHttpFile then
          local url = string.match(name, ".*/(.*)")
          connection:send('   <li><a href="' .. url .. '">' .. url .. "</a> (" .. size .. " bytes)</li>\n")
-         -- this list could be very long, so we'll yield in order to avoid overflowing the send buffer.
-         coroutine.yield()
       end
    end
    connection:send("</ul>\n")

+ 0 - 1
httpserver-static.lua

@@ -20,7 +20,6 @@ return function (connection, req, args)
       if chunk == nil then
          continue = false
       else
-         coroutine.yield()
          connection:send(chunk)
          bytesSent = bytesSent + #chunk
          chunk = nil

+ 36 - 7
httpserver.lua

@@ -13,9 +13,37 @@ return function (port)
          -- We do it in a separate thread because we need to yield when sending lots
          -- of data in order to avoid overflowing the mcu's buffer.
          local connectionThread
-         
+        
          local allowStatic = {GET=true, HEAD=true, POST=false, PUT=false, DELETE=false, TRACE=false, OPTIONS=false, CONNECT=false, PATCH=false}
 
+         local function startServing(fileServeFunction, connection, req, args) 
+            local bufferedConnection = {}
+            connectionThread = coroutine.create(function(fileServeFunction, connection, req, args)
+               fileServeFunction(connection, req, args)
+               connection:flush()
+            end)
+            function bufferedConnection:flush() 
+              connection:send(table.concat(self.data, ""))
+              self.data = {}
+              self.size = 0    
+            end
+            function bufferedConnection:send(payload) 
+              local l = payload:len()
+              if l + self.size > 1400 then
+                 self:flush()
+                 coroutine.yield()          
+              end
+              table.insert(self.data, payload)
+              self.size = self.size + l
+            end
+            bufferedConnection.size = 0
+            bufferedConnection.data = {}
+            local status, err = coroutine.resume(connectionThread, fileServeFunction, bufferedConnection, req, args)
+            if not status then
+               print(err)
+            end
+         end
+
          local function onRequest(connection, req)
             collectgarbage()
             local method = req.method
@@ -59,8 +87,7 @@ return function (port)
                   end
                end
             end
-            connectionThread = coroutine.create(fileServeFunction)
-            coroutine.resume(connectionThread, connection, req, uri.args)
+            startServing(fileServeFunction, connection, req, uri.args)
          end
 
          local function onReceive(connection, payload)
@@ -89,18 +116,20 @@ return function (port)
                else
                   args = {code = 400, errorString = "Bad Request"}
                end
-               connectionThread = coroutine.create(fileServeFunction)
-               coroutine.resume(connectionThread, connection, req, args)
+               startServing(fileServeFunction, connection, req, args)
             end
          end
 
          local function onSent(connection, payload)
             collectgarbage()
             if connectionThread then
-               local connectionThreadStatus = coroutine.status(connectionThread)
+               local connectionThreadStatus = coroutine.status(connectionThread) 
                if connectionThreadStatus == "suspended" then
                   -- Not finished sending file, resume.
-                  coroutine.resume(connectionThread)
+                  local status, err = coroutine.resume(connectionThread)
+                  if not status then
+                     print(err)
+                  end
                elseif connectionThreadStatus == "dead" then
                   -- We're done sending file.
                   connection:close()