httpserver.lua 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. ------------------------------------------------------------------------------
  2. -- HTTP server module
  3. --
  4. -- LICENCE: http://opensource.org/licenses/MIT
  5. -- Vladimir Dronnikov <dronnikov@gmail.com>
  6. ------------------------------------------------------------------------------
  7. local collectgarbage, tonumber, tostring = collectgarbage, tonumber, tostring
  8. local http
  9. do
  10. ------------------------------------------------------------------------------
  11. -- request methods
  12. ------------------------------------------------------------------------------
  13. local make_req = function(conn, method, url)
  14. local req = {
  15. conn = conn,
  16. method = method,
  17. url = url,
  18. }
  19. -- return setmetatable(req, {
  20. -- })
  21. return req
  22. end
  23. ------------------------------------------------------------------------------
  24. -- response methods
  25. ------------------------------------------------------------------------------
  26. local send = function(self, data, status)
  27. local c = self.conn
  28. -- TODO: req.send should take care of response headers!
  29. if self.send_header then
  30. c:send("HTTP/1.1 ")
  31. c:send(tostring(status or 200))
  32. -- TODO: real HTTP status code/name table
  33. c:send(" OK\r\n")
  34. -- we use chunked transfer encoding, to not deal with Content-Length:
  35. -- response header
  36. self:send_header("Transfer-Encoding", "chunked")
  37. -- TODO: send standard response headers, such as Server:, Date:
  38. end
  39. if data then
  40. -- NB: no headers allowed after response body started
  41. if self.send_header then
  42. self.send_header = nil
  43. -- end response headers
  44. c:send("\r\n")
  45. end
  46. -- chunked transfer encoding
  47. c:send(("%X\r\n"):format(#data))
  48. c:send(data)
  49. c:send("\r\n")
  50. end
  51. end
  52. local send_header = function(self, name, value)
  53. local c = self.conn
  54. -- NB: quite a naive implementation
  55. c:send(name)
  56. c:send(": ")
  57. c:send(value)
  58. c:send("\r\n")
  59. end
  60. -- finalize request, optionally sending data
  61. local finish = function(self, data, status)
  62. local c = self.conn
  63. -- NB: req.send takes care of response headers
  64. if data then
  65. self:send(data, status)
  66. end
  67. -- finalize chunked transfer encoding
  68. c:send("0\r\n\r\n")
  69. -- close connection
  70. c:close()
  71. end
  72. --
  73. local make_res = function(conn)
  74. local res = {
  75. conn = conn,
  76. }
  77. -- return setmetatable(res, {
  78. -- send_header = send_header,
  79. -- send = send,
  80. -- finish = finish,
  81. -- })
  82. res.send_header = send_header
  83. res.send = send
  84. res.finish = finish
  85. return res
  86. end
  87. ------------------------------------------------------------------------------
  88. -- HTTP parser
  89. ------------------------------------------------------------------------------
  90. local http_handler = function(handler)
  91. return function(conn)
  92. local req, res
  93. local buf = ""
  94. local method, url
  95. local ondisconnect = function(conn)
  96. collectgarbage("collect")
  97. end
  98. -- header parser
  99. local cnt_len = 0
  100. local onheader = function(conn, k, v)
  101. -- TODO: look for Content-Type: header
  102. -- to help parse body
  103. -- parse content length to know body length
  104. if k == "content-length" then
  105. cnt_len = tonumber(v)
  106. end
  107. if k == "expect" and v == "100-continue" then
  108. conn:send("HTTP/1.1 100 Continue\r\n")
  109. end
  110. -- delegate to request object
  111. if req and req.onheader then
  112. req:onheader(k, v)
  113. end
  114. end
  115. -- body data handler
  116. local body_len = 0
  117. local ondata = function(conn, chunk)
  118. -- NB: do not reset node in case of lengthy requests
  119. tmr.wdclr()
  120. -- feed request data to request handler
  121. if not req or not req.ondata then return end
  122. req:ondata(chunk)
  123. -- NB: once length of seen chunks equals Content-Length:
  124. -- onend(conn) is called
  125. body_len = body_len + #chunk
  126. -- print("-B", #chunk, body_len, cnt_len, node.heap())
  127. if body_len >= cnt_len then
  128. req:ondata()
  129. end
  130. end
  131. local onreceive = function(conn, chunk)
  132. -- merge chunks in buffer
  133. if buf then
  134. buf = buf .. chunk
  135. else
  136. buf = chunk
  137. end
  138. -- consume buffer line by line
  139. while #buf > 0 do
  140. -- extract line
  141. local e = buf:find("\r\n", 1, true)
  142. if not e then break end
  143. local line = buf:sub(1, e - 1)
  144. buf = buf:sub(e + 2)
  145. -- method, url?
  146. if not method then
  147. local i
  148. -- NB: just version 1.1 assumed
  149. _, i, method, url = line:find("^([A-Z]+) (.-) HTTP/1.1$")
  150. if method then
  151. -- make request and response objects
  152. req = make_req(conn, method, url)
  153. res = make_res(conn)
  154. end
  155. -- header line?
  156. elseif #line > 0 then
  157. -- parse header
  158. local _, _, k, v = line:find("^([%w-]+):%s*(.+)")
  159. -- header seems ok?
  160. if k then
  161. k = k:lower()
  162. onheader(conn, k, v)
  163. end
  164. -- headers end
  165. else
  166. -- spawn request handler
  167. -- NB: do not reset in case of lengthy requests
  168. tmr.wdclr()
  169. handler(req, res)
  170. tmr.wdclr()
  171. -- NB: we feed the rest of the buffer as starting chunk of body
  172. ondata(conn, buf)
  173. -- buffer no longer needed
  174. buf = nil
  175. -- NB: we explicitly reassign receive handler so that
  176. -- next received chunks go directly to body handler
  177. conn:on("receive", ondata)
  178. -- parser done
  179. break
  180. end
  181. end
  182. end
  183. conn:on("receive", onreceive)
  184. conn:on("disconnection", ondisconnect)
  185. end
  186. end
  187. ------------------------------------------------------------------------------
  188. -- HTTP server
  189. ------------------------------------------------------------------------------
  190. local srv
  191. local createServer = function(port, handler)
  192. -- NB: only one server at a time
  193. if srv then srv:close() end
  194. srv = net.createServer(net.TCP, 15)
  195. -- listen
  196. srv:listen(port, http_handler(handler))
  197. return srv
  198. end
  199. ------------------------------------------------------------------------------
  200. -- HTTP server methods
  201. ------------------------------------------------------------------------------
  202. http = {
  203. createServer = createServer,
  204. }
  205. end
  206. return http