forked from loicortola/nodemcu-espress
-
Notifications
You must be signed in to change notification settings - Fork 0
/
espress.lua
156 lines (145 loc) · 3.99 KB
/
espress.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
------------------------------------------------------------------------------
-- HTTP server module
-- LICENCE: http://opensource.org/licenses/MIT
-- Author: Loic Ortola https://github.com/loicortola
------------------------------------------------------------------------------
-- HTTP status codes as defined in RFC 2616 + common ones along with their message
do
------------------------------------------------------------------------------
-- HTTP parser
------------------------------------------------------------------------------
local httphandler = function(self)
return function(conn)
print("Begin request: " .. node.heap())
local connectionthread
-- Keep reference to callback
local req, res, ondisconnect, onheader, ondata, onreceive
local buf = ""
local parsedlines = 0
local bodylength = 0
ondisconnect = function(conn)
-- Manually set everything to nil to allow gc
req = nil
res = nil
ondisconnect = nil
onheader = nil
ondata = nil
onreceive = nil
buf = nil
parsedlines = nil
bodylength = nil
collectgarbage("collect")
print("Garbage Collector is sweeping " .. node.heap())
end
-- Header parser
onheader = function(k, v)
--print("Adding header " .. k)
if k == "content-length" then
bodylength = tonumber(v)
end
-- Delegate to request object
if req then
req:addheader(k, v)
end
end
-- Body parser
ondata = function(conn, chunk)
-- Prevent MCU from resetting
tmr.wdclr()
if chunk then
req.body = req.body .. chunk
if #req.body >= bodylength then
local f = loadfile(self.handlers.handler)
f()(req, res, self.handlers.next, self.handlers.opts)
f = nil
end
end
end
-- Metadata parser
onreceive = function(conn, chunk)
-- concat chunks in buffer
buf = buf .. chunk
-- Read line from chunk
while #buf > 0 do
local e = buf:find("\r\n", 1, true)
-- Leave if line not done
if not e then break end
local line = buf:sub(1, e - 1)
buf = buf:sub(e + 2)
if parsedlines == 0 then
-- FIRST LINE
local f = loadfile('http_request.lc')
req = f()(conn, line)
f = nil
local f = loadfile('http_response.lc')
res = f()(conn)
f = nil
collectgarbage("collect")
elseif #line > 0 then
-- HEADER LINES
-- Parse header
local _, _, k, v = line:find("^([%w-]+):%s*(.+)")
if k then
-- Valid header
k = k:lower()
onheader(k, v)
end
else
-- BODY
tmr.wdclr()
-- Buffer no longer needed
buf = nil
if bodylength == 0 then
-- Handle request if no body present
local f = loadfile(self.handlers.handler)
f()(req, res, self.handlers.next, self.handlers.opts)
f = nil
collectgarbage("collect")
else
-- Change receive hook to body parser if body present
conn:on("receive", ondata)
ondata(conn, chunk)
onreceive = nil
end
break
end
parsedlines = parsedlines + 1
end
end
conn:on("receive", onreceive)
conn:on("disconnection", ondisconnect)
end
end
------------------------------------------------------------------------------
-- HTTP server
------------------------------------------------------------------------------
local srv
local createserver = function(port)
if srv then srv:close()
end
srv = net.createServer(net.TCP, 5)
local hdlr = {}
hdlr.use = function(self, handler, opts)
if self.handlers == nil then
self.handlers = { handler = handler, opts = opts }
else
local tmp = self.handlers
while not (tmp == nil) do
local next = tmp.next
if next == nil then
self.handlers.next = { handler = handler, opts = opts }
end
tmp = next
end
end
collectgarbage("collect")
end
-- Listen
srv:listen(port, httphandler(hdlr))
print("Server listening on port " .. tostring(port))
return hdlr
end
return {
createserver = createserver
}
end