httpserver-request.lua 3.9 KB

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