httpserver.lua 6.4 KB

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