Refactored server to move request parsing into a separate file. This allows the server to be used in firmware builds with floating point enabled.

This commit is contained in:
Marcos Kirsch 2015-03-22 22:02:38 -05:00
parent dfb2dbecdf
commit 75ed944678
4 changed files with 66 additions and 64 deletions

54
httpserver-request.lua Normal file
View File

@ -0,0 +1,54 @@
-- httpserver-request
-- Part of nodemcu-httpserver, parses incoming client requests.
-- Author: Marcos Kirsch
local function validateMethod(method)
local httpMethods = {GET=true, HEAD=true, POST=true, PUT=true, DELETE=true, TRACE=true, OPTIONS=true, CONNECT=true, PATCH=true}
if httpMethods[method] then return true else return false end
end
local function uriToFilename(uri)
return "http/" .. string.sub(uri, 2, -1)
end
local function parseArgs(args)
local r = {}; i=1
if args == nil or args == "" then return r end
for arg in string.gmatch(args, "([^&]+)") do
local name, value = string.match(arg, "(.*)=(.*)")
if name ~= nil then r[name] = value end
i = i + 1
end
return r
end
local function parseUri(uri)
local r = {}
if uri == nil then return r end
if uri == "/" then uri = "/index.html" end
questionMarkPos, b, c, d, e, f = uri:find("?")
if questionMarkPos == nil then
r.file = uri:sub(1, questionMarkPos)
r.args = {}
else
r.file = uri:sub(1, questionMarkPos - 1)
r.args = parseArgs(uri:sub(questionMarkPos+1, #uri))
end
_, r.ext = r.file:match("(.+)%.(.+)")
r.isScript = r.ext == "lua" or r.ext == "lc"
r.file = uriToFilename(r.file)
return r
end
-- Parses the client's request. Returns a dictionary containing pretty much everything
-- the server needs to know about the uri.
return function (request)
local e = request:find("\r\n", 1, true)
if not e then return nil end
local line = request:sub(1, e - 1)
local r = {}
_, i, r.method, r.request = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
r.methodIsValid = validateMethod(r.method)
r.uri = parseUri(r.request)
return r
end

View File

@ -1,59 +1,6 @@
-- httpserver
-- Author: Marcos Kirsch
module("httpserver", package.seeall)
-- Functions below aren't part of the public API
-- Clients don't need to worry about them.
-- given an HTTP method, returns it or if invalid returns nil
local function validateMethod(method)
local httpMethods = {GET=true, HEAD=true, POST=true, PUT=true, DELETE=true, TRACE=true, OPTIONS=true, CONNECT=true, PATCH=true}
if httpMethods[method] then return method else return nil end
end
local function parseRequest(request)
local e = request:find("\r\n", 1, true)
if not e then return nil end
local line = request:sub(1, e - 1)
local r = {}
_, i, r.method, r.uri = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
return r
end
local function uriToFilename(uri)
return "http/" .. string.sub(uri, 2, -1)
end
local function parseArgs(args)
local r = {}; i=1
if args == nil or args == "" then return r end
for arg in string.gmatch(args, "([^&]+)") do
local name, value = string.match(arg, "(.*)=(.*)")
if name ~= nil then r[name] = value end
i = i + 1
end
return r
end
local function parseUri(uri)
local r = {}
if uri == nil then return r end
if uri == "/" then uri = "/index.html" end
questionMarkPos, b, c, d, e, f = uri:find("?")
if questionMarkPos == nil then
r.file = uri:sub(1, questionMarkPos)
r.args = {}
else
r.file = uri:sub(1, questionMarkPos - 1)
r.args = parseArgs(uri:sub(questionMarkPos+1, #uri))
end
_, r.ext = r.file:match("(.+)%.(.+)")
r.isScript = r.ext == "lua" or r.ext == "lc"
r.file = uriToFilename(r.file)
return r
end
-- Starts web server in the specified port.
return function (port)
@ -68,7 +15,6 @@ return function (port)
local connectionThread
local function onGet(connection, uri)
local uri = parseUri(uri)
local fileServeFunction = nil
if #(uri.file) > 32 then
-- nodemcu-firmware cannot handle long filenames.
@ -95,14 +41,16 @@ return function (port)
end
local function onReceive(connection, payload)
--print(payload) -- for debugging
-- print(payload) -- for debugging
-- parse payload and decide what to serve.
local req = parseRequest(payload)
print("Requested URI: " .. req.uri)
req.method = validateMethod(req.method)
if req.method == "GET" then onGet(connection, req.uri)
elseif req.method == nil then dofile("httpserver-static.lc")(conection, {code=400})
else dofile("httpserver-static.lc")(conection, {code=501}) end
local req = dofile("httpserver-request.lc")(payload)
print("Requested URI: " .. req.request)
if req.methodIsValid then
if req.method == "GET" then onGet(connection, req.uri)
else dofile("httpserver-static.lc")(conection, {code=501}) end
else
dofile("httpserver-static.lc")(conection, {code=400})
end
end
local function onSent(connection, payload)

View File

@ -4,7 +4,7 @@ print('set mode=STATION (mode='..wifi.getmode()..')')
print('MAC: ',wifi.sta.getmac())
print('chip: ',node.chipid())
print('heap: ',node.heap())
wifi.sta.config("Internet","")
wifi.sta.config("Kirsch Extreme","1151511515")
-- Compile server code and remove original .lua files.
-- This only happens the first time afer the .lua files are uploaded.
@ -17,7 +17,7 @@ local compileAndRemoveIfNeeded = function(f)
end
end
local serverFiles = {'httpserver.lua', 'httpserver-static.lua', 'httpserver-error.lua'}
local serverFiles = {'httpserver.lua', 'httpserver-request.lua', 'httpserver-static.lua', 'httpserver-error.lua'}
for i, f in ipairs(serverFiles) do compileAndRemoveIfNeeded(f) end
compileAndRemoveIfNeeded = nil

View File

@ -10,7 +10,7 @@ PORT=/dev/cu.usbserial-A602HRAZ
# End of user config
######################################################################
HTTP_FILES := $(wildcard http/*)
LUA_FILES := init.lua httpserver.lua httpserver-static.lua httpserver-error.lua
LUA_FILES := init.lua httpserver.lua httpserver-request.lua httpserver-static.lua httpserver-error.lua
# Print usage
usage: