httpserver-request.lua 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. -- httpserver-request
  2. -- Part of nodemcu-httpserver, parses incoming client requests.
  3. -- Author: Marcos Kirsch
  4. local function validateMethod(method)
  5. local httpMethods = {GET=true, HEAD=true, POST=true, PUT=true, DELETE=true, TRACE=true, OPTIONS=true, CONNECT=true, PATCH=true}
  6. -- default for non-existent attributes returns nil, which evaluates to false
  7. return httpMethods[method]
  8. end
  9. local function uriToFilename(uri)
  10. return "http/" .. string.sub(uri, 2, -1)
  11. end
  12. local function hex_to_char(x)
  13. return string.char(tonumber(x, 16))
  14. end
  15. local function uri_decode(input)
  16. return input:gsub("%+", " "):gsub("%%(%x%x)", hex_to_char)
  17. end
  18. local function parseArgs(args)
  19. local r = {}; i=1
  20. if args == nil or args == "" then return r end
  21. for arg in string.gmatch(args, "([^&]+)") do
  22. local name, value = string.match(arg, "(.*)=(.*)")
  23. if name ~= nil then r[name] = uri_decode(value) end
  24. i = i + 1
  25. end
  26. return r
  27. end
  28. local function parseFormData(body)
  29. local data = {}
  30. print("Parsing Form Data")
  31. for kv in body.gmatch(body, "%s*&?([^=]+=[^&]+)") do
  32. local key, value = string.match(kv, "(.*)=(.*)")
  33. print("Parsed: " .. key .. " => " .. value)
  34. data[key] = uri_decode(value)
  35. end
  36. return data
  37. end
  38. local function getRequestData(payload)
  39. local requestData
  40. return function ()
  41. print("Getting Request Data")
  42. if requestData then
  43. return requestData
  44. else
  45. local mimeType = string.match(payload, "Content%-Type: ([%w/-]+)")
  46. local body_start = payload:find("\r\n\r\n", 1, true)
  47. local body = payload:sub(body_start, #payload)
  48. payload = nil
  49. collectgarbage()
  50. -- print("mimeType = [" .. mimeType .. "]")
  51. if mimeType == "application/json" then
  52. print("JSON: " .. body)
  53. requestData = cjson.decode(body)
  54. elseif mimeType == "application/x-www-form-urlencoded" then
  55. requestData = parseFormData(body)
  56. else
  57. requestData = {}
  58. end
  59. return requestData
  60. end
  61. end
  62. end
  63. local function parseUri(uri)
  64. local r = {}
  65. local filename
  66. local ext
  67. local fullExt = {}
  68. if uri == nil then return r end
  69. if uri == "/" then uri = "/index.html" end
  70. questionMarkPos, b, c, d, e, f = uri:find("?")
  71. if questionMarkPos == nil then
  72. r.file = uri:sub(1, questionMarkPos)
  73. r.args = {}
  74. else
  75. r.file = uri:sub(1, questionMarkPos - 1)
  76. r.args = parseArgs(uri:sub(questionMarkPos+1, #uri))
  77. end
  78. filename = r.file
  79. while filename:match("%.") do
  80. filename,ext = filename:match("(.+)%.(.+)")
  81. table.insert(fullExt,1,ext)
  82. end
  83. if #fullExt > 1 and fullExt[#fullExt] == 'gz' then
  84. r.ext = fullExt[#fullExt-1]
  85. r.isGzipped = true
  86. elseif #fullExt >= 1 then
  87. r.ext = fullExt[#fullExt]
  88. end
  89. r.isScript = r.ext == "lua" or r.ext == "lc"
  90. r.file = uriToFilename(r.file)
  91. return r
  92. end
  93. -- Parses the client's request. Returns a dictionary containing pretty much everything
  94. -- the server needs to know about the uri.
  95. return function (request)
  96. --print("Request: \n", request)
  97. local e = request:find("\r\n", 1, true)
  98. if not e then return nil end
  99. local line = request:sub(1, e - 1)
  100. local r = {}
  101. _, i, r.method, r.request = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
  102. r.methodIsValid = validateMethod(r.method)
  103. r.uri = parseUri(r.request)
  104. r.getRequestData = getRequestData(request)
  105. return r
  106. end