Browse Source

Several fixes and code cleanup. Gzipped files now work. Indentation cleaned up, Other small fixes:

Marcos Kirsch 5 years ago
parent
commit
2f2fb26782
4 changed files with 92 additions and 93 deletions
  1. 7 7
      httpserver-header.lua
  2. 32 34
      httpserver-request.lua
  3. 2 5
      httpserver-static.lua
  4. 51 47
      httpserver.lua

+ 7 - 7
httpserver-header.lua

@@ -2,7 +2,7 @@
 -- Part of nodemcu-httpserver, knows how to send an HTTP header.
 -- Author: Marcos Kirsch
 
-return function (connection, code, extension, gzip)
+return function (connection, code, extension, isGzipped)
 
    local function getHTTPStatusString(code)
       local codez = {[200]="OK", [400]="Bad Request", [404]="Not Found",}
@@ -12,21 +12,21 @@ return function (connection, code, extension, gzip)
    end
 
    local function getMimeType(ext)
-      local gzip = false
       -- A few MIME types. Keep list short. If you need something that is missing, let's add it.
       local mt = {css = "text/css", gif = "image/gif", html = "text/html", ico = "image/x-icon", jpeg = "image/jpeg", jpg = "image/jpeg", js = "application/javascript", json = "application/json", png = "image/png", xml = "text/xml"}
-      if mt[ext] then contentType = mt[ext] else contentType = "text/plain" end
-      return {contentType = contentType, gzip = gzip}
+      if mt[ext] then return mt[ext] else return "text/plain" end
    end
 
    local mimeType = getMimeType(extension)
 
-   local header = "HTTP/1.0 " .. code .. " " .. getHTTPStatusString(code) .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType["contentType"] .. "\r\n"
-   if mimeType["gzip"] then header = header .. "Content-Encoding: gzip\r\n" end
+   local header = "HTTP/1.0 " .. code .. " " .. getHTTPStatusString(code) .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\nnCache-Control: private, no-store\r\n"
+   if isGzipped then
+      header = header .. "Cache-Control: max-age=2592000\r\n"
+      header = header .. "Content-Encoding: gzip\r\n"
+   end
    header = header .. "Connection: close\r\n\r\n"
    connection:send(header)
    header = nil
-   coroutine.yield()
 
 end
 

+ 32 - 34
httpserver-request.lua

@@ -32,45 +32,43 @@ local function parseArgs(args)
 end
 
 local function parseFormData(body)
-  local data = {}
-  print("Parsing Form Data")
-  for kv in body.gmatch(body, "%s*&?([^=]+=[^&]+)") do
-    local key, value = string.match(kv, "(.*)=(.*)")
-    
-    print("Parsed: " .. key .. " => " .. value)
-    data[key] = uri_decode(value)
-  end
-  
-  return data
+   local data = {}
+   --print("Parsing Form Data")
+   for kv in body.gmatch(body, "%s*&?([^=]+=[^&]+)") do
+      local key, value = string.match(kv, "(.*)=(.*)")
+      --print("Parsed: " .. key .. " => " .. value)
+      data[key] = uri_decode(value)
+   end
+   return data
 end
 
 local function getRequestData(payload)
-  local requestData
-  return function ()
-    print("Getting Request Data")
-    if requestData then
-      return requestData
-    else
-      local mimeType = string.match(payload, "Content%-Type: ([%w/-]+)")
-      local body_start = payload:find("\r\n\r\n", 1, true)
-      local body = payload:sub(body_start, #payload)
-      payload = nil
-      collectgarbage()
-      
-      -- print("mimeType = [" .. mimeType .. "]")
-      
-      if mimeType == "application/json" then
-        print("JSON: " .. body)
-        requestData = cjson.decode(body)
-      elseif mimeType == "application/x-www-form-urlencoded" then
-        requestData = parseFormData(body)
+   local requestData
+   return function ()
+      --print("Getting Request Data")
+      if requestData then
+         return requestData
       else
-        requestData = {}
+         --print("payload = [" .. payload .. "]")
+         local mimeType = string.match(payload, "Content%-Type: ([%w/-]+)")
+         local bodyStart = payload:find("\r\n\r\n", 1, true)
+         local body = payload:sub(bodyStart, #payload)
+         payload = nil
+         collectgarbage()
+         --print("mimeType = [" .. mimeType .. "]")
+         --print("bodyStart = [" .. bodyStart .. "]")
+         --print("body = [" .. body .. "]")
+         if mimeType == "application/json" then
+            --print("JSON: " .. body)
+            requestData = cjson.decode(body)
+         elseif mimeType == "application/x-www-form-urlencoded" then
+            requestData = parseFormData(body)
+         else
+            requestData = {}
+         end
+         return requestData
       end
-      
-      return requestData
-    end
-  end
+   end
 end
 
 local function parseUri(uri)

+ 2 - 5
httpserver-static.lua

@@ -3,8 +3,8 @@
 -- Author: Marcos Kirsch
 
 return function (connection, req, args)
-   dofile("httpserver-header.lc")(connection, 200, args.ext, args.gzipped)
    --print("Begin sending:", args.file)
+   dofile("httpserver-header.lc")(connection, 200, args.ext, args.isGzipped)
    -- Send file in little chunks
    local continue = true
    local size = file.list()[args.file]
@@ -24,10 +24,7 @@ return function (connection, req, args)
       bytesSent = bytesSent + #chunk
       chunk = nil
       --print("Sent: " .. bytesSent .. " of " .. size)
-      -- nodemcu-firmware disallows queueing send operations,
-      -- so we must call coroutine.yield() after every connection:send()
-      -- The onSent() will resume us. But only yield if we aren't done yet!
-      if bytesSent == size then continue = false else coroutine.yield() end
+      if bytesSent == size then continue = false end
    end
    --print("Finished sending: ", args.file)
 end

+ 51 - 47
httpserver.lua

@@ -13,47 +13,51 @@ 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 function startServing(fileServeFunction, connection, req, args)
+
             local bufferedConnection = {}
             connectionThread = coroutine.create(function(fileServeFunction, bconnection, req, args)
                fileServeFunction(bconnection, req, args)
                if not bconnection:flush() then
-	         connection:close()
-                 connectionThread = nil
- 	       end
+                  connection:close()
+                  connectionThread = nil
+               end
             end)
-            function bufferedConnection:flush() 
-              if self.size > 0 then 
-		connection:send(table.concat(self.data, ""))
-                self.data = {}
-                self.size = 0    
-		return true
-	      end
-	      return false
+
+            function bufferedConnection:flush()
+               if self.size > 0 then
+                  connection:send(table.concat(self.data, ""))
+                  self.data = {}
+                  self.size = 0
+                  return true
+               end
+               return false
             end
-            function bufferedConnection:send(payload) 
-              local l = payload:len()
-              if l + self.size > 1000 then
-                 if self:flush() then
-                   coroutine.yield()          
-		 end
-              end
-	      if l > 800 then
-		connection:send(payload)
-		coroutine.yield()
-	      else
-                table.insert(self.data, payload)
-                self.size = self.size + l
-              end
+
+            function bufferedConnection:send(payload)
+               local l = payload:len()
+               if l + self.size > 1000 then
+                  if self:flush() then
+                     coroutine.yield()
+                  end
+               end
+               if l > 800 then
+                  connection:send(payload)
+                  coroutine.yield()
+               else
+                  table.insert(self.data, payload)
+                  self.size = self.size + l
+               end
             end
+
             bufferedConnection.size = 0
             bufferedConnection.data = {}
             local status, err = coroutine.resume(connectionThread, fileServeFunction, bufferedConnection, req, args)
             if not status then
-               print(err)
+               print("Error: ", err)
             end
          end
 
@@ -62,9 +66,9 @@ return function (port)
             local method = req.method
             local uri = req.uri
             local fileServeFunction = nil
-            
-            print("Method: " .. method);
-            
+
+            --print("Method: " .. method);
+
             if #(uri.file) > 32 then
                -- nodemcu-firmware cannot handle long filenames.
                uri.args = {code = 400, errorString = "Bad Request"}
@@ -72,17 +76,17 @@ return function (port)
             else
                local fileExists = file.open(uri.file, "r")
                file.close()
-            
+
                if not fileExists then
-                 -- gzip check
-                 fileExists = file.open(uri.file .. ".gz", "r")
-                 file.close()
-
-                 if fileExists then
-                    print("gzip variant exists, serving that one")
-                    uri.file = uri.file .. ".gz"
-                    uri.isGzipped = true
-                 end
+                  -- gzip check
+                  fileExists = file.open(uri.file .. ".gz", "r")
+                  file.close()
+
+                  if fileExists then
+                     print("gzip variant exists, serving that one")
+                     uri.file = uri.file .. ".gz"
+                     uri.isGzipped = true
+                  end
                end
 
                if not fileExists then
@@ -92,11 +96,11 @@ return function (port)
                   fileServeFunction = dofile(uri.file)
                else
                   if allowStatic[method] then
-                    uri.args = {file = uri.file, ext = uri.ext, gzipped = uri.isGzipped}
-                    fileServeFunction = dofile("httpserver-static.lc")
+                     uri.args = {file = uri.file, ext = uri.ext, isGzipped = uri.isGzipped}
+                     fileServeFunction = dofile("httpserver-static.lc")
                   else
-                    uri.args = {code = 405, errorString = "Method not supported"}
-                    fileServeFunction = dofile("httpserver-error.lc")
+                     uri.args = {code = 405, errorString = "Method not supported"}
+                     fileServeFunction = dofile("httpserver-error.lc")
                   end
                end
             end
@@ -136,7 +140,7 @@ return function (port)
          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.
                   local status, err = coroutine.resume(connectionThread)
@@ -158,7 +162,7 @@ return function (port)
                connectionThread = nil
                collectgarbage()
             end
-         end) 
+         end)
 
       end
    )