diff --git a/httpserver-request.lua b/httpserver-request.lua new file mode 100644 index 0000000..9562068 --- /dev/null +++ b/httpserver-request.lua @@ -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 diff --git a/httpserver.lua b/httpserver.lua index db050dc..2e16a6f 100644 --- a/httpserver.lua +++ b/httpserver.lua @@ -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) diff --git a/init.lua b/init.lua index a79a6eb..245d36c 100644 --- a/init.lua +++ b/init.lua @@ -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 diff --git a/makefile b/makefile index 73b2ead..011962b 100644 --- a/makefile +++ b/makefile @@ -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: