http.lua 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. -- Copyright 2008 Steven Barth <steven@midlink.org>
  2. -- Licensed to the public under the Apache License 2.0.
  3. local ltn12 = require "luci.ltn12"
  4. local protocol = require "luci.http.protocol"
  5. local util = require "luci.util"
  6. local string = require "string"
  7. local coroutine = require "coroutine"
  8. local table = require "table"
  9. local ipairs, pairs, next, type, tostring, error =
  10. ipairs, pairs, next, type, tostring, error
  11. module "luci.http"
  12. context = util.threadlocal()
  13. Request = util.class()
  14. function Request.__init__(self, env, sourcein, sinkerr)
  15. self.input = sourcein
  16. self.error = sinkerr
  17. -- File handler nil by default to let .content() work
  18. self.filehandler = nil
  19. -- HTTP-Message table
  20. self.message = {
  21. env = env,
  22. headers = {},
  23. params = protocol.urldecode_params(env.QUERY_STRING or ""),
  24. }
  25. self.parsed_input = false
  26. end
  27. function Request.formvalue(self, name, noparse)
  28. if not noparse and not self.parsed_input then
  29. self:_parse_input()
  30. end
  31. if name then
  32. return self.message.params[name]
  33. else
  34. return self.message.params
  35. end
  36. end
  37. function Request.formvaluetable(self, prefix)
  38. local vals = {}
  39. prefix = prefix and prefix .. "." or "."
  40. if not self.parsed_input then
  41. self:_parse_input()
  42. end
  43. local void = self.message.params[nil]
  44. for k, v in pairs(self.message.params) do
  45. if k:find(prefix, 1, true) == 1 then
  46. vals[k:sub(#prefix + 1)] = tostring(v)
  47. end
  48. end
  49. return vals
  50. end
  51. function Request.content(self)
  52. if not self.parsed_input then
  53. self:_parse_input()
  54. end
  55. return self.message.content, self.message.content_length
  56. end
  57. function Request.getcookie(self, name)
  58. local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
  59. local p = ";" .. name .. "=(.-);"
  60. local i, j, value = c:find(p)
  61. return value and urldecode(value)
  62. end
  63. function Request.getenv(self, name)
  64. if name then
  65. return self.message.env[name]
  66. else
  67. return self.message.env
  68. end
  69. end
  70. function Request.setfilehandler(self, callback)
  71. self.filehandler = callback
  72. -- If input has already been parsed then any files are either in temporary files
  73. -- or are in self.message.params[key]
  74. if self.parsed_input then
  75. for param, value in pairs(self.message.params) do
  76. repeat
  77. -- We're only interested in files
  78. if (not value["file"]) then break end
  79. -- If we were able to write to temporary file
  80. if (value["fd"]) then
  81. fd = value["fd"]
  82. local eof = false
  83. repeat
  84. filedata = fd:read(1024)
  85. if (filedata:len() < 1024) then
  86. eof = true
  87. end
  88. callback({ name=value["name"], file=value["file"] }, filedata, eof)
  89. until (eof)
  90. fd:close()
  91. value["fd"] = nil
  92. -- We had to read into memory
  93. else
  94. -- There should only be one numbered value in table - the data
  95. for k, v in ipairs(value) do
  96. callback({ name=value["name"], file=value["file"] }, v, true)
  97. end
  98. end
  99. until true
  100. end
  101. end
  102. end
  103. function Request._parse_input(self)
  104. protocol.parse_message_body(
  105. self.input,
  106. self.message,
  107. self.filehandler
  108. )
  109. self.parsed_input = true
  110. end
  111. function close()
  112. if not context.eoh then
  113. context.eoh = true
  114. coroutine.yield(3)
  115. end
  116. if not context.closed then
  117. context.closed = true
  118. coroutine.yield(5)
  119. end
  120. end
  121. function content()
  122. return context.request:content()
  123. end
  124. function formvalue(name, noparse)
  125. return context.request:formvalue(name, noparse)
  126. end
  127. function formvaluetable(prefix)
  128. return context.request:formvaluetable(prefix)
  129. end
  130. function getcookie(name)
  131. return context.request:getcookie(name)
  132. end
  133. -- or the environment table itself.
  134. function getenv(name)
  135. return context.request:getenv(name)
  136. end
  137. function setfilehandler(callback)
  138. return context.request:setfilehandler(callback)
  139. end
  140. function header(key, value)
  141. if not context.headers then
  142. context.headers = {}
  143. end
  144. context.headers[key:lower()] = value
  145. coroutine.yield(2, key, value)
  146. end
  147. function prepare_content(mime)
  148. if not context.headers or not context.headers["content-type"] then
  149. if mime == "application/xhtml+xml" then
  150. if not getenv("HTTP_ACCEPT") or
  151. not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
  152. mime = "text/html; charset=UTF-8"
  153. end
  154. header("Vary", "Accept")
  155. end
  156. header("Content-Type", mime)
  157. end
  158. end
  159. function source()
  160. return context.request.input
  161. end
  162. function status(code, message)
  163. code = code or 200
  164. message = message or "OK"
  165. context.status = code
  166. coroutine.yield(1, code, message)
  167. end
  168. -- This function is as a valid LTN12 sink.
  169. -- If the content chunk is nil this function will automatically invoke close.
  170. function write(content, src_err)
  171. if not content then
  172. if src_err then
  173. error(src_err)
  174. else
  175. close()
  176. end
  177. return true
  178. elseif #content == 0 then
  179. return true
  180. else
  181. if not context.eoh then
  182. if not context.status then
  183. status()
  184. end
  185. if not context.headers or not context.headers["content-type"] then
  186. header("Content-Type", "text/html; charset=utf-8")
  187. end
  188. if not context.headers["cache-control"] then
  189. header("Cache-Control", "no-cache")
  190. header("Expires", "0")
  191. end
  192. if not context.headers["x-frame-options"] then
  193. header("X-Frame-Options", "SAMEORIGIN")
  194. end
  195. if not context.headers["x-xss-protection"] then
  196. header("X-XSS-Protection", "1; mode=block")
  197. end
  198. if not context.headers["x-content-type-options"] then
  199. header("X-Content-Type-Options", "nosniff")
  200. end
  201. context.eoh = true
  202. coroutine.yield(3)
  203. end
  204. coroutine.yield(4, content)
  205. return true
  206. end
  207. end
  208. function splice(fd, size)
  209. coroutine.yield(6, fd, size)
  210. end
  211. function redirect(url)
  212. if url == "" then url = "/" end
  213. status(302, "Found")
  214. header("Location", url)
  215. close()
  216. end
  217. function build_querystring(q)
  218. local s = { "?" }
  219. for k, v in pairs(q) do
  220. if #s > 1 then s[#s+1] = "&" end
  221. s[#s+1] = urldecode(k)
  222. s[#s+1] = "="
  223. s[#s+1] = urldecode(v)
  224. end
  225. return table.concat(s, "")
  226. end
  227. urldecode = protocol.urldecode
  228. urlencode = protocol.urlencode
  229. function write_json(x)
  230. util.serialize_json(x, write)
  231. end