httpserver-request.lua 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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 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 isCheckModifiedRequest(payload)
  39. return function ()
  40. local checkResult = (string.find(payload, 'If%-Modified%-Since: ') ~= nil)
  41. return checkResult
  42. end
  43. end
  44. local function getRequestData(payload)
  45. local requestData
  46. return function ()
  47. --print("Getting Request Data")
  48. if requestData then
  49. return requestData
  50. else
  51. --print("payload = [" .. payload .. "]")
  52. local mimeType = string.match(payload, "Content%-Type: ([%w/-]+)")
  53. local bodyStart = payload:find("\r\n\r\n", 1, true)
  54. local body = payload:sub(bodyStart, #payload)
  55. payload = nil
  56. collectgarbage()
  57. --print("mimeType = [" .. mimeType .. "]")
  58. --print("bodyStart = [" .. bodyStart .. "]")
  59. --print("body = [" .. body .. "]")
  60. if mimeType == "application/json" then
  61. --print("JSON: " .. body)
  62. requestData = cjson.decode(body)
  63. elseif mimeType == "application/x-www-form-urlencoded" then
  64. requestData = parseFormData(body)
  65. else
  66. requestData = {}
  67. end
  68. return requestData
  69. end
  70. end
  71. end
  72. local function parseUri(uri)
  73. local r = {}
  74. local filename
  75. local ext
  76. local fullExt = {}
  77. if uri == nil then return r end
  78. if uri == "/" then uri = "/index.html" end
  79. questionMarkPos, b, c, d, e, f = uri:find("?")
  80. if questionMarkPos == nil then
  81. r.file = uri:sub(1, questionMarkPos)
  82. r.args = {}
  83. else
  84. r.file = uri:sub(1, questionMarkPos - 1)
  85. r.args = parseArgs(uri:sub(questionMarkPos+1, #uri))
  86. end
  87. filename = r.file
  88. while filename:match("%.") do
  89. filename,ext = filename:match("(.+)%.(.+)")
  90. table.insert(fullExt,1,ext)
  91. end
  92. if #fullExt > 1 and fullExt[#fullExt] == 'gz' then
  93. r.ext = fullExt[#fullExt-1]
  94. r.isGzipped = true
  95. elseif #fullExt >= 1 then
  96. r.ext = fullExt[#fullExt]
  97. end
  98. r.isScript = r.ext == "lua" or r.ext == "lc"
  99. r.file = uriToFilename(r.file)
  100. return r
  101. end
  102. -- Parses the client's request. Returns a dictionary containing pretty much everything
  103. -- the server needs to know about the uri.
  104. return function (request)
  105. --print("Request: \n", request)
  106. local e = request:find("\r\n", 1, true)
  107. if not e then return nil end
  108. local line = request:sub(1, e - 1)
  109. local r = {}
  110. _, i, r.method, r.request = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
  111. r.methodIsValid = validateMethod(r.method)
  112. r.uri = parseUri(r.request)
  113. r.isCheckModifiedRequest = isCheckModifiedRequest(request)
  114. r.getRequestData = getRequestData(request)
  115. return r
  116. end